buteo-syncfw-0.7.21+16.04.20151216.1/0000755000015600001650000000000012634333235016733 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/doc/0000755000015600001650000000000012634333235017500 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/doc/src/0000755000015600001650000000000012634333235020267 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/doc/src/Buteo_SyncFW_-_Component_View.jpg0000644000015600001650000044051712634332753026547 0ustar pbuserpbgroup00000000000000JFIFHHCC4" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?:|t_ |G⏋a0ᥦ}|4gP>,GW;GӼwW^+KٯSO'?8H?7>|ioWw!?fF~3|a;}gBm|8_<^R_ƭ[ #Í?7,+<9ueE? W7#ޣW7#ޯʏ_ 4/ |_lo>ӼAgK)|OxU]J=vCg|? 4k?k?"[O~ɿuٓᾝZ?k߈~/eo>>o|83!dχZE3GšڀB?ҿ?GFҿ?GF~h7 <|<cςßx:F};~h!YwV7ß ~|M 5s>xW~?|QķNOk _;>512e}_'|6 `Þc)}^X?p~3>k-?`?~3>k-?``=K$w=hZW |x:^5_ t/ :O,<5O6> >;|1*Ϸ^ 7~ M? $g|E3>4|qhYO|?<''O6qᗁ |oo~=xs/ٳ?_ x-(̗6>:,?*A-'k4oG{ѽG4oG{ѽ_Z+ď h_>~П/C7CˉgS]"O#^ωxt(-ooOt5|T{5s~/ jzG~oV_a|Ixbt Mfۯᖉs_~$_5xQ_4<B|)/ i:6]RWPxo_ѼCm|6kN-(ɸIiFRVm>hujWLIw_FRWz;44oG{ѽG4oG{ѽ_>ntowUo߲ωU&|"|C I{"=΁?/sn,l9]_CMyS 3᭷oφ|Ph|G?u߃:K o?#|7>𯌼_{᮫#?M~3>k-?`?~3>k-?`;P/į-WUu8~Q~"7tؓAaه¿W|u 'u߈2xW u-CY#ܿW%GO>|~|iWoo<9࿋㏃u_/,h~bğ~ZxNWߊ>τҴ[ OԼ7x_ӵ +^ioQ +^ioW/k{Cǟo+Gm/|RQ5_WOh cOڋG>!:k ~Ӟ+?Y-O4iƩCU gFv_όZ_ x;Ğ5/-#OCzxMHOL~|&r鶚߈/5o^%<0!Y } +^ioQ +^ioW ?9oO 4/OW@ zjc>O Ǐ}Wlnf \x'A/xZ|Ӿ7~i)~0~ܚ?fo?ߊ|7k/ c}VO~>x7GÍ u/ +/j|uu 6_/-oi_ O#zi_ O#z;u~b_5_h~' ?e hSx]SNVb%,k_喍y OE/l McvX|ow?xchSV?O➃?/WK?w[o^񷉴Vo |9>1Q=ҿ?GFҿ?GFyWKWpOAZ?٫gm ;㷁t _ƯB~Z|UCJ׎C7/çHT6jڿ /7ž<_ σ|m_/HhRAiڿ?7A7~ íӟ`a|dωt ?IxK &ҿ?GFҿ?GF~{||J| ?c3~&oMvEix _?}-z_Z|'gm~)ocz> AwWj>|P"'u/eA~^ >?i~8|f)9O:f>Gx/vz¯j;4xÞ!Q} +^ioQ +^ioWV_Q_Ŀ"|7jY_oxw߄5oß ?׊|yϋ?oo]fO[O_FM:ߋ|/#zS_?do~2]+J[sS Gj f?巄:]Ÿx;K׵~+ҬtK~Jf|׿Z~Jf|׿Z~ lx_?%<=|??k=W|&~~_쓫7KKcׅ|1Ο?0Ⱥ?_qφ?gk$/_9.m3?_B8kXϊR隔& ^~{sҿ?GFҿ?GF~y|Rv?#?//tJM6crt?| @xUV?MwY׼!Ꮜ1m}/ᇤ{ }%>/|}$Z%?^xEm'$[;m~0_>h/^$GLV_i>H?7H?7cU#hL(_^)пfW_ßk:=;_,e+Ow߇]m8x W6|UQѼUya}WAa}$mGo|C;t};yy 75y>,W0}Wұ>}7Tѯ5P~3>k-?`f/>]7@/5 -t}#þ#fs 5>4 GI|Ijlj5'Itۙ.ob&%d=o?h?DoJRc'(OE)G&n*prKUJ읛I'kͧg4oG{ѽG4oG{ѽ]5GhOD_ _?kr<{J/惞 Jf|׿Z~Jf|׿Z~T|.E?S/=<=BOVj/i} +^ioQ +^ioW? W⏅mBH?7H?7(/i_ O#zi_ O#z>BH?7H?7(/i_ O#zi_ O#z>BH?7H?7xBH?7H?7(/i_ O#zi_ O#z>BH?7H?7(/i_ O#zi_ O#z>BH?7H?7$V_k w!UW9a_x&^_<9Oxw Kq%?y5O~ߵ9Z$9y/W($n*qm-R[JJ2&M;'{7g W7#ޣW7#ޯhQ4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~~'|,K=cO߇Zk:3G-tCVӭm;VSu;(oRxu ;=B|Ʉ~?h_W״+% g{O`z@?kOᾭKZE_)F>ן|3__Q$gVԴ++->ߊ?Kw< ~z#xOҴRm>$|h?h/|=KgsS2|uCGRix7Zl:9/~3/~3{QWI|=|]sc'$2=<i=.!c DŽ:x {7_͏C x ~˿|6|gjY>?x(k>Կ~"xSyx#)'Ώ{_|=WZ7!C G!C @8?F's?S/_wƯ㏈/x?[Ҽ=Ïx? ;ۿ iz΃og1MF-, ~|K)KB2[?zƉ > xsR🋴/G4jfaiP\_r!o5r!o5rWs@<)G—~_/ C(_|)%~A~.x㿂mxNk_F巀vxqi(է?߰׆n>kw)_x|k~'Z |\POGij3Qρ4/_d2oF/~3/~3Ƽ]Oj|D1yoo^f3_#Y<>|uzwşŲ|2It9|_jO|7+I6b_|+DW7aW~MsJ*.x 7k~<߇4OԼ] n{_?`{_?`k.k0|Y> wŞ/IH!دľcw8Oi{y9_Կ!C G!C ZJ2knٮXrY;/K Q;&w};'W/|MҼ3ZT?|g@Z+KZ?Ɵ@IOO;ž#_w~ό5[a4}Qa-?O<>[:tIiߴ71a|9%/gQ >7=q}_eOx\Bo9_ 7LBo9_ 7L(hdw W7#5|FI5U||_ ~&VS_[ލOh߈tkڭ{~˖>%3}{Ih4G[ i7óx{H"[7סou o zLZqr!o5r!o5Ox@%KMG]|7Lj7u^?{+~l𞩪:Ѿ MaWU[xoS9/ٻ[/ xDǏ|~VxúBc@E;~п>>,'xO XǨz4Ytr!o5r!o5s(nƞ1 uږH>.x]~,|h~;|iW_ |D Up~|Zڮi҇5ςEӼ|c _\ռW@|Lڍ̺]?>"_H=HZmv3IBo9_ 7LBo9_ 7L4eOg|Kڗf >N-vmmU~%ƕkzxXy'UB/~3/~3)fmw:5'Kcxk||׀5|9T>/~xPּI6mo÷Z\)*_\S Ѵ?|;]cn[n7oB0 wχߌiEE#WB!WB!WxWzŸ?b:sO'o=W_u}7&z c)j66T_=~_Hz->^ _wο g߈8)OGfGzo~<5oT~?%泬xK}}_G+!fQ_G+!fPr? x῁uKxOB/?8~?/j^>xU-g?$j+񖓥Ji^Q|M:M{ 5 W߄<_OckI|RKt[/Ŷ>uk{_?`{_?`o'߆> h|<|g߂Pxo xs3>;-ǖ&׏dŞ/f l/tɭxs ~F'|{K(^R$5[wzyon{AԴ{BMKOl/ WB!WB!9o>#ÿ <{7öj~"|IGxBl<1Z.g>#t-|eX}S{)`O6$u>/|a~*|E3x#~?>!ⷎo|AmwOxƞ ]No^e̓{_?`{_?` ~|?ߌ:mm3ž,<{i;eq\x>u ]&8=-IMKxm6OƯm?rS~ۿ~,h3'_o ;>xɬ|lkg?m?Z'{M> Buj!C O/Pe࿍>$]V5_/ }F>60iįiװQxV"o EѠGրsxJHxK]IԴ|C}xFL:ޝ{ug'(2sW:o=FH3HhSe.9x?tHF'+ԣWGlEm̽?~fx#7-k_S4/&uuq~)@uqg&YMGңb:C<O蟯_JKgyKΧe'JjO%v}#E| /''AG/?hOwER`ٞN3\}Y}/?hOwE8|qy/AVa^نT3|s?[VgѴW_~#ѡ?RɿFDK&PU/b5_ %_[F_:#B(h# —M衫PL/(95+_߆'1[fx39Юs{wRu'4[k]F7eVN>JtOS<'N:iC)Jt(Y;J2M2RN2NI5OUiŠ((Lݾ65z tk4ڇg_W8>3G'2~_u ' {)!Y7ֱ d6]\s{T*SwBܔTXemgo(t|GH?&G~'D~aC"ū~*Isxlo?zF} '>>jS {:_SտW&uX|5#OTLe%koIě>>4|Hߴ/2?U/~]W][ xcY-#]A7ܨج,04e<7ᧈeOzK6U\yqץͶOu ɵ8/g=x “O蟥“N蠯X 5ɟu?9/_G_8FDI'PRIh)?d W roŇ3uEoV}E| ?/AG/hOoEZt^ܱfkBoV}E|>7H=?d/' _7_|cmc_)_|/ ￴^<+&ďsm~R/m9 /3+a*qRלbs:tkԟ/<lM6v\E6vߡQEfYWǿdo8(~2~_;Ժ֥ʺƑ/PuW·>׵O~˿`x7_~$ӷYGeA,.'ɤ~М)_nC-rj`a(5FgrЫ({#NMJ*nPfa骍)UAoI'gk4} E| ?G '7G.i'D| ,ݳ ,D~k௙%H=]??hO)?e?|h/2?Rqz~+y3yyx-o'>_MGzĵ)kzψx Y뺽%?kuOR?!|,}WWo,𖯠_:t7{-v+is*/?O|\YžWп~sxYd.kKO}1öuS̍RWoC֣rzgV;99aXף$YÈ5^hӮ׳Jk}_yx ?dO' O?~_4O' O;bp/l&~TpoNY}/?hOwE"Ѣ~?R~Je83uEoV}E|>9|F=?d?' O;4?' _7Oo67+64?' _7|pNDK&PU%I_i?:Z& }E| G/CG.h_OoE Z'/~^,HsktW^/S/ >,h+ n6XoŦ;Z]^y~Mq7մNʔ9N1+ǚ qrRA&Vf\ɤRqzEVE!>?b4/٫Tς &١~_|_^_o'??mB>|ioW״QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQ$p/Z>1i5"o|S>Niİ5W v ~&76)tIl$i>$i|6Ot) ߃f/?OQ7?j_}K~?5Y7<uĶ 6xķz-x*%,W QAB*2I`$_Rt:mҏ)bi)*nXj1q©]=6ir'F򻼴>9:|"KE:dS&QΞ2٦2U4O~-|KMc}/ @-?QdžZRXhmfO_C Eoqas$OS6:K&^>*~? >|ÄSZnCh4E^'k = ࿄ߵď_? e HѾ,\g|Y5[?~/\ZI\B񍾫ó(ޱxr\ҼRNg?qԭ;aa+ͨRSCK*pQFN12m(洧淏-ܣwQ !]ο{]u/%5_eco?kW?ogo:iZ|7Ѯ9u6ugQJK-aja|O߲]ihS| ~<=g|-~)x"X>$b>  i>t ƞ  gך75-7o?TyO-OaR ? 6ׅ߳k3Vg7ߍl~#sx>t'(7Lx+敩_e.3/4]s ZVh ڕJ^W%Ysf*JkjgeΫ4^Wc B~=6|W _7>-2'??[ '>$I$>to/jx<;xOǞOă_னx~2q{-#t{[KJk {ojO??[*UF(}?^: k|n]fԷY>i ?žᖟ2š_鶾R˪ʬ*S*qnXuH]*jQ0*&(.Dw ߚzt:ॿ[ K|_Z?_͡>JxkK{gn_x2W gpڞ ε4:'a|uw lh'Ÿ'o~"1=m#O :ď~'⾿?ZѼ[a},mW ]_WoW G?)Yr u ',_?~,ț w;UAwmm= HK;Y_GWdž/x^:|e4oH׵Qv"2i箂ª'R*dGfWrm)+=toߛbW\~~Ѿ+Ծ)~M{wæ֗ ~'z}t5ZP)7>dB<5{Q޹C[^'}_RR߈e IhS^/tm\մoqp?O+/OG]K/?X f?|J&㕖mS~#ۧ X4o⏉^I{a˽Nyx0}oY])h~_/_\Ÿ+`zaOKՕ0W=}DUyrZag,]V~iQPY}&Pٻ|lkjM3v5qpϖ+?id#S޼؟*T1FO5~]B]Zhv:Y  K &+j f?+/>[xn)?4 mM6x-++RPxX~ |C1x =஋6j-s|N |rO9Yj u>i3 Ɠw±6z_KqRqs´PҝӤOeR+N_ͣՔNIު>~cn)7RO _Db~|'?h7t=wy'WsIxjzmaycK/x9j:~$E ꚧ?bLl٫ cc|~+g_&5u,G|@1h^)լwf^&Ҵ-35熴mGTu;j~?,ׁ4هua|xf=>J>!~Οt?A`ugG <6doπh WS^_Io?K_^>uoGW7_Z6Ú-<;=3Ǻt\EYˆѣiPR:PX7U^i^RH)F/ sڻmMw? |7 oAŤxt??b78EoTm4/Ư#^ +ZZ6W+}~W?aX>(h'O-AH]]$0o ?/oȕ 7U~<ut/|P|/R;Dfx?>^>0&Ov^g[5 Ɵe.w~xs%|Z_'!_?f#G$}X~j'O fonC<ZoХ*,(mEpqzx%sUW22W]4]~'* sקBןN7wwp?ٻ;KjP< f/krHruWw+˯C(P濏C??daDYCl%E?^_d\;Eyg|z_Yڃc+'Q|uO6i=߲ޡ}\|bm5?x~t*?q4]yO-o?5KWIc]o~4I5_~6K)?Soo5e%dž h[oa|ҥT 5)B%Rie:W[^WM-կwm[[K~޾1Ep? B_| ÏCv5OzW>ckֿVCFi/EdW4WkO~_~^ #U^ #L|)?Y^|\i`tCymo 4hW=?~Jg^x |$~4? >~^|/){ϊX[x|F|G:wT.mOO{_ Q7'uo 8|mu?]#o-S1X?x:o.|KRo|DNoKO MDŽW. ȱXAW)V0t>TJTp(/gϏJWJ"*R^v_YBO~g̹i&wtkgŻ?@_E?c_xß>k -asŽMGhҼOXY?⇇87[޻&amh t_5|uO'׈s[=FݤߴO4<υ?/gzv5~7K'Þ"5:60h<'g_?kj|!|x]73i+gkyc/4ߊ>(-|?|W^GZW d+/~[š>3VP9]+Dx3(o5_^ythNOן/4M\qRMN -)J|ww/Bmj'MT<!$1mjO xx'Y=?Sk iֿXZϏi_Ggۿi?~xƻ-x߇u_hZMZOG׿o 7߱ƫ ywX:-|#n4=gB'U|4 _ -­{{'O ~>4߄?a/?~|xwo GW _}$|[mo^ Z᫔m +xྭx/d_"o JXtr "P;q?ftNpʕNeCR9t䔡fkǜNnNֺkoϯ]/j>xC>X/ڻׅ:x_.~u/C:ft+êFe?㟅W,o'wiKILMΛ.Lx@|$Gi}_M$~Kx_+ÿ߄n~fе;WmcőwV/>!^/ԾOK/5*X –''-XܩO `Ihj_%{ԴyyUjSwR$nͫ1{*;Ӯzh~9~?Q(Ŀ?m։(oI5oqMM|>#-:ngyجtoO Q-~@|Ha€?% ⧈?Wm#S|O~߰/۫ jS-!?ۿ?᩾h rG_|e ?z_ ?  !/&k!x;'_>*_/xPl^B~xkBF^/(}J#&ΣPfNyM炂%ZKimj%Ꮰpe?_VWO㎯ZgVC⟌t_7ټS/+w( oɬZ\x^뺟?o­(1-~&꿵 kq_Z/:|ǎǾ3u}4oPnj|KjA_|R~C}i~oAG/WY1#xwa?jj]'Cğ ku/x>?tMNĝkTտNd!292t<h"??&/U?" PG=?e($b4o+ay⚄4Teݿ~~ſaM[|7¿zFxjh>*ӧuEij6 5ӵmWKOP涼* s ö?aOW-]f'$~0A"o%;FWC+WٶVyۇ<q{՞;b>wj7|MUc7_7wz^ ɿ|57~_n?nelK޵GNUOe!>?b4/٫Tς &١~_|_^_o'??mB>|ioW״QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQ$p/Z (قؿkOڣIw?d_k|W__ ~%|O%Ğ&M'DZjbZ'#޶,|A!^z~_iaG~$D>_!5mV4>O\u=bj ٧>c_m52_CǟxSK7xKτt߆!4O'ksiC>)+sY? -=FI/AkQx|nW[ S է9”cJgFN<_,%OiԠ'9qE%)I=v{N{/~m#O q?qxu/fٿ.| E$Y߶Wo]wo#z]oOkJ ~_{ _W  _4g]e<_7.,]/Vnn=xX*԰a/ӓS9} ;c(օ^Y9+ѕ6Ҕ9 %&zii>WSu~ҟi?Mj߱į522 Y躾OwZEׂ>#xx}LjᲶԾ.xro G|'?xKT_c? 8~>6_? ~[O xŚ3{7¿͝/vż? ?Q.?O㗋eO6W^(>#WaZ[LҼe?]>NGvc}/^*s[[j'o:&-wgp+MSR5i-9n*ֳӕNoVWG7~ܿ|=a_C]+nj i:Gg?|So^WG|Rϋ6^ ӯ |G+\˫dM??wQg{oSoo x'<iEj _~պuk~$l x/I?ho6+?ΉOֿ?J; |m2[~mf{Ɵ}z7'x7mCf6>"'/~'{i/Ej |m?ٖG8_x;ž[ w% 2-աΕ7U 'R3){Q䌥&)InrNvqWne͵WM-oWFi\uG*I/_| M[!._ڏOQxo_/ ^T_?f/_:][?C|e_7k|"7)?ߌZO~49u/ h*{xwI,lsoᘮ/^_'O_^B[xⶋG5|$B/oL|O_ŏ7/c?g?=?ǯl?[+}B E:? cM? YCOߗ \yeh%<}R/~֚,?(s[VLzᬟWk?i.TRjkKTikߗk-?k/L??/Y_W5~n~׿v~%+arP??s_QEdh|_ɦ~?n?Z{Lݾ65{" toc̈́,V}\{b}Q_Ч~@*u鈽?Jt ({Ч~01E{)wߢ2.~~dw>55F(SO?D@W(wwյ=_~*x ջx8j;7C)t_IEWPϛ><A|GOKO~xBF]${Ϗ? xKº 5_R~<5ZxxFƖ5woǟ(?/O?i?eo~d|& ??<-;;Şuo?koզ^Y~K"8[Z.tVZ'9ңJ*8ICQ\>h5ۂ\1i;7ZI;Wy~:Q~+<ڧ/o?5"<-jx'|jk537)qYUuCW֯us]&$lo '"ȟ=5~/W~ NΚģo/OhiL/}]R88l/[߱ǯ:'gv7!>~'|u{'ᦷ/<='3^M[DŽf|%|R}=g9|*D~Ҿ?ƿڿ'/M SQ^¿5j>"o4moWe0Pc*`ԄJcYVNN^5r*xXxTBwQ%{mV[yM4~EӚ&Or~4G??jO |՝+qC{=<ׅ'5W+|MkxJ]>kgx]?ɟe.T_oRkWoZ'ů\:eRm3#/u {χ^ ]B|=/muf?dǁuO|WP5?<g_GT/O1}SV(#ONxOKemim[$^# q|m<3o|woK⾯>9/x+׍.7m'HI}wsh7Mռsh҅ KK FXZk1#u⛕LM9TR)$Vf2I86]/ʿYώ_'ρji*~$׌gOž);kG/GSϏ&|Yς V|BRj  Ob~Ÿ?O<]K?ࣖQ>)5;?x I?k:-"u-gJNv }5ե#?ٿvAw"kA9h>;h-eyTZw3ugßOO*`hd5U?du Ƣ?c?{ɃUUl]J*t uiӓU\n2U9%Bc8y-TSzu%u#>kiڇg? >S߶g%W;_W ~V<ǖZ k?隧k ?Pu[x F]WJXD^E TJ>;= ڋgڧc¿ߋNxşY'~^xwV8|{Ǟ7ψG|S_sĺuxtZv W^x?]ЯZkԹK !+sl/>Yo|a?.Ox[A|&DqWUkO '4M $PCoO 7Ítx6mhzon/帼5յ+[[/^S䏇_I'Oi?寄1|$|1 z6|bO |=qJlj/ZI<o]׼KAj+aK/e%UxU*jM$NQ\W.Z.Ӌxcm_ Ck_,cNj>(~ j_Ǐi"Ŷ߀~>Ɯ u- 6_~XۗO?$gr?Ɵ~|:k3zUyc_iԾ ^;DY_//&A#=?4 L#_xJ/~`ⶡoO _|AON8-Y|3}$kğ~&W_dt|)xS\u v-GE4 UjO j_Í㎱'aKi"M | 7 [ _co|-u=c[w j-CV'Ou]OX?=BYec7_7wz)$m8Xj(4>B|Ʉ~?h_W׵L#!BL.i_,O;~|Jf|X#ޯh(((((((((((((((((ڣIw?d_kՃ[὏ŏxª!7L6>Nx[]Z,k%WuG?k{Noß/_fi>;+8\G,6;.¼Ƥ1FSU*o ūњ<$Ҳiҍoc)N=.>Qy?u~z‰iN|"`O3NsC|? (o_)\t/?B2sQ?P;; τ_ _4p?k; _ O16~RͺmROiPx 3 Gw7~QP;; _ OqpLK5ۯZ_G#X ^x aXi_Qh$+;qG(o?>+ ֟frwĽv ,3=ßO^lL:>O5.EOMc:i*g['ҝ7_5$綟 ",_<ͻ6%Nw>k][]~tŽiM|!_~׺w_붟 Ն[r2˚}Z~-~q‘M| ^KNkBwS-|ey_`Ϛ}Z~Ԩ;>W)(w)?(ӿ?н]C|~ Ge%"Ud[h~׿v~&1?׾.|&㎃.?ПJz3Nok(x'ශPu6o涀 Wӊࡆ (:rE)b1i siFSnI'i$m%G^W%^o@+Lݾ65{Hψ~(|CGONoC|/6>RͿ\y#WJ?y$=(QP;; _ ŒM"_\Oy/$es[ˏ 2@ǥ~QXoߊ~$K/6xSMoQ<3 X?ԼUjO/ mS`#\+d/M~3M|To B~*jt|f9ω VէWZx?b4/٫Tς &١~_|_^_o'??mB>|ioW״QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE?H⾇X xǀ[Woy7ִ S£Q)# *- CcaiZ[\^%uÞDŽRxgEOh -k?ţ~ @@-mf?^o|=ok]Wۓ m*x<]?~ּO}id^|@c<m^}GTLA%4;κ ( ( ( ( ( (>B|Ʉ~?h_W׵L#!BL.i_,O;~|Jf|X#ޯh(((((((((?} t%? h+/KP0i#ҭ5(/-$]a0dmUa{_iV77n_z׍𯊡g+|;~_ F7O%uЫSn~ xR\@ p`ڳUY8HO}?|yЧ^?hgnuxwy/AxxoRGO9м+5tiuO wX]-gyyua*R1aޤ9 v:}6vvіg)G(]Ɗ$( ( ( ( ( ( ( (>+؟Kz^a}&}Z,f'H~zkٽx?Kj:p*zT-Xbp\&ehԜ\ydrWҤ ij4vq{][C(((((((&f q>вҿYk{=M7ğVLIX|ej6Zk7Ek+[Rmu+{SWz{:IsckG8+W^g|X_J}t׹?JЉ:q3xl~_Cǯo_u-C_|K^ ug֔WkhH?ga+ۊ>2Mu_:S_|! ]| .?h5VwzvgVY,4(tLW/ ymHFrUp8ARq9۞#u^WvN"-5vwke{M[~QEy_ ~>?f@|Oj?M?v:֏}OٿfAseskkk>nฏlF-|9oþEE&[G_EXSԧM͵:yW[ǺYcFxf>bUy&rc+nGi6~8Gޮ4ΜgCN~?5199^>=~?⯁!s_Uu5?ee/&C޶קD߳g\$Y2~m'~x?A/7Wq?iٷ 5mt?]N;+f{Ӵ+Jeiaisyp,/Y>0ӵ[i<ea{ե..gs][O[K ȊT~{|Uj Jo*m&]eUNpKe|*|F7şyIlvcxm>;95xZƦYF̖P' 1_߲g4⿳ƿo ?o5Ri1_Y|eymh9_q'tmĺv{ cF~9j:.jfPO3"N\xž7`Z^^xkL=մ żѤʏGaC^†":ҩ>[ʛInzW:pT+דfWϿo_>|Us7X,,&ko6tCd۫ fۓ5GC;k5x[< 5߳V|+Giiֳ :>&7:cu{gm1mG@x'?iq3:+ok3vj?᯿d:g_i7_OWmK~g4W߲g4_~ɟt?φQo ڗϢh᯿d:g_i?go= 8=_@=/OD\a/~ŞG|imW_~%𞷦xont߰k:=y=F2}m,kW4(Q\e(5(;8;4M4іi4M]5ix?υM/Ǟ y^j0׎|QKIծ-`-~OQO)Kk+&gO)?go= >oeD_̿q.q}Z|0ٶ[ u\]zSZ:p9(y+^Ǖja:Q %Cܿ᯿d:g_i?go= s_֌Il^#❿EofOSW }&{>M^Ot/_5M;\е:XuSu'SM4JY ;PI)HG?#Dǿ?Z1'?dχ??i>vai  |Zj2Hjm"T(գ^WR%QBrJW\q2x2ce{oW=?,{ִK]z4Dtۛ5{8d[6[GTyaEvU$=dG/6n5}S1q/? t(# x~[M6ۛO^ >x?x~n USDc**첱v1PEPEPEPEPEPEP_O0؇ j3B|Ʉ~?h_W״sSekOڃ]}KG?+_*/_:~(%~km Z7u]#@{'gEچAY[sq&Ȣv~qf. j<%GS9~Y/X6!ӿ3\?7$yzpP9թ*Toύ4mk"R_ xtobx gk5}OxvX=oMk^$5 K_C% o-s׼9m)RoJWKth|-ҿgsয~3)k{[mU9?kio|H> T/'Ïw^>x3^׊ |HkQc;.n'e_N? Yx?k_#@ia2yO7ڦ体Oܸŧu>$?P_?[2S$ׇ^'i{M&_?\o/ o~ѿxM_״Lhɤ>; v|&m/|CdWō3w._k'{=i7/0x7S&זi}]kXzejex uJJr|VڔqZqtd̫I?z*U+;{Wfwn*ncNaNN+r?^6ozNq4O)OPXtR_vZ-7U/Gx!TZ|9@yu>=ƚbW4+9+ >?Cox]Å4HW>15m{]~m|#Rq>3$Sd·fy[ dm6v,>OW_s;;cN`]F5oOz~)k'>5ğ6ns,~s |%ѯwWZ+x>⧃eo^ w+]oI/> 鷾%.dWUޣ,~̿\o>xcgfk|JĘ+_A!|}=i3&^g2xjlkIw y}sS )'9A<)|7o»m-t= յmc>>0WY}mn|E/Fƿfo߀~xU?گ,^'CC g_[a>-R'W]?#ZKqu'R>εH9Rj~|09URn:spJ9S8N1Q+rzV?dMAt +_cS_w4?ٗ^x.__]x}g< bj㜿'> /;_|9~#þTZg~ ׌++M[ľ|A$i=z_~_ƟZ:?G{P=^i'$GxW͔96x_PuAEq -/㖛_~д}&-$ci*KrqJWn1~ꎉ{]9U.ۮJX~_ԣ7uf%Կ'[6 +> K&gr#m5m)dfا>'s О{{ω<~+mm͓Z O2ꭠiQ]ByM7?{O]x 3㏍kH~|L_WռS+¾ xF(  ՟Ě߉>hZN+$ebϞ0|7wjM ? ?kσ? ?kυş߅ 7|/|E> JU9ϗVѫ)BԨOpq&J1ISP~;!B2X))8:7׻̛{'6;%x'w:φ,~|^90/ ?ď^xDӼ??ǣ⧆X'[ ux2 M+E]j5V`{,k&ƿǺp~Wo¿ُ__%_~35Ww>*|pOI^dsmcZz|8a-ΙԲ1N9BOb)FmœqQ;:UIӣ8]T~YsNm5 +IrZJ7q^[jCmp>3fg4G|B};7>xgC:xnM#ƾ"ҍNJ|?o ?OK}.7٥bZ6I/Q/#Ǟ U?C'>'DIÏ|`yx6C5oZxk3.^)YBx~ßR_<;~>%Ŀ_>Ú3⟉|Cy&O|;hk|Rƿ+I@|5g#YU>0Mf|2ߊe?_ٛo8?kk 1o~͚$*~w_|5qxT.&n7xJZiFqѧV׽Yeo_I,s|o:*خuy~.|i4M@߾->veH~7ট ?d~? <<?0^;ފ/j?<1}[MF*ĺ iqCMO:'į[ ਏT:~~!$'û?xZob;>|2KxIwK\g"u(Iq,SMifz\KN>Kv߮_ZƸCϽAzC~{QWi}kM~ ¸/W~~1' x/.CG}`fZiV1yV6V~?3$3#ߵ(Ӟ߳Ƃ~K{֗u&NO\8|&qSTQEyFпO_B_ P*xs㮅$~wŽڇ i cAtx=Q-tOxo'$ψ ͍K3.gO>>]?޿o'o2~?e_AFO-N0Gz߇ľ->\^5 |B}NDҵ^(L7z=9lU(x_i'sV.Ҝҧ8{hBk֧}~K mZJNы$W+rW+~^ៈ4h_W/Oπ|5X4%A׮xj MơvGOs;FeoƱ|6?iO:YyxKPe/[Oͮéi ` zZ}@dUj:_OMd|Q?gràO&gφ|YwMݬ0oKs{Sτү-7Z/l+mr[|W_t?Pz/|Mb k~'ͩ/\7< cA&~ƞgJ410}:rNѝ)Ҕӿ#GRRNIY[}ԔԻ+>' ݯ _~;خeRgyꋨ7ZOX295Fx mx$6mL$ɟ4Gl j|X> dž;Fm{~?to$|inGӠa!?e?=Fk'OO?)ߌn_~?>!~>l<5߆\w}xs>֕k?_8tk>|B,nڟu){{|r=*hE߈__&!֥G|{EwéÕlF*aZ5-U[˭)ӽ҅Jq~H4SmJ+W^dWoǿZ|O_ᧇ?bټ i~.>:? WZE3D|>ah7VWQih_ 74Cn-+nk 3߃l$d߅z_čOXXӵxJ-Ρ?> x:{q6!}2/ 3?o} #Ꮘşٟ ]0R_Lb.GoOz_vl^0Uq|)Úvi:v]o fo>6L~& мy7V??/ / J c .><>⾧o|TԄ]Sszj8J4Ӎ9(5D)ӻ>etQ7;4w7߂,:`Z~QMq?_~N`ӵk IinW7|__ z?(O)x[e^xWFvj֚g_Z#]xOneinbR'Gx"*[k^N<_vx<S~xHw~֣[LƧwSdѼss]1hτ~y?3G]ZC~ÞԼI{Zk3w=#>ڵkG}GD#ג҅і:m]?iQJױz>gɯSOY-<=~*^ _d*_įuS;xS€:'_ oCt}nf5{~/|/?> /cxoc"?ƺ~<[|N>$U6x[cDn"'h߳W7Zo|D| {п"~Wŗ훩D~x <':xƺV<?^hN&$Eჿ_rᨇEȹcN4&3g8ӌ%;QrMWRǕ?WRI'fMi$5e)ۭݹanP~g?|mO>ѤO 7^)tSƾ Utu6xlL i_յ%Ni+x╖Ƴgu}{\MMRþ Ӟ[ۍX.z4JO%i/~\ߴ/5~QC?g韴=#moϨ<]iO|a{gMgOT?o?أ a3OÛ?|qOmR{>|O}rkKR{ Yoە֩+sA]*zmm\FڥJNֶv{??aZiz|=7#:!_v_^(ٶVD%?UdO=Epc߱6 ?^ŷӮjPkW6V?Mߟ|5?ٻ~𖕭Q-oq\g,n"X=_5~ч}>ھu/p|Sr?d?w[%o+MMs[|! iPx~!4{Ww^ͨ&~g?<'ZZ*!')I7蟒f0ܱWr4vIn_?x?y?h폅':Uh>/"p=J):|v([~0~ǿ9co|/>x?t?Bw>$ѭ}e4 !=:~Ͽ:(x/??cߵ3|>O5i. xNCK0:5RYA(SrWWZM;J259\zoѽhA 81~҇ y> :7'xK㟆|Si{Mq٣ּ?4y[YiϷQu |%3}P` d4)?kqM3^Ծ| ׿i/Q7| x'o x7-ƕ}o-o 7MQh}[U>|r}GGr?KËig@tCW|#k v_gO[c0峒.Y%m[~SVuSKn}ϫo>{@?jGFּ//4]:ž/_FP&kqh_VRk~߲/Z|>oߴw3o¿|D>5Kt=>.{Xּ}s>>|.Ӿ+]Koû~5^1[)tI (=7 0]w*+4->Tzh HS>au-xkzmfG 57/~%zŏ4qY5mog|]xrOhzDžq'v?u-6Ot^_ gx綺k{dIaX]AC ԭ13SoP%X%{4ۧnu6Y)?_^뭙ou? 5FP5 ZdWwwS\$\\M#4HXxG`QEQEQEL#!BL.?agu~\T|_A+Ğ;C E|7 K<;O3zށ蚤ZnmΝzŎcs=[$mSekOڃ]}q5Od YwJϥtzx+x:'I~#w=?NO[eW&x"Nf|7.<t ZhhzfqGi-\~Ϳ;&kNM"MIhMX4$NK([bџgQ) UWJE̔{N_k{=}T(ǚV: o>5_|e߄xÿ|q]]'<{V~*2\[\\.ެ,6W4?g6^5>|1N\k [/6𾇥AJv;ẹIY'[٪T_WEՖI&'94N܋W=.WIyi՟2xw$|!m?a𽶽u|U| }բ|)lxSԼCW75sPյ]NAgo k71෇3|/;]&Yt4J[ӤŠ6wƤ^n_WJOJٽZKWi~4x.*j '߳G}wᶓ|[_:Q=Z|+x[IьQ'N氳ӌhlᄢ.~eD~=YxRi|/i>)ZnaK7W⏈Vt?ooxgiiio cc|}˖k,NcxٶoJߝbL{S Љ1~z~ IAO}+B$鏠g^oZtce/[>xt*Y~G{[h?`-3 4S]mxY/n/ݾѧxoBXD)T?j|2?`Cӟ+ ;#5)-/}_w#ʺFw>ioW>.i$cOhF^› S \Β!姵xJj^꿧6Zhm*>*~~KHx>ko4CKot߅sBj\Ķ.O&i⋍mV4[m*\4^WΣq~?5oI6YG{_CMw?OƕL|$G0Mx_mKOYKÝ;N?u(gx__֯gv?aS ;M}fWܢމwzlfm8{[[^ݼUwGK^~jo{!~#׌<]RO/mwuH]{ooa 7W>"^ѓĚ>7Q|8kߵ'? ?j_/(oMx/#w/ׇ0t/i?!\]xK׿_ҿkOZ#Q$WĿ: 1|;O~⮛?o~ωZ,Of}'믆<π>=Ѣ>'¿% Ь<5hLk渑 M,3;pԡQw%ZMۋmc-n˖X<]{oD= Uנ\%mj_EYz_ѶOAd'K:xsv?igω_fKNſ~UövV 'U𖐺Lj|Kh6yyi?Ƽoߌmy8C|Q|2GZt$| ,iL wßYmiqo!qsV07(85/4/iGFi$$vm7eZ??>ͺY^*~9 |Nj|k~*ʾ~ xnV~'XѾ?vjc߉Zc߲x7?hK۳Ou wxƖwou}>$n^'4]?>L|[MVŸw:>;Zx/'=S㮫KoK'ſ5/|[]W4߷Mq~?O/Jt׼+o|KZ |Uyo/Y7[vNJ@?|-,*Q4i:nNgW)ԝ(U]I8rΚUTi[Imm-ݔR|J ]w薑5^*?>8il'?I[m,)=3[uB3M >cyn>"j-7D`Eu{=%~~?WxT1/s?^yqy?:=Y_,>v5=?_^z?_N|KM_Y^_ֿq(~Ӿ?Io^?rju&NOPO{>xަ~߉?iO?OJ}5EWn~X̫U {};>Jّs8>h9>__W?)JnW Bx~4j%*ug8;P9Fqvi٣iES}SM?w D9|Jw&~?>.׌>xƿ1|DG/%mť~::jhfX~ ,/|`Y-O@o.tx7Pf~F+O>A]gGmcIvCmki滋$?ֻ`q9֩8YBr%ml|͹7#UM%6եW>?l/k_kzg3~2:5{Ǐ^*S|Ea~2;\Ӽp6Fmw^} υCrSk%߇xGoi_||.V?òmxO߇_X5xDŽ%wB:5dGI?ZQ ?/;տnO7|)|_{ Y _xᖳᯊ'>  /<;O${oiΡh>!\N )&ӵ))0IEz2i6:թR\_V/I%}9z3Oڃ/¯ wcXdc?~x;V| ' gZF74¶Zxj9.uG/ a|I~!f> wOŸ|!@~%x~F zwĞѵ_ϟ~Zw?\~!+|V6ܛ~C5>$Կh}k> <2|Sq~o ^xz[Q֖{*MYÿG_ڏGgT~|XO_?iϋxrj7)xŸzG%;G΀B YiWw_?f0^S|ziKk&&~-ӾѥxvOEo%G>!|,G*|>FtXcw^.7u_=4m~&f_:Ə+^ifCCӳp)FS2m)rUѭdRiIӛw/+)sKKiwʥ識w )~ t=cT?f's o'"4|4^N|_am/[7>|B iZ|A'~;h:N}/B_>5a>""|=Z-|#Ij5~4_"3_*>Ib<'Oa&eQ?jI|~WJRvJ &^9.VokZ[Ny7ϢaॿE'@~͟?fOOMO7u[.kM|~|e/q,xxO47¾? ѯOnO?ʾ@.?eo{x}ʳq^I}|)-$}CSHO&^WJM2)$k'/+WׄuQ@Q@Q@!>?b4/٫Tς &١~_|_^OI?juHdSekOڃ]}@44~ 1'O{,Vw>_JY''?9&ngZjSZ/Nӯ{]CQ_=\?s s(~_z~K_ȳk9bt]?K}2qM_;s(';<6ӿᮿe9Ooik֣f{+oKc,EJoC>'5G=<m-HO?Q\EP_6~='*[Lm®~ͦCe|?O_ M͏ ׾n/~5ߚe>X7?d98֥V^}'̥(^ZfXIQR ZɯD1քk{_g ڏ ~U&н;gX9ˌRQ~seS=7"}t?ZHnۭ|?^S3G? 4;'sEݿ/ϑWƸ~}jIO[_!êd5wG:L{QO_wu<+uKn?̿O+>ʉ3ϯק.w=O믉_g ڏ ~U&н;e\R_-OcW?_$??үęoOa|?^SQ?FJO,n5[]D&SdWA$2G,{FXrmT^[mݝظӪ姾_Oh8=c}X_~V7CᎱi|=|kx^&OIA%ν(HƱ^h%?n4'xGƩ~~/b:g^-c _5='X6xΓJutJMVwI{ajo-eU]"yib)N|njTP~ڍJ.SE×Jѧ>kr-kmgd|嶲V_c*(?uhz:'_`W͟-~j? -| +K㛽+>(<Mt@m> XjP//䕩a\jӍ:T91Nr}W9qc'd~zw}?Ѯ?=Ͻ|tk5?f+o_U:f:Nɸ2yT]Ux B+_?#/LO?O|V?k%a~yW?/?Gnįk=WaZUlƹ9S}kOξ_?͕'(wf)xWC]qO0=Wm)j{5UuQm$MO"\uy;_|"7f&N9j>Տ_|GW:g)$u+mtB5MRٲ8 yH8u@XrO{b45ʆ?_9'k}?W"^~>:J>4׼ \Ǭztx.47+{}^-'t[_mry26 }_H4??k% O9(}?ٻWњMb0֒&]MJiM&汌{¥Qi/w=~jv}uk0s =je?k%`qUW__6C\sq].E$*['kzr~@~Iuk%O9?q% ?:͋'Oġkt}_f _ݨzH}/\/bw>{_>xQɸ:Sş><>|P_ W?~?۟|J?0֧_f;G;G`а_Zz})hχ~x FZ%?kҵ 3RG? ?/@_ﱯ~ӿ__~DŽ>%uC~x7|?+Sѯ[W5;O)_}}RuWp5\*µvbwC V+o' s?9Os}}~߶6#o/@/"xĉ/ſ3^Yܶ_|%kfw%+Fˏ'VOZk^]xfZxtԬbgji}vri.Yi;''ew)1Wrj: ]WwVzkvm?cbNyAO+Y? >%LdePMjcn/eEz;ƍN/r^՟vD?^ڮƹ?|Gf%NSY/p?JOA9a]iOƼu?\e^~$[>dbDB?VG7YaG'įjVqWaTIrTM5?>z5_KPK6; :uk%`֢qUW__63\U[ Fat_;o_Gw m+O[W\cۓOz_ڻ\ j'H2uk%`y+\u(WL/oTUQ*OB&_W6~О"=c:?u MtXi ⷍet_]5+^ߦl?lF/ K#ώ(yim'/ٮ]mN(|A_9tCF~%|4q|N_F&+>u{qsE2O$w0?RCO]~yu՚qt*PVqI4IvjSV{n=;+5?b/?hoڋ_cW৅'sC~+Ǐ^"׼MYm{_CӾqOx!eѵ GG{%{_%_>s ?|}G|2h-i|0O5kFM:/c?es^F.ߗW#e1p;:&y|7Qa7J {)r4AFMRnNMRV:}QݶےQ}vkD;u>{\߱o?hxO~_~⏅?oڷŭ\?|"egGú| O-7CIc.uuCFGg>2:2i+㟍?j_#ukX:cj5-e*ܭi7ݨ;$jԋmQo} l~~5Ǿ?Rץ|e*_I#'z?Ssǯ'ן l[؛=qSESHNCM>1o|{\~ʞ3><hYceӿ~}y/_-__Ğ?Ǐ>3Nρ^xR66^j7cӬ/m'nnȜpظҖE):5RcV >TvJvԹԦԴۄJQnie}[z}|5ɗҫk̾ Id_UEPEPEP_O0؇ j3B|Ʉ~?h_W״+% g{O`z+?_OQw[$~,G,-tOM3^ʱ_&Ii_,O;~| u8:˻ u8:˻TP̿_w=_w=}5E| u8:˻ u8:˻TP̿_w=_w=}5E| u8:˻ u8:˻TP̿_w=_w=}5Ex k7> ?{2mwĺ]^m}gY-S}֟g/+:;UIQ((((((((((h__5Oͩ?iVڲ|k ȰǡjֻX͆z9O-Uԭ<+VW] եݬ[][\Fqo/6 #?~^i, 㯉6zu*Wg5}cB @,v$ʹj`sI -QEQEQEQEVnW]RMi$+XL^4"M?ڝѣm.5#JקZT7 νgxFfԼ/zZxٛ>)ƾӼ3xM~x'cL}:^ο@Ğ>KU.kfþUndRQ@Q@Q@Q@W,~i֧࿅^.%iWa}?z4>6:&i7q}"gs ^RX ;ʌ}yo hCĿk}O! {Ӵy5MR"o'+{Ki8۷k;[It{ ䷚{)[IYm${wt- FUt7Xo÷u. NJmG k/'GCf5]}{@!~ҿY?w4 G_^EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPc㾅aFoh|=⏇:ntx+P(iv];Q[2~Džx/D𷉼?:Əլ:}9(V~dzw4nm-浻7(緞)WheVXIWGVV55PEPEPE|ړCZ߈t~V4YCcLյ/c!a|VU/HַsS]6>|Wnn<9Ks{c-6WAM7ZŅƣ-iZ-wetyQGtIHI(h^|]k~V>%u >m=[ukIluM'Uӯ#N,) HFG`[ xs& h'7%iV:&Yǟ.MҴ-l,m'dD;Pf(((((((((((((?agu|пfS> h/WK7Ɵ}{_!~ҿY?w4(((((((((+'|K =4V0[Ir$v3̝|_ɦ~?n?ZӍ\NR*rHV}EI8Ӝ񄤽T[G~ߴIz?H?kI~˟I?q'L}?޴"Nu;"qxC_iZR/>}E[^} ymou.WwuOُJ_?O" ?|/x:{~~Sr(6,FDS#c%ozևX?k]Xh_u Ym0JW?t2ӧrO2H˚oWVc{J?3lw^"Žι_ xʼn'4k7Nn2\<6aMRѦmtM[QzЛFN9+ :jx+(+_/KO_|9oG?=t0[J{8.ς601sZƅ]Q{]sT>*S[{ZxD^&B B[?/$_~X|"CC{.Nڇgk 3'^⍟B}c66qjPZ}yoj6w -MsxpLXZs9O5Q@Q@e⎕kZ^&ּ}i?5]cǾ5|?ga 6?3^6S^mmlN'~ߴIDfOHAgs+ej/&O޾,rجd*ʬ1hE´ƆW*VmV m|5x“7w9;Q\m\N)1_JD&r&}hDOHki?MEG7C̿N z5C߳]4]T/~cF_]}yemqw?A4Α5Kcg|Y?@<?}Aɸ:Sq6U8{ybS_ج+Ymz9~&!UZ|-/{o(P((((((_l3q|H㯆S/ ?> x/>6-|m?7_ >"0|C-kA|;gHơpuhʟ⯈? <'o <9g/!ցχ?g߇ <k#>%6𿇵aE|O( : __^.lٞEeIkwBCsY k:xo_~-GV^#NJoAN^yxcm? ~߅>h'Jt;߉1(׼?/ TѵIc>iY|D?^.^$^}\+_G Ux`~.?f |7 :Ʒ?/4 ?b4/٫Tς G_^_o'??m ( ( ( ( ( ( ( ( ( Lݾ65}_;~iV&°zuWIgqgWS~?x`*zO.G|Fq^zs?{a~RXr>B1in?xG8G|c7Gǣ {k c //' v- Hm }]O|U Q5/o[Z|;ֱKc:gW5 EZoc9Uap+\rN.VQg\0pE;%%u;.ފ+짥~7m;po3a?}7vf٫_n}k><i> oN5v/K>|5.]cwz/w\[e<h_P!~6W]?Ʒ#s|e+)mJ KЯ>jO>=Y_Q_iu ?h7 xWY1Gź/>iψ?gJ%ཞuoڏgmM'W>>&x/"ԶrGz^#_QzՎy-LoEycmw5M9A-~Z_?i-'m)iPԵؾ|KEmM'h*'ψyJGڌӿ?k{Iyg[,YtO$\ѮŭsE5oWYN =l|7㯂 >)4_:4O{kwŅr,c̰tytm'$7Mt¿ ?6??hoSoP٧?i=[?g/Yt}Wu!צn~%k|Dskz~ޑ0SIJPNVm{Jq;\seRiЕD&ʒrkEhy[WW[V?Dc >??&Lc ?'&,|'73n:W?cp}|plcxZwGs(|/u±i⻕P(h[-Zxk]GW'Xs5~)3'$|%߾o H}So?fOw6~+x«/ᾕx7Ó7ljnĜЙG\SbW~>8kߴ[Ɵ#]#ľ3%H`|93P>ӟ/OoXi~(/,,<1k} y|*x;,_vGhC?7sQEk^ ^Z:g>x"j(l÷-fWȜ!7M΍:7T\i>jwwҥrkI)FuqOK@FF|iψ_ U_5_ۣW,|#Mg 7U/-Cǃ VBs\~vw|M;Gn|wsjUeJ𥶣ϏqؼEj'>|oj>KźW@x_42O?Q?$Yx\ e+,x7C#QӴW^ |obOkmWx n?zod)~ x_ [o'c:Aw_/s]{ú`-uJ;seд;ƺ4չpT6߼5%Ҳ~ 7e%4/zo?w}ǏXYWx~|/Y2LWυ8Yv0 bPm5w8.\xǃx'؋HWCVkln'?-wwWps?GR3[Ѯa?ڐcߐ1Wy:0FhƥyB(鶝YԚiMoAjrrqj)VO~ᾋZO"ucj'%/?<) xjY,]>M,Z\4Q\EPEPEP_1~whWfGu-B}'NS@Vk7ZmȊBGէ乇M53_N&q믭?l!eX֣9^{aIG&mx^uѣX tT!~U xk^?jk?k"Oo?ֿ|xZBt>0~[? ~;^~5xֵ<~? ]:|to(]I <6gia'AN.ɩM8Uj.g.D#ݫ6sU׽b_w3~ҟCv>|ѿ<7|uǺW%K<~͖_n۾?Y0 Oß#7?Wjڦ]Լ#K7y߻<2?o> !k"x?o „wOx6N|5_x_P+SAu=R ۴FNl.!S~4|+c]_~ڟO"G gګ ?:<tk?hOڇN{} ><},>#O 3p\x9\"<>3B4+ک/ք[QU&PsK J#C?fo\_q?Om{źW|57 _:m+Jx COÕ I%)0r'aF^|Cu –څ怋g89M -(ʫj63i+9BޏKIyk%x/윒߮i _w'BML?jf;?O$i/?? xxlH D4ukOh:-K\t۫-Mkc{Oh'OK>(ǍIƾ3ex*5^(j '5^V5wV5cS5=B{呹\ 9\msEԬ.>rQM]'j?{/B=t4j?u |Ha<9O¿gZ{ǏlYasx+ּc'[MxR;K}_LJOOA1~z~o1(?p?T ria*xZTN' ¦)B*A7MZS_ij\Zq9-cWSŠ( ( (?/)~ 'x;ş?/ï~+CG½K @X~k 4KӾ,x~&xW:y|G9?2gZ*|@Zc¿iO ]⷇%/|/5?>о1Z(i//ju|# x"/7쁪b'&|Vώ:?xl"WZ%t |EGEt_῏ Ꮚ?韵gOik5_<mxx|Yό/ocklj,<3i|5{dbG:~Ve2Ϫqw I3K'%ڜ~xw"cxc@[g۽7P> ( (>B|Ʉ~?h_W׵L#!BL.i_,O;~|Jf|X#ޯh(((((((((k4ڇg_W5}&Pٻ|lkj+g[UyLiۮ9>>vO?ϧCc}=ϯB$8{_àW'zu?_#ֿՏ>,~ >3esV?b_t]S-|7nS]-.¿¿b՞i7K_O{ _|Ko47-"G4|aX>|-4^ / <[}C k~!m,=3$;{_+_ZGd|) ҿcٖ ࿌>&>?MߊҮžJU.ok&OmCWе1e桤39Q?Ƭꔯ I9ქo1~JO8B6kKKԔ?a> d-SíG~:|T}?Y OO? `:|tG4-|@EUV5=;bgbfS_wm\>_~ ěĖw>xKxJ'n]?g\x^OiZƏ~?/ٟ7|kss~w~߆?~ }{T&5>:WOJѼIxK#ojhX> iVmW~G6sv>yu_|G_};Ú a O[?Qm/Kޟm'Ʃ{9aiPs(2ucRpm6$QW5?mq{rMn$ؿo _|Qx>xo ׂ>!|3'\BJ>7|[. c6xIbLe;o⨼C]MXxGA9/c⟉~~z?x]ⷅ<)4WS|=^ K٣ֵM"Mx.K## Ƅm=jTi)Iqm;>e7a%yr[?$_%~LJ<6io*vzox kxPIco}U6WlwuOq4_Lh5߳8YhqoFm>ܳ|0O·|/6?n~j~ m|A{隅7? 7oď-upD@xǺï3IOu y~~?YntMgOx^Sg ğ"P 2=mk_oi58ɦvVtn[ꚾ|"08=<ӧ}ϧAt{JЉ:c}zu?a7w3w?S^\Aiii:xVXබ{e Ie8VwePH{+>|[Ė0j7]tqq>Ϧ GxG_.5-XQ{y?qAAi4uw',KK|mtzi%Ģ6^vڍqwgsmoiɧ˨iO ccr^YHsliNjI?k1'SL[kx6^%Pm|_x: VX5| 涻~_K85VO0[F> h$ŌE|ox-wߵKEXu n~ |b/,-C`/oc ڍ׺}y\][ridEǫXz\R:&{-ݹ@ٕ{TOM>ƿ&/Ï"ů?|/S/?^oOzߍo>x{HW/AyjN𽅛}a/D6~h<(I3/dC1r7( HH0{a~?~댅*ѫ(I(xKVdܡ%e$4(r;?wTMYӳOF?7(~]_lj kgv:|sV9|;/_<%^:/M :5H-5e_x#nGw3㏎?Sx\t//!,U7V=~zO=kx,4=qj*"+^NMZ'(֋ͷ-I~|S _U_sចAyuZ~!|{<;os ¶|Ci,O o~*z^%(=_~~2>%>|P<]){g >]<>5 U{EDoCW<]yw?]v<7id6w?-~ gwG??f3A_ =:O\sG־jCxw4_ ®h ַ:I' ?گD98~͝ ڔUգIE-tiPi|KӴGO<o^i]m>o| VFǿNҾk~,;[?-+Oώ/xǞ6 m?|*xzEӡӧ𥎑;g#'3'1'=j w |=?Go!eyvT_;$`tVi=O#t*ߧ\սɥmE~_kZ*>9ٛj HחwK=τ#i  ˫CKk ;;Gp:x g/i\ tz Oc{ &_ĭq5]]m_k^-__^"Q׀dx 7,3Ś4 |dGj+Ct{}ym{~:E;;LyX5_ؿS$G[kgԵ-Bq 5OQҴN\M:Y^WfZuޡrc+ [۩vko4Gr|!xj- wTiOexxn]Z:[_~8Y1<>Ptv7׮כx@o mQ<5 _Q9ᯃ*Gi LOσa1m>.޺[ Œ;BGoW4|6n_ j??e-j0jygT|4ۿ%~,ZGmi7McYC,ȓ_?dߏ/jhowi~|Kf=/7Jе/z'Gt ? !Լ'iVMOu]$ǰϧs=uf {+^+5fix??_'On׹ӯ'4o#Ok_2k?i|h2X' OWh^!x h aOK~s߆=Z?|i:^jƹ$g|s޹{x7w70e 5{ZЉ:n+˯J:PsvJn<ѿ,ZRqrNri//3w_ oTئg?5go%_7_|W>4&|3o/WY^=#kw~4A|V'ďX7LRʹkkx(~џ~)KǏoNl^<~GHx|>Zx Dh4ƴ48__é5gzFW*Tkz^JZ(Ar(E%hE.-oVNۻgw+>,?`/??g-kuٻ$խ<wW3ēx:m7M<;iݾ`o _cM1-/߳7?_~V?~1fCz|4[:W]fе?z+o]ڈ}?Үę9?^)99S'6mǑRi4ڵ۾ܺ6_M龾<_ 6~>7|yG#NjO i>(|-Gׂ, uƾԭOڗgDGâGJeEMҴ;-8s$o|t-5oNJ~#|Fp6xoMDH{]6W=xQtW ែ>-xf_A? ZKY~ע_;1DĈ^~?cdԴk gŚݥ՜sk$3䈰OM[ -͸|SO|A|,W-\i׾6ΐ 4csKY'9/꭫g7>,I?5K7ށC@5gXC ޡh|I7Z'g?bЧo0|S YuK]_kT|D$^gohRY&\Ԡѹo/lj^Kע>b^[F |Kuof[A-Xiچ:sg| 5?|$~>,?qXiFhαO0%x{Z~(:x6/zfizߏ~4^.񟋴=Dd++_ m>+ەe-o{upYLq'R*xtrS\%M.E-DsiǑ|&KZQ* A.JU׽,D%JI:j=W:qZ 7[#f~{x@k[{=Dw7WmkռS㯍(\z_Χ}Jb?n'*ftxUdI"kQV,;MqKn7|qӖ+l7%ekGia(:Σ#RP~uceZt%ZXȢ+c(( &١~_|_^_O0؇ j3>B>|ioW?~ Wwo(Ij8Ezg2mFMZ-tm_uuo?h?Do/5GhOD_{a?M_CzL}w ˏ]B Xw '"/aV뇉ҝt2"X)eZ/_.5 O ?kr|:/Z4 u i '.խtú;4R%J+ aFڨϭ| kz͏Z5*M5OڶMձT?l~dk.F,~89. 'EU+8 JƴSMI'xU:Ju#%)򴩸R~V=S}O((((((GVҴtV.;BIQKWTKM7L{"[CQ;[(H$t(k4ڇg_WoK>ee^kW6>xOuBiax@!5=J+ ;+TtDO'~!oνs&mj4O[ >xC=3nJ37WSu`p3*K=V${gZ'OSzPĝ=E?Ҵ"N_՞-O_ߺ4IכM eV/T4-/¿uO Vߌws7A<j~4ɭoKxI=J5zctڿ <# [0K^[ lQ,_O7Mx+?x~-o&mPմ/~״U>h|6n|z,70"Iڮęzʛj1ֶWmY]_J{A7ݗ_/ȟt/=/5M/G<'_ῇ4/^ei֛?H5gYӵ^V]ojsA}r<%_^?/OCš> SӭÞogZiz>Zfkoeem 1DHZF=A}=+ͫ(J[Kݿ{Ɲ1?׮jIAO}j?Z&O޼]*_k"ռx?Kj5L1>'/~բJkOp?.oӳ (B((?مs7ϟD~~$@_^t^0/co}~'=''L}?޿qOߚMT։/D'NO~4:Ңp?A֯ĝ?{=+ʭ=/~:`D9o'^Pƙ#'+B5'?Ҽ?:Z"'بN~ޮę98~ϫ=/6EZÓO==?~5Ӏ=B?BD1?ּʳ^orxӧWNߩ?bNϽhD1zj~_rlnK}οc#'0Ua_e hJ0k>Yf,ҒMR (B(((?ln~ @=~7Կ똉3_+9)oeU֥5X1A}_p_27>̕P*UɢO?hDA 4?Ͼ*kw}>!Nׁ׭^?yf_WVV,I}y?OOO{OJ4>zOWV{_=_Ԛ4o_?ߵ5_~ĞS> h/WK7Ɵo~:o=FH#&BsNst]?hO)?e/~34oE _}cF[_k84YS./49 0YURߜz_cO_0>WbrA|e8_"l|A^|C'M7_n,mO,$F*]9tK5NU)ڔf]_HWJWWvvG>#Ѥ~ПR~_tA?h!N o~ʿƯ|oE~;ه<)'W /F~9Œk-KOgH|7im$f.S_Ͷf?|a.1g 7O~ȭ?ŏ<+ !|=]?M>*ZÞ:W; eQSW:qIsg6^85qqϥ6#C&51w8?c? wmT<=_7߉>+xk}/U^6./ |<>V|)NA5>}g?@uoWf[x/Z}?;Z-WCqS6OM{Wd4fqR^L]'&f#N g*Qat^oʂ֊I[H"Ѣ~?R~FDI'PWM/|cZU2 ?ڃ c'4ۓQM+QԼ ss\O ҾC-J.|GGg{D?gޟ7 C } |YGJ5` |5ez >+z_M}2/)[s ZGZDymN9E<“N蠥—M蠯 ?`?-׈tj/ٓ CF|U6|[kcoP/ Ox߈?ּ_m{Z&%:MSڿ <y_ vMkLts J[GSdm6oN6Nh˕.f:1Im_D:K6D~#ѡ?RɿB(+'şsc?-u F/l|9,o:í'eW n/)SV>ѯx Ro8kvJ.$-TG<^*YvYSUtVM6i--+r=timGיFDK&P G/C_A]O|yn`~? F(uv3R?z&-+ǟxx|񗃿eHyMOožd>Bg?_M.Y77վ*?h+$wa?^M>{k[xJ)-|7kuaus^(׼eu;N CYiw)x_V5zs^ͤҽY7mQݷdۻ".P]%W; ǾGYaoxw[t^ixze֏}Ml٧^\n`#z**c)FQd((-FIJ-YM4m&i4՚z鮩~1Z&G\?>=x3ɰ֧+߶Es~57a6<;i~Q^mC< q?}?V=O_qX[|u?nNOOqwZ*sGeG,հ "~f'O|B?SC{}>!q_4Rc¼G,\?%}Ë8?n:~?71Kh= o?/?5?x[^?m_˝c>#~%et#LM\|yHAbF_(iSRM-v~U~ᏁM/LQմ.KEPu}.ß < n[;|f/V-§/L|Ai~a\|{k-NPk|xk[#io8[_Ya vZ"5?~-?kp_Tj|X?DOoǑx X=6+ǂ|{se.-3;t|A?l}>&|w*|9^*t> v6ך_υe~,Ӯ k> ռ|M/75mhϪ.?ɯ5']x_3j|T+{3"ſ<*ɬhZOŽN=7Uֆڜ u8ζ#5ྷ7|>5ϧCxo5o_#{R$l<]P^ x? k7(a:?CwR{uRJRu%ksT+]^M]krchEvI% ⯀|C_ԭO +YX,_~xs7zcx'ğKe_ $$KQEQEQEDw/?,o7'K+éWZW<;"𞫡I|EZޕ%Ŕj4-,Ov/u^F~9h:EwhW=OMk+J]j֯ۮQn.L$=CcqxznnCZڌ\)1sqWj1M*΍**sVR#&wJ7dv8wt>:6wOw?z6gn'l?ȟOǁu?zQ]yO_3gCb?`}_>('w;C{D_}Ë~KO2{+z #!a|Op|r:~|H?W]?U`{  c~c'N7O#O'C&Ə^K?6thq{Zdg @/ڿ4ynŃĚ|jw)_?b4/٫Tς G]Q$p/Z?i_,O;~K?g "X|dů &w>?/>( w|#O@iR\𧋴kGx$uBfXXc0a_lgO^߲7ur?e_||V'şc xgIM3??/il+Ԛ]]V(&"SJS:h׏4a:qcZENI%R,RN1kݨiW6vv6u<-)ic'SH~7'[ k;oN|gm?O<;s>b麄7Cr8T>Py_7W$VE o$_Q7?h?I^3lʯ?>%QzT=zXUե8fx9+JLLJR8ᦛm^dCݿh߳WW51>*~~%I"OUxU7~~fsĺxgW#g{ψ^,__75ZG?Ƒ?±aW&cᖍm+B|G^HG>gZI y öao߱g-\?f'$~0@"o%:FSC+W~Wjbk5*)Ss#NjbjrB.1SbduZ`h]#||t| 73мwA7d_^>׊|WKig^zwM4ٓ%$|??'χ|!`Ÿ_ [GC㿉?[蟴4~2x[xd9T¥Z9֝5vM^+Yؔ<~Og4}O z_>)4g?/  5/3ok Zn?> O&<5 [\|6ΙK{emһ NE5wh;+WwOnڶIocTo~oXso;0+@(((((((((>3~6'ᗀ<>hX}Nm ?ȗP|8W]Dj-uk>FOi#9TR$HF$FXR@xGE'rH)OxW<-]7tht֚Ȋs]nD.4 ( ( 5'.?k^xឧi^8vϯhw-kg=拮i&mZr]zq}}|׉|E~ i~]vV|p^j(Mq/u/k{|4YxB>%ϊ@>CSmKVn5:V}vM5 8n/4[HҮdJnf汻x}Z(6}w]k^u}'Pl|Yȴ)AừYm4X|OÒd-k512n'Aڧ⯂l?i_@C|R[ҭD78_i&XcTxXԯLh-~͢N 2dgA)hI~ E n? cM{4<]Ms$ԗ+-؋{+˿"3ZE 0.><Ѿ&xKLa+Ox7F{ZVcW:Niոyqu}^EPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_O0؇ j3B|Ʉ~?h_W״+% g{O`zڣIw?d_k~ҿY?w4?xEְ+J[?G?_fr@A}8_b6H'd`cҊ(R6V"p0Vvӳ[2&Z(J0ẼNs_ΥV6oQNA+ԣ [Vo* ~'KEцw}^=N}9{jeL5Qv%M۱Jݼu~?(>vwG_CV%PBØR_|fQEsQ@Q@Q@Q@Q@Q@Q@W!/o /D[#l-w4mFO|=xz!ex~3ǫ{ۆÍ&kwOza㻟ŞwK?-_xj+a|Ixv;CrAxkq*mn&ji@||_k-EӖO"9n&yQԵ OWյ OXկ/u]RQ((Uk;!W'lq;`p q ~!>|;]Vß| ?>m?h6"`n85u;{,+q0M?ϋmexHu_|x/Ilu KRf}OD跲ҡ&X5Bd/Fҧ?:w% ItO jQ#+h:ed# mlB.0(:*( ( (2gc|/Yhמ+kH%ᗍt}G[kf [kk~xΓB^$Ѵ& NEPEPEPEPEPEPEPEPEPEPEPEPEP_O0؇ j3B|Ʉ~?h_W״+% g{O`z{ UCoi#4mߎ|9W >?Y]}K #WK7Ɵ}{N-JI&JQw$%2N-hM4iOVz=UZogWsoG9ggrek`|~os(`~Zw+YvT2tB,?5?DC?'>0Q ' Gu9JX,mk#?kEX~j_P5ޝ/ w((wޝ/)a+l&^.TLVozǯoQP;; _ ŒM"_Қ*"-OW?+^iM|!_ӿCп_U ze9b@o[ww_G(^M| _j*Ӧ+˗"j:&~m‘M| ^KNkB~T2ܽ~X`_WVͯR?Q/;;  RQ?;; GEZImG2 QSUο~_t?;]Mԭ?aUD jחZH%.֧R+:gZnsiɨG݄)F#N)0#dTbc{][nmmXQEAEPEPEPEPEPEPE?:ƋVm^6LiG2-o|Iygj3x~񇊼 >O?<=c.VUz䖍4k֞?C9tOxMnoa,Ƶwojě<&s_Vsyicgf| [XCk"ԮP|Cj:w"QEQEQEQEWzޯ^~0Nӵ=?Zß>ҼEkm4V+{L]SMͿQEQEQEQE>27Ou Vm?ⷍD}&x IƱw>ڋ_u-N /R֝K-m-໴Kb'Xghe)cfIъJPMku W61IżO HK2I#2$W|/i+H<1MJj [|%KcVρl_x?B үW:4Vi@EPEPEPEPrC&(h$i\.Nj͈&9[]cH:k.W_%8l/xrko7R;_^}{]VGZn:}Z}zm~cukyc{k$v0K :$Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@!>?b4/٫Tς &١~_|_^_o'??mB>|ioW״QEQEQEQEQEQEQEQEQEQExx y ex>(Zl|AƍJNgmq[K+/ɺ&ҕ9VNZRRu)E94I6dݶObe%N\WvSd[4z9󊴖5:9p&mYf./3Y?_Ԗ?>68ji_[~77k)pVgyn&/C}i.8?᷎u9;xTK?:}!Ӡ5GY8-4tŤ1WuiʍZdӕ*'BN-ŴMk[d^ҊjQEfPQEQEx/|ZW}߇:՟⿌|5qs2%KwKy$?|ݿ<^>mucᶯeǦy=+ȶ5ƿgmu:֩E}rOmOO=1#J#A+qTH4Q6VI*,@.x#OO|9Ƒxrijv=ĄnQ.U/eԵmJR.$() k}__?5:bE?KVn.n-X'ӧ!իxi v?Y6+2y`ujΌ9Wg.3,5V1R|эZ]=᯿d:g_i?go= ?~cWρp1SRW.9i˚iwk3vj  Z@iO=vNX| >-խt{K)K7@5 4/5 ImYnaG$ǿ?owx7)Ҿ&x;^C5_Oku_2P}fc˜LNtiNc8哂hGszJitM*(7KCj6jKbTͥӴ76}贶K+m3žŏ~MNO½OpQÿI⛙-&SʷFN4zygŸ_ś?eg>I=&|8%ֽwp#]ԴԎb >p"0=N ( ( ( ( +O?ho|*ecmx ZMKWԿg[CUloͺIwl_frZkܶ5ym}_;WĿ|d1E!wź>+ݧ<1x?qk-w^& xK'A#-b; 뚬:|t[mAg[V&' o|;:+4hZG,!HY+Xk# !%*#xQgFzg(<.FRhܪIV JgVSQO[ܲnfQ^QQEQEQE2X9!4)QdXRG$n :  \w>v7x3>ɠxGE?c[k_PJනԯ^#E̒O idwnҊ+qxDQ>k? uIl$O;x\[|5#6#^In__di࿆>,>~.cB־,x#YL~xKⷁggJ S~"{esmH^/ \ai?QZ-Eӵ]C_)TmBJJM7xlm<|O Vp(V 4[g;l~%qOS/ď̌į1߹򫨼A#fMes7_XE~!~m5vd;J?52o?'1WEUj"=O5v5?rK#vxSf*c%/vtWXG>?57ƭ\xOo xGGtmJPouiZE՛xN̲ngVMn'_Ez{~5w?$SQ7tX*xd4e ŭSi֎'*SQZmU!M?f0((((((/'GCf5]}{_!>?b4/٫Tς G_^_o'??m ( ( ( ( ( ( ( ( ( Lݾ65}_;~iV&°zuWIgDS؟?^S]}qJ~у'ӏj]{o?i;|VY[^~5'=$'VIO z|o5Hgd4h~|w&>0ldA7ľ)>hz_JK߲}^}Nգu*8Bs)F1cv'Q()6i$i+7{h-nM3Z;:&qO?>'|6/|_}~˞:/$ZNؾeԾkai]#ך4OKg}4HI9ڿ;ÑVS*֫5G.j4krVz41'Ek+^JpcxY5o\~x??o>!ѓ^.kxWgbk~"xKᆟǸX.8N{k_ڣ|e+!K;oC/K{w]CXS&.m` X6u25 Wx+[Cƿ톦OG㹰]i^+݅񮯦VG~o/McU IW8s^h_l QYՏŘu=F+.'A'^XDY?YE{U4dMbߕHJqFf3+ߊ?R~گ2/|1 YI$%iڿ&Ľ{ƚiޝ}k5ؖW[NEy ^TTDFjnhKↁ߅<?#=d$iҵKٗzvv/J?i8jU(J3zSK$TnRQIݓ<t'ʜSQiЋ#mvH?C I9_<|Kad΍W{{|_"=K5&(uCӼ#iv({|sc?Wo_~5?k߱ƛ?i3Eῌ)c|𞑧xWosWǂt/>+R#\HX;8o^ W[mOoA=]x#Nux=>/_T_#x 7Ï2xGkZz+5nt'U8*<ޕ:e액:rqSRoGNjSUStt%(RJ)d}?k'Cjfk~2|?O]}c45 W Rx. Z*+wcG,I  )?yoN/|\~||ߍ>)[],"Ǐ/#8xF$Ʃ+{ߍoxzDc6 )~!|L~x[q ox[ߋm2|6 h*”f_x_YAw{2;G|wx7 s*MnkKn6hnOM~ jv>gNs\q&ٻ#7>%W_ 5|C [C^j~Q-|_,Qm=Ux~ieh3o|Gsj^U~#xOK_ᧈnM?voe9H?Q]_75<?'WQo??h-CߋwVUI5FtO/Ctf)adSBfc'5R7$kJ1r۶GzRVוөm4*wkH3I~_XX%?Dڷ~^EƖwW5?7)Ofx{ᵟ_Fo[jZki^R~8;[x;shܗqMǟWMǽ f_[1?f4OΣƶ |MJOگ3x_O ^wj^Os}s neVM4Ʈ%v}m~hqƒD~G\U[+Z|qt=OgV?jm;Jt6[GSσPipu{{u4Vֶ<nҴ}KLt7Z5 cFl,]'Wү- 3T x5:I//m&imhdxV>3Xb2>3|8'O/gZ.\mՄ.T.QNj-[&쥭>"-gUo=J4oe"meo*b6(p (?&fZB?{k?ʾ2x7V KU~.s|"mq@zt/|l4&$Ll<[[{h>Ro kxV3'ïzjڏH'@!Ŀki/FueoݾjVwf\B8z/GCJ9g.^yǛ}mM?ONsRo7m{'ws*kkᏇ??g|Rݭa?V~k3sw8e?%}ީ~ ~FH|z=Od$>渥RӔ*.i8F2JTR7U4'Bkx.2IM4lMqk٦}SMh?\~z' koL_^~̿q |ǃnm_ko:vs]̞🍼[ NxYXM~]&NPN :N]@i}UOZYXDZZZ^ kkx#Hm4#(<+kV9a.hJkIrK5SQnDRqn,)i)4E%Z4W_+ٳ>%o3D;5G>-ź|=>"xZ|O+^N?w_[|z MXxT^p?/a<FRrwkNJ\3*QQRKjXg?r-J^C8I]PN{vD-^W4OݧO>i~<gD?ǯC/h+o}/YbϾ #'?O=?-jyjǂjnX&sacQK&YM Ξ$Khs qVI7 <'sV?a,U{酇5ͻ*qZQ_>9CIΩ7+]?!k_?k /Ŀ?v t?]xSW7: ~ZMPޯ#0:Ž*ՕNX΢*n7,+TmPRs'{&k2NZ} m>x 5w8?f>[ K.mo~1}:B,fߊ;}WO C6|y?xS4vm7_ [wz~ׇ gs߃ಗ?hgdm;ÿzUx¾!x+C|?[o5/ 8/u4O`x{Pu5iIJ*Mʥ*PRqKmf);-QjE+JnʋVZ/yM+<|Kpx?ß gEW߲g|cV4x^i_ںhwۯvimݼ2r_߇io+_\|Ks㿉Z>>i׋u?ѬemGڶakmiOjoN/Kxhd[ R5_+g@|aq/;<KO:o |:_Ğ4J?oGس Ŀ~~~|eԾjw?>>w3G?׿ CQuu߉C]sEOFE_<(׋/՗-\=8i½ ԤRW*8E=kپ{KB3Wt [N_Wi3`Wx'_SW(?@zYcßG?°2,Z 7&j{Gϋ/~2kOI ukD"k k[|@MW^82th__ >1xG H+OÿZo|.<9 ώwODž x6W,> unV uQkA^NN)5oꓽK_m[Tmw=Y~j7< N5@_Bc7eW,3\~_K#)> Hqa^-{v4_+O׾Euw[[5CGĶ"]Ss,NBiޗxZ9࠿N EɯLoDOO|IYx__//: h?4yA&U/(Ro Ԓ`mRe7q^ҫ~ڮ'f__hVz2Tc?J߲OF3ڿ%!6फ़<%ׂ{Rվ#ºշWJ_-{kM%=gG+?k/#Lh3xWSQd=7o/Q'Zg'n0i9M溒y|Iͯ 56տe/|d3Z_:g~A?+ +=;OO߷/ř1m*? uK xBֈxxU'V1Y~'QҳhWda5lӎXJ3F2MFII}ӧG6ڒZԊi&խm|j(# &١~_|_^_O0؇ j3>B>|ioW׵+% g{O`z(((((((((v?j7o|M_DߵgCfU?oUsYq&1r{OUhD=?U I{ִ"Nw?>Y]o禇ɥED9=W(~G?_2w?3oDC?ƒsHw#𕏅O!5ϵ}+Q~7^>9x|5'#EU9)JM~WXM{T*ڒmvTRHịy/zZ4G/_RŇjm? IBϿ 5 K2S|iּ5m}㇏n#7k8w[vg_!|Ds& 5ߵED>w U~!ؗ3 |㿋-,^Ʒ x0\j~ ՟Y CS~|.h -fD;kP|qO<#N_5x 77>-|H1GoA?_Rm޼_?BTRJY轪VNhۓm~6WoşLf'?f_rφt_D?ho+BT񥤞"w_A^1~#3ߎ9W߈?Y߉ ZB >x-ݵŬGDM,N-'8c?tT_MM}CrOkk7-4[62> ZKoϯ?go$/q/O|'P>x_'sཱྀ^4wKе]SEԾ۴FNl. |!\~ֿ=мg)?E`>,|[<}:>:~%׼?-Í?~=ׄ<'/~_z3>%q{[=ᯇ>5,-&]Sޡ| i5[o_vKG 5+扪{⭇ŻG%5M.?h1h_©m  gZׄt',5Is'eQGJ J8Z:6t|Wz4/W)I4NJzK%_N6N?c~+tOu~О;Ӿ ~ n$揪{CZ|OsxC↍n~LwX~gM+~_-ohwji '6ʯ!?c %®$W7ρ|&=|Ge[~#kl :^CR[X񧈴mzđi^9'n㻳Qo_%~Ou9x-ë ^/[|1I'CM kE}yM*Gq<11O=4Ïq}}ߣO^ܟ_(ә=|h' ĸ֞Ibh'dÕ~_-~[_ï;=}5_2_n?n|QE~T~ Ox?3߲C/x'? |<[/~Ѿ j?Iw<n ?~%-΍]ۋxh_2cTOV>^O[_fJxV:X'4´=)Bp|4f%kTt`~'Ԭ+M&շ?_۳? ⯉m'>.?W}7~ާxך+G-ō+L~:ڿO~(@W ~k^oU|}i[l/xXMs}k(Eu6ֵ/uïp~/?bM{o#ᯁo_4o m᫿U_^ V_>->Դ+^|2xŋO|O|E7|0~q'G߅?t-=| L{q]j7$}*֝&b])Iִ`5)nMVvpX/JJ/ *\9,W_]I-_$~ڧKo?޿mkῌGTt :_ :K55iӪ¹U|>nL4i۵L@֯D?'+ n _ڸ~>NNdJuj+u -=N|]QNrN6I]vIX_ >mW/7 ৏>=|dg_?^Ζ:?e>j6^ğ!|s|__~џPW//~ ־5xZ_m(xݛ{yk~EA'P~.8+cdE?3߅^E.L,'曚J+uK^]]M7vl-"mV_t|)q_]/~=4w&75 ~ f]sKԬ|17]AOK>:0I)_Vopfg{?ÿ$x?&l4 ~]j>fyK{u5ob^'V_I޿=M7~)֯ut +NoZڪyR][JO[i̕ݝܛZIYZVM=t/%ukV?xCşuVKgj}Vo|%|<~_~ x_Ğ koo/z-<x;JP<}Ww~%k?g>C o>x>O{7h7ZƟ^OOhڞeƿ'Fxu>(k)j>mk4*Z%[U]!_9`) T_~&=~|:oӳ s>{|__}_#?oATSVnӈ0A zF;=#K]iZևwнiOeX^[I$VwpKosѼlTcoׅo߲OKc |@6ʿپ7{'vn'c׭/<ďO?Wc^ӓ=~V0R!)ZJ2c'ZI6Z4uɸ&;jn|+ G=~_%%a "Gx6^"ׄo> MK|/mJ}&/~g?|qI{Kq7S׵^ OgL_"мYְѡe(~~ʮ=ם,>%V4:PZ{8J~rﶔm\Z {BWJN?b3^<!~*Y⏂)ċӥ+xr OW1E?-CgI|>{O]e \υqi?߇,>97"Ŀ~ 4潦Zx_Kƶƛjv{g_S~*n>x?W,xJ௿)]5|"tg&T^m$kڅ旫YxDSNO7jXzJ^6q财sͽ-IԊ{KGE'N 3S~Q#Q< _ Ჵ ;wu3yԧ:TM7-^ <8b~Vï돁?~-|}[ͺoOGhOťkfweѻi]j6چ _#ǚce3t+'H7q\>/xO;Giqj_w>^/ kI$ӳ~pG߳I7wWk_أč~Ǟ?hcGχ9oh~7|#ᛯ@tMCߋ"52J\/<񲗳rѫ(*jR {HTOTTFnM+9mT?؇Fs ?eW߄H\#X6[Z+*jkku0},Hg?G(<| |feF焿dvw>'~!<[?/:{ǚ?x2ռ /\k^|S$W3|%D?d/Ꮙxa/hu/-G?|A@ď:Ѵxsׅug$Ʊkxj#|ZO~ct"kAaZ.Xv#7 kxCietLF&qaO F`R1񐦩jSRڝ5w'7.g[rtJi-c^tg_?_??Q?;Ni?'=]|^nii&kGbZiƐi.QKl2=[F(bG~^5O[Z~3>>7||iVBCsR>3i? >.AO~d5o\ut? 5<9{aiv>#&ҙjь':5mG@>N_m"+tV_T<gOA#{Gi ö_?e!ؿ H|wv!J^cb\'G|>7?f|NLkI/>+~>6_|h~ΓeC'8wsrEdA?J>rt[]'gnXwo[xf[ x'?|Yxo_b?xFЬSDuVf $QW ~ǧā|J=SzW`1Y~?s0t窒VX\nZJ|a0+:?agu|пfS> h/WK7Ɵ}{_!~ҿY?w4(((((((((+oM3vM|_ɦ~?n?Z?? _?%mtqZ'Oq=*؟?Z'A?~V}|ӣ>VxG5fiID5-#F񯎾x?+BҿuVGZ4":S\Gt[BO;VZ "> ._lLpx?̂U#Y677b]FZP^+lگę.َ w*MrJ1\M6ԡ&+qZtբ){tVz^״d?`)?_@<wů >]x/Px)=xhaбiZ|s|9 @oDz-|^tAq={h׷s?δ"Nc~?y4jJRuy(>U(brq9S\-KY%F)E$&}޷K?_?ࠞA?^cwT/ϿAVi,񧀾!fcSN> x}oᯄVi>7m_/Wz B?~:G:6=+wm[υ~/go .5;:O5i8CZ-ZfF/g2Bύ>=~M_|_~&x^M"cq?|yx~:#wϧ>5lbs1XXUq$SX% &i)="wJ]/zh8rھ9Ҽ)R|"A~ZL|] &>0Efy RWw}6Wo{'|becԟZh ?i'MwZ-Κ1[hFx.}K/f+k u6CgĞSJ+MsIUZ_:voGmVGEۖ?u/BxU?PƞShFSqԟoE1{㯌O< f>m5 ߵe~&?\Blah_;0++֟}go?jZʾ;~xÕVsI6oL/oGOcVIobNZ'L}?*~o/zG& xBtMK|"{-~y-W o%3 S?;Nx_ѴzOxRKk[_ '~!]joeѭ98|45T]X}dt<?^>"JR#)5n~QǧΪnQI&֭%]!C}1m W:C?e_@u XxVi^5X5KuQWQGZ?$ex3)x_L%.<OxR.t-ZUSe?:V _'6wum6ὃ$?^\aҊ꯭wvw~JrwjN6>!ovӿM& me|,xTni~h2&<\&u솥xŚ][ֵ$7ɴnB?c~/ς??><9h爵?>"l_x <=^}ηxfDUzj t<~pTF+Jj+I4{^6)|#ۏNOŭC׆b'Oh]Kz7[*}UW^%K][!E\\j5nn5>:{~oҢrsϽ_:c?>QnM.hZNM/$Mt[J?ioO[}t?5NmM}<p|۫u׹y-z~߉:qusw~':Š(_ّs9~Dg5ٍ?gTGN>^O[_ן;iyI/$||uKKBxנ?S@~I"\~a~\{ƼWy l%>_%2/.WNw?ޫƹ9\ _6/7] :^'4k=׷VkÏǿ'=/~JvϹ>~_J{Mg;~ޮ"ӧJF8=WaNN-^ww5,FS}K?SOPA@>z5=;5՝v/5J.qW^`?}.~5>t-?<~Ck?T?~?wPO\kP/C]߫fz?;+ KT_?7P1G>cW֗"/#>Wxנ=?>{%d?\z^O5 /A?^*/~ 7] /.N=jگ@:_TQ1?F3?\5%俯*-vX}>qWEC~Q?Inoo6_֝vرw}?j:Z5?OVc:mYb1zׁ?ޣAV^}_Ս`BkÁ5j1߹ҢQV\}Z+oE}~ȼs~?/Wc:-Ex$Rt^G}ƿ k/'GCf5]}{@!~ҿY?w4 G_^EPEPEPEPEPEPEPEPEP^Shf/7@4G\uπ}Enu=[Xյ?!t/M{CQ/'nf"YQZQ֥Y%'J*$MJI;kfՙ24ejnYޥqx~y&Jg LԭMżWVh_u\ĞmLXOOx\g^_UQ SO_AxWUr S劏{[T  o_||[O:ڶ츸ϋy?@hNO_Y”5D^U> $eUXK1-՗.zM;Hޛ߲8H _oޮGWy?oG_5]zo$ *)k'/ +ʪX,>Xש^Χ -?7|c~ Û{TQMo>8? "?GX-l|u j7bӼIMugi=hE3G\.ះu+mg j~w5]ZZ mn>}euk4Ei-E͏ ZK CK>jM|/ڔWVVJ^ICӥUr˒*C[-ynFuiSJӵVŧSa|?#^"OˢI~zȩ'E~^\pXf0}_F_zx3GI?:01࿀ԧ7E~QY ^OmX,?ؗ  \<|%?E'Z-;P}ήWm[gԖ :7}ukc%הW67I,6*T<2ӭE[aVw+?_6>o)U/9Uޫ;@%TU{5<?#uQ_^G|ox֛O xDҼKQ_i:Xƙ{<7z~YG" n<A0X,?Q?W?1WROϽ1τ.TˬNgAqU_Vo3Žp_5S3Eu)/3?k+J~&<]{-4/~ G#’Z- JK,a&k hV' (?VR%^2M5Ϻi0ki]SVi O|_|]dICOe{SK.lelom̞_ɲd4:(`(cO>|;ρeῄ_c@H𾟢" Lְnd:}֓[ W=3qώ~x<[{x?ڄe <37G,i֣es_UWόM4wڼ&9T#kVHOKxψ|y|y$եgP9zʼnϷ)O_I>_UQ SO_AxWUr!M\n|8%$#LOZ_ۃnxcX_v”5D^U> $eUXK9|?R^?n|<7Խ|Q8a?~L?5_G~ʚմO|cZCkD_>>\C^@x2ޕi !m ,LR|2 ¿c? i]{ڣ{xAƍ_CxIڦf ؖH]}=pI;bUZ-}[LA_XOu]OaQ#g=X? } SO_AxWT”5D^Uc,ޔ|%M{bYWo#G{~w=OX/$մe<WO}] SO_AxWT”5D^Uc,-յ틇2 Qڤ?[ˉ_g 7WV */_v8jR|2 ¿$ *bp ׶.1_n*_?>e/wgMB|ׄ|Yi^u 7ZuơsBa{BL;,e٦1u<%{._x?:e53׳YI,3i-֝em74Vv:[ SEUQe7MjQ2:s$8I7utץ*SrQ-ZR\չI=/X_cã9'?Ez Qw; ? \gg׹s_tVo?|]wp*DP3):??;E3~EfͅY/{W)࢞'W*u `_~}>EdY=^ &zGGOjHgG | kފ]_R_>?#x/h=/dcÿ>"x+M kLծ|} /_x" &|AľT d->wY2\e[@Akn%O*2!vI֊(((((((((`ɟѵ kRumi0[Xu;}{w}u$W3J4QEQEQEQEQEQEQEQE|2ľ,^&/5<'M|`5#=CMtx"-#ah{#bTQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|пfS> k/'GCf5]}{@!~ҿY?w4 G_^EPEPEPEPEPEPEPEPEPEPEPEPE:Koj2G~7|+0j' tcC$"ԼMoxSQ\l0xKG0t OQд]C[_[ꍡwVphku$FUc@֠((((((((5+~7C&g㾕/ck%^gρlnˉzwKNZj>cῇZĺ [ѭfI)QEQEQEQEQEQEQEW|1[auņyAJ+a=":O*BOgM(Ks{rpO ࿈J^|0|Vvjx xSO;7·=dP4As f袊(((((((((((((( &١~_|_^_O0؇ j3>B>|ioW׵+% g{O`z((((((((((((97AGGƚWwo=holj|Eŧh~⹚S(nMB{m:Wy'_ >> ?x^"S_xPxÐ>7VZ).! ٬~XFl2y|h|V*Dּ)㏇vUK~95{Z}Y<~%|W?iI.f|Гiml]\­׫B t!J\+%$TӍ#:5$){gH[潠>"N:^mEu `Ǥxº4|1iwW^jzuiVRo%߷Wߊ>!&l|*ͨxWIWId?|El1gޑT[*,RJq]XiTڜ'*jNM$eN|EqI4UVVF5xi9F t燫UhQ#%QEtEPEPEPEPEPEPEP>5Ͱ2% ( ( ( ( ( ( (<⯀->)|;ongGĚL(Xšݴ_{z!#<1i+5|]YI=G5?;? j=7W];O߇O_,"פ5Zg ws';+9Ś<_k¿qO-|{i+y<[BXF}X~!id>]VAm&ɥ_jVw~w|4𾡣//1yXu 2[w( <qUjB8NnWT劏NknOb3^Vܣ(9SNN2JVWVvW ܎rNpo V*V\UiB|sQEVHQEQEQEQEQEQEQEQEQEQEQEQEQEL#!BL.?agu|Jf|X#ޯk/WK7Ɵ}{@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@dI?|U?>%xk/<%ῄDsx7-/?57zSF[݆OEhl^om{q/o φ?xC5^h"𿉴]kwcu}&PC{[_kZnFciZ֝麅7{^X\$]Z][$FM >"ȟ!};?7#x[.4hU7 |I-4mt 4}kgY}G!яďMI|-Ɵ |+goFSᯈuGQ𾣯;?9hTJNqRNQOxENn2vG5|&*ԥgNenh9Ӕ$˞;.h+|5wf?xV-}.Ok GROU٪xZDjB,s !Þ𧉼q ^g_\ŧsze˪1H!v `B?e]/?<_k c=~џ"xvS6>|'Ɲ'X~|L|]eD_k)=[H1bI%dZ$F)40"B*0b1IF)hI%GtWj_n5x+>"瀼GSL׆9qw&E2(((<_1C5Ucx_ٺ5/i{3?í=wj|7oakwe[J‹P螨 Z\[?<7y^xoǚ1ܵݥݥ?WHgw` +WU0S)ӧSŸJjURT6MRvP8&쒻QWJ[fˍBWӬ't{M7S]CO͵+k+׍m-%;7 !7/h>K;ύZt-;Dǁ|]}-WJG@ҼSZϊ-ZOԴ4wk֟{O< .s Rzc&ΓZi %fyt/ǧ szB4Q4XAEPEPEPEPEPEPEPk6o-.}T]*:Zdw>$:l:vDְxCKJ?wßw^ oOvsZ[[~(#vtdi^0խ.l[8(~3x7Ŧ xg?e> tdd|\9$$YRUivJ*q\cݩэ鶮wwP =(ҧ~X;JRNrS9Jsz^Rn (((((((((((((( &١~_|_^_O0؇ j3>B>|ioW׵+% g{O`z(((((((((((ͯڣZ</_k}'$x?3Z/;;¿ Z'&~N? j/ nZi>, ;U|[ϯo<3K񏇴dgٟ o?E9ukMrƗmx{_l? gxB? x{}8ߴoyOY|,eyCEymj?/xEOg?|3mS^I>wKtx 7' _KKxڿ.+l~ 5_x.?7ڏ_cK\>j >(>[9|R񿈼-xg{Oڣ eAz,?-~Zx+ž~!\ij((((8@M^i[}C7eY~L#K"$m-}^[(llj?j/=W7"|o'[C{?cȣg_|;o%ǃ K½'[$b߉S?t__/[]VZKu t+XVt_)`W,+<(G3<%QJ[ծ,dVR)Vbt9u_D~IV&#$Q '[D_{ěБ_ 85.ׄ4πLȞ; ?~#[mO R'|Ixkⶡi:'}x:k5MCY; /P/ğ>xEik㯁>|9cӥo~Z^~LR⇃F+ xTg|2ߡyUz x|&\*gJFp7-jХJRmk&[Rچ˖Z?nği9/=qN|HG5'$wosGB5~ ~ؿTq+A σ߰_95gw/|M\?u'1~{.+c 3c~0x#~%~߳؇gǵo |@g^ҵ^5 h^*7=w_|/kzKƢ!XwJ* `I֟>+3iۂTU"jٹJ2mVz{]ڵ}>WWx'Sm) "?? +w~Sow]3_?+~?|;nN4O9:nrfV֒s*R֒Ϛn_'t7_1/#Y/Ít)쭤|3SNjoMxK]3~# o~g t xsG$GihZoim!:}+8d{[/K6?fډ|Yxok^i+=O⎕xBgc5/Du[оa?>7o*?l_IOG?x<7F6{QU$0vZ]OVZߙZ˫7ڿA'x'A'!.|j / 2[Q{|-/|/^ӵ/o4_e֡L#𽶧cXim||}xZw%'<cof|GaMh wn_i^վXXie͌7]~mO'MwYguɥ,.NmJU%V ^/0ZU!fqMjNf oaDKxġЅG%/OB~bx |W-~RqY^71^M/?cWJ9h%}2Gמ,6>,xf4O]m5/|.V+OmS2Qݾj^J[}y蓵oKE>UN %??WBs?|UO࿁#z?AxIJwVm%ƣ=&M;Q_\h>w%v0:^2*OCdž~ͿRLu%GTdEw%ŝ'? O"_kBxi ǧg]K|-+:5pxX^.W|tjS["(4NTTZߤ+5*M;SM^-M_GJ(Š(>|A~;|@?~A?#n >9 ^JW^~ :o|Z|Z7zzVVߠ'w&?js!u?<%x"S_goþ"8Av־0xwOx9Ծxf S񭖯3Bxl?_/_g|y 'ggI4z?w ug'ě;֑2>:|"5GZ_Y5oxbWڟyo r1ǺWo 1x/ > ֿt_K𸱛62Qg| oⷎ|>&KٛN왬|2_G 7/'-/!z_|/W [a þg ?~/5j^ˤ|2j  &>&,=gV6A\|t@ү_ڃhj7#gƭ_|Qi_ ~->xgƖgPw{xYt/X+_xgW?^Ss]B מIk_<"|SxHoCKxŞ ]մ|K&gNT~xmGU.mk]k M)5I>C'}aF,mc ^'c;Bm{k߳]oU|P|3fojIkxm ~*3>?E2~8xg ֓aVoٝ.?f?̞|A2xto|=ㅷ_^>,Yo1/u9t^1&uϊ ~G9mxg:'D< ELҵT|kׂ1>[f_P4'g5_ߴKVxo3➓_E0'KYOx/iϽ|,n55 UU/d>*4aBۖ=Zww+?Eo+g]/wĿm|~~|⩼S><>'>/ߵU|[>~-?f/,a~Gw;N+|EQtMiL_cO M)TW4ZQq79TZJT539ZJ馉ZV;#0?ǿu6(KSmZ~G }_+*?JKyW|A~^j> guO$m_~ x⦕'|Jt~% ^|S⟇#OMG qgVw3 ! o+?~j7c|LAfO:}'N'F]'ڃLOľ%ּa /|0^ lo| o|0߷UF)ًXgWx/EV?]޻{uwg]>-_=nu};V#9%VxNVSc*s~ߕN׼24|W^/&/_j?eo 8lh_/!k W> _OMoǾ6aZ\~_/ڶjV:ƙ4:M9_Y?|iw'KB +E_AПPk ?~:|l\+Vx7__ ]j߳v "/~g ޏfO߈:/nS?mOߴ⯏xW *?M_axES Z;Kko ncZ(&2S5HBnr~18NnsJs.p4wK+t7j*ˢs[(˚'2?WYO|7_|"5[2 _IAM>k=_|jjޭvm֝e^/R_+Z[⧃kA:quyx=[rU:EΑ7OANڪz^tEeɹ? >|7>+?(?o{x2?Z?58>De׆/ |F5}rmbKiV gmkP Q5;^|Mj^0|<ƍ[_pahZ1ib\~^)voN%ނiK?9?g/&'OٗϏ/+G|A?k/SQ5^8:71OZ<7Z 𧂵/ 3-KH~#D.u-qK|H+ ^17lz{Zov͍b,u9]*QӴ+[~EPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEL#!BL.?agu|Jf|X#ޯk/WK7Ɵ}{@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@3NA#Dpo7ڃVG^_gR;Xԗw`.U+ H=1^*}|m|o䎠:שF|. T?_ ~8&-JX>xG_h5X=M[Rx]@LJ#-b~~#ᾇ==I?eك_gKijidOUom~ÝŞ>)tm;ľ"M޻W^NIGݩk>UV<\9Py[JK<&]Slxd?Hhg~p[H>;K2|dl|OGn[6!MpF, rc?e#(D>|;([_i {Ö_f}!؟?/>]OA}?Qu,?_|5g~!C+pj|s]E'vG \"L?xרRRw\n˞Wɹk9V/ FךFN?ң%Q^m;}o2@_S'mCzg_S'mCz,PXrUW|`~EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEL#!BL.?agu|Jf|X#ޯk/WK7Ɵ}{@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@)W0eZ3?xo^g| GoGUg|<[Ěs.\6ܼ?_>?|u_SmTj+7w3,o攥eeݶKm-[qWoEv/͏`o~пr;y/.͡{8$?^K?6?GB`пr;z)m)/y{8$?^K?6> ~s>;#W 3?&? _Ο?6> ~s>; :~_8~OV?9{:  GC`_y?ߎW[W$ T? thu7??!~s~<J)kW_$/eK}#^K?6!~s~</g7?$klN#TRt 7ÝejNw^IoGj2kEuߊ>9tZM+SΩo6&Fi#sw'|QM1mJ/qe '^Rӌ%gE.IL=$T馚iFt֚4izQ\ƁEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_O0؇ j3B|Ʉ~?h_W״Y|EPG2š~,O\?iQT~"|.|_GI}b-Կn//l~44Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~4oG{ѽG4oG{ѽ_^@!Jf|׿Z~Jf|׿Z~u/|+e=ҿ|sq!/nuOox?ᗆ<=-+OFt}G;Xӯ,ݥjmߓ˫Y"((((((((((((((((((((((((((((((((((((((((((((((((buteo-syncfw-0.7.21+16.04.20151216.1/doc/src/api-usage.dox0000644000015600001650000003021112634332753022657 0ustar pbuserpbgroup00000000000000/*! \page api-usage Buteo framework API usage \tableofcontents \section buteo-apis Available Buteo APIs - Plugin API - msycnd Dbus API - libbuteosyncfw API \section plugin-api Plugin API Refer to \ref plugin-guide \section dbus-api Dbus API The dbus API can be used by application to get the live data from msyncd. Both signals as well as methods are provided. msyncd listens on the bus com.meego.msyncd and at the path /synchronizer. A dbus method can be invoked from cmdline as \code dbus-send --session --type=method_call --print-reply --dest=com.meego.msyncd /synchronizer com.meego.msyncd.startSync string:'abc.com' \endcode Following are the signals available in msyncd - Signal to give status of an ongoing sync session \code \endcode - Signal to indicate the transfer progress (of the number of items transfered and the kind of items) \code \endcode - Signal to indicate if a profile has changed \code \endcode - Signal to indicate a backup operation in progress. If a backup service is ongoing then msyncd does not start sync until the backup is complete. This signal can be use to keep track of an ongoing backup operation \code \endcode -Signal to indicate that a backup operation is done \code \endcode - Signal to indicate that restore operation is in progress \code \endcode - Signal to indicate that the restoration process is completed \code \endcode - Signal to indicate that the results of the just finished sync operation are available \code \endcode - Signal to indicate that the status of the sync operation has changed. The status could be that the state has changed from in progress to failed with also the failed reason \code \endcode Following are the dbus methods of msyncd - Method to start sync given a profile name. The profile name is the name of the Sync profile XML file. The startSync operation is valid only for client plugins \code \endcode - Method to cancel an ongoing sync. The profile id has to be specific to cancel an ongoing sync operation \code \endcode - Method to remove a profile. A profile can be removed if it is not longer required, like for example, removing a sync account \code \endcode - Method to update a given sync profile. The profile to be updated should be in the form of an XML and the XML object should be sent so that it can be updated. If the updation fails, then false is returned, else true \code \endcode - Method to request storages to be loaded. This is useful if a plugin makes use of existing storage plugins (contacts, calendar, notes) to perform the sync operation. If a plugin has its own way to manage the interaction with plugins, then this method need to be used \code \endcode - Method to release a storage that has already been requested using "requestStorage" method \code \endcode - Method to check if connectivity for a specific type is avialable. Currently supported type: USB=0, Bluetooth=1 and Network=2 \code \endcode - Method to get a list of all running sync sessions \code \endcode - Method to get the current state of backup/restore. True=backup/restore ongoing, false otherwise \code \endcode - Method to set the sync schedule \code \endcode The format of the sync schedule is: \code "" "" "" \endcode \b time is the time starting which the sync schedule has to be enabled \n \b interval is in minutes is the frequency of sync \n \b days is the days on which the sync has to run \n \b rush is the sync setting for peak hours of the day. The idea is to run sync at a higher frequency in rush days than in the normal days. The rush begin time and end time and the rush days can be specified \n - Method to save the sync results. This method can be used to persist the sync results given a profile id /code /endcode - Method to get the sync results of the last finished sync session. The sync results are returned as an XML string, which then has to be parsed to retrieve the results. One can use the SyncResults class of libbuteosycnfw to handle any of the XML data received \code \endcode - A profile can be made a hidden so as not be be displayed in a UI. This method fetches the profiles that have been marked as visible \code \endcode - Method to fetch a sync profile object given a profile id. The profile is returned in XML and the SyncProfile class of libbuteosyncfw can be used to parse the sync profile \code \endcode - Method to fetch a specific sync profile based on a key. The key can be anything like sync direction, the URL of the target service etc. If there are multiple profiles matching the key, then all of them are returned \code \endcode - Method to fetch a sync profile given a type of sync. The type can be client, server, storage \code \endcode - Method to start a sync session with an account Id. This is useful if Buteo is integrated with AccountS&SSO. The account id is passed to start the sync \code \endcode - Method to stop a sync given an account id \code \endcode - Method to return the list of account id of all the ongoing sync sessions. This method is useful when Buteo is integrated with Accounts&SSO \code \endcode - Method to return the status of a sync given an account id. This method is useful if Buteo is integrated with Accounts&SSO \code \endcode \section libbuteosyncfw-api libbuteosyncfw API If one needs to use the API of the syncfw, then you can do so by using the library libbuteosyncfw. The main use of this library is to use the plugin API and the profile API. The profile API provides an easy way to manage the sync profiles. Following is the main API available in this library - Logging - Profile management - Plugin management - A dbus library interface for client apps, like GUIs \subsection logging Using Logging API The logging API has four main calls -# FUNCTION_CALL_TRACE can be used to trace the entry and exit of functions -# LOG_DEBUG(msg) - outputs a debug level message -# LOG_WARNING(msg) - outputs a warning level message -# LOG_CRITICAL(msg) - outputs a critical level message -# LOG_FATAL(msg) - outputs a fatal level message and aborts the process Logging can be enabled by using "DEFINES += BUTEO_ENABLE_DEBUG" in the .pro file. By default logging is disabled \subsection profile-api Profile Management API Profile management API can be used to manage the profile information. For example, it can be used to retrive profiles, create profiles from a template, update profiles and save them back, fetch the results of the last sync, fetch the logs of the sync results etc. The main class that handle profile management are: - Buteo::ProfileManager, - Buteo::Profile, - Buteo::SyncProfile, - Buteo::SyncSchedule, - Buteo::SyncResults, - Buteo::StorageProfile, - Buteo::SyncLog \subsubsection update-profile Update a given profile Following is a code snippet to update a given profile \code using namespace Buteo; ... ... ProfileManager pm; SyncProfile *sp = pm.syncProfile(profileName); if (sp) { sp->setSyncDirection(SyncProfile::SYNC_DIRECTION_FROM_REMOTE); if (pm.updateProfile(sp).isEmpty()) { LOG_DEBUG("Update failed for profile " << profileName); } else LOG_DEBUG("Profile successfully updated"); } ... ... \endcode \subsubsection sync-schedule Handle sync schedule ProfileManager can be used to set the schedule of a sync session \code using namespace Buteo; ... ... ProfileManager pm; SyncProfile *sp = pm.syncProfile(profileName); if (sp) { SyncSchedule sched = sp->syncSchedule(); if (!sched.isEmpty()) { sched.setInterval(45); sched.setRushEnabled(false); DaySet days; days << Qt::Tuesday << Qt::Thursday << Qt::Sunday; sched.setDays(days); sched.setScheduleEnabled(true); sp->setSyncSchedule(sched); } else { SyncSchedule newSched; newSched.setInterval(15); newSched.setTime(new QDateTime()); newSched.setScheduleEnabled(true); sp->setSyncSchedule(newSched); } pm.updateProfile(sp); } \endcode */ buteo-syncfw-0.7.21+16.04.20151216.1/doc/src/Synchronization_Framework_Activity_Diagram.jpg0000644000015600001650000031455312634332753031526 0ustar pbuserpbgroup00000000000000JFIFHHCCn" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?4y$u8՞I""BYI L=xs ¿uO:@E>.!X->X'qS¿;^_ aW]o>,7~KG.u6dgOz ' \jO4oG{ѽG4oG{ѽ_ిο w-YjϏ6]o;>7 -Jzŷiןc'"O-⾁~ҿ~|H_Ӯ2?<  <9[gnoLm~0#,~(šƘJżKÞ./@?/i_ O#zi_ O#zsq |9,>^3/o٩n\χ>? o<44?asϡ|>&|_?y~7Kw#76~%խxG\tP ˿~=_}x_He?ړxz_x[ PG4oG{ѽG4oG{ѽ^MGֱ=k|5gT| >xS'熼{ZKB-xWDѼCZQ?R_٧ß|qOO7_ٳI,|.O)4PϟbO ?h߇?c~l?>#g٧o5_?@~3>k-?`?~3>k-?`wQ|}_UJ_~54_xWㆳ>?h/ ?k {_ .i߳~oÞ,>)xA7B|wb]x{'4M23~9ˣI~?j~ /_?q?|Wu;6? WּK4oG{ѽG4oG{ѽ_ட?c؏ǟ7iك?_ 4߉㟎W0j~xşn_?foiC_gtߎQ>Z?O~;tϟ|?ewh~;O[h>|:gxg~_4`/+h/|O;H/i_ O#zi_ O#z7;Sυ?~*~so,>R|h>L # ?o/1KᏇ?|+]gh? ~'Pڟ_W_dLe/U/5g__Ujko jٟ6$ g6Eo-/do5_>H?7H?7ƟX_ڟ^#)χMAY=~?~(sKgs ~z~*SwM__t=㇂uk|[kſha~Ò>ִ_^'O?x2l_n |A?úOWI >6 }_Zhdž&߉U/l4}kÞ27{ ?`~3>k-?`?~3>k-?`'??h~"~^o> ~?4E[⟎4'CuþօV;?? -u9k?O15^|)%k~[gřaO|&]_KZx{[|_.|iФ>%ͮv#ϋ_1|A!H?7H?7{~Y߳Ÿ^w[A ?<>?⯌υן>~GcN+>d7-?^ſ|gj?S'?;FGdl?!]jj:G>OG??I8kH/e~ _h2|"/ O'įg|eO|5oNŰX=3j Aw xSWO 5o[O៎|x7 ~~<}E +^ioQ +^ioW ?'߳ŸiC|]O x_SL|1Þ7O`owGǥd_fڬw~~OGx_~> xko}?|rׅ|~"KY1/_noChw>~͟|[?>_ҿ?GFҿ?GF~_~?Rׁ?cO$?d$>7|Hо x: X|&ŧ?W>~^ 1lx/Ӽ0|g?g_>$¶'^ '0}^k~0cV7PO "~ZO&?!-?gOxG@>7jm qx+[<c 3xK{Xc^Կk~_>0KSN0x߇xGg2v|'?m> |$.>xR~,|IωĿj^~#x7/i_ O#zi_ O#z/ euKGmG~hߍs; l ".gM|3O g;vG|E 7u}ſޟDt[f~~3|\|aE~ >xOcosþ8#yh$;OmGB7\L|!H?7H?7TO  5y0o~;|`~П ¿cZ#K9X|?80i?ּ {|M앭_dgYogGA/S~>UW7#ޣW7#ޮw`?R+?IKSk_Zxǻ;]m☴m?Jvr|s5SMU4oG{ѽG4oG{ѽ__߈ezJ8x>_<#9f} Oo NcXWxF?|k~27t=ž&|Wlo~)z)rr;2R86N׋J]inc.e{J:iE=WMlϣ~3>k-?`?~3>k-?`/ {&OxY-|u}i ??koڟY+Ϧ~ʿa߁_>%x[~;~_5xg_/|6Y ⯄_g;.i??>x}G ~^0ơ?3O߀|? )SևGHtk? #Iž;}G?ҿ?GFҿ?GF|}|9ow}S_k>(O~ÿGm7B/o~ ZE ?|\[xcN~7>-xW^ZoP¯ּ?|a>xĶ?+^"?/<@>H?7H?7Bj^/죪_u7F֟f|EqYo>2:ac{w᥯{k_|-cS[gN)ꖿw_ ,oe$D[o?k ~_>x/ğo~—3V"ѾtOx/4oG{ѽG4oG{ѽ_ߴ'gOK _oqOX~\;koh^ de/^5OX?z ~%? cO> Ixğ_? 7_|$|U $;f|N6!'~-g%|=gO4'/M7_g_ | )/~T4O؃_>,e 'v}K񏄾%jL𷌼[ֿo{OOH0x Yo bPj{~ߵ.=-?m|U.xc^u_ @7]_T-u~3>k-?`?~3>k-?`wo~*G? 0|R?g؃F~b<+5 . kƓ#:/| /o^;GSWo໏g< OxWU9Kx?G5WW/ۃ߉گ?m؟@?3OOf|!8xN=N|= ػ6d/_~GdO~j_|oɿiGot}3XZmO'ɿ@AT,%~>|@/W;mu7gn/? wKcŸ~~-|o(i>)߉G߲>|BEd4oG{ѽG4oG{ѽ_xO _%e [ÉmO /㟃+㇉~ž|)|Bk^O.C⿎<ƿ*o loQ?i?uG*wc!/|-#u&쿴#; wo: W7#ޣW7#ޮo Mc▗oE {p^IOjE|M-C_u OÍDдώ'RN~0~_Zχ:㿍6|)}0|mxv?[ /ǿ x칤~~-5 ߁4oG{ѽG4oG{ѽ_|Qࣞ? M9~ |?\|X| υ~! j~(6~ ?Sksᗇ.  <'C?-P ?fOk |A~?x?ş|+'x㇎hx_~|>xoO?X~Jb?|%x4oG{ѽG4oG{ѽ_M)?C|i~෍i7৅z|??_+b:e d?C¾ t;??l4~?s +~?_?/ğ ?UKF |/ ?>x>>~7>'|,ռAsQmhx:P~3>k-?`?~3>k-?`l|Eh h#w5c௎e/i˨|3ÿ 3rkz(x7^<9߄+x2o\ +^ioQ +^ioW?4 ~ a/;O^ x$߰֗?h^!Zvhu]>R}gku'_ +^ioQпfS> h/i_ O#zi_ O#z>BH?7H?7(/i_ O#zi_ O#z>BH?7H?7(/i_ O#zi_ O#z>BH?7H?7(/i_ O#zi_ O#z>BH?7H?7(/i_ O#zi_ O#z>BH?7H?7(/i_ O#zi_ O#z>BH?7H?7(/i_ O#zi_ O#z>BH?7H?7(㫯V-[jGSvͶMG]~9|r~Y MkE,ĺg[]wACO}kJAk:jiwz~jv2eiֲqiyk4H#:Z.i,~i3>(4?k|+XBG7</iGk_uŲXmghuQ@!~ҿY?w4k%xxGx$m=S\$|^kk ~%h?xG!?Z7{Mo@Θqk6^>|wY0/<㙿kaǾ־#ONaJ]OWX~ףcc??ڕ՞qkc>r!o5y~4|Kuoٳ:?mxTh>7j? Qq>./h 3D񧏴 >7ZndG|?xwxrXmkŞO븼k޽+S|G{@1/G> jm)xOU3RGiv"W_|y| |A>8A⯅^4M |++=uu?Bo9_ 7LBo9_ 7L>g+ :χixY|N|Y_:?|3okךFwHTӵM:{i剾`i&.%@۫?KSũПG<[ o /^Q'.W^#MO|*Bo9_ 7LBo9_ 7L7~W\lAj ^,קT4>%5|?-cYׇ|7sbޑk^ r~ǿ5w e?[=|I ڗ"_cٿ~7&¹5 C/⎃߱x V)[Ѿ x ѭ|T:h:gϋ?>GO7~/|Ma߉ _ I5o`{_?`{_?`?`:_~N-N$W[OcM K{/o?X\ckJG "|5|'|%H x+r>*gS 3k^ZW㷃)/D[ס{_?`{_?` '$(+ {?MEsTӿkO_җ?V?A?~=gIMg/$W2ju/U{_?`{_?`oieOо7 |]o=b_|ş|:п%# KP4-cWh8 /sN'go'gOU> 2+ ~?⣗/s(UGjՖ{_?`{_?`߉W➛/ŸHu|@OOoN$;~ ߏ?t/ >~˟~‚}oGgg\7u_AwKo;x_ Bo9_ 7LBo9_ 7L99?c N AymᎿ}ğ9¿ 9!߉Z߅f7o|C7~>WWMs?<4sNwSx3]O7l_~('{6b/_ C'f㿃 ~0j ~;8Bo9_ 7LBo9_ 7L= | |Y #~ i)jz%;>|4)e - :gm_Gg꺟 r~ǿ5w e :?~l50~V? l LJt<'iڏ~'W↳O7|7czN!C G!C @|GGxPO%^-h K㧋?GgGKG/zM)B>xK^S??i >)_߈>,iG?П~x??>%'/⟋ |]OGĻ]K_ x3v/ne/^25{_?`{_?`_KONE'L }ß~1I Mwe?]*_/-sxO4_]c㷊5?Z G|[@% Ꮔ goڗ~sITA˟go =්4ڕ!C G!C @ ~>)")ZGNG|- B__#'HuË@d s¾о -Ɵ ě;~)c W1?7[M\>KPҢw!C G!C @+U/??=oڏU^ ;1C״~N(ͭßXjMo>l|c[WO YTmk>-l<~]k?Z꺍?c}*)<{&r!o5r!o5~|>5j%X1?l/O~y ~#Cխ4zwOmŗF`¾uvĚ^ ~~3bEE~_M[Og<ߍ?g/z6⏇~<|B]5~|aKOxM/~ KB d/B0j? d/B0jk~^|G"C>~ȟ>)immx>$|,z|B gχhǿ_%㷌<o?o|?DjxW>AwӼx·?j;h=S3#>}Nscx[K<)_G+!fQ_G+!fP~~??_?ƺG x ZKό_hO+~ߴgu ;PӾ$:Os!C G!C @ } |G⯈k/>1|>?|SM_ |?>:Yx?>=GgGs-#]y?g|Wk:qW{~kǎG~5C5T|k2P~t>' _r!o5r!o5y_%Y|6 I_ |Mm7|}|P&x6Ş,ǫ*?65?珵Wz׎fCGRYNO⋩x^ XozWO &~%]IG'_ok?_;6g/~3/~3{7m<|W<{/xDׇ*f?^9o7.~$F׈5+oٷ/?|Q׼a=O^|5F<3.| wexsh&+_E~hCu%x____źvXp?r!o5r!o5z׉Uc>*{j>7?C?<+׀:ƣsZ'?\*ִ/B=M3H^ ɤ,׎~3|s3~*;|M'ǟ~%x?Ao?:3Un=|{oWB!WB!t9?cؼ+ i_ Ako>|>/3|sχ eM;7Km_6y;>1N}K>_Gh5Ŀ>O?cg?xïKw/x~P/GEKZ6?E{_?`{_?`x6Kqh.o|L__#N Ư:O >0WuɾO?oŸ ?`:>[)K5O㯉7z߄_Mxŗ^1źxo&?v|Mx/x?Yw!C G!C @.| m jZ|go^/?KόY^!e 7|k  ?x.|Z_eƏx +7;g<+&㟃_?gx’ %7⏁u Ko\O6_r!o5r!o5sUux4|`O |QWW|JgO^hƏ1[Qiys3\+? ??c' _}T06H+ſu|p>x7'S߲weOy 1mw>ҟ O_tj ռAI _|+oMៈ "x'¾*ѵ Fm{yWB!WB!_t^5?LGǞ U|C57N9|n<|t~0h|-iEj_oV"/~3/~3e'/"?|?o-?!oL'xg7A|#o><+>) |)~71u?ܧ |~t_r!o5r!o5weρ?7) \?>'_ /T[G Muxaehٿ:$~)J_Wk'/ 8gu=FWwt}kO񧊴~κ?~"|Y ϴ>FOBo9_ 7L>~#n DX_xg://~+_wO_|.~],׼ok&MҴ[g|?gO%_ |"gX1{k=mV{'$JKv$s^EQEQEQEQEQEQEQEQEQEQEQEQEQEQEW^ пb\PjB_+fP״QE|Jf|X#ޯk/WK7Ɵ}{@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@\AiW2o4J#(ԳUA$|t+u kxvs][ P%.n!A!lڇWԬ/:%oZfaq%n|AK,RVk`vRZN$hTdc002TA֮NRE qQ9BNRrI(>nT-nݖY=ݬw??|2{/r[=-o,֐]Jn`útז^8.%Idg00?Z~类gV֊.J>k`6בM%?L+{¿ <K|U5zfID&|#dl;I$&aIgsw2xៃ[f-kr݉b Z#]&q*P6Ix<Sҥ$ZgJ)}W-h`ΝȽy*߁oM/6[L&MXy[kUYB$P隄F"YܲGߍ'ߏ=3S?i<;.gVKSe6઻ѥpTIeKKG<)mk~5hk{?-p]MqYڻ+ZMo%߈~*K$XN~Kd`xTY$X%%A}?52+yĠ ڷ!ouS 8{m?+߄N~!]]̞.mO>[6 Mc&t[y5 VXYL|`M+¶wv>ixm2ccmoyxsVP=^-eBCSF8ӷ浥f<}JwIJjiO znpT/+71wW>|]^eZK;G|5mk=}kegmx!}Mu1*M%?'Ap?RHd*Փ[TFWwwm[k0tɝ?0|I<#ޖmcy.GW^^&n6ZKvڬR$Q,)5xaZ^[6%Ky5O ՠ3iʲ "go7>*%}+/ CUuo|.O^ n!KX[V.,-ebOwo4\_ͿptA׌?kִmmǏ6|hҧOo/~п h) ],/e7|XqƟx+ ƞ*ioOrM]WMwMIO/!1ӨSŏk uW|Kw|]Y#!F ʥ&SyIRoWUquN1մ,(k&]nI-w?j>$^eg&[MFK[w%,$7PZ_Ϣi9xtnbTK+Ws|*|L/:.X[T)~Y_jwXIfaXkc9sVd'| ?_>0Z|mcz^g44io>/h 9R}o'f|M!1M[/x?&)<+–j~OeD|?-gM xx{gͪSa&8sӣNyk;;IIƝi4Ĕ/~T7+k}}O |Pw{4~2{}N^x[:Ky5_ ՠik+VXXNڗF->M#WڬvgkZ}Su>iG=N5lct!oG|_M U 5?^|4~xcF~:~_s;Asٻ_мS^Ioo>eտd߀W4=-uwj>x|<4k^0|O ?gO(|ItY~+xCըQo+ j3_>,~9 b3EcFߵ<4ha)_4 78VH[WiexZ{2@ib1"]ZR\Nͥ\=Z^[SZ<ʟrL }x~~_M|&%v֍nz4P,'{~R&}&X\>cNw|1-4]nN`^3M۠`RN?Zӭ7 bՖO +eKN-ߕ/$oŷ} D6ׯiRjm%6vڕwmeizUHgٮD[ 7QOUBXWu1،O2VUJg$e'ؤs~(Wk]J8? O5)ڒ_Ҵ:8H[Xiex[O-τ|GNxsxsKl/_BM>x ѭVU&p6k?Z,S~%y b9V@c~~+~mrU z䌁S_Ŭ+^gyqk^+ZUPJ>k齺\v_Q2@4OJFHxA>ޣ`~?meQJA@ֵo P`~S Tu:VVpHi98UsOM\xד~дIvuߟR>i2 ?5q|2k/^ğٿ|?:6a^NZViz֛ ëiygo4}I ZɦӋRM;4ѧ&n~z_+m.~گ?E7oh|:𮤞[ _Y6ׇW?Mtm:ۣO1_|O⟉z-i#>|aOma^!B?-G&}&m`~]i}ue_iGkҿh{KO]| _>'k{o=SSn=oPOk֗wrt fO^,_'*?Y&/׈>"g1;NK:k':پx>g|OW'LH^hoدHlʾ |s m_o377xß |N7k$ѵ-WjƦdž.A7||i'Wi[5kVRσxOŸWZ2S׼-^6o^TP<;L5m:T>kMޕkrկk7fW3ƭ(Q=ivJFH0Pu?+`$ҢQ_z_ ZFH)0RJ w$`~?DH=kvAI6H?R5F"saS?\Q?3Jmd^iuw kq=^)c%J2Au&NO]/Ҿ|k13i >$_,Sifc.yaKbW5ɸ:SָiuS^4N崝)S3nN˞vvOm/Tk}5_!x/OBsA ~C ]>(/WK7Ɵ}{_!~ҿY?w4((((((((((((((((((((((_vw:{iiP6vF3$7WBWDQ0QNRiE&m$WmIj -^+'':C#/߂K2I FJ:6YH!  /8|íO_WWC,agsI/{m_/:/:/lYag'_yWK?pxK?pxke2<?mK_q%[_q%[[+_YsI/{m_/:/:_ ,5OhgF-?ˇYNM k,{*#1+ؑTI#u9^9JH`A]41XlJb(blVUER)%u~;_;_Z6/!9YpA&$46*|8Np>=^CxvkG-İ?>![E#%>rܤKo{kXP޼ FHҿ1XMgJTQSN]ᯝXDyx.Cڄk(oAY? #Q;X繸 W,HW-'/Gi'#xW_~6H5^<. 8՜'9bBRoumŶ] )&a[>=|QN;ߌ_c ^ ge31񥿀u'ޏx?\%Է6Simij5|ޡ2x l5zHg]< :F^LobHo"wzo_~Gx;He||-6V4 #_?}Ii2{WS.m'%{uNU×1_]kPs$~:~zo_1?oূ\x uc |*\k~"o*xR~ |?mV'#పPTBWESsN}%F 7%$Ԣn}Mt?Mt_<|CtƓ߉G~<Z5]i.Z%ŞY\B6$jg7Kc E[O ߲>xMgWc=9u^|%[~#x7Ju>i^ |7yk|io%Z-4շ -'|JMb/$eoWUC߈--7_:~%w࿄(G5m|-|Ki"𭷈tsπ|yz"d}֒K r/QM9rZӨSHj*itM}'mjFց|O5CK1S_4]c!}! |Tt-'5':K[oo-cM7o/#O.O>#ƞ">.tg j?.OӼi~*|ci m?\ҼC$t詫5x[H8*m?' / BO >8>|&f=SG4 |>i ~'g~+|FoV)=b9^Cx_WW8~:NᆟGwߴׅ`O ᎭC9Soi|Ysdkp^2j=Mo%&|85O],_(5/~G tK\uK=Gkij:ZXAn~?f?ѿटR/:x7M/k^$Ӿ IF9a~V>e;X4>D5ڏNߌ?e_G {5=gƿ QxS.e h|_2Xx2<Ɛk7«mO>S5\2_XZ:4I^^Yjҧynk~'C?l_ 럲o>_A_Γm x㿆~x|Ev⇈5=!h.=+Zާ◀?_ׁ?f'wۯv^]xOFGUպIYC_O%OҴ_|[.x|=[t7_t\k_IS o>(//| ;?_<[LTj:W|c};> L,+ wïeC|4ѺdX<I<.M܉&Ti履3)~ Əi+Mo(7ELJL;˿6/ yW?>?j =!_ inѼr]گO6v>ѵ일 k;;cۢh9ϧ&s |.^}~5Xׯ`~^M\5 =LbBK8ӍW5()=hڍvW>դ@?_T2~U^jK3>+x[ xJ9mwXiv^6G4M>P(9ltkyh|>OZs}(~'I{xAX|Z6Ɖ,KЬ,m-lmfy}Oq> |f`@qn?fRjpʕ*3\ԔJi֔*M9ysv)mhvKZsi ‡_?H>g> 9Hqn?ז~Z7/U_o /oG7gB|3]z@|OEaRZ-;A.n-8Dh9J+Uw*oDm\_j_&}?n'|ӼS3_&_|[xvş |ִk:x7z}淮hu-/2J-7xxᇉW+<o㎅?cQ}{7~;GINWIԟGgiޗ5=5yF_ _~ կou~jNS.$jRsۼs-WPۥC-:C{x_aV99N᎟Cߴ`W dឫC8S$oN=Cqx裔.)a+AFƒr(h)[,Tnݬ5]?U|m~xh|0߄]_Z/lK⏎ w/1=7Yg+ i3V^/ӵNoDPe-{P_O>&|-|4? 5c[xOoN)~|G85 _Ee>JZ]j:J.)$9_4_ {EO<%kO\ßf_zG/O|7 6W.'4oKfdOOÝᧆ+]CDEqrᖑ53_$|[uS7ǚ$ 警OCYNSXL$״b)(6]% &))O=?7~+߮sW?gW 1O']n5=hD1ۏ?ⷧ`z%C}5r)wCDݼKm7WPͦcx\e:G<5izOϩA=!'D-nUKQ#J3źmuIJ]izt>p S nFD^¢ ™`o=z_~Ɨw ZV6V_|ewmo#_K{Q>Q9 1نO Gѡ[:4EWSHҍNn5QCI-Y)E'JZ i=VKt,=ςbRBiAf!T{:qusw~'A w1 njU_ jO $&M! q UB* שMk7i4ۋ(MI?%MW^ пb\PjB_+fWXh G_^_o'??m ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ֤ xĠju_x/7$MkH?9ZU/Ռ65~W毃F[~hOv+8֥GGjzׂ~o*:iSөNx#d.%7zeoF"4$ "" EPUG`Vc~>jK`?yw>`O>&|8|?A<9'~xBЇl<9(ڮ}&!#MJ YH{y>TMɦSi^Wj+Vm3e!ƃӿfKCdٗu>kh ok~8" g7ki]AĤ^y4־5S}")W_ơMᏉ.7~-~;og}?-G"߄Mx?ƞ.ЦҼGx>]c=s o/C_;?i^?τǂKKA?>"뚇a h|MZV&S զIԿmMgZg샮65 O?.5"~=>UvKǚ,zt_i6:uO)9Βqg5$#uV/VNܩWDmuouzuಟNgςhM3g HO_tGRw-)~ ǬxUᇍ,t:@4 ;]<3ov>?~"~ f?5o +?mk~ |m4|r?ozi o<{j >;i:>3ß^ nƿ~x:ƝoW7W"c${>22|+Sn< &g~i_M([=G#X𵗅orb 4?[/wďl|wO/ IU.~?BӼe/w>(xXyhZߏaKmVWaj qiFMs6zŭIY$uB|{}{;c?<|Ar|58gҏ ㏆  JJk|Ge=̈́z%5i^WP%gx]dH*|/dej0Q&)F6{NGaWƾ>>rx~_.o.ur?#<;Yo j;7 tҵgՄiɩ} it)5q8B8ʩ]rMVNͤݮpVG\c?]z~g+ô_CvmnݿƋM#◁+I`_7,Gj v+M$[LP[NRk*i Z5>iuDhGk->j^G DYڋ+]b[siq*NMܒ%{'{uNA]wqt ~UWj ibC◁ ;y$esqgb(G"ԑGGF?+.mB$][S_s|Puݧ>hdWVΥvӥQͤOM{mk}^|AG:~̞|4hk~*:O(K[x eh *@м }CߏYxW/+&Wj?,O۳m%|.ßş>/|!~.pXMw͍垇w᜖ocy⋴!:??¯(~#КO٧V.x_=W6~<5|K^u,g=6x_/Ӽ=w S6Eن <+x/ty9O!ԼCwl>O5w^0Yf!}OSIԚsBJҭ:jmFz[/~qNFNm4[ʿGhPsg+ֿT'o2㯅)x:>2| n埃> ZY5xf]ҤV7W= [~7Ia'c ۢᯆ~&h5G؆V:W~'xBG>&>Ԗ+T]͋xœߩ +xGZ:/%x?\=m'1uѾ';<iڗWo9$OǏ? [k.κ_d!nx?hk f/?'Oxm?WZ~ڍͿ\9'$ԢM;Y^Vup>U+[Moz,ckgWxsᏆ>6xt>3.t\־;|-UL׏S5BKծ`)cWP^S~Z[N~ 5+/ <[]xŸ}y^7l|GM;@tK_N.tynӄVZ'gҷO]7G1jӁZ&?eHI4OVǗ>ҿ0} 3T[xN9-a/>4_ "|o_<>eGxz5S/Sw|:ӧ+/u۪Z.I$gkʏN]jDz?ko[|VeOW,N3¶_ho~ 7Ïj7Z? x~~/f|jW^9.tSQX>~_Ə~>g'|+⧁|wOǨixu/͵[5 %]M?8DGZV?WUu|[^??}9 z׃_0vF8O*?A}x?`Q?>_?H|gĿOJOePDǷ'W# 8?x,-3xT1/Ng~ d{ĸǷ'W|j?_𧏋, >ী߈ןz~*-ƹ 7,o~"P _MW?j4ܚQWvZ+ޯM??$u&|7iSDo|*Ro j6 1M6K㫸>%xMMox~??l=S^4o+]&0XN>N#P~ʺ'JjW'??+3h;iL@n͠]|&ֿ~9i~ kOϊߴmZ9jvƙ?_H?<}{mhMπ<_[+b4+^kf_O=`:|G u&5<lOvwYXx^>|sM_sNZ%EB)SKsY6F&ʒ|^M){\euw_N/|oφ uxžj|p|Y|B-tĽOoO:?~jqxsw_N0}>moPό_C_'ৄodHϊ|MIO%V_j foKOE_A$V~ ŝc ᯀ^0~ڏpj?7Jz໻MKA&x-$,Ʃ%qӺׅOk~̿uO|E񧅯 xxGjox@必ks8.׮[Iz6*TvSilFvJ']$}'Et\{<{Uu& "SǟZ>|Gu໵4 E6[/ G$k7oo#VNV~%|J//|?sC>>I𽾁-[H'o7| CRj'o|Qx56H<"úcRo;=Kƺdb{>Lwڟ<+|Hq|Y~Yao?w>1qMvk;xQ|kCV -|c?ڏwh0VW~#v)ssrI]6{mOD.1VK{3U\cۓ?+F?_Ϸ֔vf}b`gg%;Z?=8_^(ե/)a{g)VP L_QO,]tXï;=v-`'1-߈11 K 6(Uxݐ`85~_n?nFb[%\6b̥6խ& 6m3I&ғϦ/ҿh_o(5}{_!x/OBsA(>B>|ioW׵+% g{O`z((((((((((((((((((((("<{@+Mk 4?&~ؚ s^*cM7W ^k&xzU j*mm/euik/~zMO>:<^$t<j.6:o%Q{SxWt{k]sOOUүoSy~/jukƚe Ժj4:R:#KI׺]޷dC_kokyKb;|8g La?k7/^.O _sC&⿈?;||<|k R?]G$?oow;|+7x7_-|;; E? E/o 7EtMDZ42G4R9bu9##*J22 kV׍O0)Ңr:V䭄xkJ{RUFnRE$(kɵ$UmoIk (8~Ğ7~9ߊS[$iz? EQJ/*]K?jټg;_|'¿S[x G/t:??i''oC?Oc_x |6AS:aoGg4_Oо |S?t$}qXX~#VKc?סGB5aRT&EFQhhZNw#hɸ*m$QNoe_vϯm$kT~v:G&<-߄2O&?H<t)x3; |f(%x`}#vWmIu~ڟ w-OH2h0ߴNx{Э~F~ߵV<_ ūYxGօ:>Kn9玿vQQ;9S%xKZj|n?[o+:D?g_sFo #|5W׏>xc/íӴ4]'Hy>:e;Px?C [Rޕ,6;ூ|]㏉Z7 zIa]S7?)<:z槨Ы)15u[?%=?ɭ(ҺՋIQTե)ӥMrh߳rm>i;5dz뽾Vmo#૟R|Ǟ1~ o j>:|> T,m|%㟃9?i 'wO xSק^ !w<+>-h7sq׼w;KOi?nmK&-`RĖ+m.Iuzڿ>>7.=@|?BpRH6M-mY2%uۿK??D|Eilk?}['Ubxg/4t}FV7 xncDsJMR-(~D>.`|X6i:skVQ/5=ZN!QXm@<M~N~j>|&yuw]AM/5;y4`ÞMZ&m;N,lq\n:ip،E^UҥJI()rSujB3"okm薻kHυ-I4W_2׮~Z?-,/\i1/gbMSHo@RγTakxqM:PӕfU媖wO/6;~xL1GfUw>KG1Pwwmj@M ܈I V%)JZ~)F~՚o-;cQq~UٷJxšot=F <:HMx#W|c.hz| qep|=zmn k!%-LQC$WR ߄WiRqpʱ؊s,jN^MrV5&(FNqP?|=?!B~zd .UE__j+Z_?|yW}x7^M;OMn^g->We7KGŸg?eo_G/>![<_!~~.vw-qR+toB5ۣῇ/i:>+ycXi4Sȟĺ5yA &[lo)|9~O=J/3rr2|Q6eMR8S!F d8~71!GiG'|^R?G {V~ ~2Bg &/|7_ZW5|#yKEҵ[V,ޣuC \xbo'k5Ci?yN jMwB5_ ΕK]Ǜie /r~|Vqo*u<? a&YjJO&̯)B:񽰉i:qVI+]wbp6^U>';KjI8JS? ~޳oc>'DO>5~iZ ~;i~!|=6wt~Oa3\|U/h|=o?^_ [xw/|{ߴm_$l/ fh?LU~Z~̫ o Ho _Gj똹ÖX<.h>gt⢓Mn]}4Y򥳆$<;oO`!aKN'>_qxgğ?h44O x{#33G'n[xZl4@;~՟kmsQ4oG_, ƿx:Ÿ?o ~#xZ?gMWfh6ot8Mw໨tR?ۛ |Z|4;bd3^ Wl3 E60;>gtnM֡ȴ:m}~%] i'i ~:.`>u??!if'm?tO^𵷇k%%7 x #&kze+/υg_;W5SOs්$5׊~.|M_5#8?3Q/ue8coO0͆wm'fv۲􋓒Z{n"^CR3wo={q]9Zܺ隿oi>_d~=݁Z_hoxA߁=զf'Wۯ|v_KFKV~ξ>~J=< xI.xk n.XҵOxYִjiO(g_>֫)^K5ŇzƟڥm$暽:OO#K?ࢃ-oUO>,gWh9ϧjO |<-C.xP<[om7:9[/G:zAtK4_hv<׏^^xzp*~_ )9vUM0SQBpQrqv+$jVܰz*zTH9Mֲ^Z<%xLH ~|XwFKMu^kӼi +^Լ/oX/lV]A֙;$jz-լ^|3@dMW"|:mm> |6MkR4ox{^մFQTǪl3zcmkc፫^+WT\EHV8Q9QtBpwR3pq2\6WZwm+ݴybL~'_Qg} >k_Og?Ux?&ޡ 5#fwixIGc-絷Ï˟WSBq\oWɪìUqfQW36+ߠ+VsOy/Zg/ge"|Y?^; I'ww3|A[%o_-M.ƍ;hY>>& u(7oh>*V+_SQ|0'?? ?lz_QaTϊmhvWz#XMk{{m]CS*od_߀gɫIox z6yŧ_s7שf-Ug̹fQӯ$¤״?u.䓍kiBlKvKiL__G~x{_bkb#=sw??sѾK?MTkV8xo[ss ][_\7|`?h~ Ѽecwí>G97u[:f?j_ߴڿ&yCq> ~"MoNH/)w$b߅ onH-l4cJ/rgg K fi4,'e(o_hZ?t[ԴMM{?U柨[sk:' Z9%G"$;% }S0ch<1ے3oFRs~gl?d:9}~ ~ɿ|m^Xbk_[~~ռ[xºƿ1)_xCxS?y{Ks~<⯈?o(W g.>=xko_O5S3EB\^CVJncr~|b,_TZOc8aS5tٌnQi+/gdn7$<}oj[x< h~|f,~_U^O(g}a]񔕯C1VWNi|{56%wz$:}3O??Mb_4~ɿO߁>5[c]œq'ۣOúsoi4-a1m !3O;(7+O++pv[;{=N'/_vSoXm:NWBܿ}'/h?6?z([/=?%i"u'n*GR:e PA ~ xCFy6"g]G\ؐMw+{[v*leW#kQ]40\*aaԭ̨QIJ׷2vwb\-vðzOu)neO|A"4FKɮ| 5幎%Exs5(v^Ok߾=N>>(z#ȏ[;>&gp@!wRq+dqA8zWLl4ulZ%MOvQw$~g5F=*dak5>c1[?kSҾZ%_ oo{_T($K FOW^,=51jFHeWVY%:3?ɩsO5zT0>Z7 SD,=?Mֵ^ZJTe?^8zT8'׏{V>JOӟ J//ǨRO7hxU cڟ^𷃣s5ŭs෎?_/Wzޗ+:b55q_(>&zng??x|2M55Ʃ#sm7_玴(3Rtd N-cZWwU3?ɮ6sI.({v_ՈנOT*2GVu?=,^?R(*?n:*%aשx% n*/@Uº,Xxyu&NO]'(18~'Tsï;=MXnK*404{ҵ|{ _ *xuUּ-a~0xO9:\02|Rwq0jOg@J״sQ@Q@Q@#ߍk?a Kzkz Lj<-xC㷳u Kwt%ӯi/]Rei[Etz#/R)2A(i"d?2xF5hEkre,nTװF :v5ef9C1I` MJ?Xj);Ym~[[߿Nş|X5E|C~Ÿ~(~ĺ!|{}a/Ş5 ܅kFџ$"^fg-, ;so?;Gq,O`8 _ R8JUZujO݅:XһwMrZ:I]WҥA5}ETO|LDU DžUTaUTx,yu ,qy?ngeA!U(- /^TjWhNZIJ.RnQMߙF': KiM|y<+X2ѥU$$b@$M[z|Kk{=մ˶[{SS*6"M@8e# US˚&k&B3'%~D'3qgmG]_Hii?=5^w0RWôg aQ ^A%޽e-Ω)rlK঍ 2P+8'0uurW0R4~VR獾Y[[߿NJ J -V.muO Od_4nᔌzX?BqU^JWFY9S^e|A}Ԫ2۟_.*xb"UU<( `08&N7~7eUks;* ,y Al(`VX:JWi΄l X6M()^Vom~eLKJNʾJ?`_cž񕮧wkWzŸvp_l'ktY m h]e[]] W! hr)t*0ڧ I)֕sJV\ џ*4ݝk޺lA z ?8'0uOOUWX𢪪*5 Uah E\EU0n[ifm|})J{~:}S?|7yu ,qy?ngeA!U(- KiM|y<+X2ѥU$$b@$M8{{JaԟV*8+A);)G E6k7y_*Nӷ}>A>ڟ9bKpI<9'_\=> ^A%޽e-Ω)rlK঍ 2P+8i]c#$paJ*JWz(Tk>g8r]mk OW5[/x6sh:ze{{WhݪZtRSq.<%xkpoj7)IΥFU6xC港:kaxS}2_Yjm$47~i:s {y/-.DIS"HSWTa%1`+&9rI9;}+/ҿh_o(5}{_!x/OBsA(i_,O;~|Jf|X#ޯh(((((((((((((((((_f_?hi ռĿ~1'k_xR¿^:cUt^i徍kk6qm [<{8m ~!?Ek Ï?}_ſ4>]cᖱ⿆~-ž ώ>о)|/i3o9"5?^.𞥫WZ~q cL4˯*=(((((d.wŞ<_O,?~23fxVKxv#ѭҭ,>BjI?hjI?k(/fo#fo#>BjI?hjI?k(/fo#fό _YFđI6#|Ѱ(O>?_Ӿ -|"z:x~;<1?#~|ioWѾ>oּ{#ž?;lZ߉OZh>o{{,QŽ3C _/ۘOM̢K]W@n?蚄 wZ^~چLh/t۫IЇgB g{5P״W_K_?CUK_?CU}{E| g{5Q g{5P״W_K_?CUK_?CU}{E| g{5Q g{5P״W_K_?CUK_?CU}{E| g{5Q g{5P״W_K_?CUK_?CU}{E| g{5Q g{5P״W_K_?CUK_?CU}{E| g{5Q g{5Pпi_S>4kwm[{|n?VG~)|]_'|{^tX|px(\DŽx;ú"ִm#Ikъ((((+/ҿh_o(5}{_!x/OBsA(>B>|ioW׵+% g{O`z((((((((((((?xڅڽ;h&xźG;ex~(Յ̖FNvڥtFk߈pC~?Yh* rHo-;^ ؚ~(xB/mVcdt_ūNl Iiy.R\3)4lLa?%͜ҿ?GF}{E| +^ioQ +^ioW״P_ҿ?GFҿ?GF}{E| +^ioQ +^ioW״P_ҿ?GF|n?bs/N>60|})SŞ?l|+Ÿxڇ'^?gw֗::VrYE0w]x ~li?<3;>.9k|-_O?^ y}oOmEaG|uJi |g]|;>'kw}ڣ:xߌWĿ|0kqfO~ӿk-?`?~3>k-?` W7#ޣW7#ޯh~3>k-?`?~3>k-?` W7#ޣW7#ޯh~3>k-?`?~3>k-?` W7#ޣW7#ޯh~3>k-?`?~3>k-?` W7#ޣW7#ޯh~3>k-?`1z0?HOpa]s6mgkC\[ΝZ.,5}6kS5Q=@EUf5k]Z,l` x'*lFn/vmnCڅזm,O>/x՗7v;˧ߝ' ^cpȋwhwe}iyc=͔\JQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@|?W !k/ҿh_o(5}{EP_o'??mB>|ioW!wCg'7u=WR}Zj7架POGe{K {hvQ_nRxNv6(|CxJKԵΛOY@%ºn|Dӧ~jq)0lzƿ,^ e9`k { 5O EE.mFX/5-S5;袊((+FW`N.sSþvw"7qZ,zvcae}ZLvayp]\k;ϯ?A?l?zznj?|-8hϊ'xg~y]c?oŎs_o*7_;L |[Ot['5Ӡx{Bw:֭JMX h#T[GMvgLB}tW _?9=;=?:g߯3Z6ʳ/-H}?zE_6j^!r/(|11:h~=6 [] iOtV>uLKjmj;W'nm(x$IaPId)у) ]ld1.=Z(J !SJJjS5ҫ8BtRewM;٫uNtOfh$(+/_h_o'}{_ ?PٯI["GfZ2MJ5w|\/{Sf qXgėl>_ineI{t[v^jQEs,ѵWYjKׄ+y:scc >4M׈<9s|'U?KZS^)>EZ_uzgt/ۛ?O)/_g7>x]tZ߅~';6a>'51i?39V+|zo~Wg5➩r\+O=z?~WsE|G?H^h W~š|/׈~c(lYgݞ)5WURþ͕Y\^kZyeohp]?P5 B(15_W}I|l@m`>kwԅ/e _3⬿T1ʴeZXHa:Ul)qXe95Ω{HW9Fe3M=W5{F2v{]j E~eƯ?V^5>_ًş|K?)yZ#^9?%_Ѿiamw7ER5θ {e+8ۣﳿ/sӁ3>GׯErwKVC ?/U?uF +H|_:xsxi/=@wwVTʳ=e{iS?0_߇OSQ_2'>$7_O''xwvU~?i/RßܖTl lay $Ѷkr*z}Ԅ饽H}([?hscMso/M|EY_ow{[SKFEm,+ .*.#8J*jF1Q'(8N39ө ƥ9J./M]5]]i4M&QE ('4=~J;O<#.?m6Rou_ kktm_?5ZIX\OG9dQ_U槃Bο@y3eeGۼK/gJjR$F\k{kn>m? ?5 n~!࡟·T~;_q$?ඛux@g~kIջ!k~W |+x| ?^ }G7{͗mQ.o ޡ߃ZX\)׵o'ӤԾϧx#^6qW2]Rv_Wmmkݶݒ=uK{[߆?0xSOuc_[j0??xIm/_ digoڤ߬@g?G|w+? F7칩|# 07.'7ϋ$hO?ٮ MZ>5 Ux [|,g7h%hmʟ~O%_l/ x>#)93 Y)i|:}7 V:m|6-|H5KFҺXi/< A~ߎ? ZY{G)MYd]="VI_a}~IEKO_~)ZI5:K| 1|[Q {2jWۼ7sUxw<@̾4ӿg~ 0%3bmKZ_V5Yh~/=2/z\Yi .m&Ï?m?l>*ȟe&gcx|=މ]U־xe#𦡥j4Ft. {A.fOr|AWO랕8jsaP,IS:8ŪSx%kM4%8AZRuvj}վK+̾ Id_UfAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_!x/OBsA ~C @^E+% g{O`z8'*OOJFm?=AxoRS7v` S)ԥf2bDOG_[/#ߊmGGf_㿆5g=BE⯇t×Fxze4#w>+x[uzu ;^Mr>|_mw<%s,BH?7Ͼ'|c i6_Qtx?O4;0\\[x<+D4h:ׇ b~`f~"|3'ůHݗ'.cxgW{4 f]:S="&m_I}\0ſ_o燼?3ψ|;2o>d[;\zů~Ե-+#/ ԓVΤv i<#{_M3O_+ow}1x? X|TԼi|.ϋasZs.^5Ysjz}RN?yjt,fv\:$c\6BC, Q׉9OsYӵes9e80`}\oym27E*:SĘDYW %  Rq\SNqiNp^2MsROkWiKm?{oQtWEh*G+|5? L:߲][#߃?:3JN Pn~&x 3IѮs<#a}D:~'Z/+?0|T.(6_ xO/ o=C*h_,uK=#Vkvj'&#:G]`/SPh2j> X:ƽk.ٲԾ02|<"7zs =!_҄t?OֻiޟSnٽ5U5=9a:I(馯{ӱle*?Q'{]? >~>3|6w~!no4C=a/{g~C8KΟ_/^C' ?s)S xr.3woh? <7 I xþO |& =k|<z5^,Cj 4Wy7"Ӽwәh\mG?শFϪt7>4mxhCß~?Gu}C<'];Jtm{V5O}۵}JRoo.&DnHݫ4yѮ2[ :VﴭF; JNCsg{ VhGU<?jo7_9~m#p|1,˽:I5 (R?~^ X`٭Ƈå>lMVVL~ߵ>?_|A>Ft] '5k'|S-g)1kecjwye x#PG=6$4fCF:Vi:[ش3}nĶ0LIt'5 . EH׫: Q|FzyQUmZtPI4''ugϱsW7#ޣW7#ޯhX/i_ O#za_׏cdI?C7O~*ֵ%u_ڳ4txeLj߇~ZD?ZlAmؾN׬_~5J]|}So|VC~5MĶW gN+FԺM<=gm6gqg>=_'>"tIG~%k<#uxOź_fZݣo|xcǿ>/|gae\x ^utxÑ?ZG ַE_>b||`{?/5|'?S&F7 ? ~n z~ ߂n/_^R-X2OTjn-zim5{~:jYY[|[\ob۶7|k%m=XC'Y&R/Wr{Y|L.m{5&7j/`:qmy;[KȾեOgNӼYoxS׼OO_<9]ƙkҵ [^1^͟f-5-S:)gb⧏|]߄o~,|3Ŀ_w|5m]x:ߌ!mS>tOL(NQv夛anӗJ0VV[_%|%;/.A]gWM(> ω aPxsQn¶UJ?j?~4? //_¶BFGO~4\ŏ5xw_,м#/xXټwm$Zf^géw/dO=߈~+/M o&{ xN{ki# 5]cơox,7߬xu^*F-K5o]mt?|>W|l/-l^bVkuS |G7v 5U_ I~OZe.w6amm*MMmhn%ƾӯ~\/ d]Ϫ:i:qjg(>3`>OK?*c37eU]Mͻɮfʤڊm/oS3 [kWgGW|Q ^|OQ֞"k⿆u'?\-м j:s o|ufk ^iX xگ4ok_٦|jSxDx/\~7|W,Ե(5gj<7ML։䱸"^SU3"s|>'x쁪9>[ᖳ ~Ϟž!Zi:ߋQxC>2ү|Ad5KB__?qk?c"&^!72s㿉:>$&~>Ciw?jK{eiwwxm>x}Q~'?ǂ>xg^1U?+ -x߳>⍏W5~)ֿ<&Y~"ռ4^!qfE? ?akoVC R?`CGTQYkW: ~|@W/59Dd_9N_|Iix"v/6<1$@ω|{ycwB<=^m](>Uxr>^ehMz+~ +ywo|CŸV۟P>4|rh~;Z|A[o}|AP5x<:ߴ<-oŞ _ZTڽ>mCτ7eK_)@S/?h61q4_ cxoa}Xoy5g.d?f/ߵcox߉=ggJ-WVu8`M~.k_~3Ѿ?ϊZo_|!+G5k'L_o z.tk͢ksy e?NZ >Z끟zkzi>3cgukڿiYuXuj⧃ſpܭ{n <M}A^W|D'<5]`,ishZ8TYn(joĭ>Z9ww)tXi]*Y>1|?>!uKO6fOIG"0'auY\ oeq`o[?ooW OGL.Zj|)| Lz?j W9;^x_kW eK~1|c_Eo٫ Iִ" -#DӴ>ߕ? +KهY׌gwU_x- xⴟ|K'ޱ i|YO3xKA5/ jѭЧOҵ'8!pG|,F%jgj'nVݛDz/?JGD6ouC_?/گEW.q t+ |>$V |9_^'Iz^r1xLyȟL |񧄴}x 5c~/  wnmm;?>:kF4:_W\l?/PѾ|5|23]ğ߲g?a$.,x:w)_vZmZ[o]Kזro'MGDIoOwU4Ӽ1kxu~_ |E~o&/ޒ?iO>⾻u7?0~~ҟKg|\>|{ 'пco|~WO?:f#SFIA:׆n4<+Fls列Rܧ|A;tve?z߰N.y/g_xDþ1٤ů4>#,֍Btſ 9_ 2+  BO xS[Їio|tigos?i_xLYuz \7FK];)-/KҬK^kRRkjR\z{N6ҷٓ6?d{oCW~&jž)ď_|qy]¾/ǟ7AG𮐲iӭ)3oV@$7js隿«t]Y$Q|b:h>5V;-/O?O¸wca?jfW8a撻NϳӾtJG='aa?k9'yInN֙ i߳ޟZk{ +Okq4G#z6kW^SHO&^WJMx/m?%׿m?%׿>B3B_{3B_{(/m?%׿m?%׿>B3B_{3B_{(/m?%׿m?%׿>B3B_{3B_{'m;ƞόt{oY,m.ŞWUkkucmoF獼-Y|=xÚ!Ѵ^_?R[\]˧k1mu^_]@dڜ Շ?C߈bO ^<$+ G¯>U=đ0^" Q_o٣۝?]xB_]g.k[[MiIƓju419UI%+$ߑvUnI?4|c=wW_x_A;׎?#otI:#> tK"z_ψZ_Btl~'kWlv<J xKēyzo%k~ٿ>!x?/Wߊ<9VGC¯| m k Rf8.>ዬ3K k|C|aj~"@dS! HjV;4 fS.ᏳnƜKۊ[yZQJunĺ=5/ӼAi݂jfiej[YM]bDhMu+]Wk]Š(EPEPEPEPEP_!x/OBsA ~C @^E+% g{O`zi_,O;~QEQEQEGƍVOx&oGæUQZoKO"$kƉ1G/ӴVUPC(ү rV [>Y+;] 6iM?8'0uErd-W8'0tôgA'+n_xWyO~|ôgvSoX=d-? ϏvSoXm:윯_O?_qm:??Mb_Qз/6G%#fJta4dyDU n76Rky8cHD(c8R8E *"( *>!NN%BPbQRQKI"oV`~5kk3o|>>kv]Fº)g=Gt^goz捨i\c/??AEm cV|z[_ϋJ.//58yEc[ѧ#Ϗ<]'<?#]PKo[UxNࢿLXlnfا~/UiZ7Ư? u٧ڃ)/~8xZ`o¨?~a;(b|S–v_ ^|2 oGj ja_٪?g<[%*'<=?>)e;=e_0|,[KSax׈9 >?xJ|?׾ >iߍ 3Y<>hq ?q%[+% 6>i|)|!>*Z]g?xKC)S2Oo_[|[|n~|7g_xW^ Í7FL~w?dxo$:<&mw G aռ''f`|MK|s8ύ<|=Ǻ¯ V>0ޗ '*e>Ok()ܿVi?i{>W"^ |3x?g>#uscƍ>W4/:CIiߋK]0,|RuxSA%Geg ^%}_?ߴ ^'m}.j_>7~^-|?xW ikD~|U|-h?G'퓧|Qψ3|NB|g7|Dվ-О"|`S+ ?ٳ_ `k\N/v>'o4/TOe}P__V#\ ;|,i?_p|Mw/ſDt|wxsM|]/sm mCW7?岲F 1bz>"hU>\Y4wW||DtDy"^?x{⎛es~!Фt 8Ѯӵ-_BmZ(`]\Z\+/_߆/[O? "i1e5 I^NŭすaӭF4cU0YcbUq*ZS5N1X:*Ԝa*n>G'EvBc%6jM5qR+[@?_گ I _?o&~ xI?=ou/}~ x~gkڇuBX٥Эg+q_ 8[{]Y]\cž=mQgi9C()J.-c)vNK6qN: Γ_uOC>G;|S?xcſk_3⧀. > X18j_ |asAmmsQR~y$WvV/TĿ6U~ٞ(]ߋ|H>ĭş~2.`|S8xK?{ C^ 6je/O ?K_ˏb tۛ,(4ȩRJݻ6Ri?_C ߴoƯA>K\gj*ǟ <[kgvYt_ү/ëi?GOY/oH~+~_'i)g Gᕎg-Ax@gYt]2OÚܨPt9 ~?ɫ ?-z|4?<ni۔M9rVYPhwmy/s f?k?|;~>mk~ m?jpZoc&{@GƏY|z"o7>>]ŭ9|.S /[iS/|k?CG~xJA߈~0b$Qrl:;YX6cXab UH>)dh᷁b7^9#𾖎UE~}x<#_iKqP;:o&ßLSSL^KȴM̏4׳iEqpEHjqHDEUTU*XjZ8PRG C:Q(9SyT^Og)S)Fu?k5&һkDni^QEzQ@~BxٷK|sw}NzD.0x_4]b׺omnwF}Rk]>U^ vQFMOS;sĿbI/E$\~7|{kE/QhuSJBSc?y+Z[Ju99jNKNvh/lm~Mg|3m4KO):ğ5_jk 7 SO_AxWUGjlƝ yv.]}GXkO^Uk Y/xOwJ|I/ 򪲵߃ 4=fD_֬FHЏ|kTk $wzCZWiUL8øP*y_fŻ? ysZA_m5ZP}/φka=;G֦ox;l܊5Ie5péi]w_o%ǁ_]| Ⱦh_ Hf|e/iUuS jZ͗i??~4W'?=rᆲd>  H #Jr] %ELv$~lY6Wi6N\+7r\{H{ƞ |]?Njڽ|mIOגBR|2 ¿$ *xm\LWZS^_ջk_̀ r|b+?j_ڛ(7Qk(5|>&M7,3II//exJ|I/ R|2 ¿֣WSUNNLj{:p$N1,)BiI$r%]4暺iz> Id_UVLӴK]?Om,,ⴲ #(bD4TP{$Q@Q@Q@Q@Q@4?85 c`~|BOK_|RǍ:'8|`#\_OxIkǤxo 7|u? /x?~ xL|~0faګVៈ$?kX[MR/ٛ7/~ |;g0pxo cs{}x5>ky <#cÍA~'ZΛŚ=ᧉ<;/|:~-g%|=gO4'/M7_g_ | )/~T4O؃_>,e 'v}K񏄾%jL𷌼[h_W{?ٓ8~/kD|<ퟆ/iY>|SE;E)?G?mO iU]?c_|Wً>.9Y|nj^ ?4ڛT~ٟg_gK<K.'Gߵuoƚ"2|;W^(xn࿉7\w" k߶o'1=gxJVuG7  gju|G_R*iw@xcx?`_H)Phogߌ cW}/ߴ_/ _kH<GB>|ioW׵+% g{O`z((((((((((~ xWs֡ '?K)SwZu T^_4{H\hZv_iZKg1cOWTO:WY/8iz-~οaexfسW ?BO)qC?~0|NwmOͦzE|h%/zw%DxşO| 5|u )@{/֧xr?oKR|E|9w, ׋-6 4I4{x4gQG?g__uN j) +xNҴ{j. 4-Kn,n4J8?dcDߊZ7?~|?|ua㿃 4]o|9>?,Oxᧂo 4>&_i- G|E1E||+qc[&K (((((((((((a+gƕ|} 4mf?ezZh:loumgQ,-2\Ih(?`I?I$f$y$༒O$;! G;! _^@/K|{kjx{Fqh?Pf 7ៈ5o Ҵ[ ]x& 烾7|^j*Uu:ESŸSO>4S_¿'@տW5gW\?e?ρ> Ggs߆i& ~~i)XAl앨|S, WǾ7|u>4?;Ś6? ¾#Ӯtg-4{yy:6guju͍yƿ)]?gŚ_54_O *ӵ]uE,w\u{8};YlaKmF)?< |'3}d'?wğz?>~?_t/w߆^>mO>m# m#揬Ϛb/WoG( (8uGWS7*ikLƯڻk&iu~<:e4/< #Fy_Gg~|h?x~~4^~ַxsV4΍ztxlڢy1OR %  Hi}>g+]KedG $[~A!ǟ3 0>|8<?|/N|zf ş ~~?Ww>`~_'</G?r0sIN ۯ]~_y㏄O_<3π߳ |$> O:g \x?ofNj >@~6h"gF.1x]f-L|:A87xWxG_h?g ? ~xG? ZµtO u/ZkV ;u/x87c M~$W1'x'MK~ď ogo-aO>/죬_ i <1C}oOMQ%7~09tZߎ~~4~KPi>׾~+|uŏž #~^-4M+Ğ-sD~ ? wnEk5 WQG?◊u]O-g/^e~^7?|?/?o.a^*=;:[=Ѽu{gZ_Y|M o'n? '}N bxßb%_IQ|72Ucg{ڶxS>m|)_oXq~|PS' ||d?#Wb?dcDߊZ7?~|?|ua㿃 4]o|9>? /"-gBNeo~*cXW ?WY^*a[s u x'%)u/oٓ_g4._uo| >-i?~3xxįώa?)~+|Xi'xKCu>|zx/ռ>;Ms?c1~&lσ-4O<={-|W l7O~*4bĞO?٫ h _Eߌ_i^"P>)O~-?V`6\۫y|?h??ӟY?xK(((((((B_+fW׵i_4/؇W7>(/WK7Ɵ}{_!~ҿY?w4((((((((((((((((((+ R?hĽ  >>x/,Yzsm#߃>'Ӽr_jz'R>Ьu]:O/'' ؜/+I~x2IdrygFi$rK;3Ē{_?`{_?`!C G!C _^@!{_?`{_?`!C G!C _^@!{_?`{_?`!C G!C _^@!{_?`՝s8bx_{㧃~"x^#мgo]Sෂ_ o&Mߌ|U $u wJҮI붖IPEPEPEPEPe]i/?k7<9|voƏ ƿ#>! _/E;?#?%v??Ï^1<@xj? ~xƟN,fO7ra4|Rdi7{feT~>=I^@U?O;gM#Vb!gůwuN^xKp/~??x?_%D-MZ~_"&?~&)۵?x ggm7l|0%?~w>#k7?w Ϗ6~W߇_7Mx _hwt?%|_㿍''WߴN_:/P|=FJX' h~/xg?~4}~ ?B&~֟et?oÞ ŗgu/*|3oxX^gkCvx.^$9o(A2t~e#ⷈ>%Nȼ A=;;:ş _j?|?~u[xG:+|`<3j70~|ࢾ'o~~h{_~[L{^/'T? \ҿaπ `>6->oڷ&~0x {f|Hա/υ_-S_~՚'5OFkw||OO aZi/ߊ?O?g^?fo [^%xsA?oo?Us? t^տ*_~ٟ<@xYo0h/ѿ M}E~@)?d]S\{e*] OEq?cXg% ~̾Eo<9b'c+#^5~k__ ( ( (>Bfo|5WETg#Kli#f9-j|/6/|M=WAxX?g/ xkƟ~W ^ g#´,[G?i /73._}?Zp-~p~_oߍ*>.~?Aχxƞ W_?\Ww|O_NHR?l د fӬ~ [ozv~ 犾'x m ό1q~W& at_Ǿ ]_Ş*YU歨__ s2QEQEQEQEQEQEi_4/؇W7'J١~?Q@!~ҿY?w4 G_^EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_!>?b4/٫Tς &١~_|@^EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE?f4/{W7澽i'5١~5QEQEQEQEQEQEQEi_4/؇W7'J١~?Q@!~ҿY?w4 G_^EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEy=|(Z_Sxsu+'Դh"մخ_%Ah91U|=%~ʼʹOBͲ5iƥ*-:pU(=Ԓk-Sjidiy.~G~ƞ?j?g-C?h£{#{g9KUu? ϡ/'GCf5]wCNzG\?g/c?i_ q|^|~_ K9~ym??c?nO:9;5_ah{: z+‡2E啫|xaj5j<}_4Ze.Z( |ZEI"a\2)58<)I*&I&i-X{95{-ǰQEs,U|Mg]gׇoxŞ|i] S1x{Y7x Wg]ﵟ ~:^\v>,ZşAQ_X4}?~9} ?R?v~ɾ;qM3US6o-X~|:&k<)|akŽ&W?AFǂto_׎4N?>xşƟ/ĝkC^ςO;~=Ey&Y<g◇?߃|GcP牼9^8uME6 ?ʼn3𞇤|Ph?r~ϟEϋ* sS3x/7maz`QEtZO]iuFJӭ_om!W̕wZm|>4km4 c$z QV_bkGZ4GK?}G4GK?}_0L௃? >!`7O[+"յ@|Owux2+}x_~*4x?Zеm{g!PJqE)*Sx%$Wv]*KyIi7˖4GK?}G4GK?}_Jb~3?N~-v+"Ï$|y^7-J]#Ğ:/|;td*񩴿Z<xCY_Η:VOԴxs0sp/Do$w+쵗 i|]|bg8_u ?g8_u y ͭoExXŶk E_|9ڿ_{6c/i^/cмwgtm'{-Ei cLmFRޜJJ-|_FQ{5uM3y?k/4GK?}G4GK?}V\ Љs?+XcY[zF_$;O_F/zO4|&u[Xt7MBF`YX̪lŘ WWjK\i"Ѵ{I5>SW{KfTu$A~O*[P-Koukonk u9 י2 UCNԭV0e:taRt&$Z\\iJjWi7nϪG?3t߀H?i ?:\GS~<~ΟOR~&=?g/7GMgY{xf>_ Yϴ8^f} E|>7x>6~Q\?g//?ɴqu(QK{y`1nSK7=5oK]ދ[俗3 |/"ѥtW/x[:mރ[7T5KK"s֢E uby5b)Ƶ Jw9)Eٸ]7ddqM4i(AEPEPEP_!x/OBsA ~C @^E+% g{O`zi_,O;~QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEix?C׊|9syA}? > 'χx_>ojKEn;O4?'/ 쇤j _9???o%;|w^ ͦ*~3ujo|QKICiz}*mEF\YД[b6D˛N_ޕ࿎(kş1 3> B|KƑswSq7_~}MDx3m?wG_⧇1|i ?~yt߇:= |#?m|.u7WN5n%C݋WrSI%Nmu[O_߱ś_$-{'ŏJ{M#@ƣ[E_ |N񟍴}k>r-<\hڍ<[Ye89'_|um_?j ?׼ e-Czu; x.1"մ4}:; ,C^~ϯn߀a~۾v }2}8?~ |A#C ? k>YIgx~,M-뻍>+.q9UW*IΊi˖)_OsW?i߁:7ߌ 0/z^N,>{?Bo_xRTX|S>(ޅWQ/O}߃_G^xS]+zOj?ScoWf}'^Zşxşn d诉>|6??ğ"iOaY4h\o'@[J߷o5-?Loo-hF.K;8Vzw%/?a^iφ?hz|KYZ&m%³[\ FpGqo2G=Mr-8?fQl)qp#8N.3)J2MI4iј]iovx/WĿ'n5O|נ5Q'}3Lj//53C'W7j+֣r}7_c)/~ti~ %<+~/̶i?5}7k?O<'1xe?a_ۋkm'LJ7^*VmcG6m>h^mxWYտn^y >sπZx[Nfi=go '|C&-|CWu{~$ּMy⟀OH?7Jh°V*Tp}ԿOXl)_߂ -χUk2hxN |zx}5gUsR\:9]QRp$p+UMm`|1.+/x(ǎxNk[hKĺg:??|GaKW >/.>_ c>M:k7 _?ň~3f𞹤|/?h?xǒ~Eτ>*vól2/φzះevw;I᩹Ҵa:-OcY"buXw^[~|gC~5f6Ӻ>^2ƟkMwp־'վ-7_g.RV3K[JOjU9MJMgۚ-I BH?7H?7A^  Y>??l?Kz ~̟?#HbfZֵq$ΫSYVx& X{ HZMPO{+Ņ֕qyeY{?ie}>ݚᬯo-X )3E^LyTʚj7i('ӔoӇ~bsuʴ"\? Am?O?Gޅ-_ m3g+Sڦҿ|O;㵎[?/oσo5? 鯢?wJo#9e?m_w\/co|5yexG\m:?#ax3Cĝkgl&WKueuRYTRRԣNJKٴ璋䄤Fi;4M{[^+Sc_?{ᮌKਟS?KKk/uzσ^uO߰τ~&x  ?xuz'|K`uۍV%Ӽ?c߈/owmW? xbѾ?~& ⏉ i^> ~ϏKt ⏄Y'ÿZgL(IfVi>G?% пk['a|/ gOrT|2?~8 jp6P{?mcmx]gh7eٗo|[> B_E>x֋ >;ji3|D|Uw⟊??c'𶃧h–:~@?n*k{ÓJ?>*h_fw'A_ > t?X>.~/bPMkWO_3Bw ߲YO&9mqg£9⯈¦:/$OL?uu__ U_=/?;>8CB|E ~?.~Ѻo_(#'1xIa߂+c?i=cx>GST7Ӌx]$SNmm/e>%?+B%?+To?|Om㟍>$j? oK>$O߉e\_-KW xᧇ&UҾ7~_ANxCw/6 o!+?|G=A|ki~̞y GW>h.iε_?gQݷ]P~w_'tk"qkj-vmݴ]CyN/Lwxiג'hݼS؋!1OoM# D8db&[۷B+ q"x v]Zɡ.,U>|G⸵29kwO2as_ h+$^:]ET *6wC2I&vM$ا&^vL:+[g?voj'P#'mfg[.υn7Ӛ-GĞ աhnO/j}W7#ޯ|+/i_ O#zi_ O#z<ǗS[M? 'i&K:ς/=G]o:]#x~v&Q߉f~E|sgguo_ (xCŞsZKU'6>۪V5ψ|GƟ kZw}RN~ [A=¿|LLjPĚ+J_[Z'm{|6uj^)6=msឍPz_ǭkx>~|jFc]oC:5~1|A׿f _<3EK$ލ;O$g? }e=Ea⿃zR)|j?c/ Oٗ]e ǚŸ]M/YmOZi*׭R8ӿ_ "ύA'5OGK~דR}oƞh/cU.x⿅fx{WxWc)z7GoLu>$ϩxoA~ |=ӚK=]zߤ4b_\5O\o?dE{(V{ %.~o/*J>X?Mc]h>/σ"Z\<$&FԳ^;7xi' ڪ߂Y۳u_h =>=Z:l91|Ap֚ݿi+`]mayr8M-[||rvRNJrV[V]5ӽό,ΣK j_ُGs FX?~ 5ϋ/<oÿ/(>|*~!|IOMk/|yϑ5x ]cAu⇆"<5g=7?~0~?xMGVDռKVP~Y)?`| .h_|VORmQfgO|ASsFWR~ᛛ CqOٺm'φ> ?L~5~ОG_+׼uOῊ|gWm >#[xoQxk]𥮇umg%uV~mEkyT==Ihܬԏ{wl~bx' ''c ||߉_şoOoχ0~ZgK5;ouᯉ?~sق/QQ_ + i~ M7m>)|l kY|__,%E+th|#{F 6эclNk<䋎i[ev[OYwg3OAǽ(_|Mm#VUQ^8>-{i~"ӞQ)5F~uk-Gu6C6i1LZW^ b2qfD1G?Ҿ)$k'/+S*PVXa(E/kz/4J /L(R(((B_+fW׵i_4/؇W7>(/WK7Ɵ}{_!~ҿY?w4(((((((((((((((+/ڳ3~}ȒO`I=ѵJ=ğ7siɯi]R5FխgPhdekYmᕄSMfa\/+̰Ru8 fmE:)9=rz%.jBOh2~IѾ)x<$׮~t~ .;_Z|>,n Đm"MX55?G?V?| I> hUľ^׍lxY<'u}wCM3K|QO'ix `~ڮ{J5> Y|=#y>Mη;Xx3pR6%D4۲4}IyVY-.L#Pud!iC.5QT,\F9'*u)T٥gͣӱӔկSkNCS>WYi;/S WWO4g>&;~+-΁9q>|%7Ako}mK蟳KuO߶>6?PxRv#\^"o#Yxt!%4fQ"(c?~;Fxg#uq) z> wj&଱4la6'yss)&^?O~gq|e~)O~ӿqV-i ya/7^'oWh/GMᏈSk''uo(|()|wbWÏۣ/{ =GxG~1x G<~ٟ=:֟|j'}Ogc{Vww1<6vsms C=ѼSBW!<_,3ֿMi-e D}j[y.' 5&x^y2+Rr{,V'o󽓓QZYy9uh ][ ;y-o)-c^߄'_pNJ&-m~-O~G_xm9/X'qڼdz./nG>F~Gþ0*xsxdm5?io{xve(חsCmIga\,vr,,i?JԛӖ4RU'nZp9IF1ri<ؗ4[vIknj#?5_ۇtŞ׼ iz|Eka7wjN֟ZHU*RH?xCƋ YMoökhtڎ?Oa/u @٫KO !3O?Z;7WGxaIJZXh6ڥ6:6~4t"Ÿ}ƻ¿w?ڞ0/߉~8>)>P_x cß|w ;eӼB'e~?4e/ HO1XG/gR_xKQ𗊾)x]~_~_>w/g¿/9R7w^(.|smba>nk> @xs~6Ӯ|/? >h2,gڤ:&&/x&7oCsji W߄캳s6 >.uIKI: մ{+:Uz?> ƗG]͎Wg|S4x_?g~)|@o_O|3ѯ|yŞ S?/xCž0%Yx0xRڏu?L95X?E͵dhj#T )z;¯ixgOsXJG5J2o>j9Ul'mӇ~zK}~< ſOjgj>"oƅ-woS'xO\xQO.Ӽ_vVV}}ZԊhE5J*IBII%Ku˱n5ko~5GwƇ7|g-u]*aY+{BN.V5K=+Jo-eh}O__&τow_ о16/ u kw}-mt8u=;ž.MӬqivs7nqy(䖺Σ}__|K /?;ᮡK֧)h^>⏊o~-Tc^$<@ejug5.-gQk_ǍljY<'B%^'}U5XDxⷉn9/wԾ{.|FCg?ޚjύO߉v>nŚ7QO> F}]#Ŗ:%nt CZ4څ׍c1K(|O%i [~ۺ|'OxeׇG|/?~י}ݣZz5\l~)c্ug5O | ?>|@׎9(]xş5_^|@pZЍ}>Z}O_ZPH{]ݗ]}>.ߧ9j7,~;~kہ?ڃ\d &3lj'kO|Cmoc|Io*1dWe'O0Veז:%m &;Ikiz*wW?'x9|kNMh7!ouؼ9m*hnSҴ^'/c}+$(ԁ_2*#—   2eLJW}SoW >i\Y_M$F/q{5<[{{e5wei=ΧawuK~ %\񏇵kY CEվCqĶb."l ʬ~I``srӔ!bTk8B,^&&*VH*έ>eR3|T>IN1qьZbͭϦLK{ 'Un?^:?ڟz>%'-?L?֔x_? {qQN/Vo껕g_?/+~W 9G'^)>x@|2~׾2+|H75o~*4yM&UFMWVr#PHt?5osvԾo'ߊ QMZ? q|GTWr|csa4kESQֿWWO>]|^,N5W J笤ݥJWVgN[7v*mɻIue]wgb߷_ oo,Ww?xoÿ_h߳O|]>!Xzx5gy#ß5:޷4 V+?>%OZ_Y,"Bx>- ~zf~xiO9^|PK;1 I8I^Qij|N[rϳ֕m}Oo_x}㞵Y|=/ sĞ = ZRq kW+ {C1.?>|w1s?D9z>5|^ۧU1NJRh;VrwջP2_%_ϊ؁0z!G#J/gkR7xj>0~#S5vOۗie>%8٣OŘp2"KZ$Fk!IQrO1˖ z~]n.wn_ys ?7DIo|M X?^x]  ~ Id_UQ3⇏m?x[^𞫠?еDխm5umKTxe_xMya7i){wuquu-Բ m|-xbK/hzNh${]"Ow ;En$'݁jbJttV*:΢HBu1SR1sZ)ӫNL.NRvJ*N*ٻn(2(((B_+fW׵i_4/؇W7>(/WK7Ɵ}{_!~ҿY?w4((((((((((((((((W JD~KdcX[HMirYRHn3 [~$-ҌxU0U@W4P̿_w=_w=}5E| u8:˻ u8:˻TP̿_w=_w=}5E| u8:˻ u8:˻TP̿_w=[g}:Mn$i}TfRjzݍ>h-卆C!}ZO;{K;8"BE* hh4Q@|]~Scn?#߳K]|EKJYg|]Vwug,/i]?]u?>xsνgƏHן?ϊiW+_s?SwHG_?oԭ~>SZ>|&B~o;?ۗvگ!/u Kφu|0~0 ;?GԿG e_?o;X"Xx#G|;|4[_E G,keAqs=|[ρσ&=럆 º/ic;F+i_i.o6px/^Ӽ;SE ~,ioA>64eO2Q㿍| /їGW\|m><_]|N[ֿt};qş>xS|=Sþ;φ ?o~"[GG Gz'47>O& +"x5 \85-:Skfo,v_h8nqJsN*8AF\d8JPRPwk]Yi].K;;3Mn${xRv1LE}e:ou2;Hhb7N?/Xxm1W~VaKHhb'N?3TՅG.?|ZqQfT\9_+Kۻ?o/y~/ߖ\x;$j~_r͝cH~Z,ml~``+$?KB~_r͜x$:'#G'ٯO^;Ckd6c+yeߕj?%][a?i9?ZdVh#O[ˏ\^>mmk MF-65AjkgߥTWE:U#Zjԕ)%NԹ:T)P.Qu9RNZY%J۵{_[\($(+w wGėq;z6p$ڍ̊pX: 먠2)k'/ +ʪ?J|I/ 6/R|2 ¿$ *h2)k'/ +ʪ?J|I/ 6/R|2 ¿$ *h2)k'/ +ʪ?J|I/ 6м3 Z5|?xr{- J-Lck}>|qlqVPEPEPEPEP_!x/OBsA ~C @^E,Be|~2 h <:FFY?_W~ ,GW;GӼwW^+KٯSO'?8H?7>|ioWw!?fF~3|a;}gBm|8_<^R_ƭ[ #Í?7,+<9ueE? W7#ޣW7#ޯʏ_ 4/ |_lo>ӼAgK)|OxU]J=vCg|? 4k?k?"[O~ɿuٓᾝZ?k߈~/eo>>o|83!dχZE3GšڀB?ҿ?GFҿ?GF~h7 <|<cςßx:F};~h!YwV7ß ~|M 5s>xW~?|QķNOk _;>512e}_'|6 `Þc)}^X?p~3>k-?`?~3>k-?``=K$w=hZW |x:^5_ t/ :O,<5O6> >;|1*Ϸ^ 7~ M? $g|E3>4|qhYO|?<''O6qᗁ |oo~=xs/ٳ?_ x-(̗6>:,?*A-'k4oG{ѽG4oG{ѽ_Z+ď h_>~П/C7CˉgS]"O#^ωxt(-ooOt5|T{5s~/ jzG~oV_a|Ixbt Mfۯᖉs_~$_5xQ_4<B|)/ i:6]RWPxo_ѼCm|6kN-(ɸIiFRVm>hujWLIw_FRWz;44oG{ѽG4oG{ѽ_>ntowUo߲ωU&|"|C I{"=΁?/sn,l9]_CMyS 3᭷oφ|Ph|G?u߃:K o?#|7>𯌼_{᮫#?M~3>k-?`?~3>k-?`;P/į-WUu8~Q~"7tؓAaه¿W|u 'u߈2xW u-CY#ܿW%GO>|~|iWoo<9࿋㏃u_/,h~bğ~ZxNWߊ>τҴ[ OԼ7x_ӵ +^ioQ +^ioWI@ܺ ~7~Yz~>U ~ CvW2OLӦ⏆K;{_p_ҬmگW:Ľ?MlO&n j_ӥ]:OKk!Y_V0T3Úw 4oG{ѽG4oG{ѽ^п9~8пh?~ŞK^ƞW& jx γwtxOKr:Z9kK~?oj\+e/?3Zca,m)^.X2_C⧆4"kگ텥x[>xne8fmN x?&~j'" O |Y-Ϗ~Jf|׿Z~Jf|׿Z~MPj4f]^a~7?KßW%a |JdI/kO|+ yj.޶3h#ܖDo< ?'Ľ]Zky5Z0?4oG{ѽG4oG{ѽ_wQGV_c5xTпg/ 4:GzO+/㟄i-a[@x"K>3$>(|5*|!xh~*njfO;n>_&/Ɵڿ~Ӽ-|m<_A>:kZ<?qiږ໏Ɵ}ZM_kxs3㯄7?ml+[xK|G gko숿ρ><_~=_9ƭ{ Z#R5=/EH?7㿇-|g~׺6?&;ş<}ᯆҾϥ~WQox, ?z杣_jMJJ|FN*=C(a>2[ [|J5jxS◎8'_)k|MF?_|Mx!x7t#G5}CPд3GGt>(-W7#ޣW7#ޯh~3>k-?`?~3>k-?` W7#ޣW7#ޯh~3>k-?`?~3>k-?` W7#ޣW7#ޯh~3>k-?`?~3>k-?` W7#ޣW7#ޯhڳ?~|<(~r[cgX<' f'4V?_ Z7+7J_^_o'??m (>B>|ioW}}ߴ7տ`uH5ǍO-׎ >,x7_ռ9iέ \_>wqWS>~l}a_x?3~׾&||G_<= ^GMB~=+=VR}Q d/B0j$?5Xt{?3ZDŽw_>}~*?i/l~$^d[Gx/g G7 gڗO~ ?5Z/~!>Y;xK{}j^f/~3/~3'_|gU  Xq_zW#qgWyw/Ym,#:~E_~ω>)h_|2_ ~"gOX?"'j^vCH_P-WL,>3P WB!WB!SJn~g>(R?e|@+QO>$¨4O|wMi֞;mkoN-#WzՇn+/OO9&kX_Mr!o5r!o5x׋mO?2x?η]n_O_|i.g/mCRI\ %o\&?,X|K/u*f4= ^ ߎ _^%5x~T-oǞ0A5ݟBo9_ 7LBo9_ 7L?sך>#Xxc_|kே/|TO%gu%~5#⨾\jz͍eC|5|N}h#SooCZa/~3/~3ITMrS]5K'}Iu*6'd֮vO|1%OW~+^jh _x|o+G#>i_<1'x4/q &,71y Ӿ xNxI<;,5>߂ 60<|!|=t #ioZOO_?Z+o^61m/k j~"hח?#k?M6?O&g¥L”*KrK+WMoםkbw֩䮗$u{^׽}?`5'xtMLj EM_⷇'?hu?|-㟉;Ǿwx>?m-r=Ce叉l _^g$>%|Q=VZc ;?/#HM?|BzֿÏڃJభ[k~?jls. [cj1Y Y?dU5/6߲?پ/^!t'?–Z}2i|CRk_ޫk/Ŀ,;t?֣9,?4ߊ_=b >|߲z행_xg3öF/#3Wu]O d/B0j? d/B0jt%? 5''?fp;K'{g-'pD< 3=;_0 tU¯<ok -ğk/V:W>8_ |UO_ /~rgm>/~3/~3q.| I _s_T1R߈u/I w*oAC|C$)'1֛h^0Mz+u +wO5|Q:%е=7RE·M)/~3/~3|y>&/ !ǝWUѮ~9Ж ҵ?ڋO^_m<_5Ly~_|FY.>"^V?b?ٳ|I!6/>'t>\x3Ɵ > xt[_k~ |ioW״QEQEQEQEQEQE}&Pٻ|lkjӷ{}&Pٻ|lkj=O^:OcQY1Oo* ?F~ gIa*~;|V"1??2?ȯqWi=?8|`_ l!𗊿1ӟCifԇQ!_њKcF*-> j"SLeT[ M?hl=JqTݗ4jmEԧ6hU7n]R}u7n~Q_'kF]gWO_:N j9wģaڿ4|5GzU҉}j1䏵WY-_ݶJ豩Rj?%Oo;!oz_()c)S&}E|~/?k2@;sV+Ҧdg3V+ֳy&%o_ WqT>gTWɋ hWgamQ?8;3|J踬SUo¯'O(l>%=Ej_n~-kZwo?BO? x_D+|B75[ 7ϊ$@eucog\y0=|RڍX?m̥R5%J4՚=Sm;ip%YŸKE?RoB(#P(((((((/WK7Ɵ}{_!~ҿY?w4( G_^_o'??m ( ( ( ( ( (>v?j7o|M^7n?OM3vq?=Ͻ~r 5WEfG//_7 Cs~:E@NO}G~?/>5ſǂ67ծ~?k4w+m ߱85Lh >5k7s`|5`,^Awľ(>zM5ieem:?_F࿈,O>*~՚i֛6OiV\2jEB#B1.nX:P.J%욕JҌҊaRu9' NI^\^OṶSY=.'~яB&~)K?Km@χbo:-/H<]OK-#U'Ўh^E}O//O_w| ~^Eǃ_6×7Ə^$W_>5UCi6Og {ß狾 ~GYmY|Sm .X?D$k^&O¾ \֟|W>y=~kog&x  E\>"BXܪ-MF)4ۂqM%ʒkvn:qNp-{oegm?ث߈m.?[֮5{?+cWq\x+ghDijoD\4Vצl|kt$Ѿ|J?m@/ (xZ/d_6? .|D_~Ԟu-2đ6~PlZVޕ~1coS7_'oSOajÿ^Dac[)ӵVvO[#kg7υ_.<# F^3sos㖧O_IgĽOᯇ|;]\OM5m_Q|9ơ' 6.-;*ZӒq*.K4:Q}%NJI[ΛKݖ"Z|m_]-x5koG~_?o]Yo|:OMx1x_txGǃӮ-;Tx5z7>_o^~~¿>֋++Mߋ~-j/tKL6(~_w5 q<h"?/e??uU_c 'KOQ0yX?OoS?u6~9~]{KV^kLsþ+;xKM5m_R׼7hơ'4Z2v0^MJQvMFڍZu^m=m%g3A+#~?~+'K~'Oᯃb+Ɩ5v=NJ5{5~Ԟ97?'~"csg{k_x)|U7|W.y?o&M3-υ |K.~"xN烾_|^⦡wga|+/|G?Mx/~'f^ݹڊ+蹣Ur󳲌_#vKVwwmV<l_  |5i$ _'C?|%qszĺǏ>-LMg|hKח-m?/=>%_@gfٯ 8x/أ0|G|Aj?>,F]'۾a]6G KNX357i{xY{*_($ ( ( ( ( ( ( (>B>|ioW׵+% g{O`z(i_,O;~|Jf|X#ޯh((((((oM3vQOo*_M3vq.?>>?`q*?f<<ҙ4k{r~Vkw.? zOװ?Zv<-o_Gѓƽo&"=O5 I?Jz+Ϋ?oW kfX:cOzA>ҡp:{_.1}IJJ?-# ^_oɮW"L5 /Au>殨?k=_+ӧJЍp3xS輁םV{ ѲVVMA8= 5=;5z5ҼosX./^xO+=?ho=\kǁ__Ol8=-Y__8#HGd~EEPEPEPEPEPEPEP_o'??mB>|ioW״QE+% g{O`zi_,O;~QEQEQEQEQEQE|_ɦ~?n?Z5_^>'T5/N4-sNXcIԴSM{=CN, )ž1h~ɞ0&?WO~xOᮉXZĻKj//?~ul_ȳVkTujT,$`Suӧ#`牜%*1jWս,sqnO*Ѝz?_N|uWB_Lط:@ׇg^^i:tujz!Inn%bFwuPMziEۿG2jnz6)/A*kS}uWBU~ɟ/j?)k`sNm,%T/W3븗a\rA?Jg]_2ZO :;]gu_Hn~3RyE3kڿ_g ڏ ~y)_LR9eo=Xoߗ+>׍p3\ji=:}?:L{QO_wu$?cvP:vn1IKՅk+Kͦ6zvՅʹ{KGBkWN955~5_K/ [[[hMe DIfi>qE+!~ك/:L{QO_wu?٧ci?OT_ٖ[}]|sy|34URiʥz5F҆"-'EfuJ/W*1:Q}96p}EW QEQEQEQEQEQEQE+% g{O`zi_,O;~QE|Jf|X#ޯk/WK7Ɵ}{@Q@Q@Q@Q@Q@Q@W|`_oxRP|1}s~=vZ=K-nçi6ƣOaXǗu]׉hs| -QSoT_7ދh!^ol IxQMׂyx^ ux6,me|Y/3Q׬,tr$؟Ť O_QtmsZ]vQ_%|J/~֟ k3x?EM+> xT/(4:ͯ ~џuO[G{·eV}Eǟω?|4x|pj?É?c⯌l~*kW֚=G ׬>,k ωU{ELe_6~8> k~ @~1Mak6 h~ZѓDx & _x^Oa/Ɵ>~^+EÝkweX_%TiZę4Ou٣k(Cw~7WoXMNj|Kh1Yo? b z= VpVWvU|~ɚ_i3O':ODW/m/4'kg=ǀ5m&n?j:KMž#Fju wp׭W? ~O9Ŀ_>|7lo"ǚ'MkŞ_|M>>'<]f|eoǟ<`./|g>=|!k$= %|b|77iÍ#k>2igᇂVu#RP].) >Ym )ctb x] __fi55E'_J )E'ஐ\\]Oz_q:ТѼ 'd|7T~ߴgo'~_~=D4i%ō+:~~~ct__^.<)>@|i>$m}qfw{ 7RKR UU߱/+Z`|Id> M3BM3iw~c3xW~ux{ xR[[Ķ( ( ( ( ( ( ( (>B>|ioW׵+% g{O`z(i_,O;~|Jf|X#ޯ#|F:֡{oM4ۛ7@u5J,uU]櫨^^^Yhs}^5m3:ߋҴ>x3 |K{+!n|KskcOolm[<0\- U]m׼O(u^{7o'joMtQQOt x~ҵ귺u+K{٣XVYctVpJRN:qsIͨS)5)6M6cܤI$oDշGoE|?lg.j#r?lǢ|k?Oc?+NgTa/u?K}_NYxf?^*wEbj1>q}'_oxkHM|m?O_?ꞧltCYn~0:>cw꺶0~Ӷn]:llaXiHј/lYP:$~q|{~? t?|I5υ)F?n |A[ O? )~ѿ>_t[߅i+׼=7É5/ş?_/ 5|;/~.J>>oѼU+? > x?ò cxYx_4Wn.?`?+[7uٳER&^|NO>"~)M` OOoT<_? Gּ&vޑ@oxwgS <=afMxXN)7P- jv02h_ ~ͨ]mxS _Y;6wkwo߆7K~:xsz_į'g&7ğxV6̿|Oş|9>:2oQnCޔd ?(`+QhGu_q~xwSAj?l?x\5?|<|e{~|7 O! &tA( _?:Εqi߆|#MQB>|ioW׵+% g{O`z(i_,O;~թg ǯƟl|WK7ƟOoOc=_鷺ْ͟G$ӢԵiiCG{}"-w?քIcOaO]xl|ujs8~~?~;gk߳W[w+qV_[-~Gę=?u6~ЇwŜ}⯏?>70=h1~|_??)E~1k>8qi7:?g84mKľ[ ~>^]CcR^KmewqH6ܠ[ni7SGi'{?/y~Z޳/^|Azy O]fSԮOZY-`6V(Slq?.MOk_B7w:63iiXm5]>]FlpyJ?iFeiziy %Vܧ}o?W?DƊL~>)nxe1O[D~y6då[Giܟ"}S\/^eez?O_io}y}ۣ![+GVo ٦ZnH#?d'=Wmu3~ɿ,x^<5=4?|;׾OVtX~6Z 5+=Ŷly^: TZ߼W IhME6b(e=|5)z.E?]MӴ _-5):ޯaqa4MUmq"Z'}kl/z-Ab̖I4ۼ|ׂn'(Yq<ΊG-C"x7k/c_hZ^,~ [A._A_G""#YmcMx{MOI'i<IzⰫX_. }5_+~?[_+>|Y6VO>_f7|t WէW<iqow×zuJ9UNs<-{ؚ2RqjQjQm4Nb%a1t+kgOFtMZ<$鏠gw?^v5㦽9=_⟎60=h~VUpKw-߷q&q`;O]x.񍟴!gA-먣6O7 A pOC/~fvώ>xMΧO 2R/u=mFk_Pu{Y]Gn46*M8($۩dIG_jSWY+Vi{X!͕!hؼI xA<&l_JKx;_3}^ɴ.4^jf{m=n/oLy5+[c/|v/wsk=ƛ'uf_~:u`o6 *'k~ᯉ?eZN>V,kt}_0~үO|UL#>u){u|$Z rQ?_  z?ď xZ2񟌾$?;[KwO%ty~Tկ[N;;P>?`/;şcL~ο<_jIWS>o#w6kk_^$/Ũxc2B:Mj+?w˽:Þ _\hk;mK_޵ǂGK\5|k9g>~?+ I]bwiiί[W|i%>GD=8ӹ>qSo٣ik߳A__v~7{9XhȬZh>^GֵOѯu}NHо8IҮ4O;K*i^OѺD$PVk2E )[ǣfn~(bqt)aX5\#R8qriYJpO]z}HR)|tsd~Q_&ڃħ)?:?%AM~?I!k$Vj_,+QKx?h~?5 |^z~ߴOe+|:^r4?j+|d?d${V'ƭKOe^_n8/\F`'O@~О9<'ƒS蜩G?;Rb+8K]O@bl>,*ƚ~/ B>|ioW׵+% g{O`zQE+% g{O`zl!eY֣9Y?o'??meX֣7^Je/guH܏==}UScxj[N7Wx5G |=KN_~ h^ %>913؟f} -|oƽGG4|Q?>wš׏l4]"ZO|jjZn&]i:. FsuЫj:RTfc6k\%>n"ROIJq|˚Fzו]Oc RGj|77'!? G~\=Fשx?SNGŚ|]⛟.# xcQA4?-' G{U|mx'oǣ隅>l?$~DmSDkJ-g U>[ T>꟰͇moI7KE߆/?'<uvZ)|=\|Y|/h[Ot_:S|Օ⿎+:} ŎokZvi%է!)sTZ5H(UFyzjF*\~nceA?zQdoyzOǿ-7|N~@gGY:콥~џI!kL?nCо&.~>l}U[ G¾9:<'[/x3U |}?Zoe7n91OW+(G=-;ԞJ0*כjm^]Fl(o?Qu?'mOȺ|'d?axk?/|{ /ě h~xI| >$e,>य़_orV {L>?W+m⥿Cv:tk46mouCg_H~۟P? KK-!؟OYX%o#_g?K7^)>' pki chiLJ|q) LQkxݣ6tkjܥM\ҧ&IGӕ4дeE4V|ܴԖ;Iwf୿ߍ|o]im%4'k~ZiMxAψuoz]6F5 å]ɧxSXlUw ~߳ĭ I Y׎~|:G /KC^ ~4h]5Ao1j7įWRw_E&W¿Y%еִK>5j,_ tO_|E[7)tw W><'+@|{}+k7wqxG-kGox@#S>(MD; AY:Ml׮D_*AK_Zk{/EѼ'>=k/iE̩snx@?wkxMK|A_?h_ٟ?Wm|$O i.?|QwxIuk.=SڶfB-j3GF jԱjz}oB_HԴ#gfG>:|XHo|=?>:wGuo~'Ѿ&LhU '75mwZt_ ض=~XԢJd"tJ67 ӊ RRISr<NR2RiK\.ѳi=R2v?w{f,xw4/z^M5X<3|+mv=h5]|t%~!x3jMޡ& |dU?akſ ~ǟ ?$Ox'WwxA k:gپ(խ4E-a_"gk|%[Di_-_Ȱejߋ>1Ӿ$<l~ ]gR9Z~=׋լ!Ͽ$OS|9eƟ1|;_khV>8ּ*E+6W4-eShƅWk˓&fܓvIk ]TW>z? A*g? _#=|Gkǟ|Co /Xx=oÓjRIa{a i|:??~m߱O߱? u_ 4o ?9~M;/<5+Il  b3ji w_ v>_E*I5~_؃gi 7.?VG3#~.xx__4jj: L牝:rFr\t' ?xN%8)-MGRp`Iɵv4[wjٴӲ<+~??<]yO:d"Go-T_?4?O2/˹{yl-xߍ~|-?>?> 7>~њZGSį.Oj#0oiqHSh~>?wO(ெa߁:w߱j/<'sJ{}‘iv dz?wo|i?a֬>%xKj9{ex~:r[vI'%MV乱.6\2|7y#jpZiM=~}WݏU?='߰O;,'g9 I ?~)WKªeχ~iG-~? j? 3ß?>i?|suQ|im3п_m#is3 1O??iA?@Ŗ9Z??wO?n?A?gs? ?gW aª? kOs"8b);sm;9qmlIMFҋoel~}^-uտ?w_ ~WQ(Or?T ~W7N0@ގvg?Υ.OYh_WG,Tu%n&<#I1]I OBō>?ߵΩ-Si*񟋾Ϗ~ >,{i^G-xkZ+{}N |H KOkS2Czo,Ï ~_ď?~4KQcr|\]kþk|4 ?"kϋথj_۞4?ýw3IHi+D|e~0wa;_ ux煵_yWHvλ{M#_<9k[oo&([skaepJo?{?Oþվ2\K喟|@oxCWԵM@ + tL%N .MS\eR撮j؊IҳY(AIFqYKW/E8[R{j}޷X=f~Jp$u:'7iׂ:oPú4 }sWKO'k_ -c~!k_ bX)?x?~ſr+RE$;kZvmmji?kŸm_!O_M|o'ZgC 4?kGl'qo@D 7ceO!4~?_?j;b2χogk=Źlk6j ^u*PRSIc]ArI>Y=le%n]}!G A/~4{?7FOi~"~^7?[D&WW?_dg'<5k&z7jixA?f_~84^"'OF|^wb[W'IZV҅@b>Sڞ?كxW)x͚=Z %}//5߈>6 şoox/6kidN꿨?+%7/ _|hehgm'nlY<_szw}ľ$S὏; px[z%Zzbs QJWh$W7x{HI^.N7wmz]4m(6iɦfTkψ?&laٿ?~-?Skÿ _Oj_| ?ǚl|H|]8qHK_^. 8][W)%:彿SixsWԝo>7FצҾ}hQCSO?Y4W}#N5{69> 5tJ01% <uBh_O| 5j>~ Ľ1L#Eﺽ9}ҮĝAOj$ p8z^?WV{ 㩲Wi}.N;+Eߗz$Q:'>=kͭS_FD1zЉ:c?At{_kB$?ּڳkzF6^oC@/T}dsF/-'X;?]T?} c zom/ e?82jb?+q'La{ӟJщ:c>ϥAgքIve]u~Ě{^G4_ו\?F#E=;VIu?^uikm>~+ɣ\>WNOUAVk^mIm_Θ++ޟ䨽a^z IO=/W"\w?\e~-^A]ߢ'>ߥ^Ep>TH'?WcLc?Ҽڳ~(rxA*I{r~zZOo^uYy%*/O/Ŀ^6`> C~W?\v7?zfGSol|Jf|X#ޯk/WK7Ɵ}{\EP_o'??m9)n=~8~ʿԿG^wᆙuxb\W-|A7ЗNw'|OKk նjvVFU>̺0نUN3 ZIJ'6յս ԣZJ#}MY1=?WZggWooG/Y\a#cD_.~݃WyM~>%e"~W[GI.EdkcVIOz~?xVi%]Rٟ?j; ?yY-.tkOku.hy_yyyI=;{OAZ8+5Ӈ ?o*eY|*:G\=d*˹kgjzϰ5ß~ " Q» 5&efwK^b1BO~z~ֺp9rغzzX٤iU_W?L:WOO??km; U ?k; ߄5:YmRҕK'OvZ'Ov_G,t&0To ZEC^ o[kZ?=:~&ndQqp.et 3o| "ŽιZo/4k7;{`?Og:~zw 4ωrmVyQbKy4}Y?<t{t޾G:~՚w ~4TgOڻNz{KlK-]˿W^^^z}dNÿVk=Nv?J~4p?}5H?gr?k-;? ky_/&Y/ '}گD?~Z|9?h\zd > 7zGq\7-zˬx\IӕVlf{˟k_ ?k]8ܿ?َ'`jޖ'Pz~;_[Hҫw&S3^ϯOU^?>ð?Ҿt'1T t4'~0$e2Ξ/w ZKJV/X~ĝ?_a~?hD?_a~?~vPӿDRPӿB\] gaOSgb~ןO__W> ZEC^ o[kZ?=:~&ndQqp.et¸sJׄXz|I):8zT˕ ǚ1z-)jr)NRIٵ{5{7W]!c'V`*Ig'=k~22Xx6^3?uxՠ0-Žſi^ƹˡ6wVnDW?j8wG/=9m,."V*IR48ŭ}h3WB)ŨIgӑ'{'A?|?eۨt_|0?iwf_ >#ʥ"_:}62XB 8imc(''B5qwW#dGOڷN[R٧ڿNjwlO:VPo^1'Ny$q>zŃnONU^9>#_گMk/_ 8x>4{8~iGTi [ !k5=1{$#տ}lVoɟjO?Jt;=+a;~çiGC%H?g([iO&cYROKwR^^~=38'?צ>{z~p/|%T s Ի'[ +E_<^Mˌ*'wJ^^?X~DR_1o~u?P?k8c_  \cӸe嚡+W fIשF//y; =?h=-|i#~> n xGCŗޟ>V'5W⧍?h_{O5`~ xYӭ[Z/e9u]F'bASZobҦ,sYb"쟠si4 5 %~xZ{iky!`(ѣ4e*>((((((/|-!yK l4c[/QOf-ȿg=FG{?9( XmO~'|b3Xk? -bS⯇=:ZԼ 'Zχn};VmP>i@Q@Q@Q@Q@Q@O~ߴO߷| kT_SYx's|++ƟKtx:5A>-kx/^-4C:_G%/ǟ CdɾI'Ưں|5߇~[-7ķωzƥ=7ǃk`{\7xzDŽc_j?SI׼'|JxtٯW׎xK>1^-惯[k: ksw5ς:>.?૷~%xFk xw1?Ǐu_]9wS~Qd|+~2AWx· i>Ե_/tX5B_<Ϩ\0x&ڭG%4[MB9u%͒^ /X_&>&SC;'b/j&ۡ_O//3of৆<_|"tKMa\烼5k[2^9Dд].W1:wx+g_ ΋OOx'M5k&߈NԮy$[mI _^l-)VS-.HJvJO]"HRRJ'Q_{iz㕖)M T߀me_ۗ6|N x? >?4,]|BJ_0?"Cy+Q-{miVj>o _S>G_ms+N_|aqxĺƭ}izcqkMXƗ֖SLӢ1nnn-Ui#i yEPEPEPEPE~q|t)/#$ ߳~?f QT~!|1{C[5/HퟆQ{oouA wrJ _K_I?+i<|]<5v?3c>>y|^ֵ׎P'V_#k7WҾxগ4~oi>X|sgzxS|o+h';]jUn i6o t{?oxw?gO4~ πG^>-YzZR߃7Y?u$:5~mjm7be2k<kZ5,մԼ[jW^6^j]%ڍ\gSwq hׄ;6z~KR<#3{S4O2~ѵMV`S%Su5mIҩ:mpjٵtz4c$+v++=6Nӭ-4 h,ll$ kkh#H`H8Q@h(((( G_^_o'??m (>B>|ioW׵+% g{O`zn xwW|Yh'Dhh~!u쟳}k3R׶זt[qb"gScYc޾I>"nx_wwU4kߴU5=N{k+o^Yuq{4o3U(C_plJ<f ¯{jЋc3?|?J<r~߳xh#?>~/cl <}~,x?|rՕn˟~iXJ _x77~)|k+=2[]L7W:0hw6q=䷯I~xƺo/之^kl==湫Ikh^,,. uei Xyh?zI\Fλa&-WW4 Yx϶R&PG{nwu;v[ )'SL{o5=CzCG c&?iW|;iw{|(XkJq xⅰRG`?c,6"Qu#'5F^|6spU)5MEs?jFX4^ȻYzH9 wP#$sY %[*O~4eoWxKWC%Ƒqv_ýL]+/K[iލ?#J_wv?go= L|5暏Կ??}E| }&{>M];\h >2)kz/!Gźosie>qơyai5w7<-(<&.sE^RWw'lj6U )ŷ=Z(r¾hc/+.ii~G_\ش?yURlO} `(dxCN!1R WW83?޴3f${=kJ$=+ch%_Ϯ~?AzUDc >/ Fc_Umx78|޲k7%}tӯa8׼i䟏>>пo7foMwo>>i\KcPB*FjpR#Ղܟ5j-CVu])>I\4e}Rk7|;X|B֏ 1,u'^ Uk[p<>$oiN׆+He/DşxoG/abo+ tu[ZZ|_[bAԴ0Tߴ!? Zh^x㟇ג|icsoxQ<(uUf𦬾2xE1QQR<-q~YVml~|ֶ6E%PHEj"|?&Vvw`=٫5vI7t~ߖ !o-BYߧ>/%eQ7O?'~!%T<2[ѢHC?ϳ>S?jF8~NDI_ڜ>~|EGyF=o m2Wh5Y-_?>&׾(O |;}=#m<'gPxJCL jo7Z~gW2e**᪺5cR1ԏ-JqJS8ݓ$HEWN.n/I$՚kUQX~}|J]ߵ?7oVWOmD? yo~"֞4 go?WB7_įVawlo+?d-doًW<V_k> 'D)x_|p[ 1idjC_Hjҡ峫7=i/zEN"*'F)6ܒꏕSWIi6v1攛jE&n?UOvVI?Wᶕp4/<~?d]xo <Կgς zcuῌZt"|?섻ox,}"]KkȬXf_7~\zVYWMӬ$)rF.O'[[4ΤZ/kf.-*<=a'Uú?^$o=bk* ୔e5ǀ`ӭ[O7{=a_C6 #[{=z8Yt%^O)WRԓIJns\"q;I{m{P}j[tZ{.K|(׋h~Eֿ?DS]{IL_ 㯊<ݞ~-@Ylu᫐ai|yU}"vI,snNN&ݒmmeImB?:Ϻ___]D%n_4n?OY.?Y`[weGR_ Zj_~ /#?5~z ga;r~|%k¿k~$F3ĺ<;M5+6t{(Mq֟B~Osx/ t#i/٫Mdm4.aѣ/es< |+UVTRnƔZuk8U6kj´ STf95-8~+:i_,O;~|Jf|X#ޯh(/WK7Ɵ}{_!~ҿY?w4e;:%_Q3|f[D>żH+x'(sEOٿٿmдuoh8WY?-a~X_!˜CxOkx|-g[ZީMn_bu;IO*Vex0B djJrQ僌Qqi1"nW*1ݗ#滺wwVਟ -Cٗ (o?&[Ě.//>({m/ I_,]xgWkþ:po_H2i8|{q|,~5nc/(fg7=|]>(xX.c:uϋ/Yt[o7o-?ϖ?ּ[.|#bEI/ҵguAk k6;8i8I5Vj7:Y4rfNn!NEt=b)Zyyjz]l|5x |L>_>x{?|xn4FK_<#smh7WE'\4R~/?Co8O_,GM (C @|R?ڟ?[3|'x? %-cGGZu?hG`^0}Se%nNumi77v+A~ÿ;Gk.?|9w_~1#gMOt?MA/j> ^_/5xӭP ~?W|*u)Zg]abx__?➟[ZGi7?2_9-_mƚ'>$x6Cӭb+ΊO2\-+|$0eRnMfKŸ^ (n?x Ӽax/W@|Ծu$^׼!nZFxֱuh!?^ZN__ (j/Ttx'Zֳ;&4u.f$Yon_)xO|+G (ߵwOo|3?h?p?|1aX/|/߈l<e]Bmyq5nNQ\3eԝ$U*7([hB6m&z$kw~emKj+?`ُ?|v}^x᾵/ڗ? wFk9~6|ol7 g?DU֝kVCB zs K$oe9gS[vaw e=7>j,gg=[wc~?qdM4߰\Wxju?nA 0|;X>9OCQF 1e_,xjee G? +cW*N9sa.1oݤof.QI×TB/M,׫<+;B; )K_pW3SGlq8:3 7 ~=~!r{_Uo;/g|jĿ|fw/PoIt>K?mfhº}ϊ-M? S7m~(|~ ڣz_HOƭKM􏆚lj5TѼu }Jx/5ejOy '[Ή V|<> C ?%LOi^\hc;Lîx~, Sk3_^ RG>>x _PM{ML4Z啷/hZ=okz%/mMsRyi_z^M6mW:7EًaIk?tÿ u Þ/f]OME᧌"״ #Oo O"L}boox{Ś/_)AhGWgP~XxZOSJX7▵^5o|.iῇ#gs-v~56ZNfGK+sf9eڕOyT)G)SVk$'w̞VkUoll> o?|! ?Rz3_ g$ko-g(/_@uKeا_ e#FWh߇jɩx[ƒDM]Yx¶GYb+z|RC<_uow\⎽u/Z+WK7W :~^ࠟ>|c W_^4?|7;G6aOghOlxj*Y8mu G˗_bwjM>h*IY4~]{mQF~~.||_?S%hO_jx⇈1x/Kᯊ/V{oxmu/5JS ௉u;ƟO5A#O.Ծ';o.7@/ּ#uj?B<1|L^8t*?goZ'Cog~?~.1E~?|D/ G.xzvZeUZ*%RrISNjtҒi8S冱P|5,’\zwiߪ s7 _sO~:x*$b,|{ *|}y>|@׏,^ѵ;=SWQn6.&#>~m?-쏤Yx_ oi.'Jh+ᇅ|7៊8|I| ſgb/1.|[u/IJ!$eψw/O0|Q_x]-\֗Qeok_<:H]𕾑ggmN&g_hx_NLi~+׼}mG/h8.jizލۍ:KlܴWqyyIs8YrOyɷ͗.{Y=wiNF170|?3}k2)ρG[߃3Jm`_Xͨ+S:vaEWl| sZ|Ggl_WcƯS#. |>?QX?Sş,<e?Iu ~/|,GK< >۪xDxO4ڟO:WUꮮ${`vk(2,(OZmI^!$N*NZnwHpגIjW$eMl{=?C ?M_߳G7|g_/?_?Ko=kRׯ#_|_2 𾛡C-_<;_~-iK>)Coon/k -?eoA?4QB,մ+N+c6zt}Z҉03Ikє7J>PbrqOݶwZӯ4iMmݶӿi߃>? 6xcִ/7¿~:톜{)]O0!šM|:/-Y<"!mw=M>fi> 5#To>_OV4{O*EVHT? dO~'o/S 7--soďkM?i ~ʞ>|_VzݢkxM5b}Oēk6h/ߵػY 6Z9~ |14n;߆_ #FeKjگ5|Go |M36Νijb#RM(I~+{IOٹN]mIc3TV[[r2Its?$|WOῌx7t?+?W]x/Öe:'^Ӭ> 7sbˣ(jُľ;`GS[inߋ/ٗzAZ|ZxQ/<Ɵ ]/k=?T42":NcJRrr˚ruhլ7+\-kn׾moS ϟ\ߴ  %~,ZKT״خ25Cÿ)V/_x~-}FpO5=Ry/|qC7e.g8񧊾%_mPѵje} c \xsU'G'q(_t=4+vXllSJ^X4sYyzo~k|,+:ϐi_,O;~|Jf|X#ޯh(/WK7Ɵ}{_!~ҿY?w4eW0ʩD?OOVտ?iJ$xbVh{9|\ae_4I?Z҉=}/J$քIlWZKWٙB;[e]:v~rR$boڻ׊ |ou?h?|ikϋ w-Z|P_N.jiV5P$/-~Z7~= p_%h߉#e?ه39į >|WǷš7os}[xMz='! +1$>.2?ZCSG//_oPZ}[N"9?fc?r? 7%}i3#7ƣJ=<<jϗB/.]5$hȣ-m5Ne+__gg?hWM 7]?5{t+~^-ѼoGzѵ{"ᇅ维ݶ=sIM~3% |u'Q<1j_foz> 9k6kIc⛯ xLM:⽊S׀?~|qkm}#";O-𦟫x㞛(>"b \*~~<1Z~{㏎<5⧊c-[I4uu-͋4V,SsrKF՛wi9^ۥ$^}>>Vi1p>Rj({^5t~~+T_/4I~~~k=f/_'Ogv_~$W*u_sVJacEWu|Qg dG?'+z'?e*v0o1,q'aW֗^:{<Ҋ>Ƿ$=>(?j_OgNmwК4ߞ;#?3mS198HSWu_fGo 2b 4) |Ju{ٵa{>Ե/>~b_o~v:_>>7ƧoOe0ŷzA ɭoM.QբM\OcISW_~xf=x i[🊴[A+K߷O _Mԭ|;ie ? _}?OC G_ x[L=?>ׇ_s[h֏EmKn6*IUj1Qnj2:QQk0z([3R-lӕ'k޼?J_G1cKuoU<=kljxEբ}rLg"L $9ᙼ¿>5 "{nZ}Fͩ\Eoou:A I3,H쑦HnF j2\ה4)7g&vm&[emmfD|Ehĝ>=?~'ҴNOӹyjo&|M`_~xS?鿵_5a_#৾o?ӿj+s|B}Xsj?مQ\F>6]ߵĿotԟ_f+:=+f9Vdv'?kVr\{>oky+_IN;MhD#ӷӹ?Cv=}J$c?Z=ޞsV{IO~xc*FmS=?#/|1O ?+m^,׼Yx-2ž.<jZal%_-3 ž >?o?f*WyUNJ/wO>|7|)}yk&f'4-?S+{-_:eF߰ÓZH&ZԔUQR4k;]'mRѝp]߾孏oD_1KxRg>%x .@:NM_ $S/[ώoi|Hڇ>CW5Rԭޝ5 x? |'O//ZO_ƍ(BmCj:2Ƒ+ zj7~ƙ ~~J$oCSJ\OGVm3JN:NI~WMt>jЉ:qۿaCtg?tͿVӫ.˵n~d'kP7/قCN~?QtwA/5jhf߫4t_?I袊Γ/WK7Ɵ}{_!~ҿY?w4( G_^_o'??m>FFuXj2+֤:{oU}㟇>(i6Ŀ3gìZh9&VTuB F =BIV+U~ɟ/j?)k*6O Vy.j~|SM^~'NiTQV5Ւ]ZicOz+FqJU&н;?_g ڏ ~󻮙6/s-޷"8 SJ'{CdӷۥiFs޾LU~ɟ/j?)kuWBY_c4t4XJZ)ka"cwh8>?_g ڏ ~U&н;k\R4*RrgQ'sA҉:~Z/U&н;?_g ڏ ~IrbUci_%ʏX__#+0ducU&н;:go0|qM{H|=zvKuK-^ 4iiumAIpSuԣw5ҏ?5%~^]/u\+ɵ8Dmծ޻xQE_V5OO+%xռ|;<|C| >=uo+4χ>(2 mV{Ev`1r],eSJJjmUR9Ӟ'YIjQNT'ZR\ծr~\g3߁OoV?>:Kz+ܗc/1?tYu%J}/Uj~A>~'Ku[ry~X$K|uڭoNɄ,8i> gg:b/NEc,vl+j*?+S #eBh?~>e?^h/N!MiK|g_V_RG G~Пzzߝ[O)7>xgEc,te cWuZR) .|x|+G_'­/x/_-_|ifrYF/.fhT 4Q\8_||OG_Rc¼G,\?%# a>94xth~QKK1oW?/?5GOA?`oG>;uQE/ {š{? y#d~GO+OF>O >"GON]N}"+QEY+% g{O`zi_,O;~QE|Jf|X#ޯkgj h Getting Started API Reference Developer Resources */ buteo-syncfw-0.7.21+16.04.20151216.1/doc/src/Buteo_SyncFW_-_Sync_Scheduler.jpg0000644000015600001650000007270012634332753026520 0ustar pbuserpbgroup00000000000000JFIFHHCC;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?:|t_ |G⏋a0ᥦ}|4gP>,GW;GӼwW^+KٯSO'?8H?7>|ioWw!?fF~3|a;}gBm|8_<^R_ƭ[ #Í?7,+<9ueE? W7#ޣW7#ޯʏ_ 4/ |_lo>ӼAgK)|OxU]J=vCg|? 4k?k?"[O~ɿuٓᾝZ?k߈~/eo>>o|83!dχZE3GšڀB?ҿ?GFҿ?GF~h7 <|<cςßx:F};~h!YwV7ß ~|M 5s>xW~?|QķNOk _;>512e}_'|6 `Þc)}^X?p~3>k-?`?~3>k-?``=K$w=hZW |x:^5_ t/ :O,<5O6> >;|1*Ϸ^ 7~ M? $g|E3>4|qhYO|?<''O6qᗁ |oo~=xs/ٳ?_ x-(̗6>:,?*A-'k4oG{ѽG4oG{ѽ_Z+ď h_>~П/C7CˉgS]"O#^ωxt(-ooOt5|T{5s~/ jzG~oV_a|Ixbt Mfۯᖉs_~$_5xQ_4<B|)/ i:6]RWPxo_ѼCm|6kN-(ɸIiFRVm>hujWLIw_FRWz;44oG{ѽG4oG{ѽ_>ntowUo߲ωU&|"|C I{"=΁?/sn,l9]_CMyS 3᭷oφ|Ph|G?u߃:K o?#|7>𯌼_{᮫#?M~3>k-?`?~3>k-?`;P/į-WUu8~Q~"7tؓAaه¿W|u 'u߈2xW u-CY#ܿW%GO>|~|iWoo<9࿋㏃u_/,h~bğ~ZxNWߊ>τҴ[ OԼ7x_ӵ +^ioQ +^ioW/k{Cǟo+Gm/|RQ5_WOh cOڋG>!:k ~Ӟ+?Y-O4iƩCU gFv_όZ_ x;Ğ5/-#OCzxMHOL~|&r鶚߈/5o^%<0!Y } +^ioQ +^ioW ?9oO 4/OW@ zjc>O Ǐ}Wlnf \x'A/xZ|Ӿ7~i)~0~ܚ?fo?ߊ|7k/ c}VO~>x7GÍ u/ +/j|uu 6_/-oi_ O#zi_ O#z;u~b_5_h~' ?e hSx]SNVb%,k_喍y OE/l McvX|ow?xchSV?O➃?/WK?w[o^񷉴Vo |9>1Q=ҿ?GFҿ?GF|j߶JO?j/;PƟ_ٓ㏏|b,ǃ>%HE>O?j׌v^-/&{¿Px[Ŀ +Ou!/ _oMxS@7ÿiOX|0?OgíPONS>H?7H?7W߉ڏGOT/<3B?|Q z'߇oGr~5ҧ\Qw|Sx+~o?v~3ڏDB~1࡚o7?,ӿe߆Y:~-7+s@:վE{/:Sx~H7Ň5o~ux> ohj74oG{ѽG4oG{ѽ_೶)ߌ|Jҿe-'mGf\=Qگُm/?<|7~5/sĞ5gFļ+U>;^|_~"]+2~ֿSƟ <1oS"#WO>=Acčoox/z_ .|ecw7ӿi_ O#zi_ O#z8i/*?|!7!xU~h5]n]>9A|??;Oğ $Þ OZ)ᵶRĿ ~ֿ<5k:_'|A?焯3u^ ӵ ;\f+6ZE?W !W7#ޭΩߋ/<?/Zh(>B>|ioW}}ߴ7տ`uH5ǍO-׎ >,x7_ռ9iέ \_>wqWS>~l}a_x?3~׾&||G_<= ^GMB~=+=VR}Q d/B0j$?5Xt{?3ZDŽw_>}~*?i/l~$^d[Gx/g G7 gڗO~ ?5Z/~!>Y;xK{}j^f/~3/~3'_|gU  Xq_zW#qgWyw/Ym,#:~E_~ω>)h_|2_ ~"gOX?"'j^vCH_P-WL,>3P WB!WB!SJn~g>(R?e|@+QO>$¨4O|wMi֞;mkoN-#WzՇn+/OO9&kX_Mr!o5r!o5x׋mO?2x?η]n_O_|i.g/mCRI\ %o\&?,X|K/u*f4= ^ ߎ _^%5x~T-oǞ0A5ݟBo9_ 7LBo9_ 7L?sך>#Xxc_|kே/|TO%gu%~5#⨾\jz͍eC|5|N}h#SooCZa/~3/~3ITMrS]5K'}Iu*6'd֮vO|1%OW~+^jh _x|o+G#>i_<1'x4/q &,71y Ӿ xNxI<;,5>߂ 60<|!|=t #ioZ_G+!fQ_G+!fVe Wcn:j&xzo"I&[Ó?⟊o j~ |;Ѽi[{UrĶ|/i3M]Ɵ(`a-1_vob|i|Ks>]m}IP[3WB!WB! ~W=ii2 o o>1>*KᏈ>w2AV~4Q^xU<9#B:P[|9WxCw[/~?w떺h6{Q_5K'_i\GMxN|=ci6^!_G+!fQ_G+!fP0 Po~~|>|:R,֞վx5YŮͭ{ľҵ b_uYo$6?YVW_r!o5r!o5tdOOk_|I_R>>~uxGEԵcEwO?|Fv/?7^)Dvl$-aM>ZmO'?f 7+} | aOJx^F<a7w^T|IIr!o5r!o52u? ~?-/CqdυZߋ \WO; R_uj߳Wo5=F⇅uv^[?_^:+x"}{7D>(x?Afz*ߌ~%ߏ&^Ӵ":NxAt>׆/~3/~3 )CF<<-?u_/]м#qh^( MǏ>g@~(Yv㖩KVyQόS/>$pψz.|@K'G<Kׅo/#Enheկ#/{ .mTϖ*o>\j6>0Z_xw)Û_0DoO~|o_Wz׋cy!C G!C @o,a -~'CS>6x^&~2A3'ğ(}ssD.~>(U_z_=oY~_sx:x~+|Mg[1׾<I൲x.ִo y_G+!fQ_G+!fPO~ > ~kg?OP-^"'q𵶟9τ?><_a>ӵiZ"g}b:~~~1Ě*1[k:K-|+xAMCdžuZg?+?h C㿅?g/⽵LaړDάz2E,ӴOo[BSZh$h:~x?t}'wz =͌VͧN&}_4K_><^ rNMƕW $Z{۪鱴1X(/5&F(/WK7Ɵ}{_!~ҿY?w4(((+sI 43ĺׄ/h#^×:Ɲߴ]ĿcŚ<>k,n?4+3ٯV?eD{|6eϵ{LvJۑd aE%m~^ g^.YxS66m#'?ثCϫICT=kٵH譴dIPOxm"xz w=?g ״hL ezb4}:K;9[ƿOǍ?c*[x|cq#𮓦ާt^ O3&x:~<$~E>\`j >(=5oQU!㠍JaIgEޝ=bSUڧͼݹUZTUN4J%L'VB9T|}*SܰZPk[M52RW-Qz~|(|_k^9xxGj?|UiYc¼M:]e:ց Zqx#WWK_WO |ec'iE?_#YwjhcƟ,1f toxcĖ2!z?)ox[_ >R6_<F5O[Nǿ:oHex~*>~(]GGo;?xSQϪ tsŖ7->m~d'Qɣ+/_'*n"/Ÿ<3cV;+t;H>&xoc(?gi~$2k\,"3n7rWtVuwb9ZzSsl3dO\߲~l'7u@oCs& ihz5oj>z5]]^[_xZ Y2jz;SE^0u1O{OJΥX-(܆ y}nu3dZ}|M6AG⧂uxI't'/ &afhjii_[MDZLgjڿ5`O?lo8??~=WMxX㱪 xB1cZj1"c()F)$Ithn6mnmݽXQEjQEQEQEQEQE?f4/{W7澽i'5١~4Q@!~ҿY?w4 G_^EPEPEP_փ:NIUb3~#'Ua3骇.7ַW$~?Z'?N?J4sOzЍ?_s~V{ܗxV(˾"a?o=w㆗sc{d~|<+iz\IucóN+崉;wQ@e:~|3E'7e`o^/ :76Xwܡn4}}ϧ~4?Oּʝ=)Ir+J{ի^]:#ug%eehWS"fRrgO?]xg+E.Ƿ(!gq#Fmo Oę43(lWNKiG c>cװ?Zw?S?Pxu]tWW-[sO<[+xlo1G߆|4xRψmﯠ7WQׄ@e{:+xK?foZ?^o~>5xo|KW](<ox^-B-QKO|E;֯" >zrkhWueOMzu6M}o<\~߳gO~2oo^o|\tU㿉: Ѽ}/ϤSx*յj]3OR{;cogoOOsO;ko %fQOx>5kžҵ&Tg-役y}׭^F?8tvNT~%w^'A?hFSޡ:{~z{:婬﷧y߰/߶?'į^6?`~<dKIc/mC4?!Q\EPEPEPEPEP_!xOjBs>k Ɵ~W @^E+% g{O`zi_,O;~QEQEQE\3~'V`+7'<_žӿhĚ}jl->۪j6jϻ-M7Y]YY-U4Z_TM}Z&1^ 75 g; ZOO_Z=3g; [OTϭ~V5uG__Su^^~w~{i}:{_'C}?^C2?"8ssW?f%3{}?jӬTӹuݽ:7z~w=4:ҴcLs_=[՟ۙ?S y3*|cv1d)cc\):)TˣGןRw{Qu9%[=z?; gqhhZiڞi:nVRgiڅ^M-ݴOE"95?cSM5&5ZwM=SO{Q +j; pQEjQEQEQEQEQE?f4/{W7澽i'5١~4Q@!~ҿY?w4 G_^EPEPEPEP̿_w=_w=}5E| u8:˻!V%~}h^5o MtkmΓ]GX񕎗a6} {u g&x_ZB4f 2w0Z. {+ J}XKIm7WO4 V±]B/'_ |;x-~.QK֞#񵅯$xvx&.d|/e8M𮛢isO;y_w=}5E| u8:˻ u8:˻TP̿_w=rd/+Ҽ5#[< +-;Nߍn/1/;[M[_}>Ep>$#o |m~?>, ?K׳_If~&9oug෌/N Wyo~?N4~?N4}^gwݥ_)}bgzۭݱs sq ̎"T #[2ssoW+`ީyG{y>o3ˋ(((((((Bfo|׵?f4/{W7>(/WK7Ɵ}{_!~ҿY?w4(((((/~ZN\4\~?_~ |V}OXqf-e+˝OZbX{uf|a(ՔQEQEQEQEQEQEQE?f4/{W7澽i'5١~4Q@!~ҿY?w4 G^⎣sឡV^/բ? ~ԼC?4GGį86eΛe-RK &Ycօpc[VPy՚N u?k|#+|_93K? N3J+B/?O9*T"z|#<S|+-Q_;1hUYK6k^k>WM'Nߎc>xgÝ.KKy!!E7my^V7/aGIR:j"Ѫ79RRWNkIrݫ4E*ΔیiYJ֜bw(5 OSޥsVxSN'xQ~|[n}&t4=Ff9 x3n$O{C]|M_x^aCZ|q=j!o|/jv)r|LJu=SEPumZkhKIK_ Kž"f_^"~uOpaZեSMt4xn"tk=*D((((;_|x<#}Im}K:wMb mehb.'WXng,>c{oa?mzz<{?եs[޵|+a=9}eksG{oZ/xᧅ|_czO-N Mm;oRhu)sCો=4-_X[]𶋮I;⾟_'iM(5/jSsk~/ 7[{躾w4*qc# ;пj*E< _hxsQBxX jgJ;i| 궖mow߱ ?f_ZO U~1#גxR:zgG(ֵu/|D}9if gb/MKjEvpm6h.;;m'iϏ?ٿg|lMλycCW1O ^G<uF".}k E3\.:JU#F2X5[Ԕ'8ǖjW9gfz”ܤӅH䴽ɞEWtQ@Q@Q@Q@|O?٫ k/_h_o'}{EP_o'??m_?>|ioTz߳Z=ݕ/ْ=.Rմ=Ei cwivsY[ܘ&{Kxu+$[Zv}_SłP~v?ç_|p3?=~U~:-'A;}<^ߩݢ)_~֍5OvMQ߰VY-_ݶJ豯|;[¯W]>g)C~վ0<藟E3~gmw'oMi"kd_O |⟲\y?4Mjsa-#ķzue:[xLլ/nnITHQub'9Tߎaύi?/[Ҿ||!súO x]֑P/U_u7ž՟wΤ|;i؞ ZO_~ˣotMMHiiuFY]EIq_އoeѾ$"eGM*S#-[Q_+YZꖿk^ژ-}& Kt[XG9Ʃ]uO<,W]ؓ>zVIӏa^=+c>*}=^^ߴ@ggtz55wU;¯WzES&}E|jh>+W7eڬ-f?gooƯG~ҳy-o[ |uAkMS&}E|֌3 ovm?={TkG;g7l:*# |XaƱ1{SC>SXkB@;_o\W7aoï nwB\xZ<{m^^wDѮg~/|Zӵ?qaCo.bxN3jB3'Q8!NJtm.IK]+m>Т+7 Ɵ~W _^^4пm\O(i_,O;~խe QY+'GZ߶W?zk?W)RbݱQ_^{0[6i.GI$|O=[Dύ)_YkX aQ_TCxÚ[i~&𧊴MW^&coh a>hƙy֚w6R]\Mo4oϚ2etjw:Hh{M7^OG'O_~O%:JW7k? 5k=c*[{ /ƾ(xĺN}>]\Ŝ A|oex/ÿ+Jxg ݇>7 ,Ľ&Z^Z­ 6>=Ky'I9c[uJXJ8T(JUsB!N:5k:JsTIԝ9MQSp49ӌS^߀?X?+i/^|/m;޽acW8Su?H=Ey=Q%zbh/ >6_)tM~,[w~ia ƺ|)#IfJ|>ڮ~%c> ߱6*׆Ű0}gk-_+_nxoP隤~7ִkƞ!&[mKŚw5_n?i߲W??h|scnj_ *iZſ_ |-῅|Wcf9E kzalGNT}&!'8{W&xޗ|ǒ 2j6zrSpntVi?6vw~쒺7~'7M6~ж~9}u_ a"Z|9Ow[|G>X[???<1UxJxEB :/5~}%գ0G-SPzGYOyAcO4g & ? G  B$9߳e>W|kg|S.x3Շ^_>>a |!O xKψ<}3|O ]:cO<~=g¿Oǭ?l'? 2~fC C{&i ^k>2>>"x5c᷈Wï/k[JIN͹r4$u.XVr޵Ǒ]lҺ붶[m?>KƟ2$~M~ xogYE|Bg=h߳_σv(OQoÏ3o>Dj?k߂?< |@|!Ξ;w'lo_ǺƏxc_|F]~ȟ >#὎l4+G <1u_]~|AYw׍>dž>|/(e?->ZJ|g<Þ3Qд]>_?xþ$x_\)>/x{/ c< qeqSßx~MCRn4 񕏅l?Oരq6Nm$-޺8h/Tඎem>=!h7RnjO/*)9 tozπ#ҼV>&@IZ_vo C>խ|o=sQk߁?ʾ{-/| xWvM]_>׍47RkZeuq_\񂌝ޭvi۾k]ʊrJ?? BkxsU_ c ]M;f +\Mj?مQ\F_2gsO A~Wʿ_?D z ]dxox-KF.xnYѡ4m/ǾuojڽΗx;Koy?gG]|9q__Mo~U!ΧJ_xN_ %C:o"|?xG:~nj Ӽ_k_??|}?_0_>٣V so}n"l:n4c$nh[WKo8~ͱ~VOnfMm3|4P:{Mk|3x#xsNu[񽟍-7IOxJso R~Һ^~_'s5|7/Odπf ?m/l߇.4xĶgo)XZ_Yeӿ~?mg/|9|J_$xOǎ+࿉>(ܑx<9mkIz6E>.Zܔ '8>x1jR;*wKZRc'MGX'ֶDG~-G o;Էž~(tuςJO94Dž^1/v+(9Z:W?IOS/sßoWE[|B_=h߳_⿃(o=Gş|:gŏ&ws@ ^yc}[~ujmৈl#!^yxT:;m|+Ɵ|%+6߰_yOqᏁ?g| ?_෋|g~iz'k1^9|AwW>}Hc-$hvr]9[7m*mz}>v?<6[h|g׃<x; FOO烿hU4"wm`q){]N4%W g]~| ~:eG:|A?fڻ[x{Jg_ ={şQ'm.߇gn-R}[g#ϊ?Iᯀ쭤[_>iξ![k׺O/CKK/WMcxsem?%!_>xcx_/K?ٷo}cOž47OxwKBѴ=wį!;뫞~6֗of}8$.ߝu_֧WŸ߁|SY>xNmR4:Ne:_%uˠxN4>V-gH#3aSFՕ1|9 xwд K: kAҴ?xw@Ҵ= FNt2OӬ-!x#(G_He zG>~Ij3ܥoo~+//k {^gQ_>w|O?٫ k/_h_o'}{EP_o'??my+R|WK7Ɵl~oIn,GW?H܎5W^ߩ"Եojoܟo{>cffNsӒMӫFPzI8BPdFJO5iGKI6MJ2M]]+&M4?ֵGwf?o_%~]Cψ??$wşY#5?|c\mcᖟh_4k^?ޯƸ*18!SHHkaܣV1%WÖqqp̺Q[$-7kH򻷥n/ Ijxcm?aK'~6~>GK_l+#?\k_ }c;-;R!ɡNPl{83}^ 7w>>3an ~-|;? j5c~ci|F? Wig%?/=ßWUz|ĭ^u%iEF3x5"4*(>Exh+V7l?h>#!iI? \pR=8nnm'h촥moa5eK}KmVEPS ؽ_xa#?h__|U)|uu|Vycxψg?„Nj oa]M[}zFh1ބ%oKwλokw7Xygehvmj_^Fǿ'r86ܽĶi$96dA%mY%mo%,h׿syU?#\߁_W_NuR[穲]Oo͟_?ࡿwQ~U_F?Ӽ5_Ҋ;O΁t~~QEjy1/c|)?R| u_ o8!S='=+x&\_ewcͷzVcVIobNZ'L}?=}_/7y_ɣLsWNO^ޡ2F:OVi{2zO:!z~{|dѧAsr~PD8}8_=+Ω?迯}/_]d&y?z~?˥]wTHsUSΫ=~6z_~v$J5?>~$鏠יV{_/CX}'Ooտϟڴc?##1gbD"N=?kįWÓ儯:7:|/`~EA_!xOjBs>k Ɵ~W @^E+% g{O`zln~ @=~7~ԿG_M' wƞ~hg?}R~Ziןcl/~m'mm<HXDp*QsJiJQZbމEޗzփJ4u) \S~JʑAz5=?_˵%왭k:r?ڝx{ᭆ}["b,߳XZy}8f?^S}̸+Or䬲GF ^E_>êd5wG:L{QO_wu>)N/-;չk/?_;=:${`o_g ڏ ~U&н;bѢUOzo/>qq>r$'Os_"êd5wG:L{QO_wu,+}?EUޟ/@'>>VkN?޾)U~ɟ/j?)kuWBy W jTjz~_%=?Nߟ~}_g ڏ ~U&н;3+w@^בQU;ڛʏӿ_qϧ|:L{QO_wtêd5w\󩂕}WZ/o4QOKug(z?T~Wck ZUү46uͶ}yO|, luY O15#V<9U:d48ME˓J\4w8r^+^e} (,+7_Od=ue_ |Aqɞ?mUOٴt쯇 kɻйo٦W̵F,9Ԫ8|X+Y}Ì81~WםY=5;h;ZЍquo_5:L{QO_wtêd5w^/q_*/ns,wrON>=+B4u?_g ڏ ~U&н;`ä]?hUSޛ3^02/uWBU~ɟ/j?)kwNmZRgQ&yҴz>?u/:L{QO_wtêd5w\̰s_ijtĝ;gU8=_g ڏ ~U&н;+/y[5TiՊi"$;z{_ka_}GV>WSêd5w]'O<ii]/V tNec&>}:{>ծ"VI##1Xs1^a<=:qj5(b59˥TvMA.hI96g7*ZmQEy_!xOjBs>k Ɵ~W @^E+% g{O`zi|]~718?>v'[M@~ YŸ 0j6:ŎJm]ŕZIXO- F!o|f|״W_~m_&o~m_&o}{E| F!o|f| F!o|f|״W_~m_&o~m_&o}{E| F!o|f| F!o|f|״W_~m_&o~m_&o}{E| F!o|f| F!o|f|״W_~m_&o~m_&o}{E| F!o|f| F!o|f|״W_~m_&o~m_&o}{E| F!o|f| F!o|f|״W_~m_&o~m_&o}{E| F!o|f| F!o|f|״W_~m_&o~m_&o}{E| F!o|f| F!o|f|״W_~m_&o~m_&o}{E| F!o|f| F!o|f|״W_~m_&o~m_&o}{_!xOjBs>hj73k^ ]Mx?| q~;0a=:?go/-|W6| Ӵ-BӾj\C뷷z}7,Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@buteo-syncfw-0.7.21+16.04.20151216.1/doc/src/plugin-guide.dox0000644000015600001650000001502712634332753023405 0ustar pbuserpbgroup00000000000000/*! \page plugin-guide Writing plugins for Synchronization Framework \tableofcontents \section kinds-of-plugins Kinds of plugins that Buteo supports -# Client plugins Plugins which act as clients to services running outside the machine (device/PC/ tablet) domain. These plugins are loaded whenever there is a need for starting sync and are unloaded once done. More than one client plugin can be loaded at any point of time, provided that they deal with different kinds of storages (contacts, calendar, notes etc.) -# Server plugins Server plugins are alive forever and act as server for clients that would like to connect to the server. A typical example of a server plugin is a SyncML server plugin that acts as a server for an external client -# Storage plugins A storage plugin enables a generic API for accessing the data from the device databases, like Contacts, File system, Calendar etc. These plugins can then be used across multiple protocols like Dropbox, Google services, Caldav etc. \section create-plugin Creating a plugin The buteo-syncfw comes with a tools package to create template plugin code that will help the developer to quickly create plugins The tool uses Cheetah (www.cheetahtemplate.org/‎) template library to generate the templates. One can install cheetah in Ubuntu using command: 'sudo apt-get install python-cheetah'. Python 2.7 or greater is required \subsection generate-plugin-template-code Instructions for generating a plugin boiler plate code. The configuration files corresponding to a client/server/storage have to be filled-in with the appropriate information. Each of the fields in the config files are marked with MANDATORY/OPTIONAL. Info about the fields is provided in the config files To generate client plugin, run the command: \code ./gen_template.py -c client-plugin.cfg -d \endcode To generate server plugin, run the command: \code ./gen_template.py -c server-plugin.cfg -d \endcode To generate storage plugin, run the command: \code ./gen_template.py -c storage-plugin.cfg -d \endcode \section oop-plugins Writing Out of process plugins Buteo syncfw version 0.1.0 supported only dynamic link library plugins which the framework loads into the same process memory as msyncd. This archicture has a problem that if any one of the plugin misbehaves (crashes, for example), msyncd would also crash and there is a probability that it would not recover. To avoid such situations, an out of process plugin architecture was devised and implemented. In this architecture, each of the plugins would be running as separate processes and msyncd process would interact with each of the processes to handle the sync life cycle and operations. In the new architecture, there is no need to modify any of the existing plugin code. The only change that needs to be done is in the .pro files of each of the plugins The following few lines have to be added to the .pro file to convert a .so plugin to a binary executable plugin \code TEMPLATE = app QT += dbus target.path = /usr/lib/buteo-plugins-qt5/oopp DEFINES += "CLASSNAME=MyPluginClassname" DEFINES += CLASSNAME_H=\\\"MyPluginClassname.h\\\" INCLUDE_DIR = $$system(pkg-config --cflags buteosyncfw5|cut -f2 -d'I') HEADERS += $$INCLUDE_DIR/ButeoPluginIfAdaptor.h \ $$INCLUDE_DIR/PluginCbImpl.h \ $$INCLUDE_DIR/PluginServiceObj.h SOURCES += $$INCLUDE_DIR/ButeoPluginIfAdaptor.cpp \ $$INCLUDE_DIR/PluginCbImpl.cpp \ $$INCLUDE_DIR/PluginServiceObj.cpp \ $$INCLUDE_DIR/plugin_main.cpp \endcode In the above, replace "MyPluginClassname" with the name of your plugin class and the name of the header If the plugin is a client plugin, add the following DEFINES, else do not add \code DEFINES += CLIENT_PLUGIN \endcode Also, note that your plugin should have public inheritance from Buteo::ClientPlugin If you want your existing plugin dll configuration to exist with the out of process plugin configuration, the good way is to add another configuration to your .pro file. The config would look like: \code PLUGIN_DLL { ... # DLL configuration (the same as your existing configuration) ... } PLUGIN_EXE { ... # OOP plugin config ... } \endcode To generate the code for a specific configuration, you can run qmake like: \code qmake myplugin.pro CONFIG+=PLUGIN_EXE \endcode \section libsyncml Device sync with libsyncml libsyncml is a desktop based sync engine that support sync using SyncML protocol URL: https://libsyncml.opensync.org/ It comes with a cmdline tool syncml-ds-tool to perform sync. Following instructions help in performing the sync over bluetooth and USB Buteo works with libsyncml over USB as well as bluetooth. These instructions are w.r.t the sailfish OS, but as and when more Linux based OS's are supported, these instructions will be extended \subsection usb-sync Sync over USB -# Connect the Sailfish based device to your PC over USB. -# Select the MTP mode when asked for a USB mode selection -# From the PC (Ubuntu or a version supporting libsyncml), install libsyncml-utils (in debian, 'sudo apt-get install libsyncml-utils) -# Run the following sync command for USB: \code sudo syncml-ds-tool -u 1 --identifier "PC Suite" --sync text/x-vcard Contacts \endcode Note: The command has to run as 'sudo', else you will have to add the USB device into your udev list -# The above command should fetch all the contacts in the Sailfish phone to your PC and dump the output to the screen. For more options of syncml-ds-tool, look at its help or the libsyncml website \subsection bluetooth-sync Sync over bluetooth -# Pair your bluetooth enabled PC/laptop with Sailfish based device. Also enable the bluetooth options of "connect automatically" and "trust" -# Find out the bluetooth address of the sailfish based device by running the hcitool. \code hcitool scan \endcode -# Run the following sync command: \code syncml-ds-tool -b 26 --identifier "PC Suite" --sync text/x-vcard Contacts \endcode 26 is the channel number of the SyncML bluetooth profile Replace with the bluetooth address of your sailfish device (something like B4:EE:D4:F6:19:E7) -# The above command should fetch all the contacts in the Sailfish device to your PC and dump the output to the screen. \subsection sailfish-N9-contacts Synchronization of Contacts between Sailfish and N9 -# In N9, go to Settings -> Sync & Backup -> Sync -> Add new sync target (+ below) -# Choose the Sailfish device in the list of bluetooth devices -# Start sync */ buteo-syncfw-0.7.21+16.04.20151216.1/doc/src/Buteo_Sync_Solution_-_End_User_Usecases.jpg0000644000015600001650000024513512634332753030610 0ustar pbuserpbgroup00000000000000JFIFHHCC" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?:|t_ |G⏋a0ᥦ}|4gP>,GW;GӼwW^+KٯSO'?8H?7>|ioWw!?fF~3|a;}gBm|8_<^R_ƭ[ #Í?7,+<9ueE? W7#ޣW7#ޯʏ_ 4/ |_lo>ӼAgK)|OxU]J=vCg|? 4k?k?"[O~ɿuٓᾝZ?k߈~/eo>>o|83!dχZE3GšڀB?ҿ?GFҿ?GF~h7 <|<cςßx:F};~h!YwV7ß ~|M 5s>xW~?|QķNOk _;>512e}_'|6 `Þc)}^X?p~3>k-?`?~3>k-?``=K$w=hZW |x:^5_ t/ :O,<5O6> >;|1*Ϸ^ 7~ M? $g|E3>4|qhYO|?<''O6qᗁ |oo~=xs/ٳ?_ x-(̗6>:,?*A-'k4oG{ѽG4oG{ѽ_Z+ď h_>~П/C7CˉgS]"O#^ωxt(-ooOt5٦Fw{9C\g=?JDf㟍LT{-U%m$$fѫ=_3i_ O#zi_ O#z|U~#ekLK Ek.x:& Ec{O_pXrƾ1†S3 C oٛg[oߟ ?eo?WV4/ Z2 Muq{~:OG#^ o=+|9_xI]VF~Jf|׿Z~Jf|׿Z~vt___|X>+⯁|3 'whxo>2k sW7#ޣW7#ޯϟ_?nx?>9/ |V^kc<+ƞ>Ɵ ^|Cuo i>&5S~?č_=?~ |w+?j_j@՟}s/)#/ ͞N]S^*6<;{oWj_NJe/ ~4t-jXL]xީ9~$S4è<Ǣ]V=/W7#ޣW7#ޯ_ Ɓ?l~̿O~ueO/#' heN|9Ow㞋ᖧ|忍ig;F u7Jiځ&Yy}Z ,!]3Yx<'gcxA3H?7H?7𝏄h: ~8;A߲GtiMjZߊ_ knO+/(/_~>,i}g<;o[f}k><şOD. 3]-|#možW7#ޣW7#ުfGg_#ſ߳W?>h~WeO|Sׁ.Ci^:6_MqX ?N(W/ڋHd޻_f|Rb3 /=_X? uYi2/>;]3z—M~5k i_ O#zi_ O#z2#㗄5Ož-|I|/K~_?<*'^|)5 ]);%=kĞ`xZ}> c?Szp|v?jO2k:_ٓjڣfo|B)WFxI#?I{Z#Kkx>2ޏi_ O#zi_ O#z? f>*pG3}?<^ {.|;i OG%x/|yޭ=i?4_(i㿈Of2|Tޱ#;ѵ ?7?lox^>-|~Jf|׿Z~Jf|׿Z~)3'O|Fiঁ͞0oNA~_4]犴?|-}o_m~&2n&w<=o~)xSio.?#4hHόP |Iׯ=GU隞{hKwi]\ҿ?GFҿ?GF~`U/6^/O׉~=x⾏s}B 'SzWo E}~2گ^~~*]|$j|o],mwav;^ ~~~ϟ|㯉?eO⯉^0Ěe_Omk','ikяi_ O#zi_ O#z`{!_~^*ċ4e5x ^c_ / ~j/qx.+ vFCO>\x=N|9>#]\vo_X:wO c.'Ιk+} +^ioQ +^ioWß,?? >.eu<_d'mB/?񇁼=ƿcχ~Mq?YbD't|8|{=sokT[Uѿm-GX}::V}]?g(H?7$C{x¿a|wxIQ_xVY|/jΨt.on ~P_ҿ?GFҿ?GF}{E| +^ioQ +^ioW״P_ҿ?GFҿ?GF}{E| +^ioQ +^ioW״P_ҿ?GFc#/~"/Ï ?¿o9ִk%O7/5?m_I/ƁMv{~կm#> |{TQ@!~ҿY?w4[Clo ό߰z~ĺ]ErcƧzφ<4gVw?|/mPL?H~6^>|wY0/<㙿kaǾ־#ONaJ]OWX~ףcc??ڕ՞qkc>r!o5xNi~ͺ=_xkc^;>?~#{_xsνy:5=NO Iuk}KBc|'ڗ짢7h+J-Gƍ.O G4w?<'T?u/|usk:_Bo9_ 7LBo9_ 7L8.a_| Kߊ6?~/h2GN-#ѼK_::ΧxM<_n ׾pe?_<7~^9w_gAV_ x#|o↳oz?Kg'?xǚ?ioҟ|?׼%wڽu~{_?`{_?`i}> 3*u?gj8/e+A|8G|3 Ɨ6|?D`"/п? | gĿ/J>|`/|3hx5/ BxHizkhVY^^_G+!fQ_G+!fP)7?d 3Ÿ|)wO2> xI^ߨŸXT' O>;&w4O6?o[x k7|'⋫Z|M m xn}fyq'ƿ1u'_ߏwo m.i?L<]|K>*5BH,C/oBo9_ 7LBo9_ 7L5>5<[׮7|Y/|['/^>4I6h .Zxw~~ԓk~,n>%ڏO~̳|@񆅡7ޛ+ĺ?x^T=kǞ/j^.sk7v d/B0j? d/B0jWG^# 9Z|c')߱-i7ğj/4~3x|So O |4} z$S 5O KM>c}cj/~3/~37N A+mrM(^#\WI✣+񽒔]՟4Sә;=UwNK_xg~0W?jڗ>??'ٯƟ(w|GJ/j!hsZ!zy};්tDŽӿhobPsK/_ h +Σi}o? <7z7wM6&e_%G셯¶uoaf֧/?oG;G;oϿy~C A&zyvGfЩS5 j\JSn[4ؼ>5r9+9]^׼#$nu+M1y5x}q=_o|$WOZ7]w xoo5?E4ƏOFx\PgG_c[?>״a& .O_~0xU/|;71xǾ4%|{mz.P𾇤Ũ^.+_mi{g ^#[3<*+3?{|/Zj> ῆD ߉^&33gMW֍^ k꺯lZzq~߰߁ k6'<oڶ~Z47Oe(t g<;rxb=GS!x{ 3_=_[X 6EBj<*+3/7cOmKg$4x_L4/G>"h_Ə*8?-YFymWL4 Cھ6? ?h3ữ>8eo)^*BZS7~GNJ|-k^C{ ^:eׇ4n`?3oOޣ?M6=?"z5 ?[}/ zgy\x~NsBׇ|qs?Rk Ǻm%k/oBC;K<{ZMZC A|d>>:Ŧ|@t|F>:bAKx/⇇~!閿2|NO񥾻gG~15|>[F|Z6?v[_X 6?~i>/PiU_ٟd:cZO>XW?MgOm]7Ϗ7>ҵ\i o .K_6xSž6ίNe9| AvK??Y?ϳ<*+3?|Y _7oï i#7~п|Ej xgPo>_WOTѼQyt kKY?k`5qPܣ_M7=?. f>?_364 `>?xş-^*-i߳Η?uk6AkoS<ڷn&Xo_^;?9w>|ZoC7{/G:h ~&_|U:Yk¿#^ލxCޫu돇7ax|M-o,n?j{S 5_=Oo5ZcY Ya_نO0q$6<dYw[ooGx6Srmj?&?|uiisR|} ~\h&Z+lj_wĝoE<-gx_f=|1|Od6+s| O A&ڇటkRU3`?8?UoV}2>Þ8wn~/x~Ǿ.[o5?|V~&(O~uo@оεxek?~ZՏ)wͯxj>^+ψ#|:wéoƧ̚"5ֿn?j{ZX_ 5?U3`??p3S{Ț|ZM֛?? |Ko~,xK+1i~A~Ͼ֏3Wh> CoxSoU4_&[tk/]h<7}WA~%]>j>.sO|{i?nM ~g2߅|QW,,/nj{to G AVgLQѼ9=ּu+c}+N^?9KᦅO|f<}׀-k@<-6? 1bo? $4xX|{[O_><#Ş^^<#㏈om>Ie%K=CЬ5McǞ/<;}_: ?ŸJ |{U|o_[jh G_^_o'??m ( ( ( ( ( (>v?j7o|M^^_ɦ~?n?Z: O5-O𤏟?G)'x=ii@'}EH<: RrKwPۿ~=i ׯM? 'z?Sh@=9QOO^?[IcQoo'^ As}4{_y[:?sYݽ#R?ҟES=O{ԁK^~F?ʟJ=C{i|c?|~5ɗҫ_L|I<y7%_?|$e/EtUg|GEQEQEQEQEQE$gwOo٪m#> |{TQ@!~ҿY?w4 G_^EPEPEPEPEPEPߵgCfU@'{W~iV&x J8,}*i?{SwkW3Emmm 77$0[,4 1I,2+;PH8 ǚK3F]?ÿC:oi:犼wkf~xt6xRY= ⼇0繦.;(T㺡+r):TsGl&'.L=jiZNV~m7?q_:~7Z'ߴ^M;F_:σ׺x~?x៎|KCռ' \ķQCo/-/ٯAj;7|k>S?% O#y>~^S4[߅ eRge1"Ju:03-Ss@`8ڕͤH2+1~/G\M)넾 u20<(EA|9J[X^χ+v=2J $rM_į_>wKxG/SG\ѿ]Pg_eo$.M4#zͶ/<IY^6{W<~ټ/1x@|0,S Gjڇ_c/?+V>3 ??0<Qgt'%: >鶷k~4Ԍ(iNrQ,>INRj0#[^rbmevovJH[[^bC.dz_2ݰHvrFȮʿ|u6??)+U8%=K\>o|a'7o-X4/? >0K{]ACYo YxƊNj[2/u=TM_Mh/Hk?_|d>dٗk?mz~кS.Ib|}K^u?ܞ X/3ԃ?>O]R[~-&VLJE*KeEVM|Rgk|IYzO Kςu[ז0]DQtgxZ|dڞj{*~o4s&LG?Q b҅'{VU‰,I@};M֯6 !𮛤Fqnxdhuq7j_uڕN-;IڅkĂ[7vogo_^EJkzatDgL|Mի6iƌ[o/Sl"a b#9ɥpV4nUDt5gdj6]Z^_Oo+[Kvq_o7Wj%)$k'/+W햠~'9ֿH~ Id_U4`->e_ n?)]=M+OX(((((+/{Hς?U}{_!~F|g{(>B>|ioW׵+% g{O`z((((((k4ڇg_W?jM3vwc*f'ţ^vԼU^^N>2x{OռmE {5AǂW P}czAq.{_xkKwD/5{H]súj6G+vM=;T{y]RAIc iz㏁Y?|YIFڧ>zjҫ2I_y$eII|(q:}cC %L7ҕIӞUU)JGiHI5.̛5ׅnqy'%iZ?O7 IT_d|k ;MlH|<-=;;~׵ok?kl~թ]Y~AL(/5@i >xlW ğl2|Jbσ> u[w~|]_"&^f1!ʖ\Wh?~-|)Ĥχ%x3ZٺUs3ښUpis*|Lj8KsJ9*2~#Z:s~ʛIj=۪jfPkO$wzGO++1g__.~Қ-WžP_-SGyyn]_s{-3+W?Wo>j "'GH7|KSsd'ğN>#rkgi+/S{EΫIgs^.]]C# 2  A Oo[R1Nx9<:x:;¤ܦƻU%V'UJ8Ҵܛt-^rY/]Z_CzIj?fO/Ŀ7*;/wU~2~ǧZPZ׍|)~? |5~_ tOA_ |'񏏧_x>[ c쒥qȔ\'gㅍ)9AE7R~Sϯ75?{"gǿDZ+? 4ȿnٻ x_t.fsj?(M+sλ?Ggj||am6kx_oh>xiO_xQͮ 5]7UI+k(%5I7K4ƹ nf$s^9~ҿlj?|񈕑uϊYdle``icX|O|WZWƯ xSc`|}"7mkY~ǚQ;/SI? Z\%R8/ -K+ckmD2BA"ah~]zX^ a­[Sz׋ћܕ>,\ǖմ*՞FjONW)M*Zn^a?|A|n|3>5_m}e5-.1iM*ԵuΟc^J?i_gHe6HE-ឯpB/ 0E9x>8t *A\%mxm!;Y0$hr\ʰuXI~cC+IMOMP۝X]}ih׮ɞ TWWM4}WF=_kw3_jGÝo#~G[[{ 4w93\SeX" ö|$UK<Q%PYX&ȣ4B~іa`Y+aB;?fcvr傌9N|&>nԭRW,e+-Wwv{&|f q 5F2U~s~o?{5џF2U|^TO+kiozQE|YQ@Q@Q@Q@Q@|o_[jk/{Hς?U}{EP_o'??mB>|ioW״QEQEQEQEQEQE}&Pٻ|lkj{S?gCfUȂ5_,o*71<<ҘP *@uSO,`Aߟo֦4<.0KhYg'zg{Y[Y|wi`ӭovm- W߲'VwԾh)̒ۙfl#N{A($@:]y>9oJ.<<[ozıH-chl!57Cu=NeMhXV)^S+-59WviSU7Q{|Qcτ񮿠7⻳Ӵ hCk"p-4_ xSA򥆍i{ۙ[j2 `-KX|L?ז =ūoCHAwŚl>$J+ԴY|5cZ[W+C/x ,'m}}zgj旡^"Gokd x54E(־ ϭx8'VKFNRNuT9\\xƊ,$[ԗ%A(9KyI$t.{*Cm]Nh:F]K?eRI2ZlvU$Ua0mDx^n8 P9D>H_Wi[Q?jM:~2|xu#a[/3ݵe?vcc`Ys~w;6ּMmcQ?o*!\&Ҍ  )zǵHw?%l?>o\Dt^&|kTÖ=߇|1y2s9um'T7NX97Mz}iZn:I@3}QEtR܆2zyքTԏϯ/SHO&^WJGM>| ҿD Id_U|`۫o+T_-ڷ?)EWƞQEQEQEQEQEW_>fB/ 5P״QE|Jf|X#ޯk/WK7Ɵ}{@Q@Q@Q@Q@Q@Q@;~iV&(W?j7o|M^V?I wo騞kZ_Le<5l~! ;¾ӞX"xmn/5 JAixĺim x|C/ǟZFL4[ /< ڇ-ׂqwwC |AsecxEvnW_Wuk>h |>Ho}yusx/F:++ox5#[wcg7ն?d>;HQiqcWY8ˬ|Lf +M~mx畬D%cҕtRJԮK2rjq)dU9"ܗ7gSrwk릇ϺxgO#Hu&Vݲ!YIEfⲣ ]t7sx\G4 dlׇ O ?@x{N]6!T7uml+_oN뮾F0Z_PCfǍG?w[u1qd#|jxg 1?71FGqS菌\QR9 nas[_@y&;*:ӿOro >ofIh[c!&{?"Fsl6?-?18L^H-Yg</L;BSe?>+ u094B: m |O&J9SkϿt݇~|C_syj;XZL*>n+dtWuWGF 7++C+ $A%gƂUxg '_SADFQX] 0|@+Įbi˾sÞٙkw2-_&X"NX+m,NKBicI}ak)>õTɣq2_t^JqϤ;ӥdkJ<4"ȷ/xg 5ԟn_(h&O nv __?}* o>W q2i|r7yVEO& R0^!dXץ9V{( :Z{sN4h O?m5y?|%~|5ɗҫy|4߂F2U|G GHrݪQE|qQ@Q@Q@Q@Q@|o_[jk/{Hς?U}{EP_o'??mB>|ioW״QEQEQEQEQEQE}&Pٻ|lkj M3v@7b)kZ_L|a<]K-tm7&ke;[3KӭRKSY tHk[Vl g|]~m{bDGfg4io,UM>饥o4Kk,!y?h_q$ mC1ovCi|g־\7Fm9|F;k9mm-o <"xWvWr#YW|ix:>8qO9p_R .SbeFnZu5[xrС/ihRJ%~Eú2FuUWjrەkGnkYGw|_:wW'K/|P%0 {;YSTǎ5h|?c/t]W\ȞԿe]=Po[7O7ωd +þ#5/_KY/>¾(՛|Qu]D~7oOgk~ 5x|!'eڗ2ώ4Y?oÿxSO>5ڶ|Rbf??V|X5/\cx;uM)x7Ş<7[#>0]#]f\<1o1ɢwo{Om3V>0~Ɵ3ş/||3~׾+|^|kׅ<{7ko_:G?|8mcikghԕS"j'b%R'c׭OR' ZTR4UL=%[&,]??n/q~<}/&)> ?Z= xU6/ǀxs&|TPg۳W[ *x)KkoO٫oZkږj |y׎"]Di߶?q.[>xC_ߊh? O߈þ4 K^-{|FěW}|=xu5wZ]k6Q߇?y_E|KFoF?P/C|fO'G~)k xgR Þ3lni7vZ'"jUQ,7j'(T~ʍ[ԇ,xuTJXB\{Vev ܭvV[Soo7{.ּ%'_0.'#ui?go__u>+27 |xTce|3׼ c|<񖙦&DƋx 2K@g OecԿb$x[.=g%Eޯ|i&94]Su[K-cV:LOڣ_ o>#׿ >2k>5 OށⶅlG S/x;Bj|Me^ENM*TƼy=IN"Q\zK[+.vmO)Oſ xO~"50HKiؑ%LVo%E%ƫg4VTk;O Wj4'v4<-/m?%G߲Ɩ_rcM 쯍->&HIfuwxo1xS|O|-.f~d!㞗a||wSyew=ƽ S>(I&Y: +7Cϋ__~x)O,ƥ7k<5olj_6o<Xj%M,f :`xrV8:u# a_8$.TyZ=jWm;S -IΉuxE_ n^UN*7OJtI=#/2J)+@3|׾<=O j!E}"[Ү3JZ \-ŴXUq7xmýC_~05k+BR#U}BKxB,ѯ5O+׼E%|@'Ӽ ~:M^ZfisZYXXB7Sp[Z$:E Q2=#N{H|2uukQoRYc^IJ:MX-7^|gt/^е =_E-[HtOLmKY'hDuB|9x|IU͟{/0 Ha\}Ղpi"o wƱҿg1yvBYSM jMFjJ-N>r'Jy%ngM>٧eZ)~XWuSHO&^WJϯ]HDO_F2U|x?:'+*Xm#h ( ( ( ( ( 3;'_^_>f(i_,O;~|Jf|X#ޯh((((((oM3v ҽLݾ65|mN}kHMCǏE0_~i䴻[SWBF4sæW5m_BjƎ)mۓMF1MRn+VGEʽ$߳}lmL>ib9-'ůc+d>Y4g⺗ =UԼC|+MWķZ| ~skZ=+?՗<8:[o |zmuZƽ߅N:ϋ:yxG(&ֳkXZ(<%id<Ex[I6\jz{pBz|Qgे!:uTZ0Ҍ18){D 9סW8{4Sw[5*-r}>Z'h+IZVmN]OƽMW|7m>56\Lz ȷ|5~nZWg?٫7]b^h'NW| Ob-k \i*wD](+g~ڟ _Zem|5ΣjžWn?.O^# HB`|>%jJyUL>sNpVt*ˑӺOEG:׷->S? =^oe~>h-*7oCo-Lx{4Ğ~˺ω'i<9W>/>+Mv_ivd 2P2Guik3epe)9rk'΢㥴W+Ѩ 俻$_^_NP?  mh7>/eO??dvk(]w߄uSĭxMtE^_ u|Tŏ Ɵ do/j'wP ῅W |G~_ ǩk~+ot'Ěnh߷sUn%{31*5cÒ\͓ c Cxww-}Iflu`^aB k^*rjtQRSլ) _vongo[mg{VYK~ֿgm^qOxXf5%>C oFl#!"_&|?p|/kY>'~־"O*~C?!|gSoKK |$W65-0NV90I4\ҵ#S2fi!ʂiw22<ֽzt1EWiNRwM]ʃIr뚋j-+|G[6?~&O9m ?ZGT?dV~韲GVU_O57u}_ _kN=#7['Gq$x9_~ f^_ouht_&6uCE<:go>|%>|!ďVC7t>0|Ei~<;{ AxA N!jƝdc"77#$ g<:Ξ WBh.Oc!PKJI $ץKVn4F)NJҒQQwvI977yBq޺'kbٿXe_~G)΅f\j>;k>-ޡz: 6#Bu&ӍS(?|s@c-~о ǻ#sx"}nI?* s)D -k oxS"m.$;۟W `'  i:g/!2҄$"c]zZhF4ҭ]!&QUWG?ےw:/sK=4f7}oHDw#<ξCݴ a?kLjWV{岗W-׼Cu=|C98?lաo?Kcr\Y< 4πdfyoGfɯ˸7UyZth9ah>yM^j?uS),kf)&WKFv8O 4@.g^j"";dhm! 3yzt iB(ӧG[(zvl6RZw~?|~5ɗҫgy'W:)$k'/+WCnX&b?:m{~~OSh3 ( ( ( ( ( 3;'_^_>f(i_,O;~|Jf|X#ޯh(((((OƿxoxI׾9]iZO[šr?b}[ĺ1O/6wv.sj*_(Gi?-У\(k(ӅxsxU#W)MVzÏ& 4}U- K~6񆤾"/Ćo|DQ-W-#<1& OxC@Aeal<'C&4]W _?%^r%]P'IzJOU)GZǯ|G.G.4l2ߏ>oɭ5^0ӦWoxDLOjQCgu|5 k|A>Tɨ˧jkVu5ׇ< ZEo4Plt5&s[[>? #~/Z%d ixͳ4 ǃ oo xcI_'z<9e*-"{ob-L M$2ieIgI{ϭHޒ \׵:I^ct$cs4iou-tWݿ6鶇U^Б\eM5JA4٭ `gq^c~ t0^-qdrxzW-l,}Txj-k׏V>vʗ F?<0'~I8RIlucY?ލoa+맀A\`]x/7K=ߧDR .ZN(ځ<9'g=qĿ>B]@MҬY@<6AkJpB}8MM ~O{֚Nil}S,g_;Jx>;\|t#0ɩlX`k~|=Ҷ;t z| ?jKFN 1]P=ϩ?zuv 0joq}?)!ը6rk۱ 6 q $qEDT{+W=-o^۬nS׏e_}5ɗҫt8gρƾ)$k'/+Wq3?,Bo(T(((((B/ 5W׵$gwOo٪>(/WK7Ɵ}{_!~ҿY?w4(((((6miK[X68?-cҼ_@kdsݤ/4_|H%𷃼zk?xqՊmKNMw_wvOu}< /nb%_ug=A|QrMxZơLK a/Kk4ڇg_WOu߶A$~ԓO$pWXᧉ;9,9&~#|}m'`~/j/3]:+\Gio:Ƌ.K>#çVis)ӂIڕ-IsI&y9[TKޗ^W>6x:Mn5-_ž'{o|7~7~#x1%ŧ+b~soh&A jT#~+؋mA=cOo|&WDMo7_|F1x;َ Yh> Lm|9*5ַkI5j^i6^ֵii:e#2vV ..f$_I%xʦ&PJ1r7$1MU>U4ܢi%u%RgviA7&Rw)^'rݠtm/B4DtFҬ/Hl3M`Q!8!8Q@`W_WO@-!x$_\]/Oj~CD Z70{+/N76 |7/6q-떱umdifXVn@ <(_~ 0A:+ğS&)R]l]H趴Uc96o*s_ta>~,M?/}Pj~z^˟#Hcc"a8xW}/;^1g"? | Sgjx7Cϰ^fi_efMʍ%Ww }{]^Wvݴ[}oPퟲOdG;j6 %})ICi <|rO_&r~vEIKA|ğ|2'HGH%x1IOV4Q_KS™,oce]Ngt(KkyBt?+,msƯ^AهĿ~(ÿ cOjZOojV}P~e{{mx*R ,:(;~}??Ci?iσA_k2>0|4Xp?"C1ADLb!Y?_|y* |apTZgǞ.HLnn\5Xe NP~8/y|km_;Yg?|K{Mh"hmZUn!/KZydB_XjG>>|rI/4uohnfl QDO9WxRJJa7*qIJ^//S(*ԋjP^!~=Ξss~y5?QGǞЭ0vᔐ C 炮T j&y߂W/O|]|;6%(@O_uv1Hfp5V/·kΌA>iPڞm[R:Ӝ*?^oEnOJxOS}k:O)>xYQxY{kuxjj>':Z;]>c\[ ե1\J΋,f(i_,O;~|Jf|X#ޯh(((ouK_)>Soo 7Khwvw}[x+cQW~%EƇ}^I_Ux,?4|M't ӭ`Nɨ2W%އ⯏֓fO&*@n߅1}Ý w uQoe|Aj!?[gĞ$nuZu[۫{>#??φ x3+zGki IY%yn 5 WU'}NS/. t|_ ?ֳ?xAw|=e&xڭh1WԵ]J{k+8Fy4Ttuh |6֬zO:u x:xZÏ%`_lU֯ ,K)9Lf:F䕟㗌<7?~ i [k%~'|c& ojZW~O[MGlg|[}ZP<{'a?sx~"%XNR HMoڋ)Ϳ?/گv#wj|& ZQѼC:%彮eN< jz>.DH#8UDDP**PM3v̈́'^X,K{S髵*v_+z}=)'^pog—% n'tz:G&Q/m9(zKX,l_W6m6~Vt,_?jy \4x/÷kiO*⟉Uwt<]/ 7` >2h32xKTgxYJ&uig.IdH9a%DI"6WHC$J:JA > 2Uɧ)B+TZQӌ#EIۚw^GB{FuޮJ-(5d4>(m<},'7_N?; Mx'K}c_^<7/ H_x>𮽯w[дM[W+F|;iT5?m٫(G!W*/?g:1Ue?x&ڇn~ ٨ψ~;ŷ%ҵ߅&;Iwg=%b~˟w0C/W>6{?:3|oеO'O~1_z&{CF'Ɵ uZ/촏>_ Y*`k t^5iIR8^ќ?hիGs*Wu{vmutVWT⯊Z)G~ښ=/__Q~>CywcGi|1;WմGbZy앭FIazmL,u64K 5_[׿P_(/ycBVcU7Iv/ ?o/e1\|9o~ kпa=#!|? |tb/ xZY_ x7W0X5(l 5o؟oٷǎ Gx7S?m|/-~___5]NA֍wj/Vk/x[Ξ:yUr3^4qZnnTifᕠvVwѽ{X?ೞj_ h-?sVKI~~:煾?|'sRW^ 9֣?^)}K¾&]?ٺVyxGB)?~ 7G|Ig~3?WX?dONj| Y|t~>zۯF/$rBi /7 |SseR_~|š쵧|?Ե[|bE^"/|_n/G#}Yog}';K߅߳{\ @3?ko"4W>!x2ޡgi:>3Zki z0С K.%F.F&9r<=5tIK}̇ R}>|Kw(ԿgZ+][|,~k|S_pO_-xgJ𭟆uxZs-C~ҟhƷ jOgt$i{vtn_{w+xWt[STwwiqsg ]/)৾--|9>)*?m<Ɲ/𿊾hyx|X|sc|>/|K^'?bǟ jY;~w|.X2xSWot_7=? >!g_;Ug]D,?A][-6qka?Ux3=m _3x߅l|=iORm:Sw>$#U>]&$xV.'mDӒK0W,ErqTP\nZ3}ᆳ}:Q >9-tQy5e)HghXfU),RHFd7VWV*E|I㯀Ծ"~Zcaf'c8Za7g>iұc)3MCMi)Ѭu=MwO7>τ5( H]B4qc} to{cw7Ogy 70Ku?kz~!h:&=^⧍|ch|$6a%uHAߟ?ʿV˱`0TW ԅQզ*-c_w5ɗҫoۏٷ?߆5OO#_ ?x Q]*~"m\[S^E'pQEQEQEQEQEW_>fB/ 5P״QE|Jf|X#ޯk/WK7Ɵ}{@Q@Q@ $$O'|k"=K S߲{w|K'/$5?'2X*y,|# hxmyȼQbx']%+kYn4Wq[>K?Q""O;|Y|c6ffk ZkM5~[h,!iE1=тIbn*}K4K,>#L5|%I$J5f6\ԧRNV ^|-9{;{ދ]_m?֝}ev{g["ۍjvFMf|at\xd2%DU~r~ɍi~ Բu=wP՞-VR??XƦ1nJ}W\B;m0Ct7ϟR">&x"BO>!+9&ÚV*xi+5W37S&bekH*SIYs{ZEdK9Qڬe Y]sCo'u՟`+CSe~>$|Ti[gu??o|QӼU~.=wj?|=xfIn4q\d_ࡾh!<3㖕jq-~x0 YBȳim L!k!]oZ9_/?dm[1 7H/+őe?ᇈ>9x|Z𗍼oq᯲VHҽ# Q8cO೴5uSux[x4` ^G٥ x g 1$97;SGFr7:|BNRvJEh]%?;'OOYW:ğ{I~(~C-ޑ(= L ~_'4*:v n,=v>*|-w'c|5ǧCQT'?ku<x^-<x7.1ֆCo- fY7◃0SNrJRf+6mGk2Y]k ~->UYI%ڗ-۞]JJ uue~gï ?ifo #_?d{Oۋ> .h< @|-׵|/x5G=[~!mV.Լa^ YxZ~;f.|@?k:ev2k{+_9nи?벬dGζKDtmBM4ES8P׾#|C:RxS|:_m;-FҴ?1{xwLmt_ ny c k=< |yP)4o^'w,N1!MȷQR2|QR?ßKZ|Ov͈[%\3 5QXPPUPj>c+ {I7L].tIc5gwv 31 f$$+sd-<1-E?A߶o|O&I]]S3&cn߱oO~_aK?W? <o࿅ ;"'Eh0畦6m^V)ۖu4[^^tכU3'tsq} k?j^B-yMƂ9/W$i |{TQ@!~ҿY?w4 G_^_*B/|j#=3 ';/ށ {:ԟU xSCx3IӴe{^j^ j_GQ{=}[r_O ;L6o_| ~++gG?QK[V{kNG96h߁??g_Gxa~'|5@,g{W4oj]*x_XzVpkZ槢U<K/Rc#+ UT)J!.e(TqM+8nkE.xŸމ_M>='{O1|@!-χt? Ou ^2 Ea]JC|+MÞF}Αm}EoFM4αE Q)y%G*+;TPYN~-վ"ESпݭL5:O^_h^ZdZ5.|Uú(K_R]ӭ[j牯˧_Ə?Zφ9s⿄-5 ⿇_l%,7ť4|x w¶^%t~UAdKR3UVa)I՜cO[y[l)F.14Z^O?32Pn-N]':خY-G*Kc{ktWZ7 '񵄐j?UмwoٯA?@վ=B W|xS\"==e6>-vg5 CNJ+kAeǻk8~*%&GZE^mgaGoZZ Z0ƭq _Sjua*>7i)t+(  8,-v:o-Ht/ \$-"B_2-|74 -H!};C% |?{v$$Yi~>5pGxKki`V?O[wO?gƏů>368x(Vot_rFݢyǐnʞ <5 vwȳt?py|Y>ai,2juO&ŗhI9g~ xqs[>xQohy~uڬ]-Ԥ{>д MIfizFZ-4>{;H!8ײ_DC'kM* xxliv_&kឨ_.H1|`Ms{߄>@oo)T uw7BZi:(Gf/~!Q>?h'\CvGsCLoW4{qNHլ#58(pҶ?k4ڇg_V`O_O~u5_+Qc /nߗ؎Z: pG_jAOJxOSCגtF_iR׏PpsΪI<;jE4q2n-'ϩN«L+e-l?!b><רz2F*s8L)R^9?g?򜿁wЌ'UE(@O ?rxV`0׊Mkni*^Jro<.?٧dDg<3_LA.}F Z~ڜ-9 wgf!Ƿ|8\4}(ROP_YM/9ɾ}zZncOں%ÚGIֆcNvV1,V݊ݎ%D_EZ#CcIܟvDm~?:: Z?@?S}OԹc# Ooԁ?t+76?ȿGW?| }SHO&^WJn6W wL_?|?b,1ׄޯ?ΡQE|QEQEQEQEQE$gwOo٪m#> |{TQ@!~ҿY?w4 G_^|C7sx¶O4]s۴[:[Va}yY| ~zZ?&| >?4;AO ?w.Ω>4}yitLI__xŖZ}h^Դm[P~|OEj~%aMW8č xi-}x*HΓX,!U<L_@__}׈l,-P.Gqis{yR//Rֵה^pêx)OUT*b:RRR8(у^֢Z:pOM45ޏ?d?|w)4 j~)Qi\WxJ6~>:Zi"T';⟂ |7#$ mIeRѯއ5\!4>/]iscyogwJ4j6_KwV_3\izT6=BUa%Nuj_ÝoJm7j-›Nۯ'yn:Oc? x.|zn)|k+P7xA>_f>tV1[RfOq{/)? >|1(L=C[hvąvuGvi٘'3OŽEp"Cⶂ6-ZGN67Bً2>.|WOv^&93 ^xß~* -ë" _x+^*fIxcOpYl7H˂2 [눔]H/탦ik7?Inٻf:zB3Ix.$+ 7/`үh^.'/))f |P!U@]MivPWA'|Uǫx__|IJq:Ɵ)*w|J:>SFmQE}&Pٻ|lkj=گ~iV&+8r\*oyXXoݯ_`@:}(^m;%}~@\'':qfz4־ޝgxŗyi~k:p:[= JP#ʼoĵ9ka> ʡ|m[(!9y14P2YI]16+]bO'խq| ?.࿆_ϗ6+Gt go){gOov[㶣 3V_E `d/^D1kp#5|SK~x "zd&xW.0I5R];_Qό{@N/(Mwu"I)haG_ٛUKqg4,z}HҧO|+,Y6w `0qzze-y?|4\o?h{J?<%T&ޕyL4?nbP|x~Y z"yka^;Sd+?W(M:uSkV!p59y4φ|5O/ggg#4;qe;m__'$(xאVhzƫb2GIRn"{k[{YeHY zVC3ᇀ|zU`žۻC[ F{>EYmkyc`A͗*`8'?i~qVn3l/W *@tZe;O;ϊ %o#^%[y|!YS{i=;&$q7$'j "/;xUy0_M{Tox^ _,11KZaJVuQucwT^ZK{&ߑW!/>o>&ҖKY+9'Vm>ίo#ZA22!>_WBpԧ8ԧ4 Jp^Q[ZLiM5MY٧pR{~& ?N[ *yWgL_~jGx/}SHO&^WJ |{TQ@!~ҿY?w4 G^>+t ?Ŀ+h?C krB5Οg-FK}SH-VDq [9:)ʭZ)˒%.XFSwNԅ*ku'py4Jm#诖?l?G>A[?σ~6i?_`=Oz+[?σ~6iDs$h?_`ӡ{@#x~Vq #4,%ֻƗZwKwyW"m ۰O ޡ|(N.%4oG^? z x'<+.m}:=JH5]&k&qZîX׾ |Qm?jZu;ŷ~0'A7|; i^%[?h+."od:t˫m){kO a t? Jn?5  ^ ߈'{pnAMR+]B2,pe8x, BQl*R uJIF8BQSߟWTt*'ZiԢ.٦ +>?|G ,z/yx9$K]3wa +y~3xNmSZ)C u[ծ.~"b}e{eYj:uݮAyceq՝K5ե$6K,NF -/Llu:UӮ%͆io}ep9`Cd`2|կ~ğ 5ٿF^>\xv#ЕAgi)+?Q@!?He8(oO⏎R>oτ#moI{G'4˜=σlTgφ>9WM:bQZrLGS^~:'FZ߲ĭO5}rHR4jKsg_xE."[IEZGcogĭ Hw<}9 nAk / RRgCfUkRP&z%MJT]4wokV+?ݫڲf| ;M>_Eϛ'o<G]y.EyO{? :߆|Kϋ~9~xROau{ jGtу o?4?xZ+Jg'kg_o^G)x%5VS6(;ʰ_Azf ijMC6z43<^b>EAuumcmq{{q]]Mm,Augƌ6oq#ş?~~=?D~~/_o.ͤZK +ھygck_YV!I'FpFJ$iœk+J+}]ʌ,V"3NFvwh~wnZ;+gWƿp ឿ?g/:lj7W᎛ mo~~~W Go?|_ W^~Ojt[ <jX/~П5$-0x_į+ogxGP!c_hܝ6? #xPu;M7Q<}%׿h/-lغ/| 'Ljg'e:gK> jOo|[]r/ i֚5 蚿x[ſ˿ßo_= i ]=b/ ƿ$Ǡ]Qɬ/4 ^)0iU0bGSՆӌE5hNSIZr'VҤҕ&mkWMѳ]d+?u[_ _'~#K7_Ŀ_ f=c__G㿈~;^#2f= JF{ɢx{T}?RMu35Mxn,)Wſ%s/a/Z@x__?كx#sx[MKýA^S5i4,g|=|4Tr֭Y9A9N\?gNMQ(N3ԥS0wImӵMz[l_?s;rkb~|(svоpyqk?^m jV>﴿xvw:_ٿ*|V[x7էK(ïG⿎߇VzÍ;þ+!b<5kt<+?-C a'soo¾(K_&w7>;Smm࿁tOG|!~wzuŎ4iuXJce~Ƕeim8Z}-OӡOxs 0bG|>Gog/ J49^ΝG)J38F|Q~~HrVn4.ٵ~2|^~ڶ{߃ȼ =| >'khF?fݯtrR5{Aq>42_|n ;5f;tI#ߊ?dž<{7/j? 1[YiSdVweB _㐲?{4.bt!oE4(M#MŻ=:WҮ4Vo|VƟ<~3ow}$qnwkO[DZ? ϥxo'o#_௉5؎G/G a5x~M}UbʊtFNnӜiKᨬQIcZ:2[OfqоZ:ǽx>xSZy5=NѬSǔM7a9?'$}'V$2±Hg*=KVSJJt)^҄kij$ZM~o#۷M_>|ր__h5ɗҫ۷M_>|ր__h5ɗҫο+IXU*P((((((+/{Hς?U}{_!~F|g{(>B>|ioWqP*5G|(G]A$*cCu?߰_EWQEMݥ_b$I!/Q*]?Zo~wnBܬL;yb~ |gG|S| ~6 >.O~,iv_qS UFEP4ׯ$w|⎏ /Jt\i^*Y/vc_Xat eq\BL9 >-|Mx~#$/Qo7Oִ[h?LtZ~nVX&AB7ώz-j?W~.ER¿| "_9E[4蚼EyO? ~:v_|3m0~,?~#V[?xƾY+[k4R8$ܵ"e ŰLކ.io:\$R|Bw=,~:0l72_|*PfgܔW7+SL_h7_ޥmMo#Aye4gi)%^%C=kmy'i@;~iV&׏8k4ڇg_U _]TfbuUS33m|]2o'?h+*|R=ᆝ|}' kIa1!G_xDxbh&Ү4˙.Sմ &hj.R@f|j= i3,)?%a$6v< c~ux65ݾxhzxw,/ѧ:*ZX,\⮰nZƝxMF3z*qrNpO1TmFJNpONhmOgf}/^;D|=Ÿм[7߇o=+Ǿ ѼE?k~5_] |osM'߀=~&xn zGwׂ4O {Nګ s^|!_/ ͶONo_? <]?o'~ >_ c?~OoGmkK'/;Q??e,]>7j>2x;LG_ OjY|׍~>2Եy7?{17i>{k3^WHgx [s"s n|AXh|+So[%k^f 7w~U ?|Y,k"g&xsTEM]Ǟ 7ĝo,A&wX~\ERocn?k/[#L蟵š>!K۫Qן>$VkPK=|t-q=Yu h-_/gWsf9"AԾ ~ݿ>==xž)<5 'G7's_'_ [Zf}@i_ۧ߱ړ>jpFo4_ < M 3Z #-:h_5wo^/_jQ>xW&мYC3sy LiV?ߴ7|/x{ T:Wكߊ|EO%Ծ'@m4nB/.nQxLhi/T/-4v ?;Hcx?g6gÏBkO~xJ. h$a=(lo~ ]׿?ND_MNjuhXxG(T`jcMN5!5ūݔf#ߵ7|E8jhZM ;ſ7'o/~Y94_^Vk [xS^x/|-|o׿dߎQ'g~¿c/y'w~~4?>.+W4;E?ோhD>  x  kR67e%m;3d!S  _kx|oxcMo]Ği῀@'Ru:|Cg? |#ٗPϓ}Ӿ>1|c|O|I_||GO|V$l%3F;H5O'q6KRڲ|ҩI7+:rJt9qh0vQj1J7{gh{uj|dxoWvBu~מ/ďدKO+Wkcƞ%Wa_xsImw|8?J%' 'pOki?c?eu/QcXozW<)X] Yx?^w-xwԿ_a]'{5K[,oZ> K7~$𠶼ÝGX'KjG5z*Iaz[~L_g̗Ge-S]$y$u8՞I""B@P $Nʯ۳oSmCž% ?IM<#RVyjdSK|ͥZ/|#, G}u+Erj)Ԗ_th9ɨMLxS|l_gnj~.o"-oĿ>-@  xtEW6AIfJ>|04[(l5IԵ+Q4Ŭf2c b[{]Ѳr<4m[e4jIJrٿ>=ٗg9?|]_f5ɗҫoO3'+πz.~ Id_U͜6a' ^Ch(((((m#> |{U|o_[jh G]A$WK7Ɵw?,_wŠxI#qA׀~^O@=?⿃_|?xMxkK.χ aQR?N5 kAKjV2e=KHRԳxOߴ/<|rw |F_t :/tH`]/xGᯁI?o7-_=y~_8&b2XƤT/Z(ʶЬ*X҅h#Q_XJRB]2j3KS^ϒMI<iLo?W o<wÏڇ@ϥ~׿׋~i#*|L!k~3᝿Oּm `,|qoᜏ{ ]|C;kᯀb |~ >'|3O |%-3#/]+ ;;3Y^8UlVle|U4Ryq6*t1S NXxS"NUG8ӄ$ spp56ɸA9x 'dj?ߪ|/xG>/ωuGCX>)/|=UawkGx7>Qy迱m>( j9񮃤 o~ ']sǍwWڟ ; =~x{ioy% Ěux[H׵^x[Anj|Ah3¬>'`*:Ze/uO_|W?_W#״-%5n`8/ |@"/~*qeC4ոӮx?|I.)mCvqo_ I +ωז\QxPdYb[ H /,[_ZW7xKu:!;'ӵ Ht jFW{MOH๱2$r VX@~iV&_ ~8h/~]|X5Ǐ~ ާkި5-Kzm#.$kMō[msO.<\.᫨Zm!?֥'|Ag ߺ- ?Y Wڦ=YN 8%'wW^ʅzIjVm$R+ޟ}=~GWᇅ(h_!үWXt FA񗂼C[%-GBmḞ巔j:}揬jZ.i~ޖ5|cOOk@ω{=9oxR6{|*tFiFVZO|>'_ xBkWZ ZG` 'UR4WVȬ>e&|$ƽ'Mow Ŀ!Y@ou? ]iϩH_!xvl-ibVRn_QĹOQҏhUlJhfrVx/J_tO[߲Q:e>Ӽ~+|iQx3G$2<#|_cWO i^/o *ēK[ZEHFϊd_ǟ8?gW{Wkψ!xo]gq{yZ|inĚ>Q../gfvK;%GDkkvx3m&uZ,estFh7~"cvW ``$:l"()XO`d{x, 4I5?ZxNTk<;hgmu.h_W]=O𽿊 $eyLo>ˤxSxšvTCxHԃFm%Y@$]Q*,*% P $& Rv^IBmWRJwVRz[Bl-6c߲WPec<1_ <5feƝCE|3eV^S]oej6Qr) >Eoٗ|dW|>xu> 'Ȯ! Gݚ8_ |4aH# Ԓizf3ᾀ߈>QT1m[zv-yj;dmF9’<\d ZYM|^$d[i6oii4xXɯJ浝I.k66yjq2kٽ_x=<7|;-zzǏ~ q=:<c<-mJK+7Zүn{KfV0DQuoE= e^ [|1KYTGE&K'nz̷Z~o:{w(p33'>~"!*E7VZb0'Vk_R>s^M ,u~61r 2g_bO]]s!Hl<+kƽG̝ƭKKKɸIIfodcݥeեcž5x3<6F߅<)iׇ6ZViz^«j(@_hO{ ~m iԼ]7c0c<:K8pU{ Guϻ /fdb<谙.g],<[!K8|֤m.yN8g?=Y~/>(;Ed<6G0wg"KkX+Sxj<-ZNuk{;4V6p GLq]Ht~K5VmZvuNЊIE]')$#yN{}NM#O^?[ROJxO_O~u6cgWؿ?|$e/Et~>-L^^lG )Sl?S4:EAEPEPEPEPEP_!~F|g{ 3;'@^E+% g{O`z/3M^1j_ft?]/wtR|yv\ŶhcuG_^1e 38I)FQjQZjQm4M6iM4N5iSOf| 6f}kY}OI?O}["Y~ŦYyfd=qC>33Ͽπ?+3-~ (%{z ?ٟ}M~| ٟ}M~| ~QGf[BP~Ko_U33Ͽπ?3Ͽπ?(_J}2xͷ,>~#U.֟j[]nK1'dyM}+1#B1!B)F1RQbQRI$I$mmnm[ovQEP(k4ڇg_WY _PX~+xj[O|fw}$xfYicSLG.gmO~8|OmZ@j>\?j7o|M[594c, ԒiXv5zN KjiqҪ?ʚOci~ӿjZM_"(d>sŐx3*)_dii:eziP_iI=嬒\5I :I0&FL$RG$N$n]du%YXe$_j?o_=_uw>-~g]\b2X BI$ K|/&;jpz^> i"tSV[OGI4>;? >O^?H_:|)|5Vr#-XoK|;׬.n/[pO]SULF6>f:TH?j$̾>TTѤZ?w]ёׁ t.Mux#ºIkL'-ѶJ1$NO<בxCN]oLH[S`U$Y(mJ穇UZ 5TY=S}Oc=K &jeZ3k(R<9zO}*1\.?dŗBq_9 *@>\sʲ,N?n_96| 6e5Z1')xؒJcRڣ Hz|T%nLxU0uaU$eΙO uR $[: ROA8 ?Ju*=/<{HgDlrRtГM#,I9hVt}/OҭGl`FVE |j_i!GHݷ# ϭH)Ix@:"0 *@>Osom??)@'@~޿l>-L_!ީكŤ ?ן?|$e/Ett0w+Ha:=6( ( ( ( ( ( 3;'_^_>f(i_,O;~|Jf|X#ޯh((((((oM3vAk4ڇg_WL/⾣'. Zo矊*qҪOAO /%I5Ə/$Zzm:t] ƹk͏<"Ğ>>O jVbōA7ϰ_Zk#Q|]YZOI|:%tߏ J|wvZ?Ņa_K$ןO'P|1mkƟA\__,<{$ 5%Ӧ|?iG}/jv*{z mx 1M,J1gpKeͤ0n3FNmAEͽn^:|BG Y4xnKx].Soi :f+6 E5gkw -?/sx|[ᯀ3$3Fzοx^MGľ MTBduGQne&[$%乿ZRŸsC &y}dzt޵erwI5^. pwݝmI ~-YE~i7ϗmW|Y7\4|.uLV>7Ώ\K[q+`{O<_ +g<,ҧO X~Mm;٫oݩ5m_a?f^5-ic7orK%3q /uBTnj~~B?i=TOEAᴏ-b#n"6 +ͫ˚gUպDݴ*_RMzB+G+ &c/%̲e~׳laʷu\0xr ~t_:w@/]M߁_P=N8z2]15wLQo܅V[+>)\iCzeH()ᗋn8+1Mw"B0%OM7|i̯cj|y5+:C{ ıU[^>(5\ldjˉUN_=̥B|lώ&$6jSwDDw> Ș=ƃIhoqsVH|+ E'|OxvwXִF\M\Ne'ʙfaFϋ?e ֯|a;RIQswxڇxo BH)c#^/l&Oq:<#/9u;f<'nfnOiq<^?x[~Y)εo+]u mOK;Iafh䷸p)mHH9 064T^ε5t(;7v鷢g Ni-b'g~}ZJ=)=Oƻ!&迯_w??|$e/Et/~˾/W>W֟?|$e/Et?a h[ChN(((((m#> |{U|o_[jh G_^_o'??m ( ( ( ( ( (>v?j7o|M]MrߵgCfU?ܟV8ckҜ?E Rþ.4Oơo}+<7gku0,6# 5Mt7SWm+V9J0QIumÜwO岳|wN-U$m;T,Z[h|IE%'G0YZWo5!|M)\FcMS~!xCqXsV G4mG)>|/M_&/17<3_ "EsY|4[f3\~KࣿWu/vRx3 =[K4 ~?>1|G\<~oUu/ijx7VzoVuBu,hU C EJӡt)K΍NnG:N0"҄:MNg4jWwVVtW -a_~Y5Oۿ Gwcoy+w½7>%xcC׼u'TiI>ho sx|Iӟ~.fO؟Xx__ G=o};ĺNx[YM֫Ⱦ ߏ~?^5>|㟈1OOwoχ?2ό~ ƾ%>5^۔=ık?e,Ꮛ_~'\|?m{_<Mhj^z,M}ydeψ쯴?E;3<dW'/~4/11~ǟ>k|AzgQ_ߏφ)?*~(x#Ú׊< kVO~_>o,n?OM/Nc5co>#ѿjOZڧ_|9quo/6Oռkr麌s趺/9Pb:qu*N50ӭ +ڝXj5fr)ڱ.]RJs.ikm9uZ_ˡ5/~_l~f''oBiό<w}L K  ~ KOxjw ෂ'|Y&|9-|־krG|3/EŸs Wտ/g _O|3wg'mg?q5~V?l/jߊݤ.<Axk~*eEu1Tt)׍:\Ua*Sqc'ݤe-/{+ۻW}:=7g7^0㟎7?|c_L>;|Z}o?G$(vOMEa0|M|gC>EP?lߊ_#5|Q?~Lsk Or뿶"^?_~gH6CFwĝC=S.|` ú!,T}8EghڋIGݔrJRI{컻.%Y>|x |9ӭuOjkڮzFi{kz槧hM}kk/W|YUhM#O <'ωM[]>¿(k33|C/O|REi~ӿzO_~)h8`(?O(K>տLx+g_~~6ڿ/WxGᝦ?xWnuigxK\|U'yRQխ}H0KJNrwʔcG1Vۜ5̛MB)o^v輴S |1j?gv_\W [MmbrQ$ 4!)6msqB,~ Id_Uю||37 )*J_Sh΀(((((m#> |{U|o_[jh G_^_o'??m ( ( ( ( ( (>v?j7o|M]tiV& ϷWe_5_MaN:UB0 3^p5KouQe_6ڞ< Mm #nWYwƫ=SE5|#o>_.$t돊?(;cOB;|>ŷ77ŝ%//SW{7_u-ടQ~U?ZX5Kƿ%+7 y|g}4Xʾ?%6k3f>ڽ+~/:wW$?F H~_Ծh~0>&K<#73Xֿ|Ww'3 u{sG_sOV-[@Ծ?Pĝ F:ψ]?6>h-{@'▰Y2<<Օ5O xXJIÖN )PTT9Ry:u.좯yK{˙$}ҾjUi_#&4g>>0xßNx/ h:WJtjԮo.>Q~:x?hয়U'u_e4}G7i_>!)OşF_|h.[oO ~?L$Ƈ_)~'eكG~:./!/~џ9> x;Ϗ>:[x>OnÞ=s/o]?^U灿o|;g?~_OYu|tҾ9sV+Y>w4Fo4;;;˭TxZjb^!B' ֊Qú_i3v峊mͨex{.]m+7ש]<“bbx;O n_'#n7O*Ꮘzw({ok[\|?]me?٢ּyᯇ_s |G0>&դտcGw\|('g?1O<x7ŗ Vu_)7$¾>i?n)Q5#?w_|wᕿm+|g6o J=OM)JjalDt䢵(%j>]vIZW~,A -wuna o?I|:m?o5/ό-^h^ 7E^ F ?ށ~П_/|97,>2|ŏ]~Oك_#xsOٞ~|5|b/m[ֳjtu>s~ _cU[?'Y>:||Bkj?+χmx/Oj?xY`N~`0= #> ܟ >_~'^GgكPQq|}sjx3V xV5_kfktT㇫*jMJIQQ9GW+ɶv%|I;m-=a7Ohc>#x^<ҭ׼)☭,a׼Uix7$𦮪5Ƒqg\w5|?h/ڋ3MߴׅejޙxO^j? ~Xi ֬έ6w-m]\>)~A}:}AKmіik!+;沒@0Q] <>)("{_xŞm- Jru3!Y (H߰??jҴe}3ZF>Yn͹4F&K3F\{*/>/L{ms&4|/uCOkm2TW%ƕD[# >[sx>?cԖE$|1xЮ#A'RXɨ?VطWmWS]-߷-x? _U5ɗҫ`}eyI㟀iqjmcoa=-NR %'~<;߇+xZ6i C}R4 > gȺXi]H\g9KC 3| fatfYRZt:Ua<*MI)aqURtpЇ=*5*C.mǚ1k)EJ-5诞>ϟ?F;E #_΢/?A wb } E| A|15(U>hu\1EC5o@WckQ A|15(Zc<41j-5¯GWckQpy2hc տZ+j_QZ gމ-ZMO^ÝwQųzF YkK0ԒGm38UOVJPUT4S)OcbgSS ")΅X+^mEWs|o_[jk/{Hς?U}{EP_o'??mB>|ioW״QEQEQEQEQEQE}&Pٻ|lkj“Lݾ65z }]'\U38׸U?_מ|Yue[w]h6OiIBW^*i)0IynދEkZ1ЯVjsV:u"8I,dBn-I;4MnwMz3|R+ŶV XrC;q,$J&&v,oZi{Ϳ> 5/|i𥌺~Y5uqvc%ֽ릒OQ#[Giyu}3\SA>:.>xI[FӗP׾//4wO=ג;}3O''?|:O  k}x;øԥ ʫTVĥe;>"XY%(.xGbc^)6DtoF+Co%yA_g_"ڟFwO?b¿VO,okKOGN >|ugO: x#C5UEhWڇNz zWck%RTcNN?fXJ-YPsJ'ڒʬTN;mi? ~E(?e od/h/GGOKNJd -l/k7,z4o,ַO*x]n .K. K;xQ[3=>|4}7@H~Q|u{ g拪C|a/i^0 +܎oGOgDҩONN?}57h3*suZ+#?P |ML>?G=/b)ƩSI_>} gſZӫi:㏋_>6/ :UֿO:5?h/c~>7_|WM;^~!k?ů:SǷ? :|!>.jv^մuWԿp2@#Vҡ(UQ煪8¬ݪ9Gk,)FQ=nU?0𷈼W/?'\<'SO1 o˿>i~6D|$`.cKz+W6? 7~!j:Ne|WoſH_u[/_k{PuԞg_[;6¿Cyiqchic5kO /yo .żt .+Yg>}#=֥yhg@& 3ae 'Fgk[) _EkKǒ{Lie}Zk縚(~}y}_S<-7)m*WRIFy`ZNH.ge[C?iUᖟ n*zXfhU_;+c}cy/a(Ϩ ( ( ( Gƿ _>!^]#>|<} AkߴCZkZ! a#}UivLrEux?'~)~z~2_b#⏋ڎiZ/uI.,fs썵 7R:Q9J3HI8ɵi+]kЙ$դM]4ZǬ| k>3C? /mn,kφm|qs!'z'(L!~̟ Om,q[>&Ykz-/PA clwhU/e~%xYdžSx_xn4y{ȼ/_~"U49xEҬFN 4 5 |{U״QE|Jf|X#ޯk/WK7Ɵ}{@Q@Q@Q@Q@Q@Q@;~iV&A>?j7o|M^{%+o*R} @:]:zǵli_į2s}CSVay};Z> nqc ̈bԴ]FyMIm ?SքV*өө8N/GFIIM 7m5^m|W&Y~QlkօVnӓ C$>-+ \/|Gu!YY٤>&xV)_RI ~,i ~Xh,N%ǃ/|Ia0K7M2]# X]{Hc^[ȐdΩJ)V/ۓӫrrU)&R0KGI*SZ.mo>uw;>SaK&hߍ"gO?fPO.2 ᛶ}x 7-u?!bDׅx!cBW9A &rI)[s+|b[k*9+Bӛ|kӮ?y[xsjxXQEơோ: ĒSG ~hψoBn܂Eo'X|p0'| 9|Elmw\(̩i<-[Gl=YnBJ-Tq`7ꎰrM9biaG;Zb\I|U||)$Ff v*k1*A Hetp8o' og[ Pkf<׈"^]]F=ڤ;ϥ{l *({ӓr4ܝ&ڊKCREv'RQ]-%'|?3?i c>W_4d?C ٠kG~ ~.aƔtrЧ ;(osM~~*Gͼ*Ŀd'|B-Q99]![ _^i6 Ƨ[MxN\NeFU92Oa`Z _lc͇/p*_&5'(p˚t_*|iN:WO>4(UV#(x|m_¯|_k+B[*7E+k;F/0GBeşo5З: |A~|WO>4(UV#(x|m_¯|_k+B[Mo7x?J}ςg X|65=K/uỎǟռc7/X>!źw>"xU_Iw5 ^'h_|=]? |3EexW7Kn9¥ZqORr­(?Vr ԌjJixTR`^EN2wq'' )5;_7x ÿ >|Ab|V>=?ux~ |W4?g¿mQ//0F𵾅G_Mrh}L~'T~_4O|O~x[w^,|>>^.>'>.kχVs%ϯN;7 Zv}zO//Ntog>L/~'ƿ']Ѽ'RӼ!m.Ŀ~;o4kO^%oŸ>φqG?_ko 'xoLJ*Yk^x}k|?\xζG?gSbiRj䯅Ƽ (QQxrɅERnIԥR,xTRn5MkQGsE.wVosv>?xKb_ğ xޅM i߲O- {?x@'t*+οϋ#70>,a<3<_ ?|45ɥ|A2,>~˿?f_[#toƟ(|]] +;O|_Yc< Vz<9UM5f3V[J =*ͺ(*֮%|? 袪ƜUiFuUͫ{6_ |¾W?i/tB?gW?i/tB?g',bw@xEۏ 3;'_^_>f?>>(/WK7Ɵ}{_!~ҿY?w4((((((Lݾ65zXRz~fLݾ65z{X?(V#LJm_uiS׏8[KvJR)IPtZZ>wS=⡶w(eO+77O0V[.g}ۿ'>)i@'{Ti?v>}Xc~1 AxoceQџUqk_~xM3>H|y]s_YS;^ mtįxo7S lj.~4ך>&T(qU* })>}\DY̳ziaBIV799ȡ8N_jFk5ʳN`G )}^JotY柵Oh__#_K*&㏌λό|>6_ F7;I-ύz7/u]×m? ⯈u|s~'|g ~0cv?"mXe+xx/ᖱῇO^-b~E/:Ï2th?)>8e8|uoۯCj4j& o,?|Fw<]=xwiqj3Psx\<1/"iOٰ=Zq /M ~?i?~ K[F K;|юw^ ;x77xQz--~+غ5O_?c:g_xY=`|N]|Okzڻoxq |@ <3k?_/_([C!ᇂ|S,>|tPƒ|>ask ?>&xoƾZK4K G.~|-gxfƥPitd1;~~_ ]Kq᫟3~>'+X=k_iZ>k9NWBym~*ө<6iJqJ^Ҏ_tej[:[)%^JQBtc<~OJk qQ|B5hmoSGG|?|5g#мo ?h>?Z=WM|iwτE)l>"|K6~ܞ)ӼL-Bt_ <ֺνw|T_e|wk;ƻ<3o>c#ᖇ[6E^PAG~w>mW?4~ܾ qDg|-8|pOmKL{#|1]#S$'O.no.|#߷oſ?C-|K>~><2R'2񴚝4$ZxH׵-83I|H#fveΝ'[-VW5WWpu0qC R*8zfC.ER*NJ5\qrFvSwGiW Kދ+F /2tK|m}MXizghf}uPWN*T5J*p$msGF\5tvj;;]y;6ٵ'Iس?'Iس? e:ğ,~W_>fB/ 5WQ@!~ҿY?w4 G_^EPEPEPEPEPEP>ً4 /Q5s?Et{OV5mOl/KlPu ᴱkDC:wRѼCOu{?'V9~fk"Mh.`g6% )tr#~3s߁u^^K@+ot-`6V(Slq>2hgQ*Bi4qjTjJ-[וulgM|SQ|rɦ(wߦO _ooWտ> $eUG)O_I>_UV]5+bU??\i7C?п)ǟ_> $eUG)O_I>_UQ7Wºʧ ->W:xǟ`?hout<C󫯩J|I/ R|2 ¿ot=O/Z|?og|x?~]<~DŽ7:}G SO_AxWT”5D^U'?z,]?beS~C/e|yWG< Xǟ`?houu)O_I>_UQ SO_AxWU?Z6# {*._|@e~2R(:xǟS?U_N”5D^U> $eUGְ ºΧ -#Ox'C?huT?eoyC󪯦J|I/ R|2 ¿8W0鋥CT/Mҗ->gPOLuU\)S??_R|2 ¿$ *}?WK{ԃrWOW<TǟG:?~<?ҿ> $eUG)O_I>_UQº_5i_ _Aeƞ"xuǽwK?ğxǚ]MoźH|I/ E ۻ=xeKs+"Ziw-4>]XZYZD [x`85q^iG0ܒ2Y}l4)<_kz|5𭯉1 ҥӼ}r x::.|5gI= >]C]|<޿Z ~y^jd+_?5O X6Ꮏg'_wxz?Ꮏg'_wxz/\iQ12y}G,D#N1ozэ6J .V_54YI*n^6u[k1'7uk㿅~/6~74'/ߎ_4/B%įCxZ_>!9G|S/xGxn=CR|'xڪRK)(fRqI$]KD!v|Ue_?c O+;^.W_O.|Ý]ר~?N4~?N4"4_KD]>O.exş  7t [eek+jZt6w?ff& ,N1D.O] k7> ?{2mwĺ]^m}gY-S}֟g/+:;UIQ/ g+7w,{XZoaU=jOٻtpfY0 nr(o}c+/{Hς?U}{_!~F|g{sϯh G_^??|T#/!?/|V/3־+OY-ধ?o0͎g8~ɩhq\iSrr!o5}{E|_G+!fQ_G+!fP״W_r!o5r!o5}{E|_G+!fQ_G+!fP״W_r!o5r!o5}{E|_G+!fQ_G+!fP״W_r!o5r!o5}{E|_G+!fQ_G+!fP״W_r!o5r!o5}{E|_G+!fQ_G+!fP״W_r!o5r!o5}{E|_G+!fQ_G+!fP״W_r!o5r!o5}{E|_G+!fQ_G+!fP״W_r!o5r!o5}{E|_G+!fQ_G+!fP״W_r!o5r!o5}{E|_G+!fQ_G+!fP״W_r!o5r!o5}{E|_G+!fQ_G+!fP״W_r!o5r!o5}{E|_G+!fQ_G+!fP״W_r!o5r!o5}{E|_G+!fQ_G+!fP״W_r!o5r!o5}{_!~F|g{/~3/⥿<;?o< |_[\|"|M5Q|Lt?F/</iג],vQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEbuteo-syncfw-0.7.21+16.04.20151216.1/doc/src/Outofprocesspluginclasses.png0000644000015600001650000002003012634332753026264 0ustar pbuserpbgroup00000000000000PNG  IHDRZ^9oIDATxiNڀVJH_Jz7>^ y7;QW؁3<-XND'ND'D'   @t\'{~2:S Ix>nZ :ߢNMk*:Eg9jYc贾E'=a"d=ַ@t_y[yK_/)N|M1J2":~ַ@t!gQC1R_tZߢᵵb|MF9vx- .gq]HLKL#[Ijnb}e (}:ND'):Ek߷{o_ސ@tND'CF Oo :MND :MƥND'!{|m[*:^)E'Nщd޿sDDnI\wݯg{tG{XS3eOϾ>uopNKDgatYlU:Zs;Ϣ3eo9}t^ImO˻=UYw%ogc,,:飳d{Zav=7ktF{h~==Eh[_+:\>CVΜtl̲E}bOd='xH t3U%1}M-Nt~6ޕY^j}>,~:FVߓ9դ1\sWt됾Uәs8`ǚq݋δm{ :>4i>ѹN@t.2i9:߱N@t":?4i&ND'NDeӤQtZ=E'NщĞNZ]Yt4iDt":ɼYv~i!:@t4lOkO :MND' :qN'2~ihhZN&ND'NDeӤѤm? :MN@t": ƗNFF@tND' :DIIk~ :щDtbщh0@t44ZֿND' :ME'hADDtbO'mƏi҈Dt":9KtZ/-!tNVmdYSt{5}MNf3Jdη~[):E'&0yv6[StN4 EtN@tR=H+NG0~4 Qb":E' :i$EtN@t"J~E'c0~_щDg?\hRl9XNiD`ND' :E 3N NΟMs/BED'@ID'D'.4;lp}xk}^GdD`t/<ND pZ~>itY493NL_0E@tb…-Z~N ؛): :D' :̊N'N01ڽiD'&ZUnK؉MB@tbeyM~{ : C0@t ֿGxhwX9׬po{qWt%"Ny?u{Q3{]g[5:G8iX-:EVtOz궪nџ܏N{"]@d: љ=L7q~v.c :g{MH8[tΰwYt>S)tpC  :MVqxmsZLGK8y~g~/Jt/^ 4A2@w4vgoK8uL{-GΫSf_rcµteSXb)љg -c>ǨuMtg[sO'N i9ѣ:k[CyǞҽϵ΋- f m#W=;Jᵩ~nb[EgS@t+>BD`ĦnaLN)>kZ79۽>|ƴ4M\a~:~DZ|~ݘ&R"{4t#F5?Ni"ZhZ|8~έ[]c]D4bȽSt `<;(4E${g>Iy3:>l֟sd=oӳNщIi} ' ѹGU'3r>&:E'OExܺBt2jt[%燦>֕NIA{3^{pqt)ZщIȜt[OD'(:[Fgʹ5>&:E\KXW3:S,(QN=s:E'E{2Wγ l!:;Tt.[^6F)W.׊N81~Ӻ} cJl=YcLtbu.X3:sɾ4r {E4q5Lcc?_κwcU<YܻHcDe<;}Eg۰MmtG?=ۇ^'g½J9g.r D.|It":Egq9*eO3lP1) FߧrWjE>+jz"zǯ { +St C*'B9Nz>DߟfԫF'5:vb!:E嵤{<ؠ":ɝpy>xsxxm9N,b96>N [1~$?絶~;B#wZ)cCW6NIN~x/$TrNgN]'rNgNЕ ёj-U^KyźO]=m<ؠ2{tOb WŇz}:S/sG!zCt.)9E^kjnrxLrwmfbKEgyq%֚=t,9e5'tsxN4:k_FtqcN7:WOH霺η)6?tΉ^yޮΒ{Ϋ}}!6M bstFX)nF(ZiOyպ}^`^ǃ *+^H}'\=]4}]}{O'cTVO$֟pvoN~Ap41):oNAt":y^wϫSt\5StɈaSd_\Wg{kgmӇDt":yuM"OW%瞵O7@):DtbR EgERn=){hS::;-,NlE' &e&\KEgGҜGyͩ@):Dej,I װy}^KZ͉Z3NlE' &e&\C%:^#St щ *&eAa=9tNlEֿ&e΄ *9Fu硦9΢2gX2٣sʮW=Ja}ΜwZlE=]ݪWNʎ&){URo33Ns>>I%:щ脪=,){"C82r(aj-6J_tNDh<)K畜Gw-&r^Vl*8 :щљxkEI?cLt":w=&N1i\ND' r!j9o3r[ 2}?g$:N2%\LrZzщ=cщN4HE+ND'):EIi!:E' ӤޤщChXƂ¤=E'щ2.BSt щĤ щ4щDtbe|N1):Dej,I icSt":4)3֣i!:E'щ":{@^zD'3̑D'щ货ӄˤ4 ND'mݷo/:ko{ϛz#E_DNщDt":"}LZ5^DΡ=E'0KǪg__N货{jFej,}EI 0щZi/?D98::3k{N{:McӸN[Wwp֒k4GtbNRt":kӄˤ4 ND'tg!1Ɵ1&:E'щvnC~G|NO3GND'KG'&\Ɨ): ND'p_,mD'щDt_g+Z^|k]zND'i|-v ޳E'щDt:ѡDt":AtN7^whןC=kChXNZO>~ت^ND'1:y~D'щ=tStNY_)G{JS^Gڭ_t-'٫S܏N!:cDDtt}߷B()#)9΢3Xʩ+5|Lta :ӉM#{s<>oX>VDֿSt":::SWM3a׊a :Yzr瑣=ѹ\):_6˒%s.SzdkZ9t#t飩vt :E'ʹzmꡟ)c%0VWzҫ\ȩktx1^~>чqZN Q?Ent?!3j1wӥ{ؠ":&0ܮ1(:F7s45 щDtb<f :Y4:@tcvwy/΅+@tBt2iW<):oΣ<{ݓRh֟^Wɞa =St; ʒ׊s=?Ezm}N^Szy!,ҒPLyM5sE'#GYEp/(b#y|It货ҞΔCZsNtNY?4k}^$Rc<~e(:At=5/$:E'7:M c>):/ԽgF,y}s:E'wxm)_ѽ):+ߧοlyԫָz]HΜP=OΔN%5n_zAF= k2D$p\ }:AtNF<@t":܀ DtND'D : :E'щ):ND'N @t):,|n6!:Ati@[Oo64݆YptnmT9#s. >D'N{:fO`shzfb-*%*: ,\=ΌG?y4's5Dўӭ?cE~s>^(Nt2DєYܳc>~}w]gqUZy.Y.5眯<):LGO}G/ܳug_d9,Ⱥ`s:K$S^Shn1>)NqiiܶԏG#tEcc52 :E'sҘI=s<:YtVBAFgh :E'yΖ):[G\/:s%q~bs'k@tND'S\6z.t쁌ә<%29[Ws;kSKqht[kKt9Nܧkɽ-k]Vt+q\@tND'3#؎n":E'Nщ@tfGԨ):AtND'"<DDt : :E'щ):AtN :E'щ):<87;D'Nщ=Ӊ):74O =):AtM%~ Dt货/&@tN?7 :At : :E'@t":AtNiSt":N Dt :D'N DtND'D :AtND'DDt : :E'N DDt : :E'щ):AtNotN DtND'D DtN <DDt?}xk;[5^ ~ @pw8'?7vtM%~ o*N :NND'ND'D'    @t@t :@t : :L KDAIENDB`buteo-syncfw-0.7.21+16.04.20151216.1/doc/src/introduction.dox0000644000015600001650000012122312634332753023531 0ustar pbuserpbgroup00000000000000/*! \page introduction Introduction to Synchronization Framework \section aboutsyncfw About Synchronization Framework The Buteo Synchronization framework provides a generic framework for creating sync plugins that can perform sync operations across devices, PC, Cloud. The framework provides the capability to manage the lifecycle of a sync plugin. It also provides scheduling, bearer sensing (USB, BT, Network), support for both client and server plugins and also storage plugins. The client plugin gets loaded for the instant of the sync session and gets unloaded thereafter. A server plugin stays alive until the sync engine is alive. The storage plugin provides a generic interface for fetching the objects, changed objects. The storage plugin can be used across multiple plugins and avoid duplication of code.

Contents

Introduction

Personal computing, cloud computing and mobile computing have increasingly made users to store their personal data in personal computers, Internet based cloud services and mobile devices. The kind of personal data varies from normal text files to a varied range of media data like pictures (JPEG, GIF, etc.), music files (mp3, wav, etc.), movie files (wmv, mp4, etc.), data file (.doc, .ppt, .pdf etc.) and non-file formats like vcard (format for contact card), vcal (format for calendar entry) which has contacts and calendar information. As the data gets more and more distributed, it become the more important to keep the data in sync among the various computing entities.Various protocols(SyncML, MTP, REST etc.) have evolved to keep the user data in sync among the entities.

These various protocols work over different kinds of transports (like Bluetooth, USB, IP etc.). So it is no more enough that a particular mobile device supports only a single protocol to keep the user data in sync. It also so happens that two different protocols are also used to synchronize the same kind of data (for example, SyncML and REST API can be used to synchronize calendar data). The choice of the protocol to use largely depends on the kind of target service from where the data is fetched. Also, the device UI has to provide a coherent view to the end-user without having to expose the internal protocols being used to keep the user data in sync. In order to handle the various synchronization protocols, it is important to have a framework that dynamically chooses the protocol to use for a particular sync session and also provide various kinds of platform based services to the sync session. The Buteo Synchronization Framework tries to create a unified architecture for any kind of synchronization protocol and also enables application developers to create a unified user interface to merge all the synchronization services in the device (here the device can be a PC, mobile device or any other computing device).

The Buteo SyncFW does not provide any synchronization capabilities by itself. It is only the plug-ins and the corresponding protocol stacks that provide the synchronization capabilities.

Note: The name Buteo is the Latin name for a Hawk (more from here: http://en.wikipedia.org/wiki/Buteo)

Requirements

Following diagram shows the end-user usecases for Buteo Sync solution.

\image html Buteo_Sync_Solution_-_End_User_Usecases.jpg

The high level end-application use-cases are taking into consideration while creating the architecture

  • Supporting synchronization of data between mobile devices
  • Synchronization of data between mobile devices and PC
  • Synchronization of data between mobile device and the cloud based services. The cloud based services are any internet based online services

The main usecases are to allow a 3'rd party synchronization application developer to create synchronization services and deploy to the framework and to provide various other features like deploy/undeploy synchronization plug-ins, develop storage plug-ins for already existing synchronization services etc.

Functional Requirements

  • The solution should provide a d-bus API for on device application to interact and use the framework features and properties
  • The framework should provide the end-user a means to initiate, abort, suspend/resume, getting status and logs of synchronization. These API methods would end up in the GUI functionality for the end-user
  • As a user of the solution, it should allow me to deploy a synchronization service on the fly
  • As a user of the solution, it should allow me to remove an already deployed synchronization service. Even after remvoal of all the deployed services, there should not be any affect in the working nature of the computing device
  • It should allow integration to the platform databases as pluggable components, so that support for the synchronization of databases can be added and removed on the fly
  • There should be a facility to schedule sychronization sessions. Scheduling is used as an automated mechanism to initiate synchronization with online services at periodic intervals of time without the end-user intervention
  • It should be possible to run multiple synchronization sessions in parallel
  • If it is not possible to run multiple synchronization sessions, then the requests should be queued and the requests should be executed in the order of the queued requests
  • There should be a mechanism to activate/de-activate synchronization based on the context. The context mainly is related to network availability, in which case, synchronization should be intiated when the network is enabled and available and should be disabled and scheduled for later incase the network is not available
  • In low battery conditions, synchronization should be disabled and should be rescheduled for a later session
  • It should be possible to backup the configuration files of the framework and restore it another machine/device. This requirement needs to be considered carefully, as it cannot be satisfied to the fullest extent
  • The framework should support synchronization with transport requirements for network based, PC based and device-device synchronization. The PC based synchronization could use sort range connectivity solutions like USB and Bluetooth
  • The architecture should support any kind of data and also different kinds of data. For example, it should support Contacts in Vcard format, but it should also be able to support Vcard versions 2.0, 2.1 and 3.0
  • Optional: should support "Push Based Sync"
  • Optional: Should support changed based synchronization

Performance Requirements

  • There should not be a theoritical limit on the number of synchronization services that the framework can support. But practically this could be limited by the resources available on the target device
  • Usage of the network resources on a device at random intervals of time could increase the number of times the processor is woken up, because of which the use-time of the battery could reduce drastically. For this reason, the framework should optimize the usage of the network through some mechanism available in the device

Non-Functional Requirements

  • The solution should be generic in nature and should not be tied to or depend on any synchronization protocol
  • The solution should be as much portal as possible across all Linux distributions (GNOME, KDE etc.)
  • The framework should provide backward compatibility so as to allow any synchronization services that are deployed with an older API
  • The solution should provide a simple API for 3'rd party developers
  • The framework should be implemented in a transport independent manner
  • It should be possible to change the end-user graphical user interface without the need to change the core engine. The architecture should be extensible and scalable for any kind of data to be synchronized and for any kind of protocol

Following diagram depicts some more usecases of the framework

\image html ButeoFW_Usecase_Diagram.jpg

Architecture Details

The Buteo Synchronization framework architecture is created taking into consideration the extensibility and scalability of the solution for current and future use cases. Effort is made to make use of the existing components as mush as possible.

Framework Components

Plug-in Manager

A plug-in based architecture is the logical choice to satisfy the requirement of the ability to deploy/undeploy sync services in the device. The plug-in manager forms the core concept of the Buteo SyncFW. Even though the component is named as plug-in manager, the component does not perform any handling of the plug-ins itself. It is the sync daemon that performs the actual functionality of loading/unloading of the plugins. These plugins could also be called as sync agents, since they handle the sync sessions by using the corresponding sync protocols.

In the synchronization world, typically, there are two kinds of sync services - one that acts as a client and initiates a sync towards a sync server; the typical scenario is where a device sync’s PIM data with an online service (like Ovi.com) and another that acts as a sync server that accepts incoming sync requests or one which has to have a persistent connection towards a sync service. In order to satisfy these two kinds of modes, two kinds of plug-ins are designed. One is a Client Plugin and another is a Server Plugin. A client plugin is loaded on demand either by a GUI application or through a scheduled sync session and is unloaded once is sync is completed. On the other hand, a server plugin is always loaded for accepting incoming sync requests and is never unloaded. In terms of interface methods, the only difference between these plugins would be a “listen” method, which the server plug-in would have. The client/server role decision is made at the time of writing of the plugin and is defined the plugin configuration file. Since the Buteo SyncFW is a generic architecture for MeeGo platform, which is not just targetted for mobile devices, the server functionality could as well be used for a netbook kind of usecase, where the sync service in the netbook acts as a server and the Buteo SyncFW acts as a client.

Another plugin that is defined is the storage plugin. Typically every sync service would involve synchronization of different kinds of user data, the formats of which are profoundly different. A sync protocol like OMA DS SyncML would be able to synchronize different kinds of data with different formats, which would mean that the framework should be able to handle synchronization of these different kinds of data formats. The concept of “Storage Plugin” is created to cater to this kind of usecases. Backend storage is defined as a component in the platform that holds the user personal data. Some examples are contacts, calendar, SMS, MMS, music files, audio files, photos etc. Storage plug-in would be loaded along with the corresponding client or server plug-in. The decision of which storage plug-in to load is made based on the profile information of the deployed service and also the protocol request for the storage. More information about a profile is described in the following sections. This kind of plug-in based mechanism for storage provides a scalable architecture where it is possible to provide many storage plug-ins for the same kind of backend storage. The following figure describes the context of the plugins and the plugin manager.

\image html Buteo_FW_-_Plugin_Manager.jpg

The main functionality of storage plug-in would be to obtain the raw data in the protocol message and perform a transformation of the data to the format suitable for the backend storage in the device. For example a SyncML message would provide the Contact information in the form of a vcard, while the storage in the device could be in the form of a SQLite database with an API layer. In this case, the storage plug-in would obtain the vcard from the protocol, use the API layer over the Contacts database and store the vcard entry to the database. A reverse mechanism would be used to retrieve the vcard from the storage and return it to the target sync entity.

Apart from the implementation interfaces, a plugin should also have a configuration file that defines the properties of the plugin, like, whether the plug-in should be visible or hidden to the user, the storage plugins that client/server plugin should use, the target service address, like the ovi sync URL etc. The plugin configuration file has only static information, which is read by the synchronization daemon and used accordingly. The configuration file could also provide extensions, which could specifically be used by the plug-in, but not the framework.

Profile Manager

Profile Management is quite important both from managing the deployed plug-ins as well as holds the information to be displayed to the end-user. From the end-user point of view, a profile is defined as something that provides information about the synchronization service that the user has synchronized with. A profile has only dynamic information that is created using the plug-in configuration and other information that is generated after a sync has been initiated. For example, if the user has synchronized his data with Ovi.com, then a profile entry would be created with information like the databases the sync session was initiated, time of sync, sync status etc. A profile is also used to display the sync status in a user-friendly manner in a GUI application.

The profile information is stored persistently, so that it could be used across sync sessions and also be available for any GUI application to query the sync status. The profile is the only object that moves across all the synchronization entities (daemon, GUI’s etc.). Some of the properties of a profile can be modified by the user and some properties are only modifiable by the framework. Some of the identified properties of a profile manager are as under:

  • Profile ID - a unique identifier generated by the framework using some of the fields of a profile that uniquely identify a profile (like profile name, target address etc.)
  • Profile name - is the name of the profile (need not be unique)
  • Transport type - the transport type used for the profile (USB/BT/HTTP/HTTPS/TCP). Note that a transport type can also be a combination of two transports. For example, when synchronizing with PC Suite, it is possible to use both BT and/or USB to synchronize with PC Suite
  • Sync Content - the databases that this profile is used to synchronize (like contacts, calendar, notes, music etc.)
  • Sync direction - the direction of synchronization (1-way send, 1-way receive, 2-way sync)
  • Synchronization status - the status that identi#es the last synchronization status. This inturn will have the following entities:
  • Last sync status (success/failed/cancelled)
  • Last sync time
  • Last sync log (sync item count)
  • Credentials - the username/password, if any used for synchronization with the service. These items are expected to be returned from Accounts application
  • Synchronization type - a flag to represent manual/automatic synchronization. When manual sync is set, the user has to manually initiate the sync. Incase of automatic sync, the framework will initiate sync as per the automatic sync frequency
  • Sync frequency - the frequency at which synchronization should happen (incase of automatic sync)

Scheduler

Another important component of Buteo SyncFW is the synchronization scheduler. The Scheduler is responsible for scheduling synchronization sessions and also for handling parallel synchronizations. It has a queue mechanism to queue the incoming sync requests. The periodic scheduling of the Sync sessions is handled by using an alarm kind of functionality. The frequency is set by the end-user through a well-known GUI interface. The frequency is defined as a repeat event like everyday, a particular day every week, the hour setting etc. This event is converted to an alarm event and the alarm is triggered whenever the timer expired.

\image html Buteo_SyncFW_-_Sync_Scheduler.jpg

One of the main properties of a sync protocol is to provide data consistency. Even though the backend datastores may provide data consistency through database ACID properties simultaneous synchronization sessions might result in inconsistent results in the end-user data. For example, if sync session1 adds 10 data elements, and a simultaneous sync session2 might result in the addition of the same set of data from a different source, resulting in duplication of data. The framework provides a functionality, wherein simultaneous sync requests that access the same backend datastore are queued and are executed in the first-in-first-out order. In the above example, the two sync sessions are queued and are executed one-after-the-other. More details on how this is achieved is explained in the low-level design.

Accounts Integration

Accounts&SSO (http://wiki.meego.com/Single_sign-on) is a component in MeeGo platform that provides a one-stop shop for the end-users to configure online accounts. Examples of online acconts are Ovi, Google etc. Most of the online services have Single Sign On enabled which enables the user to enter the credentials only once and be able to access all the services (like Ovi Music, Ovi Sync, Ovi Sharing etc.) without having to re-enter the credentials again and again. The Accounts&SSO component on the device is the counterpart of the online service accounts management. This subsystem also stores the user credentials in a secure location which is not readable by a non-root user. Since the Buteo SyncFW supports synchronization of user data with online services, it needs to be integrated to the Accounts&SSO subsystem to fetch the SSO token and provide it to the online sync service.

The Accounts&SSO subsystem provides a pluggable interface to hook-in a new online account service. The pluggable component uses a XML file definition similar to Buteo FW. The Buteo SyncFW uses the account identifier defined in the accounts XML definition to link the sync plug-in with the corresponding account. The plug-in developer is also given the option to not use to the Accounts&SSO subsystem, but rather provide the credentials in the service XML.

Synchronization Daemon

The synchronization daemon is the only always running process in the sync framework. It provides the functionality of loading/unloading the plug-ins, sending d-bus signals, handle profile management (like creating profiles, deleting them etc.) integration with Accounts, hooking up with the hardware layer to register for interested signals (like bluetooth availability, USB and network connectivity etc). The daemon would also load the server plug-ins which would allow external devices to connect to the sync service in the device and perform synchronization. Following is the component context diagram of synchronization daemon depicting the various components that it interacts with. The synchronization daemon is the central component in the framework and is responsible for managing the various states of the framework.

\image html Buteo_SyncFW_-_Component_View.jpg

In the above figure, the boxes in blue represent the Buteo SyncFW components and the boxes in yellow represent the components in MeeGo. The framework provides handlers for each of the external component services that it uses. The main functionality of the daemon is:

  • to maintain the state machine of the framework
  • to load/unload synchronization plugins
  • to initialize the adapters towards external components and maintain the interaction with external components (mainly the interaction is over d-bus)
  • to make available the d-bus API of the framework

Following is the state machine diagram of the framework, of which the daemon forms the central component Following are the various states and activities that occur in the synchronization framework:

\image html Synchronization_Framework_Activity_Diagram.jpg

  • The daemon gets started (by systemd) during the startup of the device. Once the necessary initialization steps are done (signal handlers and so on), the daemon checks the DB to see if there is a need to load any server plug-ins. If there are any server plug-ins to be loaded, the daemon loads them in a separate thread and goes into an idle state.
  • If the daemon receives client initiated sync, it checks to see if there is an ongoing sync. If there is, it puts the sync request in queue; else it loads the client sync plug-in in a separate thread. Once the client plug-in finishes sync, it releases its resources and sends a signal to the daemon to unload the plug-in.
  • If the daemon receives a schedule sync alert from alarm daemon, it takes the same sequence of actions as of initiating a sync by the user from UI.
  • Whenever the user activates an account from the Accounts UI, a signal is sent to the daemon, whereby it activates the sync account in the sync UI Note that it might be possible that the daemon has to unload and load a sync plug-in for a sync request in queue to perform the same synchronization. This would be not good for performance. While implementation, this should be taken into consideration and some sort of heuristics should be used to avoid unloading/loading of plug-ins.

Developer API

As part of the framework, the solution provides an API for 3'rd party developers to create plug-ins for the MeeGo platform. The framework provides two kinds of APIs:

  • A plugin API that allows developers to create new synchronization plugins. A client-side DLL API is provided for anyone not willing to understand d-bus
  • A d-bus API that any application can use to interact with the framework (the API typically includes methods to start sync, abort sync, get sync status etc.). For application that deal with profile data, a client-side API is provided using which the clients can handle the communication with the sync engine.

Hardware Hookup

For most of the synchronization services, it is important to initiate the sync on the availability of the underlying transport. For example, when the device is connected to a PC via USB, when bluetooth is switched on in the device, on the availability of the network (wifi/GPRS/3G..) etc. In order to support these usecases, the framework hooks into the hardware notifications services in the device, like Context Framework, Hardware Abstraction Layer (HAL) etc. The Context Framework provides context aware information like Bluetooth availability, network availability etc. The USB connectivity is obtained using HAL. Whenever these events happen, the hardware hookup adapter notifies the daemon to take the appropriate action. The daemon checks all the plugin profiles that have registered to be invoked whenever a particular transport is available and loads those plugins. Once loaded, the plug-ins check if the transport suits their requirements, perform their work and exit, after which they are unloaded by the daemon. There could be some server plugins that are always loaded as long as the underlying transport is available.

Device State Based Sync

The device state based sync is mainly related to handling of the scenarios where sync cannot continue because of low-device-resource conditions. Examples of low resource conditions are less disk space, low memory, low-battery level etc. The framework makes use of Context framework to fetch information related to some of the device state variables, like battery level, but for other conditions like disk space, low memory the native Linux methods should be used. Details are available in the low-level design chapter. Note that if the sync is initiated from the UI, most of these checks should be done in the UI itself to avoid round-trip checks by the engine. This is a performance improvement measure

Change Based Sync

Another important feature of any sync solution is to initiate sync whenever the user changes any of his/her personal data. The general term for this is change based sync. There could be two methodologies used to support this kind of feature:

1.If the backend datastores have the ability to send notification signals whenever the database is modified. But it cannot be expected that each datastore supports the notification mechanism. This leads to the next possible option 2.The synchronization daemon polls the various databases at periodic intervals of time to know if some changes have been done to the data- stores. If this methodology is chosen, the timing of the interval becomes quite important. In this methodology, the sync cannot be real-time, since if:

   the change in the datastore happens at t0, and
   the periodic datastore check interval is at t1, then
   the sync would occur only after every n*t1, where n is the periodicity of the sync

This is a reasonable limitation, since the user might not expect a real-time sync operation. One of the possibilities to implement this functionality is to provide a plug-in that would be loaded in a periodic interval and which would be able to check the backend datastores for any changes. The scheduling functionality of the sync can be used to periodically load this plugin. This design avoids the sync framework to directly access the backend datastores.

Handling Deleted item list

Most of the synchronization protocols support a feature called “fast sync” [Ref: TBD] wherein the consecutive sync’s after the first sync only synchronize the changes from the previous synchronization. The changes include the data that was added/modified/deleted. The protocol implementations make use of the backend datastores capability to return the list of added/modified and deleted items. Though most of the datastores are able to provide information related to added and modified items, they do not keep track of the deleted items and purge them for good. In such cases, handling the list of deleted items becomes the job of the synchronization service.

Since this is a feature that is required by most of the storage plug-ins, the framework could as well provide an interface which the storage plug-ins would implement and store a list of deleted items. Couple of mechanisms exists to keep track of the deleted items:

  • Keep track of deleted items for a particular datastore. This involves maintaining a list of previous list against the current and finding the difference between these two
  • Maintain a complete list of the items and during every sync session, find the difference between the sync identifier map against the backend datastore id maps. The difference, if any is the list of deleted items. Note that only the ids of the items are stored and not the complete items

Profile Deletion

Apart from a profile being created whenever the user enables the account or synchronizes with a target device, at some points of time, it could also be deleted by the user. Any profile specific data in the sync framework could be handled by the framework itself, but most of the times, the protocol stacks and the plugins also maintain the corresponding profile specific data. For example, the SyncML stack maintains the anchor information and the id mappings, which have to be purged when the corresponding profile is deleted. The SyncFW cannot directly delete this information from the protocol stack, since the data is protocol specific. A good solution to handle this case is to provide a plug-in interace method (say “cleanup”), which every plug-in has to implement to cleanup the plug-in and stack specific data. In order to invoke this method, whenever the user performs a “delete” operation, the plug-in is loaded and the “cleanup” method is invoked. This is a clean approach to handle the plug-in specific operations and shows the strength of the extendability of the plug-in method.

Framework Client Library

The framework provides a d-bus API [Developer API] that the client applications can use to interact with the synchronization daemon. This works out as long as the just use simple d-bus API methods like “startSync”, “stopSync” etc., but for complex API methods like “fetchProfile”, “getSyncLogs” the UI clients have to unmarshall the complex d-bus message, parse the message and then take an appropriate action. The more then number of clients towards the framework, the more code replication there is. In order to avoid this, it makes more sense to provide a synchronization framework client library, which the applications can use to interact with the framework. The framework would be responsible for performing the task of un-marshalling the d-bus message, parsing the message and then signal the UI component to fetch the result. More detailed information is provided in further sections.

System Context

Following is the system context diagram

\image html Buteo-SyncFW-SystemContext.jpg

  • Accounts & SSO subsystem (for accounts integration) that belongs to the “Personal Services” layer
  • Bluetooth (for Bluetooth related information) that belongs to the “Comms Services” layer
  • Qt Core (for using the Qt core API classes) (QtAPI) that belongs to the visual services
  • The d-bus messaging services (for providing the d-bus services) (FreedesktopDbus) that belongs to the “kernel” layer
  • Interfaces

    Interfaces Used

    Interface Name Interface Type Description
    libaccounts Dynamic link library The Accounts&SSO library that provides the functionality of accounts registration
    libdbus Dynamic link library The d-bus library that is used to use as well as provide the d-bus interface for the framework
    libbluetooth d-bus The BlueZ d-bus API is used to get notifications about availability of bluetooth

    Interfaces Provided

    Interface Name Interface Type Description
    libbuteosyncfw Dynamic link library The library provides the framework functionality of Buteo. The API provided by this library includes support for writing plugins, managing profiles and obtaining sync results.
    Framework d-bus API d-bus Apart from the library libbuteosyncfw, this d-bus API is for applications to interact with the synchronization daemon. An application using this API will have to understand d-bus and unmarshall and parse the d-bus messages itself.
    Profile XML API XML file A plug-in could be deployed to the framework with a default profile XML file. The format of the profile XML file also forms as part of the API
    \section support-oop-plugins Support for out of process plugins Buteo syncfw version 0.1.0 supported only dynamic link library plugins which the framework loads into the same process memory as msyncd. This archicture has a problem that if any one of the plugin misbehaves (crashes, for example), msyncd would also crash and there is a probability that it would never recover. To avoid such situations, an out of process plugin architecture was devised and implemented. In this architecture, each of the plugins would be running as separate processes and msyncd process would interact with each of the processes to handle the sync life cycle and operations. This new architecture is devised with the following absolute requirements: - None of the plugin code should change. This is to ensure backward compatibility of all existing plugins - The flow of msyncd should not change w.r.t the new plugins, ie., the flow remains the same for both DLL plugins as well as process plugins - Support for DLL plugins still continues and they can co-exist along with process plugins Keeping the above requirements in context, the following architecture is devised, the class diagram of which is depicted below.

    \image html Outofprocesspluginclasses.png The classes in blue belong to the framework, while the classes in pink to the plugin. Two concrete classes, OOPClientPlugin and OOPServerPlugin (that inherit from ClientPlugin and ServerPlugin respectively) are created which act as the interface between the plugin and the framework. These classes will be responsible for performing any back-forth conversion of the data transferred. These classes also ensure that the interface between msyncd and the plugins remain the same. In dll based plugin, the .so file is opened using dlsym and the binary is loaded into memory and then the ClientPlugin object is created. In this mechanism, the plugin process is started and then the OOPClientPlugin object is created, which then talks to the process plugin over d-bus. D-bus with its inbuilt capability to marshall data, invoke signals/slots, seamless API is chosen as the communication mechanism between the plugins and msyncd. A dbus interface is created for communication between msyncd and the plugins. This interface is same as the interface provided by both ClientPlugin and ServerPlugin. Using the xml interface description (common for both client and server plugin), dbus client proxy and server adaptor code is generated (using qdbusxml2cpp tool). For facilitating that none of the plugin code changes and also to commonalize the code across all the plugins, the class PluginServiceObj (that implmenets the dbus skeleton adaptor class) is created. This class ensures that none of the existing plugin code has to be changed and also performs any transformation of the data between msyncd and the plugins (over dbus). This class is a peer class of OOPClientPlugin and OOPServerPlugin. Apart from this class, the plugin_main.cpp provides the main() function required to generate an executable binary. This class takes the plugin name and the profile name as arguments and then initializes the PluginServiceObj and registers it as a dbus service. All signals emitted by the plugin (and msyncd) are automatically relayed by Qt dbus. The life cycle of a process plugin is similar to that of a dll plugin. To convert an existing dll plugin to a process plugin, only a few settings need to be added. Described here \ref oop-plugins */ buteo-syncfw-0.7.21+16.04.20151216.1/doc/src/Buteo-SyncFW-SystemContext.jpg0000644000015600001650000040335412634332753026062 0ustar pbuserpbgroup00000000000000JFIFHHCC^7" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?:|t_ |G⏋a0ᥦ}|4gP>,GW;GӼwW^+KٯSO'?8H?7>|ioWw!?fF~3|a;}gBm|8_<^R_ƭ[ #Í?7,+<9ueE? W7#ޣW7#ޯʏ_ 4/ |_lo>ӼAgK)|OxU]J=vCg|? 4k?k?"[O~ɿuٓᾝZ?k߈~/eo>>o|83!dχZE3GšڀB?ҿ?GFҿ?GF~h7 <|<cςßx:F};~h!YwV7ß ~|M 5s>xW~?|QķNOk _;>512e}_'|6 `Þc)}^X?p~3>k-?`?~3>k-?``=K$w=hZW |x:^5_ t/ :O,<5O6> >;|1*Ϸ^ 7~ M? $g|E3>4|qhYO|?<''O6qᗁ |oo~=xs/ٳ?_ x-(̗6>:,?*A-'k4oG{ѽG4oG{ѽ_Z+ď h_>~П/C7CˉgS]"O#^ωxt(-ooOt5|T{5s~/ jzG~oV_a|Ixbt Mfۯᖉs_~$_5xQ_4<B|)/ i:6]RWPxo_ѼCm|6kN-(ɸIiFRVm>hujWLIw_FRWz;44oG{ѽG4oG{ѽ_>ntowUo߲ωU&|"|C I{"=΁?/sn,l9]_CMyS 3᭷oφ|Ph|G?u߃:K o?#|7>𯌼_{᮫#?M~3>k-?`?~3>k-?`;P/į-WUu8~Q~"7tؓAaه¿W|u 'u߈2xW u-CY#ܿW%GO>|~|iWoo<9࿋㏃u_/,h~bğ~ZxNWߊ>τҴ[ OԼ7x_ӵ +^ioQ +^ioW/k{Cǟo+Gm/|RQ5_WOh cOڋG>!:k ~Ӟ+?Y-O4iƩCU gFv_όZ_ x;Ğ5/-#OCzxMHOL~|&r鶚߈/5o^%<0!Y } +^ioQ +^ioW ?9oO 4/OW@ zjc>O Ǐ}Wlnf \x'A/xZ|Ӿ7~i)~0~ܚ?fo?ߊ|7k/ c}VO~>x7GÍ u/ +/j|uu 6_/-oi_ O#zi_ O#z;u~b_5_h~' ?e hSx]SNVb%,k_喍y OE/l McvX|ow?xchSV?O➃?/WK?w[o^񷉴Vo |9>1Q=ҿ?GFҿ?GF|j߶JO?j/;PƟ_ٓ㏏|b,ǃ>%HE>O?j׌v^-/&{¿Px[Ŀ +Ou!/ _oMxS@7ÿiOX|0?OgíPONS>H?7H?7+ƟV'^ K'|!GOCd~!??ړW~%x_?n=߄~5-dxCğxwI;o#W㿊 |7s7Fj k]Kß_|$)oKeZ_ׇ#?_>._|P?dE| 8) iό5ooJ~z(4oG{ѽG4oG{ѽ_:<Jifm30j?d=oƞm?|:× <&~I=ǍA2ito~Z׵ڧ??ߴ~Ξǁ^7_⏇uK+~4xĨj|`N>}GG{^4Z4oG{ѽG4oG{ѽ_ qx>2g?f Mo/C+|SYJ|H|5Q}kόd_ |Ne𖙪xᎇcŷ(~sx;\gO'Ǿ&:O/ x"r@ 9&|H?7H?7 K~!x'#+E3sźi~H<-%LJ?d5x'_|$g7ÏhZhVo='}k}7 ?eO#dž[|O+o/cm -|%oFÍCW+OŸ} +^ioW!G~0־!x{ÿw{ _e/M Z`Q'_hmuO;}FQc̖5;Un?4/)'/|A'w l&|hx/ZiOx_mG杤NKNKc]׺/٫K7 ҿ?GFҿ?GF}{E| +^ioQ +^ioW״P_ҿ?GFҿ?GF}{E| +^ioQ +^ioW״P_ҿ?GFҿ?GF}{E| +^ioQ +^ioW״P_ҿ?GFҿ?GF}{E| +^ioQ +^ioW״P_ҿ?GFҿ?GF}{E| +^ioQ +^ioW״P_ҿ?GFҿ?GF}{E| +^ioQ +^ioW״P_ҿ?GFҿ?GF}{E| +^ioQ +^ioW״P_ҿ?GFҿ?GF}{E| +^ioQ +^ioW״P_ҿ?GF{߉_ >3RX/wǁ'n6V ~+]7PմmbNխ5]NԞ;]BOG?agtQ@!~ҿY?w4[Clo ό߰z~ĺ]ErcƧzφ<4gVw?|/mPL?H~6^>|wY0/<㙿kaǾ־#ONaJ]OWX~ףcc??ڕ՞qkc>r!o5xNi~ͺ=_xkc^;>?~#{_xsνy:5=NO Iuk}KBc|'ڗ짢7h+J-Gƍ.O G4w?<'T?u/|usk:_Bo9_ 7LBo9_ 7L8.a_| Kߊ6?~/h2GN-#ѼK_::ΧxM<_n ׾pe?_<7~^9w_gAV_ x#|o↳oz?Kg'?xǚ?ioҟ|?׼%wڽu~{_?`{_?`i}> 3*u?gj8/e+A|8G|3 Ɨ6|?D`"/п? | gĿ/J>|`/|3hx5/ BxHizkhVY^^_G+!fQ_G+!fP)7?d 3Ÿ|)wO2> xI^ߨŸXT' O>;&w4O6?o[x k7|'⋫Z|M m xn}fyq'ƿ1u'_ߏwo m.i?L<]|K>*5BH,C/oBo9_ 7LBo9_ 7L5>5<[׮7|Y/|['/^>4I6h .Zxw~~ԓk~,n>%ºO~̳xw/kzo?q} _i]sHxtK mf!C G!C @FS<1㯉>5WW>*'ƺ~T_o.5=fM!῍Ѿ'>4ծWǚZ]ᆱ ^_5߇|YğK6?]|-=va7#M}!nnK{_?`{_?`դs&).%Е[ޓkW{['hsu~^'+?5CQPԾ7iW?ğ~~4ϯD[?U~UG SN<$C~ßY|bK@o_uOP|~|AQῃѾo^7-um/~3/~32+M1y5x}q=_o|$WOZ7]w xoo5?E4ƏOFx\PgG_c[?>״a& .O_~0xU/|;71xǾ4%|{mz.P𾇤Ũ^_G+!fQ_G+!fP ?a٫^4|ݷ x_|'=}y?ǿMgf ꚯ u_ص;.a/׈mOx7ߵm;? /ho?4-<P7 #ᯁ<xOwŌzC@%M_G+!fQ_G+!fP1&i`[Ml5=Ə WƟ|%{HMK_~ ū?|(/+_!k};^-?@][|a˯c/#t4//~3/~3GTwG(7?|Ծk >MxkO jS,fZ_ iZw뺬7?ǒxL_+/Bo9_ 7LBo9_ 7L:_''l>$/A|)}~?~:<#Zޱ;ڧ >#x;D|OCEftm;Lkf h-6'gٓwφ^|%}eKoKc{iqo0~|;/~*K >$ŤNOBo9_ 7LBo9_ 7L<%_3oi(>x+oޜ¿ ⷄ~9xGÿ[.? /DU[x [_tg!k5$5ϋ~>+g}<6|/!|7a3⇁oÞ5¾_[gS/~3/~3\  p??&oebN!eio!9= *~?> i>#3[Qh:^3⧃|º߈<-eoxwO_[u-O~_ ާKYJ!C G!C @njA^/Aw¿#CIg^|7mO_4/|XUP=˪Q| ~ϟ0xs\|`_[/鷿_ CxMIh?@Bߊ^!? 7^]7C d/B0j? d/B0j7|'m?ES'׾ 9WͯWGw^#Sš=Ox6w<;KOY[CzH?d?s^ <9㟇z/B&huJIZT//_~ɟ7O\Zw3|U#hc&3#@m~麅x:j CFօ?ejBi|9迴k/>|]k] ?C|G#x~|񶟠F%5 ((((((((((((((((+/'GCf5]}{_!>?b4/٫Tς(>B>|ioW׵+% g{O`z((((((((/k_> xO//?'O7⎿k~ ~~oN4}#N7Moo6^j Gƿ. g?d/<߷7M޿pχ2{|]Z[RXU~U:qN`&qЭ8v1oWoKGE{BEN_߷g.?~3tV8?5KMm?2/ ~_~L8!s77R8!!fꭄ/{դ^\ ©evُ6tb?ID_g/?о3uqB2oo$^}<+4辻YV˯3qO{h3v^Ԋ2}z-f?O+?KD~Կ5M|61EO|/s={W >Ok|B/t+˭w:G{\I{iI.fo ?=aSύoؾw^$7 ~ԩτR~Yn'){vIvѓ8Nm&Yl( ((((((((((((((((((+/'GCf5]}{_!>?b4/٫Tς(>B>|ioW׵+% g{O`z((((((((įڳ(xo/ğٿW_ K|f [W''ҽq_>'K N'{ Es֐ ~Jl-*)NRĦgR1iNxS_*w~F*MW~z| /K~S?VSO6o( }e.ZA>oHOWO GWό_ݟ+9hOG7J?V?5e>Q KOc'zĚϰ#Y(9wz4. {For'kKW9L=i>S?z_,҈ޭ(~<%۟eQSNg?g)~_J~ U2|?*~ƿI]N{yդxOxjZ|Oxo4|9ݟxv`~ӗ>8gmx'\ķ1fʱhu߫[c{VP FOO{ kQz?׆h8M<{O=Ěuֱ.5EcǪw~*|[<[{͢^Xxğ>3h}i;-5dMGS_/*':rEF+[ͨ1.5j+EOtܾ;gX?=ia_βuK']К.mr uo ~nxk>3?cn5vg.4*;|5k[WVgwqz t 떟~k?OxYSj:'?OUމy;Ao/_ oy푮|dѯx'jo?|5AmK._%> /5 >o~/Cjs`FGKռLF]IeE;өy{IAMSy29.Ji%=?{]WMm]?xWߴY?E/gy~D/_ο_~,| 7íw WĿ~XxYڗK_ؓ )?Ǎyg;߈Q~6>|&.nM:)?ޏ{mh|XgS_ ~=Bgh[~^ESiW^__oxΉ<]/2<% Ia2(Trp8F\~nPk8ɤӸʦz-=mӺ禭vG;j >1k7t_ xPF?j nS4|;^çWEqusZ;N~~}~Ը+i>_ogt?n 3g6G|x#?pfEx>$`xUv˯Q [W~1j<i߆w/>ß[g/>(|n}lj$pOů-/[%qAMds)5 ::^#i-*Ҕ79M6zN~/?M?W§_??0M⨼M/X5 hc[5Mq4O })z'~<|W- xL SQtAx?֫*&ϧON/-sZה}tNJ--n} h#oڣ"~0W@ jD y?Ռ]N+?O)N?ja/ڝ~~9'b/#[_A"?VG}ƹl-+' u3~|| grEʝ~ z'wP߰A]}5i?\)anϧse).8'߰_B~ w#' X?!M}&vϽO\ۢ?\9_ߟv|> |v$xc?_4xh_>8z<~ox?i? }ܟ~O ]å]4햬7B8dSׯ6o7_kWPRT[{jNW??J|/ /gþ*ӢO6xԊ;l>R8&;ํ.PҠ6yO#_ ?x Q]*6|(((((((((+/'GCf5]}{_!>?b4/٫Tς(>B>|ioW׵+% g{O`z((((((((o~пd_\߷=k=O++0{~_.~/{g?aÞ2>+DoSO؟'ſ"|[|5㏃¿|YK ^|%IioQxV}Ğ=l..VK 'U?z,o*??iG⧃?cS\~~𧀼YZ? <|-"Ѿ$Eug>Cii'Gr^-ӭtͱ9V[Bz3?m ʕ*k 'N/cNX8z2'5JiӄI˝MYi];W'm6i&?~uiOW7?'_i'WoGk 1Ǣ߶|4g %~Ϻs?ƍO87||;xE״_/',V v<'[1pMsʴ\xXQ$)9RQɇ(NRPS}WOv{Eom/9:}R'Wto?/~1|5S~8kǾ_i[嶅>x> ǯ?|G1/ ~-Ag"ᮗuZjuWۭ?Ѿ)?7< }DM֘-No-G!xƱZKռ7iAKMN98I 5)V:dڴTս)FTeV?7oɫ_[ah~ZG?cO5s^Iȃ7,>'3"_"M}P5' ]~4Pj?|N=M}BV?0}6/ѭ->| |T1Eף^~XoRGJΥx1USIJriPN+Eߙ^kPqW;zf&yo?oH03*aKoڛ|R%ߎ:G%6O~#/r_gş>xGEӵ}bo^_6OᔟAΕ՚#/=Mi?-? ;L-|%м}}jO>+EWO<;/4/>Ṉ+eubҌTNuT˅WJT$+qmhzIu/Y%|~_SsxŚĹ<GVOśy_Y φ M?kZ\^=_K#+ఞ!e~Z߃g/}sG>΅c]Z{ß >)x_x[ = -J1~oxcB|i|~B~->𖝦Lxbm4Ix[Ă fڒ.Y^=ՅfAӊTb8ԔqVNI="ƴF[osE>֭"_Ɵ?Q{Ox>P|4hcDfF u]kAǝ#֓C^ɤ\\G y|~l?zF^h>2saߞ[~x:'lu-2╶1|?o?7&s MIK/J3c)IR|NcNn9%$~{ݥ"ѿOntů`I᫭n[e]wC qx |- ױGkz#j?Rߴ?hƿ> Dx;?x_^-׼ sg:3i4~XNJ1~ܚKm_ kO*YԒmԾ-viτ5jV"7O ¯cuLw}k7JеMUeg V|',~?g^iZ֍gz G?&ƿ[ -Ξ4>='?Ki~/xžм.~94WR5i8htjkx*bW$:M+Z-o}WUKw?ϵH'W/(koU:g`9߶,?'{O'#ƳόGxs2'Ŀ kXY$P~P=/|KN(Ÿ||47? [vד|6~>xN4~ >CžnBUs MY(05)RbͤjJ s[s^Gԃ5mzj~~?m/Q_=¿V?$'7c+'?h? 5??|E xo [nyNX?_~e;O<)Oo~|=3ğ|8%6W!V>,Yy^mxyc+r*89*ݧ'JWJӄ旺2ۿӱZ-ۯ?<ş~u[`?R hl4?VuItM#S-=yFO޿ڳo'M(AkyoZ<G#&xCW'[ê5GZF-oiWgl]\X]iwV7e{M<),i%՛[>_X;Z^\c5jTE:~BMG|֔Re}J[w=)쉯z??־Aϰ#WS!?1W6&WY#_O#_ ?x Q]*6L^_6nQEQEQEQEQEQEQEQEQEL#!BL.?agtQ@!~ҿY?w4 G_^EPEPEPEPEPEPEPEP1B?s~ ||`B讅Iv<匹*۟DIwƭ_Q?/~p]~%E?ޭ ޢE;?ڼ/[o^:|I>~? n< 㟈ח77sZe>xytVvvWR~X91Q)JrPa)JsB1v4JZKވ`2qVQy?ɉHB?cl?eb*~_Ӈ;qάձ.ǫ_ Op3Dz?ƾKOG'g;o֣iH|G UOc:~9z?f;_g?kٟYa[A?|Ĝ ~?~}U>%db_ڈ~<~u:$: ~f}hT2}_ok jO?w|=?8v|]kΫh?kᧅn/~^,Soh/;I ˏO}ƹ]e'%jQn5iT$urU$dk-^HFk>i*դSרzzH;7_VsN:;[E\D{?ϽYw=P2q\{^_gN_/$Q:h;_\}>4 zğU^st¢E;մ^}r}ĈZLA?'-WdVq/Au>歠>u7 z+)B>8~_xgc@8~93\8^^ʜϕ;Ut|5ɗҫk̾ Id_UQ@Q@Q@Q@Q@Q@Q@Q@Q@|пfS> k/'GCf5]}{EP_o'??mB>|ioW״QEQEQEQEQEQEQEQECtg}?d/W7]"/3||b쇟<߷Ot;__|¹[}>qWM"/A֫hD@?m:z9ű" ?JW_kğߵ}[_~>0dh^/Sֿt_TGow_Dw(ѼR>Ϧݧټ#} m:o[˩y;|CIS? ڌ 9JJ1NRz(d//E ϯ?V^?ZxogOέٯjןc#Gz*fOoϡ+K֌K=?S¾^'o?I]~?l~??!ǿaakϫZO#HA|?8Q_/l?~̼'kOG,c,/q?Aƒkϩco_+3?f.H3~Uy33}?_|g|qm/bǙ㏄~2*X? ~-o?n϶/??cϺ x 'yiZ?uZGM>G1GO/g&1^yL\h]Pê W V& |<)6iSn7ngm^(~U^G#|:O3ϯ\)T.~?Ł^Vs]b4_/avk_}rZE;ھHO?gk@?/ő0 {Umm챒3hq~¦'UmYwg l;?/T;_zo[sd|\'mx?eS`d]?črδoַOV,ghsU_׽|n"xe.p9.Vş!P~/a9׍n5lߚOOPwVO}ƾO_go;,՘Wth|a' zT;n8?#?d8?1<rOJV_oO]>;[E_h<)~?gx']k^9GK_[^ X~wF5K{raF&}w=ϵsXII&I$Yӣi*=)}S9&O:g)=pq w5_v?qWk_L^^eSHO&^WJM(((((((((+/'GCf5]}{_!>?b4/٫Tς(>B>|ioW׵+% g{O`z((((((((#?0?пd/\߷UuH={1T||dBGꮵ k1g]~rGwV`{O@>5R%ܟ?Z1.1Ue/9q/CZ1.q=?aU"_ӏӏ?Zx$Re#?j7-/ƫW$wo|ңZY%wԯnt~hENJ֯~s~8C*Ju)0Z1 i8fe%%Jꮻ^߉5'sq=|i+?j. ~_o jz/ex+>4.|%$Bx[Ɩ"6>ké| cOEims/هFռKlm<}-x/zw߈ ?jO 0ͿƝ VOϗv޼;Ke](S:_>㥥ŏag͙cw5՗CoaZ\ϩj[x^Dwq41 cߙ8*jmTt䚋ܦԌdRWꮻuD)|ck|a# k:O7 xO7eϋUiz>9ǚFxOS CDBß~t/lt~6-uR|8u1֟ GŸU>4D|,y6¿Uٵɼ?߆mgqYIO m5m CS<(O~յF Kx/Kw{>5z~_PooP}~ ?Oox;/5/+U|SI^yO~m7|ߋ~KO%7o٣6| /jߵ(~+ a,xO?|B?e?bYg|7x/N?|3|7>6&[foӾ= B_?Gv/7Դ;? iG~'_ |5>\x[.U{Ěeُ/<gZ{O|!ot#4oYE7_6l(Ӯu/%+PKCN*?jJ5T.H2nRr916.'ywA_??C~e7x+^3Ι:=>"~?k|?ߴLj"<$|9bž)>?x&An gAxWQa8h?g#OG>?]j?WZٶᵗƏz _vK|?KO<^g_߳/H[>kcxRYGkG<)W[w@Ӣ|o&n4^𧈵_ˤjǠ'_/c'~Xc? ~C ͧ.co;F%!ӴKxOǂ,hgV6x&ㇾY=y=jK֌Kg>O\75G;+=^*F1It]^?ןҪϧZ1/S\5e{[ Q.qU~Ot/CZ1.q=?a^uY~:$ 1֟ҿL{=>*oȿ~":j}ET Qz_|<#=|Q3G_O?cW*UO-pVW/_coF2UzmyO#_ ?x Q]*6Š(((((((((B|Ʉ~?h_W׵L#!BL.>(/WK7Ɵ}{_!~ҿY?w4((((((((3||d>Es ( }?ᐿd ?vh;_?2aÆ*Ǜ4&#Ԉ2su(a _}đz}?_+ҫ.s%!fM}\߰}|J\ط%kO_j>[ ߐte?,oKPx~3@c By㿉? VԿ~G]#tx>+t/hWvZ Lju{{KPO_g'~~Qgikgƿ'',=hSY~3wx(WRz~d_~!=guEMZEh*𞥪-gocYޅ˥^8OZ[]#H\*OS]4c/g>{ӫRSj*p*Fn5궩ʚ8($ܷ򵒲kI˺]/W[wg:^k ~V7?k]vOtOZ^q; gͦwS$M#F&KNbue3*'Cp? eMX;LiV^"F36 c׈E [^!U/O?_:`տࣱ?tt&d:_ DŽko:׆|Mk~'񎫯 LxF<)x&O 7/JSgϫM֊yOSQIźXFN*GVGURs5N7Vzrg^٫6 ~Wz_ a'/ _74[ |QXԃW^MwJw[KKO_)w_~?6_爾xKßߏ =?k]m |Aks/ij>῅ixUoL>+Sa?>&'*i+ZFu19[m>1Y eKnk|?[V;bi4%s(YźGf9? Q S>6Kv஻ xA7ošl_ g>(}|Nl|YoR7ش/'UO [㩄OkR9즒jsue?bׄ)a(QpqJ`?d2)-~U{mjOo>?eksxwſ~0e7~ sJ| W5~#x_ċ/~GgOx f%>'it kw1PX|Qτ?w?ǯ%|_GƩ?w#Gu_k_ -qfٟ/|a⿇~ |4cs]5߈_|=^~h5?^^h:>3o=WÞ"|DiRy?/ݬG|et[|eV:mo 76y>~4u/ x[6A,2IÈG.u3 2NX&"b2FrRS/H/~w}?oW(wccL|Zw1> ?/? >|`czx7AxwΖ5>?o* ~:e&^ e+;_/ jIе-A״m|!&Ig^/G \O{L4^]W>(zhO~ ]~=мo>|Y*|00T>g i^14oVZνп_oG;~ q1?׈e Z| ~7_|?ïvm[^էtx6W<)akuegʴ)ҍiAʣ$xy s g  kCO?wB%k3H]7L|!OGH<=k%ދo})-sƷwi4@U'w'Q<(|e7~֭7=K70yxOQMsR%5ooxLuϪu‡|z4ԹYRsǕ*JRVɷ}hSFno_g_sj~̚<7x?Wc|Eiš{ ݼqa1*ÕU?F|-㿃;࿇_W?(=+>ZxO%4-e~x߈|e]Ŗ|OMU/oKox4ߋZ/|kNQ~|*/Ě~uߌ4gxg7?oB,>7|(|kϋu{񶻯h /;H>(xcxRM׍- 2?Zw|Ivr;ZqM7cDtz{# > |> e-G?=G^Vx;r֕]OTxh~[K|M\x*Yus#W7 \H6|q~/j ~jrZ&_(g3%KEMşhzrT/O5ứ|)G ˿c|&gG[ <33¯YbY??hW./gm֡m_M/ yC| "K~П_?*ׇω|AVtx]G#_k? x{Cִo!4 hڗOUH;IZoJ*ڷdnmofk??iG45 _qZ¿NG}ƿ2?kj'm=E;ڦ>ܔm]Dz|?;[+~Jï?2h+oi}}SHO&^WJM2)$k'/+WQ@Q@Q@Q@Q@Q@Q@Q@Q@|пfS> k/'GCf5]}{EP_o'??mB>|ioW״QEQEQEQEQEQEQEQEg8g=d/W7];_Ə4/W7~3\7iۿLG{մ^5.O#U/Z}ĕ8ƾU\/ش%?}`w=ϵ|F|<9>=s_~1|C>!uS!=ŭ7}i};N!_ iyk^{e4;pWIƖ+V\PRJR%.Xɥe׺ZgDAI>~?|6m%?cgkZO͜cKŁ?g'܉>3M[//BE{~{>A_+Ax 0k7 f]~<| ~0j]ǎ-oo7j>!9᯴] e4֬<-xPť^;mL`~8oV?̼~Ŏ0׍g\=![p\fpaakR Oԣ^w&sԢcNncwm k5VoӿkgGO啧?㯆/7~?h__G.FXhx_UGدIiwW񍥇=~п~=i:t σ+&x&o?/ORZh?𾟮Zyn]QԿae<ZZO_6-~fGҼzIR57ZS𙛄!JZ ꪞʔ)ÖaM9J#9n^ֲ4/hk.G?6? :,/5''] /&](KM¾Kh~v_Z[i)#n,N>2~W"|7 <| o.<) W$qw sD/>?>hol?boh/|TD۫~^8 3ȩB*N8Nq1*tьB(MTVVV [`O |h_<GS/ _὆W?^]+B) JZF6v!X?%1Z_#mJ+ 8p??2VSק7쮸+ZsrV"cQK1Z+i}QVgÏ7ZG? ~ڑi2 ?}ͱxG7'z\|gH.5y_kovT6>g?3|}j/ ~߲{~ͱ-uw>g'>:xHGol%}q7gT+oooq?l_Z%gOa'g3A|/'U6?>BxZ[/b]BO6~߷ao=?y85y99T ({?ehM9IMk5&ۓnW{ kv#oO|I#짠nayo/?4?‰Ú~%C^&֬#cse_Ɖ?o>/px8>'i>7Rŗ;iqk߳V>+?|Z~JK /_Ư /?i}'M#8|s|A>+O/-;B̟ ~wO{Uu/&~AY>z3Wf~ԓ||]Y-¿ןo*Oֵ\A/|/ξkH빮h?s:Mަ_um}N+?*8!>8~F+=>CW?jj;߲O Id_U_?|$e/Et(((((((((?agu|пfS> h G_^_o'??m ( ( ( ( ( ( ( (?i~?m[e/ O7|+쏌߶7$ڮh}|k~Ơd߳&OW}?_OB0זTkԮ+'srrj%%~>Zzkٌz_ /¬at@Ꮏg'_wxz?Ꮏg'_wxz|w`k#y}-~Oo/`emظ`^Qg_w~?N4~?N4[VJ>ܿb_~ˠ_ ?j_nد]quwquwb<XX~fK#Ubz@|'淵ZOۏ(-=h߇1D.OG1D.OY>1;л*___|p/L2O,^WUbPzA$濵}} u8:˻ u8:˻O1-[W=` ܏+=hO?ϭYO۫!Uo_ƾ:i:i7矗ð{GIG7?kt?a3e~`_?wzquwquwwgwמ_~i~s῅*e4h:-h:]l_'Ƶ M+~:#Yj|}ĿP'lC'dO X_ ]GW_j4hƐm|;o~оѭ[xzFci2<]K=ԳM'__w=aa j;B1vW ZW?o/g<E| /ά rO'z <׽~?N4~?N4/:޴iRSٮo_cCNċ?͍X__`=͏׷~?N4~?N4j;zAwOZ~$_{YOOWJ;6]׮~?N4~?N4&սW_w/p?ᵿd!s*cG9^ u8:˻ u8:˻rJ_b?{j 4{_>_ OkF7OP4kF/} u8:˻ u8:˻o&ZqImȃ?$ is_B?l/M9|GνDg735fexw'u}G;Gӵ Zs}Mmf?AᎿg'_wxz?Ꮏg'_wxz͵o=7L^Y&wF ^w{ϹtsOÜ / Xk˪;י[Gkk_ROZ.??޽k_h&Mj}s7=cQu:gxFtkxBkr 6^Q:E=~^~#??m ?g~y|3\T*P׍TRM*Z7]7NU??imKOE^>ը8;~ڿWto 45}NO}g(zGJ3pj(/WK7Ɵp?SK/Hu;R*.]Z~߳žOWA!ԵIcKusF㺞']:Oy}Jf|X#ޮS 5&r_]}q\Y]?m W2nWW53e3~9|OcOK YV@c(q~h_F ϧ?oW\cۓ?_ԕ(VWavM=o/?H?F? ic8Oyz_C񅿴x9z==: Љz{~~ԣY^[i4o|cl8Fۯ~XO3blUƽ_K(^ J~~JxX6jOBW4/-j>%kIi?HУ,|O\e~j)t/ '~>1l,?oTNe:Uq{(pHnIiA]I'9Ԃj0K+5}!nɺ~_ЧgƧ׿R~ÿ5Ld|>x2WmVTz4]GߎO{[ Z;oe+ZZ2⇇s/_xGM_o{KN-|{}fn/x/1⫽WXd4TYS6߳sr`gV{;'kU{jovg|p?` hԃtks74?_ _Vi ?4<%gI/x◁->]@㿇 w[nj<wO$xDxoGOٰ oOៃ|wS 6xTxG_Cm+ǷV?tOo?W|g_ xq>|OVyk_?1OxDx)~מ񏊾2/ i^Wtgৌ|Gūk-ZV?,Yd>dhN\Y{I7vڷ֧{|~+tkӟaojԃ?` _Z |5?<;Ghuˏ-?|;׈$|O4i^_$>=Omr/@%Ko~ ź_x?Ǻexvg =M&YGi>&oi{ ZdžGi%ƙzŴy/mVɤk5z ]mM;=jM+CקL?^0'{o#ѱ^`?WdqX(]ۼv+_?kMOͿJ?jOU.qJЉz{~nOr?b;ƚ qxW]{~߯)}#9~> Id_U)ƕNX*~{KbW}+Ŀ?lMKM;CO%W2V{-È!T`85oHt/ ŭi2k)ٴݵ`PQEs,犵^63:YӧV_j>,mg\ŚugYN׵KAEPEPEP_AiZB J״QEs>2񧃾x[]xg >ox^1oVO]uc} {]D2JQ^+Ot_x߂9Gį^_xW9ghzޡy}G.irCo~ihZmm-hot}cǚgiZxĺ0/ƭ; ׈xGú`B^w_ Kï_uY,񷆾 7RAa jzy5wo01̨g:w^o g/_:"w/_ cjOm_e. CyZLݰQ_=|.?e/:&W/%Ïj? 4|7cCo~"xDuk}Wv%^I<[4]JZ+# {?왩x{៍*ʱqkvE$ngᾀmu5֣fTI^X'*ٶaZ.ę:NON Q|**ABK_ߺO/?cOKGn?_?iCOÁt\<~ϷhDx__ѕ(VW ^/s/zvx#>s2X}?{uz??iN?xZn>'k{[^j'4~__xW=Is\q59u׶*uy\+| en0O W7>X7eCV\R4OgCHo=Sq{^yjOi-KORV!5~עS/tjMSDh/>h4mVqzo Aۧ)^3#?gDPi)gM3!&~Q⶙|^5ýtOW_}3ƶ?}w|~ZҾ'o|aȾ[<7xH5Suk=?Ruyey[c,1Xzpʬ%QTk ^.J1R(vH.HF )Y_i[ޒkh?m{:?NύOOG“ѯ'a 37j8"_i~ΞxL>&/4.N]k5)mχcz?'Ï?jO Z?C QS/e_xuk%-z|'bW2>ȹ+N#48*ƢRSWJZ5RIƣ\]5}E.{0|>$|Be0i^'kP_Z{ߌ๟>|wSVd<c#A?/o(¯?/YrъᶛɦGen5v, _Q*}Z4iUpqS#w*,ۼW~'׿T~l_a_?F~ < #M{ing/!d͟>j|IأcWCi:zo {-?:Ž&<|]>4|37kCfĞ)>1k|+Vu 7Xl[n\%cQIN,=_|7G[3h%ҼB }{EP_o'??m?_l g% p~ҿY?w4ɱ'5;Wf]# a /gD3?kF%ܟoU^~oTU<ܳܟo{քK{r~/U^0>ןR]ߛChJ'>ίF0c@ #GU^ܟoW9ogSW;d|J[⯂j?߷u_k^x]w–xZWᏆllSB1 T#:U'җ4eF!du*δcI>HJKEZtiոCziuݥ_CO,>X:Ç_zxXo a'"D/>8~2k'kƷ/ߊ^t_xOOj_hO(WO~|md׊?eozOokh^"+ÿ |?⯈'O:_B4keWN*/R(F$pBTeU8ӫ>dᢍ^:7m$;G.=%;ey'o>$:|@|g⯏W~|I~|.?,|Xּk Go~4o{wNچ.YyĿN:_ƞEşx_^WğLj&AxW;G/(GĿL'wǯ?q|i4oGZ_'忌~$k>òi:au8aHߏߴw><0> %|deZߴ yNxSݝ&x/fxz^CW[LOZ$UaJ\\c4iW+(neWٲխ;} <d/ ]i +5 6?fÚ珵߁,~6|?W_ t:V"Ş4{Dkcoٖ KH'Cnm,WqC|VnÞ,5^x{௉Eu7cö6?_ZJA_<)0Wj*W7|X_¿kÿx?>xWGj~9Z_7RZ5֋xH6k-|}՗1?x[)czkǗ? o>)~'Gƫ^+~$|^<[yyseᯁ޽; 񶳫~i<7CƉ_x{2iwqi:<ݑs6519Mt9]i7+&dQ_;ޗ*} :?֮D1~Y[K߁ϷhD=AUh}hĿՕi.L7>~Ǟ">?c> '|._k:U2дFTֆ"S:N4wgZ)n5Ǘ ~xk BIe>l׺nkQhfRkk`$@?/~~ .'Ma$A?_ﴟ Z~}ѧboW3Ѿ4^\n_| ^T1//oſ^~.uB?h h٫'7~5L?[73>=kmcݟ{aO-~ҿ[? 9|.?h5\x <b_+wu/fu\ԗ$|ycf||}"SYҾӟ/ŭw?>x{[|s?|W'OH ,|wemlF_O¿ŗ|}|u}/&şQWj^?~-x?'FƟo/oV|TL&~^$AP>~04ĝoWuxB/E; 'K/> |៌go}4uk)xo_α??e~4ŸVkZ7%k5k> ,4"cծluHRӬf4A!Enu^OI-ƭi 쑵̠(~#xoK-O߳<l 7◉cDa}s/<3Z|泹ZX$څ06>_^j|lW5)Y-㯃چ,.u]Kſ+}b[|E M>s0+}|5 \߆c='WS񿆴RYڦfliٝj/!C @C$gƟwPooVO[] ?j_ ;ƿߴkz~ؿ l4}Bt^.{{*nE'ϊ~ sYš%߀^:=iZUԼ[J|r,Pi:N 71jo kƏ1?~$GY>?oxޕ?h~fsO~pSBռ[£2'Bgkhj׾m|xo^1͓+Zet)sOFS]XĊ6^45ThWB!wb~Ӿ_ů !6#37x߅|xş<-e>x]?h_?|pγ|y#)~ϟ߈h-5opßxUx{Po?c/fwƿ$/7"_rji16wvvɩ^i3i}ޠ\:qp$V洅]FH_ ګS,_ >3~ϾqH4/߳7o^%̾,i{KBib k$ jD k4⦙ψ) U[> g?ZD獾CG˽+>WO A7Έ|K onujKW/??f?fo ?Oo2˟>*9퓯7~x_aYoj>:|xs?Mj1h􏇶BCUgOtMo/|4Ag-qs^->9[r(lm4 h'ه׾m|xo^1͓+Zet)sOFS]XĊ6^45Thz@s?iO~:^+|/J~.'wN+xo/\u׾$xdZfxgt3K-5_ڊ WB!t"cծluHRӬf4A!Enu^OI-ƭi 쑵̠um{BF諫zumFNy`y4"TemRy03_ʠѶaZ_W"!W6|maWo|Ez!Ѵ7n7\'"Bkk]O\[ڿڏ+( |"Ĺ;1T}{ϱ>U_OZ/L}=?_GՕ_3ƊK,Ľ=A{/S:>M_vfŲ:c 2|hߵ}il!TדԴV'2N*jQ-H't ՠZM7'}{i_ß$>B >|J*ek|&>"~_I~ hTߵ#x#'k0վ9x&//|=g}?['9Ԇra5JNPVWAҖ{ܮcx&Mdi)^gO&YIEQi'"B [D#?f=?gڙyz|+յo᧍W?>A߄0:#f[m3:|vŝV)#oPm?gO"n(C~+kt9c߶OtKZk+53PN3|KxWfs?ß 'Q| 4'_2lx7.O47ĝG~)ac|'i/mZٍX:)VӔjcT:)$Iym;m]얟__ >_?b5lcg3|~? |Uz|HP%x?㿅jW8~__ow_U[w|6Ѽ1i^~οc|9ni7E?HE'# tm(hGMXk>ߵ'hW[EĚ-|?K\hSNZ%5GN糭JqgR* |/Wknu{-mG?N_?~1|SUݢ?k|6Oh_wEо.x#MӴxCt߇ocg>m: [5og¿O'ߊ~g/<-ֿGox߁:ouO"n6k/[+ggxAt>쉮_6j~j_9|+ {/R?Um3Z*mnφXu;h:I0Mh{O??&J*, %&oW@~'?OɼK ta:xĚw쯤>?~»no5|M+gKt3L<>CmŢ~%vhĽ3?jѠF.T`,ZObv`Vtw~ᄒ&Q?3V\cۓOzFOޯD?3[\e_@,ĸǷ'Ux'^ߏOքKۓJ}|һK\ ~OR .oj[+_#)?_\߰~W]ެ։._!>?b4/٫Tς &١~_|YQ@!~ҿY?w41??anںWK7ƟrQnfH\1;Wf_?3 L _<%=?Nߟ8;~ګD=@?Jщssנ?WtmFOӚ}_Q.qvyeF}ko?cg~m|~~$Ӵ, 'O? ^Ec T߉}3ZAM= /5f4=JzW\Kߓ??_?[σ>4;~'>+>|Q=1+~/7&S7KH|imÍo~9hZ^]|,rV#R=zoJQKǕm)5Uj~u)'$[Cʿd_5~40m 얿<}ZMy\ϗ/K|iҥ$ϊ~zTI;Y~ CiGOѵoBX:-`|% >:r>bMaf-"C[Z}aKc|Kjh? ~׿<Vgǟ =xwYDw) 'ie!~O <'߉z5R[<[w>,|@Fwiqxtfҝ(}bSl`)S\gFTR:Ukr]a'w6r[ٸ'-xCxf }~0`s#;['Y:6ÿV=Z]j]׉?p58σ:Wյ:KO>X~:O?loٟVUgZi| Ԯ4-n /&ѵX?$}G_w>*N~ @)@%|)O|ro|'m4߅x{vG*lGe4LA~?>~uR񗈾kz5i')l5o[ e𿊴M.OصՕon]:PFxJqnN:1RT8IASZI?w`~QևV_ /MqƽLJ-m@:C]:DIng/+㕷|/=5!GI\o߳-:x'|8c}fw kD跭xF͈ܟe'˝|KKd\RjI{X% Q匤`If:|՟?'.M߂hY ~%#ℚmƍ;xmEuihŦ WeZKRQx]kzƗ;h_t/V4OZ7|)H7k1OKJ_Xx(?Z_[]f884cY3teO _ٶ4׎iS'mXÖ:=ͭxǚfOYӼWFNRHE8ƓV_ nw-4yKv:[2s?_NK{qOVKÏ>Y[o"K>"^z~Z}O_Z??ڼ겵KEO?cW*WL_Oa׉W?}SHO&^WJ./)YMI3KǺ # Qb+}gšM CHO|E#AgGG?1f_74/_>|/Z(QOگºnUO~Oÿ־ ֿSa_~?? xx|8/c84oS|T/Ԟ(<7<?Ήp_ $>\|O oK> xWOh>1{3j-/๼9x7_MgBhOxVk/h'ΥxRX>VYW?h? Gඟ?ZK_ ._е lk ?U?x ~=Oۍ5~<&._=<1߁z'b?h_[ſvJ?h{[?j ]Jn~OǫSgO~ۍ O oG-㟎OmSO7g~Iz]#ፎ ;|'+BɡCtQEQE|/؇Hύ?}{@Q@Q@Q@Q@~cף? h Cq__+ٵ87n#ld_K#GjExzjЉz{p>.1JщqaOMY[ڿ6* KӁϹ1ޣոzjͿR1/L}=?_hDAO_U_OOhDkΫ+_o@>ϱO-%Ca_??W~˜?Oş~ <qx/]oK{_ڍ1>Ͻ|K f;|#_,% ? ?#‡ PToFu/ϱUY`pù*W sӥ%InQPS+sJIs'/5{_N' z^;,hτ>(mC~ <[5;JG/Śg 6)<3SÑ3ٴ2J/, ! F? x?_ ¡7ăKL]cSm+7+߱}~_3K_ͧ}Yx@׾2<>*xh^iox^uozo}FL<A]Z/QT=b'vசq)O-V|]xyJ?/fsV)թ8ΪqRsxe^O~#uB rPd/fFr{tז u'O B"[x9?kӫ|6G;gB _ ~x: iZ ;8ϽWsU +M_m &z{~}B%oU\?+B%=?A>ՖZ/^אb^~Ah8;~گϯ?o^uYogW w_.~пagɯcw{s?aW-yu]J_4 &١~_|_^_O0؇ j3h G\Tgez߰k>|ioW1fz߰]x /ɞaϵhĹǿ=V}>:~1}+n]Tz$j%=O^"\ߓjK{Z/OҼW>ϽYz{~} *Kg?^}Iw[~^AYz_יf%ӷ5ޫDǿ'?քK{~?^uIw~o_= ,Ľ3?t>iOӟ`}yV^;~?3^}Yw~o_nO׷'z~_ޫD?3[Z/OAןV]ߛ/Ch%f%=?^ߗH2sD[zg'WV]ߛ?4Qf%=ON"^ߩ?_VqnO׷=B%=?^ߗ:?M@ߩ?09b\cۓ?VdןV[j^ןUqxlٟs Id_U qxmf|?}SHO&^WJ>nkԣk &&}4i/úxRu Z/xҴ#\~~}+ϩ+|~mc}?} tB%zzvoodY}=քKZ%ڴ"^}Ϋ+_ooe?՘?j :/,Ŀա}_U^z~ZЉ}>ּfY%#ҭFO:j}*KA֯>M_vc,Ĺǿ=hD=@?J}_Z/O~u>U7,ĿJ$ >f5=?ʼ^Ct~[#s (lA }|9D~C71^ |La_!>?b4/٫Tς &١~_|R״QE|Jf|X#ޯq|1zǃOšծxYY7/GV|]-w>/u=е ]ӮNWyÿi_,O;~4iM4ӳMjkTف-)[gVicA/5;+D?_cisMci]~)ugڼVnDDʲHgç?uEt{A(ɓܿ?!1T? FmNڬWOɇ,?r/Jb:~|8*$?kڄ}?hڴG,?r/UJ:~ן'i [p:~ߵ ~xߞ )}kAuOHv]܏?lژ}?h_Ӈtjk?_R# .G:ć()}DtjwYPWEWOB˲3O'q_E ;yg`??k_d?jrP쿯ec7č(i htk1w[QW O? ]6] 0G81@:~ڟ~?ߣ)s>u ._qIk~#O褥Lt>t?+jW}~JjK v>*hZHƏ_~ּ/Ş|%CZη]H,m*-KM04FG/9|%h-ßg'ۿ4M3dgMǓ7ME ( ( ( ( (>B J׵C$gƟwPooV((((ge_t_/>>xυξ.O__VsZn 6K Rդ_(NJt(N.$(I`~Bç-O^j_t?v_ڧn^c3zm_E CZ~ӃoOڬWOɇ,?r/Jf?kڀ}>?Ճ~vtj!wVNG,?r/JOO ?kڔ}?h?ڼW}6$;.GX]N>/`? g|:~؟H~ߟֺ)}c?Ae}ZxW$GeANK}Htjk1A_R_? .G]jWZp^XZd?jr0쿯eg;č( f>n?^ҧe/LMxtkQ?kGNLn>r?v\iOe?)?>#$kJQ l~CJ~c~Vw?6g\h_E 4|TyOZ6>)|n͏%7?5'Mm;L6nߧQHB|Ʉ~?h_W׵L#!BL.>(/WK7Ɵ}{_!~ҿY?w4hOV xƖ:w>xF¾]O^S-?J> >wk}\Nceǹf NQ#)rQ")I1RnRi$m%psڨΡ[A^_udWwprj~ڗ:+~tkqؠ?&μ?a7^A\zC8E~|MN=_.O#fT;[kpSg_(>1kƿ/jڟ Wz޻}KK7W?cӬ/|"J+,E %(g(OFi= R)z4 +aEPEPEPEPEPEPEPEPEPEPEP_AiZB J״QEQEQEQEQE|D~Z??>/`g Hv|)oV[|4ѼKk~-'bu/4f~4ֶ^lѣ[V0j׭V\ѧ:jIS')!6mݷd[>:V=?b_PE8@wdצ{?[y1p?3)&fT:[kpSgǂn(|KCg_w{'׼/xh&$>xn>|We |5A·VRo#}\5-GKBJNzSQFZŸTd[;Y)5%xt_z (QEQEQEQEQEQEW_O0؇ j3B|Ʉ~?h_W״QE|Jf|X#ޮK 9?am޺WK7Ɵp?SIu?dVoX麦ت]Lu[GumoW\ѼG{iw {V֝k$6,)c?f\s 챘V윝x7h6dod& &xk_jkS}`ogj?#5p3W|P3MS(oOP{y}>zc?kB%> ~'1'i=>(Oف&G|-?>>}Kz%};cW}? ?d|xʏXOD|9g?2s\q]Vv_ӕw}?b|*_~ɸ:SWƝc!wc-O>l`?[J_k 7yK7y߻<574a*ఘХBj?cR,DҵhoMoxy֩e3_Klc\~+T e,<8[h_Uis6_#lc^,T@-~z}?`P\oKCE|m?f_-5(ofW_c@5Cs%?̏诃*sl|]O?f/Kןg@Cq U|Ni#+rOُPm?J>.gNk@ C&KÚ=g4WKcn/<:~)~ݍfgoKq zR_w4{ϵϯ7?>/=~ͺ}C?*l$׼1e>A?_Am1x_lxj)t:& }^jvVwkiWvWWq$vP<\$s[C#4NF(JN.׳!Ū+]cgcþĿ D/xÞ&`I.|Qux>~,|+x|iŞ;~g|G[k7|Ci"Tѵ.N4kVQ@k-ִxgĚNwZghZYè֋\iivcq=M,t-k(ִ gzNZgZֵͨ3[^ڎ}q\;Q@Q@!~?F|ig{k !3O;(7+_^EPEPEPEP_H}*ٷazBȟ(}?&_Ox_oTWg~ k x4s"{ms *jQަo/g}f[,uƟ󒓌qmB+r8r~Q~F8z5zCqןQ/OA!oҮG٬j2?uLXbO<ݼ#}>z~gִ"^~~(8٤8/?Qcqg=) ͖SGq};\? !{|qOb^>iN}9{j|Sd$e;_5f?c_ٓ>L_Zc.+byYkCQ??ϳ<o;sPʿ~_7OwTW1xg돉?]gs ~(1;ҿ6S~a?eـqmOk)36+ ֢Q;*~R\T&6}.Bp9e(ݯ}_f~Q_H~m]W<~ѿte?,<8[?M4~_ C~^[ML?ed}m_5%VK~̏+UnOكW Re_M)VJK=?{Q_/ہ~_&J~3mczg_@:jKjq{Iu_ /ۙ~̟\sm|_R'qkzsG>~+Me:o@L?n3~]qk7-M|4{ϵ|K|^= Y >''H#y#S.;Peѵo]\=Kr>ڬe%fOR+/'GCf5]}{_!>?b4/٫TςQ@!~ҿY?w4ɰ/7;WWJf|X#ޮS 5&r_]w0?&/ɞ$'>>UI?ʫDǷ'VKg?_ue:[x%~hD=SUhIՕ_*7L01xgØm+귗^:e'#m>KQ⷏t4^ikZ2sքKÏ>b9 t$*%85q~qkTީR7gAGMBHQwo[Oe]x߲wZǾ/߁u_ [HO[9-4_|uG e:Qaϟ2Ij퇨?U>Cg=¿Cc+P^oE|%?ٗ_?f8Эx|97?noG}O_^Ue8~V%]χ>o;?  ߅ٶ/+u>j9eG jO&4OZ -1 oMx[iß|3o'#xU>*N}o|-e w3?]þg?xO|f/ |g 7,ZiG,쭾%xm>;FcSug8j (9J7 txjܣN.SӚ|֜Rۢ}z{k^mkM|y @^u*Dۗa5]n|f>M#?uCQ|l>|H}|s[|)m$>(k?~pwwx~WO|E׼3k3ƉMj_3 _k/[;}:T?nAoa9x];B"Ÿ k:?oi xŶ^>GeYh]> ޛxž'; ?zŬ xoW>x@״YKOxJoόfR-?lGѴ>4I|W]z. Qīsbe'ȓrwӔڊES#!s/[]՟:ßI:;#GZ~^3Ҿ=W^p|QMC߆<\H׭iOMjZT7> xB=?^-Qx^N{o x_<+S4MGP|/7fuy; څiƥK߁ϷhD=A{2SMTsWN1Vi$ޛ]Z+w+K_K;tj2@Qϯ?Vc\__Qf%=?A?ִ"\ߓ_j?+B%U՗[~^Oc> '?_q5ɗҫ)y#8?6W,zOA*U.š^w6tWG=ֺN6쵫O'Ku1nS}`9AYz~vG'uG?.9yJ?4th_ FY?ajVG[ÿ~ ikYMU [7េo~BA,l?%G[? 6~>]/7\u~'<}5߶>>{o< |['KCkW~&{/3sN׌t?|?xo?_SGHxfdO x[ZG\?oUxC[|{~ξ#q ̲|k?'ÝgC/wTеZ\<53StM+U|Y;u? _j6]k>W+oBM/U)'fM_4Ƶnvy?>'+xgvu/_?% =Ξ5؋ /uK<_Ʒ쭾5KAxÞ'b|Z׿h?Z? I6Z/;KDxW(|c |=⎩kҵ2[]#sx_v}S|WZx__5MGӴG~)i_[As-~^ ^[F xS~t Kߠ(b#>4{ҵ|</mS?|A D>!|LWU࿃_>?τ>x[)|@>F|Q𽶱o_ÚװW\Y~;oO[eIk'߳G?kiom_J#ֿ%^?O|R'j^,uE#şig~2)KB.闲/73>CW5O5\i[~ŋoXZkajZ1_ 4R?Ӽ3ww.U!x;~ IGu-g#$j7"x֯??oe >BngR_B4OqQo ?J+?Y⛝{/&k|Vf?g~~ >~-iߴ/xǒ]/4|(?>}om߳) >Mί䟴yTx/_gB߯Ih__ڿG/L-Ě ;z7O$exTݬuq6FW; ~7<*Cx7~)k3 ~w[ -|KYmw=NﭭaE~۟/yn׿d|oi:n1[_o<--o KF? >x2e k|@8|_&Y _kٿ?⏄_4NUm?gµ={Q(>xO?t<"~WO??fٚ=#O~ xSKmko6 ;ٛ˺;kk\fᨵoaskuMN]z?o[][J5NⷋgzQ$z>ssנ?8וU*AƝWFM栧Zm;I]^֎2OzM'vm|_q>x i{ON@ռOOO>( ,J›/ ?l|gs#) 7Ɵ~>xZ0o_?D?tw߳ǃ__<?>*h:o OrO---紼Ke')@ ѻG,R+$쎥Izo#ӿd٦O|Ce|1|UwM<-aa%iZ47(Д{X8hr;J7JJqwPu9W$)Rt4m޿~TJ~4_A>=|>,jo4~ dgO|Xy)t_-5-2X69l/u^; $h#+~Ο}5߅ŏږ W>+"=V7h+xߵ~~;x/i?m?f|^r՟l5ԯ~;t{g gzoqo>'k?xoƏ_~q/53_ x lj3<|TώyD4|Mx=#&7OMM4كlӭukV koe+P/ A+oڝyO[Aw6"Uims4ZPUqƛ2SSZesGz~ӧwݳR۫/a^4?Ah?g x]_b؃ƶ^ >?'~ڟ~g']xzÏxC ֵ&e/~˟?kAh'O֞>M}/Z%ŏV:,{ |DVIxD.ͥX<áSдMWmFj0=<9Ox k־ %o]c¾c]+D--y'K'5(rRn|Iڿ2jRW޶릶lg~Y| UG'| o[g.> x7:~Ҿ$췮SxUB| 㻟;_ 6ʐxX:z{~~Z%=?^ߗOJ\曞I4շ{R+%11UiN}8SZF֭ĿOJ/W}O_Z/On>Z%VK{qOWVVͿ0?1kQ ?\߰~p1{<K/ ~W4&&VI|пfS> k/'GCf5]b3(>B>|ioW+bO9?k[vG\_M?9Og[v̻F ?_zg'VKۓ=?/U^քK?SO_ԕe_CÊb\cۓOz;Aa'ٿkID'~(OGi>_᮷ୄ.FlĽ3?k\Ϳ߀%}Cou<><%agDzk-񯏴-+U-xCw+T w:+l2FR^uNtTW:uxQԥ]06Ϫ[]7W}|5//?/o 4/@|YO('ÕWqk/ xVc7)x×F%UO4(Vsy~3Dh_ _ͯ|rAENw<_W᷌u@^ A+[4;J׮VlrjW:x5?*>|^'~5Ѿ9wlOj_<GxOօ#TF:H}K }>Q-gOZx¬cZ&oVNJk+g Ui?-h_O\{/~Ư~x7I?4^ KDsm'/k߂ߴĿ ~?g<1'Ƨ5[Q|iS@Ohr+ǿ ~!5+Y5}S w,OhQ?־_ŝ3O_ 5gˆ>1xrxWC5kzt NIskY_ M =F|\n|E^xN ~+_çu?֤ψҼGiUpmBQ919TmFX\)N4$)i:VmjEnvZ\?ߴ_ſSؓ7ۃ'+OIjg4o ~ş/>~xn}ZxwOv4.Z߈ ?o1mxxwRD i?/z!n־9|JG:O+?~im;m_~|I㟅?<5 ~$ o'YaH-⏆_/SƚcIt[yd N*Oï|r7y/xgRxw<~|J/+c%?>=D vx F8zx•IC׫UuW%>zџ49BҴky6b֜-{%ng +~j1]~ҺW|Eῄτ4q]UM?P_kq4[ o x;//ϑWoKZj_ k_(> ~څg?hG'|BW ڟbc]>/+B|_o0>VO hjz?ŏ_NkWR|?r(7KP?¿5'ׂ66~> ||v-|tߴ߉tgUx;Iվw>Uy::fg&jB*nI)Η;ꩻVvӌT`3zΚ]ZJ׵z? uƱ9XOQzÝ3 Ҿ7+<|/A.Xk.O?4>xsA5x/~)+ UO|T[K b?J|91~ ~|c~['V__⿇cG_CQ/w'|]麊ZXO'-/Mc"oA_=P7x_Uğ 7og3k>1eGuZ_ R2|iI^ӌizUI]|ҕ7|mZ䝵V?*_#k_>]S|uS')>OG7>2ɤ< , Cj^i>!Gum]𯇵}KKHӼV6+9ٴmJҘݓi6fXJo(mǔfmI>&CI'.CA9_ cDGftA<e=m{5/wUɾ'xJ7<| 4Cn]֑y?.ѢX|}T󪭵 43Ms9{ͫߢOT$q/O/Љz{>*/PK?9= GдrKP4銏xvRYZkrHV7n&$e9 ^~/? __x;|2L;>:.யC⭯v/߀[F%/4Oh#ϊ|=|^ />ESOş>? fG? >j~ WïxO]?m Rx:'Y@WoCOIǏ4O׋u+?>~|2<#$~_h|e /~_ծxiĶ~1_=#߃>JO. ?cK|1:OOُN~?|4h? |9sjm^NEc5.~x{7rQ8>BjI?k:k->Q-,5mE4 Nh!Holt.;nSMt='Go(߈~#xIJ?h/\v࿇>u? A52ͨEuofy~IvktQccedm uPo<[J|joYmiRпgM#OЦs+:T|&d-cOX@k{.3p}<_~%ֳh1'SSkk/xB7%Pѭ|{_:{Uh4{ҵs/?kYдoٵejI?k(m5cgsjv|Z4R76:f7)~gcin#~#|ˮgirY['oU56ſٛ◌M%OɬEe5[ҟ9[pZi?/H)'?_I.c?f5KX G~Z΅xğMNͭi _ó3 CF|oUT9{F@!5|f~׿E5m5cgsjv|Z4R76:f7)~gcin#+࿉<Ӓ=3úWO',mRe7y+k>'#|)p߁cYxZ^Pέmx?|"74>k^ɨY+eaVh} Id??S?*Dա}_U_O>ֿ*3Ȃo 1/O~u>qFH1~f5qw?ҼmwKԳ~[5ߎ6? 6>9&@v|VzYq}7C2{zo= 㖣Ʈa_??mg/Au coG| <:'ۿt\Rq~!=_>~_N7<o^K|LG/x/ݿí:-?·>.|Ux3Y-gT*?)m/5_~?cCZ¿#'}'O׾+'BOj~->Ctc?|5 t2+%Wȝ':SsZ28)[',L$rzr{:^^~t~>ߞ?Of`~(X mKDԼ-ચO}wτ4xC? |H4~k I㟃,t?G> >l(sW~e~3 /? 4Tмk7ÿ w񵟋_v|*?ſ Zo| 3Wto~4:i7Y$t_Ok?C?xcZ'e|Zo 1|[|ï |G/}^giV>= ^>~mWGҼ}1[)BhԎ&.U&)V:Q,CJj(ui%mu7n[RiO?Ɩh[M7Y/†C?i 5?\h,ЧMG#X;Wм^:){x ^$KSPG.[i4ȦFL'Am_GX/-?dOfoƝ,5~v#qk>|W`ߴ.dm0467%lCmAOV5iNjauNMƔq|lܮiǢpKKM?G\Q?i~іzٌx' >&𗅾y~Ƌӯ+7ož,!RWM7XqQ/L{j> rue9FrMIҥK[Wr|TQI$[wovRT9}K{r~/U^~sԗwEEOzVK{ OVzg'VK{r~/^uIw{~~w}]~NR &?k?'/ojYȭI|$lL#!BL.?agu^E+% g{O`z($_r]G+% g{O`z(?,#?gZ/ L _<%=?Nߟ'Z%?+F%3ߟ겵ECǂ2t^¿hcwįۻ|7\C6L~1]7Pώ|)mmwF_x:w{HQ*K{~?^.? KBՍHrK(N-ٵT7Y8M..2qwVi59^"xH?j_|T?|_ dC_!||1~~5ڥo%t^j=Og_!t~>w##.Bn~ |)/\F?<#/Z5=&6:4kK-4kYmk{XmN+_QQZoJaF)RJ5,JUTokYNW'{m]]Ώk zNz|߲Wï|=7o:  nտ~x_Zk7%Z燵4~ìhռGKqkWXVA )?=~?G?%b?h?f`'}f#߹/OnO^'bpZ͹MjKEE(J1ISbbIڢw)h%~>q_w?<-J־|nO[]7?Q/ۃ&|o/~iK{IƁx+MŔ$^2/i':wu8`ڳ ki~?[j0ƞ_ċ2W6fgckˈo,?{~_ޭ ϧT`ڤwU_k9*4b-s˿~Gm2ž>|WI45uWkW߳|?<{5Oٞ=w➱kg@|,ԼRxK f@ x~ ~5||m_ᮥx8B?o4o a]w66vz?#VKx<"RQe4=MS-i򦢒4ꭢ^Zw?? ^kG/:G xJ|?D>xN⟎-kZ)w/>9ou/ i'b[xF_ўQFSgе??+e>7o~/ohc\摫5VΏcnjk?oϥYANSqO N.4';F=bg[? < ?yI׏lOo~+u/Z| Oyo>mcK BCs'>-^mWW%Vi ~$~o^^|UmF?Z6xc|^/xI\ⷌ|xĿO x)4?[b\ccSu}O_\S}I+%fm%$KY}>ִ#\ U^}OƸ?AY_~xp Id_Uql&g?kF2Uy]/,(((((/؇Hύ?}{_*~ƺ_u%d~v!Ҭ-WMx巶TMKP/To;{ɝ((((2kVjq _[؟OG־?.?Dq z{p>}^DǷ=hƸzz+EϷ<ȫ$RP:/L}=?_WdSZ%gm#Mv(#wSgD-K{B6Hmui?~)w|C:Գ_k`k}G%GںʣwaIҋ4*/)ƚ/?/<;[-k>.Og_g>.Uo+oWcf?o мSwGxr<XpqJ?h{TfjJg+ڔ2׶Rmw` ~NڗOׁ|U1Uvo S{w_u|2~xK=Wn#j6Kܽχد?hRKl +4#.O൏|ix τ|Oiymټf階+?=OL/è< iΓ GG|kkKRwO?/|ÖZ&f=1| J)j Z{:-Dԡ-]zB.xݭӺZߥO ><[P]7P>1+3|Um|y|8|C Wag_wton5ytk&F%iهGOuWv|~?O|?0_Wo{jom>)WΣoOl5]F=#(7_I(dρ/?g~&|u(|W>qĭ3BӵI:L[[[YO*أ3G㾃VRc??m_|We@ 7Z⟄>!]x/tN|6o 5*XlE9Ҫ)T\`AN>oSJ4i>efk[ZK[-t?ga#_W\_5< C~uſ ~Q]/kּIGTּ?&X-sE/?ރ{3~%xz=Zv}2ºG> _xw,4o %K(SO/GE#k~|"j7r|'[ ~)kg$ {Z/0𮋦`t%RXYQNJJpg+MR)*5 WnK,+Z?mockgO޿)'#6>|K|U#oHωg9㮻m5ލ|r/J%3ߟ?nWtv:|M%M<%?o_;#j|C9k/?S/ʗv xcKGX?O? >"xvď'v>*||ÿS[}mA?࠿'n>%<w ~ȾgOGߎN<.t}g}4'eiwZ苨@XsKVtX\wWy47?X'2?g_~ß?>+|'~![O|AgiGAkzݎ'#Oh5}־ ~.? x}kO +|Nd~6/ź:=bڮE'\j~ޟsOm>(h/ixH<[ioedv|i6FmkQů߅~Ľ?3[W`{O<栉s~Oӷ}ڌ.U?oo'w 3~5_߶>foc^tE|пfS> k/'GCf5]@^E+% g{O`z(aq:MJf|X#ޯ{?<' !< h0Y:g|Cevi,&cg}oZ*m+ЯkS~^og8ϗ׵gka5tt| ~}}z}UkG_#Zu}gtmNS>ѴϷuX±Zek O6O>cs?_Ggm~/Km4<?cc\?]+B%?+ʿ_Ggm/#?3ζ-׿}S.q8;~ڼ'!b?s?k\xWo?I@~v%o_8Ø`$>:?_Ggmsˍ[n-aK?S?ִ"^?_Ggm/#?3ζŮ_m,گoZK^V^ܞ{~=?#_Ø`$>:?_Ggmsˉ\ YAk=_b%{~_޾1~H|9uØ`$>:|0ۿFW ~'V\c=OJ1~H|9uØ`$>:lZž)y?1/xҴ"^ߩ?_~oØ`$>:?_GgmsZ{?? cVdӁ?OsDß@|[G9C > i9ۧڭ6= K66g j#L_G_kDԴgFciƑ}i:࿄X\Gwc֟ b+Kib9$EaYxK>-t-#^R^eimrA ]IophpnnE(((( !3O;(7+_^ʟg/GiUځҥ'տnUMm$%KCis{k7W@Q@Q@Q@Q@~gփ?74G2'_?fOzg j;> Z<}]oSneϻѴ϶X 1}Vk-$y(Vmo|?uF_Ggm/#?3ζA.Wk][F7OyǤĸǷO}+~~~xWx>k7_k'u, P?YxM{~4\ks:,/#?3ζsDß@|[\8e%69~%}{s_6Ø`$>:?_Ggmc>3&[^k%폨_OZ/L}=?_|b?s?h1~H|9u>,rQW {Uؗ}|gb?s?h1~H|9u>$QKr%޴"^}?sDß@|[G9C > yeF}HUؗ8簯ϯsDß@|[G9C > yga'_5?-'ZϊcǾ|o㿇w#6~OoxGh *l]3žռ%k]W泤_z< /~oxM>WWh>ˤxg, JhZuEkm y;s_Ø`$>:?_GgmsOI7J0o' *_z1~H|9uØ`$>:uܯ4!F?oo'a|9D71]K/4O <F|5难GjW^7:=Σ^X+3Z"sV ?agu|пfS> ״QE|Jf|X#ޯk/WK7Ɵ}{@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@!~?F|ig{k !3O;(7+_^EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_!>?b4/٫Tς &١~_|@^E+% g{O`zoڟ~;վ(x{o~|=g^co|.u_GǗ6^ rk?kWlvb[.'G\s{9;N+0j_&)TJϣv}5M<lWn-R'Uy+X 1?Ws#{Rie=znQ5C52߶#cϯGN^}B%޹'F[?zۣ.i^>~Bu?lv?eٟ_K@J?j$,̼*H?}⌐?+| /ojM[~'_~*P/|=.Y迶oějE_E9jw:lij6<)q{k xtM>ܟpQU9TufV[uP*MQEFEPEPEPEPEPEPEPEPEPEPEPʟ/Ii4__ݦt }:E?h};m#R#Kf5{Ngo/؇Hύ?}{@Q@Q@Q@Q@|OQ~_ |GCP(x'.+UT$'7%^TgZN(u}G/_7OWnm4a Vx+XJ0jKۓ=?/_ÞԱ\nnQ?eoٯ_G(jelV+?_K{r~/Z.1?mmK|TZgTqu*߶Ct?fM/5'5OF2iS\c=OJ'Oڞ+}?e=RW~oٺG> x? < ~ z> x_Wn<+i?)|[u/^C\iz 4fR4٠e?u.N3?C*̸.yE5F4h*s4ܟ3Iٵ{[C8sJ׻Z+m`+͂((((((B|Ʉ~?h_W׵L#!BL.>(/WK7Ɵp?SHdV_i^7ئ-St{Gӵ?mo4WC|9{鶗gQ[;}gKd>|ioW)`_9/o[v̽_N0F fkt& &~~ ~mSѕV>:~~?<~ك?Z.1}OjKہ=ϿQWkawoox|ϴ'qOfgO~<~'W>;H;W/OhľA?޸*a(;Ws;mv!|ܞ3?W3&"G/(2 Po\_Jh 9RO>={Y.4Icɫx~?k? ") ;Xww<]^?d߉V՞?n7RTԧk;P!I m <ޑKw:rRjzpnf4WuO>U;`H kxz 5_ w?8~>k/ ?Y-:߃/ xKUg?]KĚsCw? \j |g>+O6|A<|mR!  ?[LJ?j~7|xfAS?f/f覃K_ᮟey%JLv^QQJU18kihhrCRltvmY'ov_g1wRoο7} ?jhY3a.^3co.VǾeׅupY# wž)>89G^~@+o߷7(">5M᷁ /?xAc|ceh>|~| N⇎f/ qdTG/*"Rgm.ܝ욓;;'%Ju_[_d>]}^k|ևpܶNJ8IŤ9&ֵ]vMg=ܓ^I}-EnBgL9[C7G} oU#5S.]+?_yb_|?~C¯ßX]+oq2ߗW/LO?ZЉz~gָ`{os˿O?x~)|D-x/>:='a^/j>#|s}1/ẽҠJ.'Ή"Ѵh?l52YҮզv^(.g_&gvɢEdSu}#U9'' Id_UbaTʝ޷jmiڻzm%(~ ?fWҼ?zgi]G⎏-&oemrm>8EۻC6~O_ ?hf2?>?ࣿ | xQǿVuGĺV%>5 w|8м}Iᯂwབྷ?g{V?J+|G+IwO/TV@Ѧhuk|"u-.֫xEu]?xs_h/iO_>ow~|R>~Ͼ&T]xN^Ooิ|Aiٯ^2)Xx ״|n?z?To_<%a:džg}gſ[If^9kK~x}>: 8W>j~⟈>N~o]? xOLje oïC~?oVOt5񟊼7OOw/ #Ft_ۏouwΈدF/w.$y| ,q\/AqgĻ/~BCgk}sƈ(S5LEh-7UY?jۻP7ZTZCi͵RѤ}#Yb{x5{m.omc~/ mx ׁ|M_xzΣxO^ c&ψt{=_A6Z:^km}csJC$gƟwPooV >~/ K9}G/> GwU W_Bs:5ϷfkweP ŏ(O} BE~|[ƺĿ ~WO-h߇<]wSş k78<3O/|3oh*o9gǟZ5^ҼAE= j/Wŏ!g?Mk`5_ 5SB⏈nV5k ⿏)}τWßKxN(?`Os{o^5nj_>kxIҾ,x/K74o |ug/GhN>o"jOk#CiO 7vĚ #Uɭi:,9B>@gϿ~|.b{-gHO7_!U-jSx[?f_n5|WGx4 4oٻWOǿK}u  B6zO5_[I%A_Wپ#x+xUOς~˿?>7FFY|o?񟂤߉<Cz^o|FrCi?lx~[CUx[oڷwO/#Lu+m3]O[|. Þƹ|7''</W3.)njxC#u_ٛ^,Z}+/:/|#ox_ ?h _z?|.oh_|'{V~~~(M0|U?nkO/~> _ÏZSɐ|/_<*kOǿ߉<:ECυWiêX-m-F;ڿ0lO;Wͷ՟ p'rJX9Ӛ'Μ88?3CR}WG['K<]\ŸO٬w9~(?q.qvhD=?ASG[T_y];}ϴ'QSfߟ?Ƿ9O/;&|SS㼮Zm_1C2%|Sի1Kd'a?|=Ut+>~:~T⾰6vMÞ Ej\ *thS6i4i1]QRMsc)6ݽ~Zߊ߳WK$_V;(|D^?7{][C,hCU7ﱗn|f<~)~ښOOpρ|7?|;!yE2{ߍ/xߊ/MkuGax>%W񵆟_>3&Ool-3 <#N:yT|aדg7O h~??kYG7 >i?h t>o?xAoß<1k{Y !bhuo0; ஖Z*Gk,j7^7s_,<BkĘUcl?n<_}clbNjӼ%oSᵕ&]S)ZFmU;֎qQsRmo>)tMw˾o6y񞂬.{׏t4yy[_"'oo?޻>?RBC2IjSe߆V*_| ~3|Ũ۟OxJO?;c_'>"?oq;YAwGïxo1#Z: 3H5~ܚ_~>3wo|?Y:0w e㧋m>xc=={xto}|+iMbnwɥ~L m]Β_YrrR騶yj>g5Ba'XWnBL7G|G7C>OKOþ3ޟ߃ _k42_'hu+K#h)𯈾/jN^}^Y˛ (sTMiߪ.I5b9Y^KG,C'0?GOL-orq !sVWqq>f1}?:_[PޟΣ \/>uKݿd!2O ~ȯdє|f }>ϽhD1~<&mJMs˿Cg53O|-{W >|OU|@)WZNiɮ$JWZ E~H~֣77~+18Z*Wn׊{kML#!BL.?aguϯh G\kManںWK7ƟrQ6$?gj˿a/3%~LIϥhDǷ=Vq?+F%+?#J.嘗{Zϋ~ Ǐ? > CR1 |6ޡ[ KU-_F.˽/F5+m& /ҵ+ȭH*%W{^9|5O~S|~~'Z#[ <'=Kƞ9o x:o5w2_U7:&x\-j:j-F\-qrQi''fOU:aCB~M9|ׯ>;u3Z[/xLZC~˩x C=H}%}{s_ީ K& >}~wwK,[~'H헥x__3ٿO%Ꮛ" u!|aRŶ>3Ҽ%uh:&Oy^q+)PT]h`eTRJJThnQ)KGI]ZkV{Z;ukvմzR1/SםNNO?^վ*_to|BqnFmkI{WZ5힯=Y<]v=ŬJ_Q~6|Q[(cSxgwOd?syt||M<[1'_5=AҾZb ň~9k_h?D/]CSυZ~:xV?5o~0Ҵ264:kEhStVܴ8I9^-8&RNrFV? I[uOI_xFSƺį:N|_yck~}v? t-cI־$JMNj>(;i׿lo/]𷅭> xω|os8Qm cћP:֓.ovM~ǟ-Mc'\>7xkG*'>(W/7_fj?5z߃ xsuAC%Of_٧k O~sV߇~|-'Wt|m{|eT״/7M=7~e k1UՔ\ ;Qx֔馜u~u;9B1V{om¿S&hE>8_5/h>ѴMkU4 xoCկ|/+= ï_x\,5_"^})|;+x^|G~8~ҺW?i7l$CkW Oÿ x~ ռQ뿆f#|'mKޭMNj|im+Ŀ~ޟ lEo:5o:Դj~<<)zw|;iʩ('Vr`MrKTdxg\|;|c?U??~(i;?޽czMw >|XvzN }vN9&|&~8|w?(G|cO_ >|@j?^5:kO ҟᯍ'YhbK/z4VZg'xş?4 rO1nՒ%&dcF)T:ir_+et_Ɨ썡{ocōSLju߆c_<|E~ 3"}/}Vխ4 6=B𖅨^)(cUO_s@?ᣏw4o2(w'„OͿGKXzʢq\ЂZݧxow]4lk$UmiO G?tE h¯XhW%La$?%FFs~OTs  |BOڗ^_ÿ##⍖^j~+?iWڃ/_Dke~x[³ؿ.l5x6JVxWzm,>7Ӯ49O|(mG~ݟO+'F~6;~6> [-J6wtosb]/(+$56]KK}:^ 1%[[%Ӣ\Nvv4[k[x-:3Om[}>MGQ5}BHm!KxVծouMNZQԯ. \q,FR,,7?h-3T~֟W l >3x#^:7zwK뛙Xs _ {Pu/ k{b)~ҟ_->`#ZOϊt ;ftK=?P2ہWL񿆵 7^$6|]İB_F񧀵 x÷BtB$1?kX7+_X0}lv6j:Ci [ڶs{jwj:yw^^{d.Q_>FR,,7?h-3T~֟W l >3x#^:7zwK뛙Xl|-_چxK]իkpK9iJ~6|TW!6Y8Q\+xZτYizXxC>.}X!wG/xZ熼cȡue!xcEo~ ^oZ?b#>4{ҵnW)u>60|})SŗZt|TS{P5kڽ}vY=gQӣJnR+9o?b~AxNݯ5N]3^|3uZu/ h#G>xƻwm<-oL+S5CcNx~#c<;m]C7cL_`@Idw?Th9OQ%׵\|ߌHui =[È||fӬa46%muCW$mZT8ծuJP+Ooٛ/x5I,^iU|І!x7<wAG~dG??_'R|L{Qj?n>,ß)f-E{oz^oh[Zε//{/Úo)xCG"~|K?f]V[x29w.O {Pu/ k{b)~ҟ_->`#ZOϊt ;ftK=?P2ہWL񿆵 7^$6|]İB_F񧀵 x÷BtB$?~ cZ3㿇yVkշ,60_Vߋ|9T^$ž;~k׼W}^φ~#&5'?K?~ ^oZNLtgOQu _PHR'յkSSxVu+˽Br73K$IeTߍKYìE j>6t6.xI5o  גEu7R[i~>>GgCÿxfUcU41 wc,yesS/b.$VƾҾ8=Nxs¿E4|6|ȿC&nE{ |=ZƟ~KOh?b+U>~/j=ֵ?^.Ɵy_^xBO<쵛5sş3t??:x xk@u]g]E.-_VԮ4 kˈmE@axK~?'ou/T𗀭MZsSĖ>0F> K^ئ^[iv q}q|o|k.cs>4ľ'&SVhZUީkީwk:Nksj7Vvv\Mm^|_k4 ߵ}[M:8gվ,es3~7M;OWAoCoWlQx[JZ *Y[6:pjm$[{_ѦN3Z_+_nm8'U'xm-- TifyY"bFYde4Fw`"h%ƕ֏ᏊxGhkdKʑi^oT-A_|+x1[񧇼>)w W~~_ ?h~:_Pl#!ٷ^5C|_>M^b|;>g]j^NxVmƞE]J.1qri{0Rj2t^ʚI^I->oY^_O/Wx?9E~j_xWw>1r@u>񶣯^Tγ=V񎥭]RֵKN{%=?^ߗ@-~q)G>|=C co><3WßX]L?h: 9Khᑴ \^h>>}ZX,[jOL?%?booqj@X ?DV0oL 29`\z.tVOVN>6'߅.7C_33~+zō6knuO~* |Եio~,xDV/5.P{I%+V]Z+i?MjOF4 Cyak?/e]Xc㍿.1%n?x_>m?DBZ~n/I_+~Zu]&"⼗5?b4/٫Tς >(/WK7ƟrQ68?anںWK7ƟrQo6Hanֻ2l~aV3%~LX'ܟo{h~ִ"^Iw~o_= w_Գܟ__7o/Sqٿ K x^1u{xOo8Xx'NtxR6X//m(?NN7|U|Ib`|==K'~#<DxV|>ǁ#B|_dzjOOfk{-*_=TӎSSx~TRf^OkK[^忽䷵ ix?F~:|+skQ4r|'a~-ω/KxoZu||BӼ1⿄~x?i!M.Aῇ_ >*j)m7aoc6^ >xWZ_84o69{/,ߪGz?w*O5D~R̺~|Xyώ:ďs~4мKE|^gY^?4C {ٳ:wk7T&|Y}Sms&׋|'vK~&~(P ^ĶcJJ^ɬ;n5g5:p*#){yԂN=\Gtou{ue׺~Vo|cOSK|!^=~S ߇?Qm?|W{Tƞ#&}/?w'~ɟuxooB"־z[3o^b>(njt+k-5xoQ3i3AmKm~`xi! 9M+U >:'oX(c±K]´NۏG?SaB bܥ%ov2QZQR'T^?7Ǚ6|6玾^C~|-=/m?nu'?jxs~szŖ wkx{j儖ǿ?? qux"l5wFm㯍_k/O7xk@ּ quFey?߯ _<?jxC_cM^„o?~2q&T'^AwZ})R ?7%Uf~P)'~ya%y]؏zM4}ޝu>6w~˟go _sIi|:USaL$Ə(/:ov t_x{Z~wm{-u7еk8e/ua/|='mWzw|a~CumV>x:Wƿg#/׵o|4Q_S^J1:s+xoLu-|E'֥>/^?i|_V tkOh~6uNM+žM/Um?:?ho-3]-so|N5-;>MoP< u|OI߂[CӴ!m)B JT(sY9b%(M;\b"z[墾w}E/_[O#⟄~Ѵ xMk~gѧ<)_G.kcSOO_!|Sծx߇^cx/ڋWS>cT!ZCIh_+~%_F|I 둮ج0}5[(ne_)5׃~0ht=7> j_m$[–mI|Y1ՕiMQg"7;|~UHxWg_ ioCb_1YxG":7گ÷~е^]gU&.鼓5r{%nU7ĥ(nr⛿w=a'>( -PS/1=?h!_MQ~H~!>%.'0l/kz>?dۛ{_ tx+_)=G?l>xƟ|G?*E-ii c2~|4|cƿڗᧇ~#к]q}n> КO^4Ӵ XUWo-C̿|[iVz=Ƒ[O /='UwEtzޣ P.m퉷#Vh_~|phA{ .ldg٘~ۿ|um׊> WOWT-?C<\/!4?Oz?~|k{Ui>5<jWmѢvx^}g6ͅji<> fmoOR?k/oo| .jiϊώ~ j ZOj_~GxR[|S jۏ?I#_0xHB  />OS[bPЭojEPEP_+GV)_] jh•<'.?LqľA~jЉz{>/L}=?_hDA?޿j/Tȱ>_Zi"=j%|St sž*b-? {vĉ6F@>C !wZ >2R_[L/Ѯ~YX^ޅ4Ή~>k֫uaew7~1>(Ԯ-g&suJpW8I%-MUJ|]WTfʯn}m爿?7"᧋~>| c^.K,sC%k_ǚ~? / x~~)s?)Co>c0qcO>6in5|y'^ "|Jw]ǿm<^tσ6~&<{y+Ŀgƾ+߅|e-OVt > x'OeόCmo |k⯏4mO>6_/|M]>ROZj7?I6'w=~Ⱦcßo쩯|K֟ u/ ?`iM񟅼GöM:BܩaPSQUфVRSfGZ}jҾuGy+ 8~?|>{~tڧH~ῇ?| p˿>3ÿ>, ߉?~+x7<*MC>(>Rp񷌼I>(x{i5yo|6:/[-=>sV>T$ׅ,G\Jn$ݹϤ7My9xڷ{mo>?o_ʟeǟ?k~xK~[~xWþ3>"*|ZQh_&BœMW᷶e~07ᯉ DxL4~ؿ?fω_> ƭ3/7xN/xoNj7_}~"S^闾  xƿ-n?ƟߌRoK{΃ßeּ_ ): põ "|2wogPd.)?4OؓLօ?ǯo~)_X|+څ[Fy<x4x5ŻO?tR+o|8iSƛ&׼tE-/?|?؟ʨK˚pIp\+J7MEUrR-Fеo;ﶖ> _oY+|f4?ƯxKx ~¿W/Z_435Oj!h 7<O6vqftxڿę=~ȓhZ΍ K ~(U񮑫?<-GE潦{ºv[g'og/~-߂-~vMs>;/~=xᯍ"ZR;&Q|6Mj=4{iu)K ~`9?xĞ3hz7Yj_^)!|l'AMxxs炢,-k>c1K??-|us6-M|l|:>ٝjφ(-5˧NU0Sⷈ|+h?lgo7rKouOK[i4tqq5z6[*A[RM7e^M|cm' g^_G^Ѡdž%|2:Ih_g>?>k[|Fw_ |Cx5}> +~ǿA(~^\{/P:5_|[jQuKKV^Y`.'->~ǿ/çnᏃ_+H%]m­W]k=G<{ĉ 5o \zO?ˣ߲/h/x/zφ4q1įLK+߉\^K>k$ԵNP״wvIG[Dnk-֪õo__\c=OJэq3Vz{r~= ~'\e{ۮӯn_.~пagɿcw~s~5^]Gyɯ/|пfS> k/'GCf5]@^E+% g{O`z(?,?r߰]?+% g{O`zo)vxoNm3@B~ֻWV_=_jKi0Oyy<6vqq,p:-,n X;m$h6z$/ɜ$Kߓ??Z/L;zz?G8Y'xZ8juy?m_;_ٗ'jkFaWlE/JI.W?/>QD3?t>iy9q#__j gy9?l9>?/ |Y}h>nO׷bsG?l=_3_~ˣşLnVvO䗿z,_sF[qnO׷=e?b_˘ڹ g~h3o 3jz_c$z#ؗOzVK{ O|gG~h?{qW?b1߲ so^}L^}]CVV}Ϭ\~~}* tb1 *o}1ݟlLch]O}&CVg3H>縭zz"?ۿ(s÷Uodud?rSK~}~8F-%]Ϯc\~~=ϿYdSZ$~޿h~cVaa8h߃O?ZZuon?}q/OhľA?޾AާLG'_5z?zG 'n}X4]ngï%鏠t|O}{}@?`_(3I|ּFӻ==⬒O; ~%?\?c`>߂F2U~UAl/èxK9|FλD<់Ye_5L^ cQCUdloo<[iOOF2UpMzmQREPEPEPEP;GO{6PQӤ #Puk/4RMmZ͝Eq !3O;(7+_^EPEPEPEP_+ٴ8*'|5QV" |?y=ٚ- ѮmPӬӵl"HY=)URG)NJ1KZ;$ՙWW5%քk_km/ufaEoSsV5g5uLAWov|5F]#/ϧ\Z/L}=?_|ُ~=+?xg‘ygzc}1xgoIݿ_pUSľA?޴b_ׁjZ?s.w3@?_^1\1N;߾,_s /O~u>~Vrkpv>F]: 0WLf"߾vI+?=.?VK{OW߱(b~K|]qW?b!nh_|S^֫g}z}?1???eP:|#_VaxB0թ;hR}ϭ\?+B%=?A?־Hٿ\O?pWa3W`8WLE.i?yM$z~w_y v>f% m88,j+O9~opT mR/yu6}ȗ{~ϽhĽ?3ca.3?w#ϵ^;@'?ǧ~}HiDz_4M`D3?t>iOӟ𯐓@~ gV#cdQ5Os\fO%en?s?aaOO Cw|Q W>!9(GZoh/Q'++d¾B|Ʉ~?h_W׵L#!BL.h G_^_o'??m>xe/^"uk>fwΫw O-թjwr\PZO,M0j3?Ꮏg'_wxzjquwquwᎿg'_wxz?Ꮏg'_wxzjquwquwᎿg'_wxz?Ꮏg'_wxzjquwquwᎿg'_wxz?Ꮏg'_wxzjquwquwᎿg'_wxz?Ꮏg'_wxzjquwquwᎿg'_wxzDѴGkoaiVtetXmu,SEs4˷|#3:((((((<52Fgiw$UK|iwv7"uj #w/G,kw_/ Z׊φG_FGд GQkWf'K/F ;8&("w_+KP]wźgiiOX4ͧ *|uW{$[wK}g0m B؛x5;& y$ox߲G7wx/_xR'4ɼ howmPԼ=Ğ6PZ+R]/;◁%jD?eo?u?~47>⏎<#U>6Z_xR>?x>~1ƽ/ɟh S<;Bzw?^_?fC|Z?:Ή[o6 |9τ4oxWz4x/[xOZ^ڦ^xW>:ϋ,lng.OxF"g<3H״ˮxc_(6?Dύ'ih7.|k?>~~>gWּi^ "w<-!dM}{V Z'E?x~7Im/|! 4mIkG B v#c~>&w[w/Oljt]'O~-z/4ø5ٴ ^~PYލF?}HtUVe}mY`ϵXi-9\i@2~?N4~?N4/1D.OG1D.O_MQ@2~?N4~?N4/1D.OG1D.O_MQ@2~?N4~?N4/1D.OG1D.O_MQ@2~?N4~?N4/1D.OG1D.O_MQ@2~?N4~?N4/1D.OG1D.O_MQ@/=;m>'ei¶lWÏ Kw}̺[ݺ0jVAE(ᨩ>XF5)}]Wbc)=WЉP7_>~}?Ϗ*O1?㉣,uNoQ/TWY~Ӎ:s߰_O3VK[Kv}?)x}qޞKTzۣX-9kMn1|?:*+g5o_oÊ\ ޞKLzۣ.?+Q_!~ѿ<=/߶Oh/ٯB_U|f^dUT7Ч-/a%͌[3/w_fؿ< Vكƿi~,o+x_Ÿxt}WA|puxIO xT֝c7¿<x:o+^,Agk5֩:uVl_o+xsHxߋGg¿~uKmGR|X4Y|ׅ~|}; W'Sx'Q>0i?;j.— |Gψ/_>#~xU-r~-|yŦkƾ2\7a៎>>#xWN_?kohx;şmմ;FF5 ,K߃l ~| ~'~? K>6m>xuW h&7e<o<0|Y>47?Ɨ? +3~!w?ŏo~,oshw;>(.S[շD9Ud`[֞LJ<)a}jv>tYj^*.o_x~$E}zd?b4/٫Tς(>B>|ioW׵+% g{O`z((((((((((::Ag{W_2O׉,SMτ:?8t9J|'w[^i{˷¼}AܟVd+$,g_{j#{YGVq|r5l+\oZ]5uw0?6__ 3y?foOk_?F2U~KFMMiZ_kjzc:>:Z]4oHƛ:Z[y~l?'gaV6_oieegkAkgikoA6AE Qq,ONx1 JNQ^2qf%n4kB\ZtTy5G_{N>#Լ~.? ӗQ|{IϏ/ x";r?<_h3[/φ4ω~?|g`Cor~jx/N>&eemcvz tkFG׮4@OٓD3ć|0.?yotOGtkRƹuΟeqjͩjrҪu<U믃_W㯈~[uk@v־,;⸺i|Qdv0%SLʾt_y4 mVZ_r3ڊǏUu^OEj~p?bפ9?ukޮKmR_/?K͔~?kO?5q?dǯ/XčsX!FN_5V/e/_~Q_I!|2nkgOay)nixr7~dMӛm[X֪RRi*QEQEQEQEQEQEQEQEQEQEL#!BL.?agtQ@!~ҿY?w4 G_^EPEPEPEPEPEPEPEPEPEP_P? wO_u?aW{p?eDη׹,"\ߓkB%=?Nߟ/OҴ"^_ӵeߗiw,Ľ=?Aգ?nO>1=4>~?⿉/G|O|3dkz <rjQ͢O1/ߵd߳ī~+π-x_Egmz/ti=S+Σ0=#k|G/Je-> 7? wwˏ 'ķ^ 4; :Ϟ)ψ? tãi__ `?W፧ĻO~o~Ο|P~1/SxΛ??t/ms? Ie>| j:Wxs?čKŸ??/hO_˟}x?> ӥm35=~;+~+|9?xW׃ae){veǾ߁|eڻk!CMvZOg"hamri-|eqoyPݬ\k2E*;xn!Fq1?iN )*PFvT JQbRtJɭzLI~,?RL]'Yg^$.=nm7{_n[[?]x UT(~*"E6U.x^-ϊ{i?~?7|㿏/ ~>Px oX>~+A'8~ Wn]|G6~2_ɨ|Z_^4+X[1}5x xž%LӼ7xJnl5/o(iVׇo5K}/rhWP=/OH|Kˮ~\x4?_|?Bŷ xNOGwV׾|A~|@׿cg}[BWiS0CzP|;;? ~//7'o7h?']~xE?( xy|Q/wR}?FhuK+RXGMx>O I;Ot~1n+^?6ڿl\niK7Z^Z |069WM1:I8Ʒ))rR.U%+w~{kk>0|Tuz'gov|9ohO~xa#~x_b ~*|pEZ1nWM'>'Mz/4x{ox70BK~ [K1:Ogx/k/_4NMKM K1x^~FuvW? taGW?o%v-`tO"f|݄ e߈^<I|τk^ G0;7|]JUmoEKuJU߈f/"ӚyᰑQG;:7:mϚ1ڊmFVe'sK^^u|OOֽ7]?Mo߁-tV?k?''5Ee5uK"j{wI◉[_ӁOk<5E^,_Mox k7/=CQ i>iN})wxFM4,>j/<em2]7Aiş6xF~"x/6[V,=/N-{Z鶰{

    RtKT~XnڋMY4ӛ{E;.z~]#\~~=ϿYd_??~w|MZNu_ۇPƟ [V7wuKό#m5x[OŰh^懩Xk:.>[:^j6XjZfg$֗֓Eugyk46E<Ϲ?jKہ=ϿhD=SWYZ_{^~`QWO]ߵOJ.<9 {~3E >^&'~FVI}EV ( ( ( ( ( ( ( ( ( ( &١~_|_^_O0؇ j3(i_,O;~|Jf|X#ޯh((((((((((gZ{~5!_?η_['kd76oUsYDVK{v=?_ʫDkB%޿/SƂ?bK{/ҿ2h}W+_* k<|#wcVҵSqxH|KCo#'c?W ?h||o/ 5k7^#6O+? ?xjK◈ Zܗ~~0oU|GSQuq4 b1΢rRV:IJV䔚馜i^]>iF6uw=[W_M=w߲_>1Wx|c8{Ro? G\>=ZUǎ<+]ọ?0ua Apş!O ?S gz] K|/[񽧊 o X3Έ|1|E M|yѭk ,|j~?>*e~; O& Gë~*ƞ6>6(UO8i_뚭__k.~?_o j:?_'2{T>2|"y{π:/=c~"wh./AZ .qkؚY=TJn*9+Q9{FخyͷRrM-mm[ߋ]K7F_ >^?L?ஷ?U߄zş_5oM ?{r7ÿYڽ>5&OAK}_Ҽ'-SC>SC)|r4ٿ¾>:D~8wwᯉ+[Yx˯x>$tc^$zO=bi$'Q~?i=y k?l/h uC{Ÿ> _xCĺ4;|AqGOg2ïO0cG쟱-)/??/@tMb2ڱCQaN50TRԕJ^679'0N⛊M7kr7 WdO0_//ػfH%`'_z_4o׼~}\6Sn|pc:ο[~9< _:gu ]jxkIUѴH~WS.| xxÿ >~?$L-n_ S-Z hOâ^ #Kzvct?௟՟|'ğ U~-?>*g C^'~g? 3Uεru"΁IOatrN#8ǚƕG)&-MbO~W֪ݛvX_I-<CW~?~ߴT? j0|,>.k|i?G´⭯ *O X?wY|iC;A\?l'>{xVg~?k㗀o ƵkLcxⶓĶvm;\_oZ*[j͟ ?ி_'_ x^HDm_|fw?ڏn5?wǟg_h|aо0hLw~&|YCAм x{M3~ھ'[|q{?pe? |o4ß~ ǟ 1h=>k{j.g[ g&濭.ƷZ9_m]a<;k|~{^#yl?ioc߳/?oiF'e WH]/<o]j>9<7,=TaxC##A LC; Wᇉ~1k0|M_O߄>9~x+ik}$P?]4\cBO<#} `|,u-Zο_ Ow u.|ocm7+X (ӂUQnҌihɲq⬯Zl:O*g7e_ %5cx /z[FnxGq?nZzկ-n_?S9|9|ckXM&OXfV埉$_ꚦjzotGGү,MKX VB~%5~Y+w ] ~ER^&пm[4߈zًkG?ux8Yco_[|/~|B/ŝkP^[ez_GߊOkh)xH|EFh(5)9RsiyZ[ƪqkK6l~ieKzj~Ľ=RJЉqoԟK{r~?hĸǰV5eyߦy?T d_e~JS?\]Z~ד[QYQEQEQEQEQEQEQEQEQEQEW_O0؇ j3B|Ʉ~?h_W״QE|Jf|X#ޯk/WK7Ɵ}{@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@~y~|뿰?l Ol6˿2_UsYDAO_Z/L}=?_Vz{p>s}kBEY%|PO/TM|v^?;/5C| :=6//:巋|iOӭ]O,4m2sƴ"_O+J~OkN3䛩e~Y{9ғZUjSwԜ$dӤڽW>bo+Ś'?OSڟw𦷮Xx?hz|I'߈Ug5]k:(t M'N4+F/IӬzc?kB%c^mJhjn3|5]OwN$Y[jPG3vExf|*~!q߅^'7 /Si #":E/m%AOP]z{>IE5 VmE(F1n[F1}#%3φ^< τt +_kW'CxkBbogji+3;| 5?qKsGPWĽ=A{քKǏu>UPnPoVbݖm~>E]aسUgꟲ컨=ok ~=Ϗ{ IxV7:q } O_K%엗 %ß_;O>?ei/_ 'qewoּ%{ ]u΁9-=\XYOiZ<>>*_^mXRwN;4#%IvI-w}&x07eĞ /W?DžskO .|F7:6p|oXҴVW0|t?|>$׼1-K_5? Wg&_WWoZ S}ƥxN_$T?ƭDǿ'TP|MuQI-4W( oK6Roo.w:h _d/|cxV+tQlC|EѼk᛽kL>l}>S $f??gIw ;^ύ55>)wiFOӟ}k7/^VKS 1|OZ_9Qgw1?[:f&ߗ (((((((((((B|Ʉ~?h_W׵L#!BL.>(/WK7Ɵ}{_!~ҿY?w4((((((((((+/ۧSi2g|_QG]~~#_ _Wjt{—b_ď wc <:[)~b*a18|U\5zXNK*(\W]ViI4ig?0p?%+?sެے`OA_=r 0MK_Vaj]_~VudsAWs'CsV+ߴ^N}췩x]z.+~ LS?j~߱/_EO^zᢲgRަGaj]_~V#_( wKyI_ڗwSҢft:˘aCm'M*>i?ࢶ+H}8U}k\Iyc?IQ? Uc?d?jjN.dVZ}KyRt~-J^/]]}=+i?ग़B?kJ]x٪+)go*z]2#_~VlS-O_OOoS}zQH?~YK2Kw m@䏟M[? k^H'd=Q<<d߯8zُkڊX[}|_[I~Q&\ҦO*ׯ]u'j?ɯz+)Vk[tQ~Gz׆g sO>@@;8y[^ğ:dcP-$еVMVEPEPEPEPEPEPEPEPEPEPEP_!>?b4/٫Tς &١~_|@^E+% g{O`zm^W;ǿqknxǾ%|=Sm3W^!Ӵ};GXt*헐R.x7G!JҀ>B~{߲$m(~{߲$m(+/x7G!Jҏx7G!JҀ>B~{߲$m(~{߲$m(+/x7G!Jҏx7G!JҀ>B~{߲$m(~{߲$m(+/x7G!Jҏx7G!JҀ>B~{߲$m(~{߲$m(+/x7G!Jҏx7G!JҀ>B~{߲$m(~{߲$m(+/x7G!Jҏx7G!JҀ>B~{߲$m(~{߲$m(+/x7G!Jҏx7G!JҀ>B~{߲$m(~{߲$m(+/x7G!Jҏx7G!JҀ>B~{߲$m(~{߲$m(+/x7G!Jҏx7G!JҀ>B~{߲$m(~{߲$m(+/x7G!Jҏx7G!JҀ>B~{߲$m(~{߲$m(+/x7G!Jҏx7G!JҀ>B~{߲$m(~{߲$m(+/x7G!Jҏx7G!JҀ>B~{߲$m(~{߲$m(+/x7G!Jҏx7G!JҀ>B~{߲$m(~{߲$m(+/x7G!Jҏx7G!JҀ>B~{߲$m(~{߲$m(+/x7G!Jҏx7G!JҀ>B~{߲$m(~{߲$m(+/x7G!Jҏx7G!JҀ>B~{߲$m(~{߲$m(+/x7G!Jҏx7G!JҀ>B~{߲$m(~{߲$m( &١~_|G<%~ iGL#!BL.>(((((((((((((((((((((((((((((((((((((buteo-syncfw-0.7.21+16.04.20151216.1/doc/doc.pri0000644000015600001650000000066612634332753020775 0ustar pbuserpbgroup00000000000000DOXYGEN_BIN = $$system(command -v doxygen) isEmpty(DOXYGEN_BIN):error("Unable to detect doxygen in PATH") QMAKE_EXTRA_TARGETS += doc doc.target = doc doc.CONFIG = phony doc.commands = cd $${PWD} && $${DOXYGEN_BIN} Doxyfile doc.depends = FORCE QMAKE_CLEAN += $${PWD}/html/* $${PWD}/buteo-syncfw.tag htmldocs.files = $${PWD}/html/* htmldocs.path = /usr/share/doc/buteo-syncfw-doc/ htmldocs.CONFIG += no_check_exist INSTALLS += htmldocs buteo-syncfw-0.7.21+16.04.20151216.1/doc/Doxyfile0000644000015600001650000020430312634332753021214 0ustar pbuserpbgroup00000000000000# Doxyfile 1.6.3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = Buteo Synchronization Framework # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = . # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it parses. # With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this tag. # The format is ext=language, where ext is a file extension, and language is one of # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by # doxygen. The layout file controls the global structure of the generated output files # in an output format independent way. The create the layout file that represents # doxygen's defaults, run doxygen with the -l option. You can optionally specify a # file name after the option, if omitted DoxygenLayout.xml will be used as the name # of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = src \ ../msyncd/ \ ../libbuteosyncfw/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.c++ \ *.d \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.idl \ *.odl \ *.cs \ *.php \ *.php3 \ *.inc \ *.m \ *.mm \ *.dox \ *.py \ *.f90 \ *.f \ *.vhd \ *.vhdl \ *.C \ *.CC \ *.C++ \ *.II \ *.I++ \ *.H \ *.HH \ *.H++ \ *.CS \ *.PHP \ *.PHP3 \ *.M \ *.MM \ *.PY \ *.F90 \ *.F \ *.VHD \ *.VHDL \ *.C \ *.H \ *.tlh \ *.diff \ *.patch \ *.moc \ *.xpm \ *.dox \ *.cpp # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = yes # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = ../msyncd/moc_*.cpp \ ../libbuteosyncfw/moc_*.cpp # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = YES # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = src # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER # are set, an additional index file will be generated that can be used as input for # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated # HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. # For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's # filter section matches. # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NONE # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be implemented using a PHP enabled web server instead of at the web client using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server based approach is that it scales better to large projects and allows full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = no # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = buteo-syncfw.tag # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 1000 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = YES # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES buteo-syncfw-0.7.21+16.04.20151216.1/doc/doc.pro0000644000015600001650000000004612634332753020773 0ustar pbuserpbgroup00000000000000TEMPLATE = aux include($$PWD/doc.pri) buteo-syncfw-0.7.21+16.04.20151216.1/.gitignore0000644000015600001650000000020012634332753020717 0ustar pbuserpbgroup00000000000000Makefile Makefile.* *.so* *.prl pkgconfig/ *.list *.tag msyncd/libmsyncd.a msyncd/msyncd doc/html unittests/tests/sync-fw-tests buteo-syncfw-0.7.21+16.04.20151216.1/rpm/0000755000015600001650000000000012634333235017531 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/rpm/buteo-syncfw.spec0000644000015600001650000000502112634332753023034 0ustar pbuserpbgroup00000000000000Name: buteo-syncfw Version: 0.6.1 Release: 1 Summary: Synchronization backend Group: System/Libraries URL: https://github.com/nemomobile/buteo-syncfw License: LGPLv2.1 Source0: %{name}-%{version}.tar.gz BuildRequires: doxygen, fdupes BuildRequires: pkgconfig(QtCore) BuildRequires: pkgconfig(dbus-1) BuildRequires: pkgconfig(contextsubscriber-1.0) BuildRequires: pkgconfig(accounts-qt) BuildRequires: pkgconfig(libsignon-qt) BuildRequires: pkgconfig(QtSystemInfo) BuildRequires: libiphb-devel Requires: %{name}-msyncd # TODO: needs a proper fix # Patch0: 0001-Synchronizer-removeProfile-remove-profiles-even-if-p.patch %description %{summary}. %files %defattr(-,root,root,-) %{_libdir}/*.so.* %package devel Summary: Development files for %{name} Group: Development/Libraries Requires: %{name} = %{version}-%{release} %description devel %{summary}. %files devel %defattr(-,root,root,-) %{_includedir}/* %{_libdir}/*.so %{_libdir}/*.prl %{_libdir}/pkgconfig/*.pc # Should not be required anymore, sticks around for now # in case we need to test something explicitely with qt4 stack %package qt4-msyncd Summary: Buteo sync daemon Group: System/Libraries Requires: %{name} = %{version}-%{release} Requires: systemd Requires: systemd-user-session-targets Conflicts: buteo-syncfw-qt5-msyncd %description qt4-msyncd %{summary}. %files qt4-msyncd %defattr(-,root,root,-) %config %{_libdir}/systemd/user/*.service %{_libdir}/systemd/user/user-session.target.wants/*.service %config %{_sysconfdir}/syncwidget/* %{_bindir}/msyncd %package doc Summary: Documentation for %{name} Group: Documentation %description doc %{summary}. %files doc %defattr(-,root,root,-) %{_docdir}/buteo-syncfw-doc/* %package tests Summary: Tests for %{name} Group: Development/Libraries %description tests %{summary}. %files tests %defattr(-,root,root,-) /opt/tests/buteo-syncfw/* %{_datadir}/accounts/services/*.service %prep %setup -q %build qmake make make doc %install make INSTALL_ROOT=%{buildroot} install chmod +x %{buildroot}/opt/tests/buteo-syncfw/*.pl %{buildroot}/opt/tests/buteo-syncfw/*.sh %fdupes %{buildroot}/opt/tests/buteo-syncfw/ mkdir -p %{buildroot}%{_libdir}/systemd/user/user-session.target.wants ln -s ../msyncd.service %{buildroot}%{_libdir}/systemd/user/user-session.target.wants/ %post /sbin/ldconfig if [ "$1" -ge 1 ]; then systemctl-user daemon-reload || true systemctl-user try-restart msyncd.service || true fi %postun /sbin/ldconfig if [ "$1" -eq 0 ]; then systemctl-user stop msyncd.service || true systemctl-user daemon-reload || true fi buteo-syncfw-0.7.21+16.04.20151216.1/rpm/buteo-syncfw-qt5.spec0000644000015600001650000000554412634332753023555 0ustar pbuserpbgroup00000000000000Name: buteo-syncfw-qt5 Version: 0.7.21 Release: 1 Summary: Synchronization backend Group: System/Libraries URL: https://github.com/nemomobile/buteo-syncfw License: LGPLv2.1 Source0: %{name}-%{version}.tar.gz BuildRequires: doxygen, fdupes BuildRequires: pkgconfig(Qt5Core) BuildRequires: pkgconfig(Qt5DBus) BuildRequires: pkgconfig(Qt5Sql) BuildRequires: pkgconfig(Qt5Test) BuildRequires: pkgconfig(dbus-1) BuildRequires: pkgconfig(accounts-qt5) >= 1.13 BuildRequires: pkgconfig(libsignon-qt5) BuildRequires: pkgconfig(Qt5SystemInfo) BuildRequires: pkgconfig(libiphb) BuildRequires: pkgconfig(qt5-boostable) BuildRequires: pkgconfig(keepalive) BuildRequires: oneshot BuildRequires: doxygen Requires: mapplauncherd-qt5 Requires: oneshot %{_oneshot_requires_post} %description %{summary}. %files %defattr(-,root,root,-) %{_libdir}/*.so.* %{_oneshotdir}/msyncd-storage-perm %package devel Summary: Development files for %{name} Group: Development/Libraries Requires: %{name} = %{version}-%{release} %description devel %{summary}. %files devel %defattr(-,root,root,-) %{_includedir}/* %{_libdir}/*.so %{_libdir}/*.prl %{_libdir}/pkgconfig/*.pc %package msyncd Summary: Buteo sync daemon Group: System/Libraries Requires: %{name} = %{version}-%{release} Requires: systemd Requires: systemd-user-session-targets Provides: buteo-syncfw-msyncd = %{version} Obsoletes: buteo-syncfw-msyncd < %{version} %description msyncd %{summary}. %files msyncd %defattr(-,root,root,-) %config %{_libdir}/systemd/user/*.service %{_libdir}/systemd/user/user-session.target.wants/*.service %config %{_sysconfdir}/syncwidget/* %{_bindir}/msyncd %package doc Summary: Documentation for %{name} Group: Documentation %description doc %{summary}. %files doc %defattr(-,root,root,-) %{_docdir}/buteo-syncfw-doc/* %package tests Summary: Tests for %{name} Group: Development/Libraries %description tests %{summary}. %files tests %defattr(-,root,root,-) /opt/tests/buteo-syncfw/* %{_datadir}/accounts/services/*.service %prep %setup -q %build %qmake5 -recursive CONFIG+=usb-moded DEFINES+=USE_KEEPALIVE make %{_smp_mflags} make doc %{_smp_mflags} %install make INSTALL_ROOT=%{buildroot} install chmod +x %{buildroot}/opt/tests/buteo-syncfw/*.pl %{buildroot}/opt/tests/buteo-syncfw/*.sh %fdupes %{buildroot}/opt/tests/buteo-syncfw/ mkdir -p %{buildroot}%{_libdir}/systemd/user/user-session.target.wants ln -s ../msyncd.service %{buildroot}%{_libdir}/systemd/user/user-session.target.wants/ mkdir -p %{buildroot}/%{_oneshotdir} install -D -m 755 oneshot/msyncd-storage-perm %{buildroot}/%{_oneshotdir} %post /sbin/ldconfig %{_bindir}/add-oneshot msyncd-storage-perm if [ "$1" -ge 1 ]; then systemctl-user daemon-reload || true systemctl-user try-restart msyncd.service || true fi %postun /sbin/ldconfig if [ "$1" -eq 0 ]; then systemctl-user stop msyncd.service || true systemctl-user daemon-reload || true fi buteo-syncfw-0.7.21+16.04.20151216.1/rpm/buteo-syncfw.changes0000644000015600001650000000335712634332753023524 0ustar pbuserpbgroup00000000000000* Tue Mar 05 2013 Bernd Wachter - 0.6.1 - Change from xdg autostart to providing an user session service file * Mon Sep 24 2012 Bernd Wachter - 0.6.0 - Update to nemo upstream, contributing to JB#2310 * Thu Sep 06 2012 Bernd Wachter - 0.5.40 - Update to latest upstream, contributing to JB#1813 - Move tests to /opt/tests - Adjust to Accounts API changes * Wed Feb 16 2011 Bernd Wachter - 0.5.4 - Move binaries only required for testing to -tests, fixing BMC#13446 * Thu Feb 10 2011 Bernd Wachter - 0.5.4 - Add patch to fix BMC#13367 - buteo-syncfw: removing profile fails * Thu Feb 03 2011 Bernd Wachter - 0.5.4 - Update version, implementing FEA#8027 * Mon Jan 31 2011 Bernd Wachter - 0.5.2.8 - Update version, fixing BMC#13137 * Wed Jan 05 2011 Bernd Wachter - 0.5.2.2 - Update version, fixing BMC#11995 * Fri Dec 17 2010 Bernd Wachter - 0.5.2.1 - Update version, fixing BMC#11486 * Wed Nov 24 2010 Bernd Wachter - 0.5.1.25 - Update version, required for buteo-sync-plugins fixes from BME#3888 * Sun Oct 17 2010 Bernd Wachter - 0.5.1.12 - Update to current version * Thu Sep 23 2010 Bernd Wachter - 0.5.1.8 - Update version, fixing BMC#3860 * Tue Aug 24 2010 Bernd Wachter - 0.5.0.21 - Update to latest version - Change BuildRequires to pkgconfig - Package documentation * Thu Jun 24 2010 Bernd Wachter - 0.4.9 - Initial RPM version buteo-syncfw-0.7.21+16.04.20151216.1/buteo-sync.pro0000644000015600001650000000036112634332753021551 0ustar pbuserpbgroup00000000000000TEMPLATE = subdirs SUBDIRS += \ libbuteosyncfw \ msyncd \ unittests \ tools \ doc msyncd.depends = libbuteosyncfw unittests.depends = libbuteosyncfw msyncd coverage.CONFIG += recursive QMAKE_EXTRA_TARGETS += coverage buteo-syncfw-0.7.21+16.04.20151216.1/tools/0000755000015600001650000000000012634333235020073 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/tools/tools.pro0000644000015600001650000000036312634332753021763 0ustar pbuserpbgroup00000000000000TEMPLATE=subdirs OTHER_FILES += \ clientplugin.h.tmpl \ clientplugin.cpp.tmpl \ gen_template.py \ mypluginprofile.xml.tmpl \ clientprofile.xml.tmpl \ client-plugin.cfg \ server-plugin.cfg \ storage-plugin.cfg buteo-syncfw-0.7.21+16.04.20151216.1/tools/client-plugin.cfg0000644000015600001650000000711212634332753023333 0ustar pbuserpbgroup00000000000000[config] #Type of the configuration. Possible values: 'client/server/storage' ### MANDATORY ### type=client # Classname of the plugin ### MANDATORY ### classname=CalDavPlugin #Name of the client plugin profile. This profile name is used to uniquely #identify a profile for which sync actions need to be taken ### MANDATORY ### name=google.com #remote url, used only incase of network based sync ### OPTIONAL ### remote_url=https://myservice.com/sync #Only used for testing, if using account&sso, this username will not be #considered ### OPTIONAL ### username=myusername #Credentials used only if 'destinationtype=online' ### OPTIONAL ### password=mypassword #possible values: 'online', 'device' ### MANDATORY ### destinationtype=online #Only used for display purposes in UI's ### OPTIONAL ### displayname=MyServiceSync #Use to identify if the profile has been enabled/disable. #Possible values: 'true'/'false' ### MANDATORY ### enabled=true #Used to integrate with Accounts&SSO. If false, profile credentials are used ### OPTIONAL ### use_accounts=false #Enables/disables scheduling. Possible values 'true'/'false'. If schedule is #enabled, the default will be all days with interval of 1 ### OPTIONAL ### schedule=false #If hidden=true, the profile will not be visible in UI ### OPTIONAL ### hidden=false #If enabled, this is plugin that can handle synchronization on change of #databases ### OPTIONAL ### sync_on_change=true #Proxy host, used only if 'destinationtype=online' ### OPTIONAL ### http_proxy_host=http://myproxy.com #Proxy port ### OPTIONAL ### http_proxy_port=8443 #2-way or 1-way sync. Possible values: 'two-way', 'from-remote', 'to-remote'. #The plugin has to handle the actual sync based on the sync direction ### OPTIONAL ### syncdirection=two-way #Conflict resolution policy when a data element is modified at both the ends. #Possible values: 'server-wins','client-wins'. The plugin has to handle the #actual conflict resolution ### OPTIONAL ### conflictpolicy=server-wins #Only for testing. If you already know the account id, set it here. Else, the #framework will set it automatically ### OPTIONAL ### accountid=2 #Transport type. Possible values:'HTTP','BT','USB'. HTTP is used for any #network transport ### MANDATORY ### transport=HTTP # Agent library name. This is the name of the sync agent that is responsible #for the actual sync operations. If agent_name=atom, then the library name #should be libatom.so ### MANDATORY ### [[agent]] ### MANDATORY ### name=atom [[[props]]] ### MANDATORY ### Sync Transport=HTTP ### OPTIONAL ### Sync Direction=one-way ### OPTIONAL ### conflictpolicy=server-wins ### OPTIONAL ### key1=value1 key2=value2 key3=value3 # All supported storages (like contacts, calendar etc.) by this client plugin # Only required if there are storage plugins associated with this plugin ### OPTIONAL ### [[storages]] ### MANDATORY ### [[[contacts]]] ### MANDATORY ### enabled=true ### OPTIONAL ### Local URI=./contacts ### OPTIONAL ### Target URI=contacts [[[calendar]]] enabled=true Notebook Name=Personal Local URI=./calendar Target URI=caltask Calendar Format=vcalendar [[[xyz]]] enabled=false Local DBName=XYZ # You can add any more key/value pairs (the names of which do not conflict #with the above) for your own configuration. The framework will not use #these values ### OPTIONAL ### [[ext-config]] mykey1=myvalue1 mykey2=myvalue2 buteo-syncfw-0.7.21+16.04.20151216.1/tools/server-plugin.cfg0000644000015600001650000000300512634332753023360 0ustar pbuserpbgroup00000000000000# Server plugin details [config] #Classname of the plugin ### MANDATORY ### classname=MyServerPlugin #Type of the plugin (client/server/storage) ### MANDATORY ### type=server #Name of the server plugin profile. This name would be used to identify the #binary associated with the plugin. If profile name='myserverplugin', then #the plugin library should be 'libmyserverplugin.so' ### MANDATORY ### name=myserverplugin #Use to identify if the profile has been enabled/disable. #Possible values: 'true'/'false' ### MANDATORY ### enabled=true #If hidden=true, the profile will not be visible in UI ### OPTIONAL ### hidden=true #Transport over which the server listens for incoming connections #Possible values: 'usb', 'bt', 'ip' ### MANDATORY ### transport=usb #All supported storages (like contacts, calendar etc.) by this server plugin #Only required if there are storage plugins associated with this plugin. Optional ### OPTIONAL ### [[storages]] ### MANDATORY ### [[[contacts]]] ### MANDATORY ### enabled=true Local URI=./contacts Target URI=contacts [[[calendar]]] enabled=true Notebook Name=Personal Local URI=./calendar Target URI=caltask Calendar Format=vcalendar [[[xyz]]] enabled=false Local DBName=XYZ #You can add any more key/value pairs (the names of which do not conflict #with the above) for your own configuration. ### OPTIONAL ### [[ext-config]] mykey1=myvalue1 mykey2=myvalue2 buteo-syncfw-0.7.21+16.04.20151216.1/tools/gen_template.py0000755000015600001650000002652612634332753023133 0ustar pbuserpbgroup00000000000000#!/usr/bin/python ''' * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * * Author: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA ''' from Cheetah.Template import Template from configobj import ConfigObj import sys, argparse, os, shutil import errno def main(): usage = "usage: %prog [options] arg" parser = argparse.ArgumentParser (description='Generates Buteo sync plugin template code', usage=sys.argv[0] + ' [options] arg') parser.add_argument('-c', '--config', help="Config file to generate the source templates", required=True) parser.add_argument('-d', '--outdir', help="Target output directory. Should be writable") args = parser.parse_args() options = vars (args) if options['config'] == None or options['outdir'] == None: print parser.print_help() sys.exit(2) # Read the configuration file provided from cmdline co = ConfigObj (options['config']) # Validate the config file validator = ConfigValidator (co) validator.validate () tg = TemplateGenerator (co, options['outdir']) tg.createDirStructure() tg.generateClasses() tg.generateProfiles() tg.generateProjectFile() ''' Class to validate the configuration input ''' class ConfigValidator: def __init__(self, configObj): self.mandatory_client_config_keys = set(['classname', 'name', 'destinationtype', 'schedule', 'displayname', 'transport', 'enabled', 'agent', 'syncdirection', 'conflictpolicy']) self.mandatory_server_config_keys = set(['classname', 'name', 'transport', 'enabled']) self.mandatory_storage_config_keys = set(['classname', 'name', 'mimetype', 'enabled']) self.mandatory_fields = set ([]) self.configObj = configObj if configObj.has_key ('config') == False: print "Error: Invalid config file. 'config' section not available" sys.exit (3) if configObj.get('config').has_key('type') == False: print "Error: Invalid config file. 'type' of the plugin not provided. Possible values: 'client/server/storage'" sys.exit (3) if configObj['config']['type'] == 'client': self.mandatory_fields = self.mandatory_client_config_keys elif configObj['config']['type'] == 'server': self.mandatory_fields = self.mandatory_server_config_keys elif configObj['config']['type'] == 'storage': self.mandatory_fields = self.mandatory_storage_config_keys else: suppliedtype = configObj['config']['type'] print "Error: Invalid plug-in type '" + suppliedtype + "' specified. Should be one of 'clientconfig/serverconfig/storageconfig'" sys.exit (3) def validate (self): for key in self.mandatory_fields: if (self.configObj.has_key(key) and (self.configObj.get(key).strip () == '') and (self.configObj.get(key) == None)): print "Error: Value for '" + key + "' cannot be empty" sys.exit (3) fields = set (self.configObj.get('config').keys()) missing_fields = self.mandatory_fields - fields if len(missing_fields) != 0: print "Missing mandatory fields:" + str(missing_fields) sys.exit (3) # Validate specific server profile properties if self.configObj['config']['type'] == 'server': self.validateServerProfile(self.configObj['config']) def validateServerProfile (self, properties): # For now, possible 'transport' key/values are checked possible_transports = ['usb', 'bt', 'ip'] given_transport = properties['transport'] if possible_transports.__contains__(given_transport) == None: print "Wrong value for transport. Possible values: usb/bt/ip" sys.exit(3) # End validator class ''' Class to generate the template ''' class TemplateGenerator: def __init__ (self, configObj, targetDir): self.targetDir = targetDir self.configObj = configObj TEMPLATE_DIR = 'templates/' if self.configObj['config']['type'] == 'client': self.classtemplate_h = TEMPLATE_DIR + 'clientplugin_h.tmpl' self.classtemplate_cpp = TEMPLATE_DIR + 'clientplugin_cpp.tmpl' self.profile = TEMPLATE_DIR + 'syncprofile_xml.tmpl' self.agent_profile = TEMPLATE_DIR + 'syncagentprofile_xml.tmpl' elif self.configObj['config']['type'] == 'server': self.classtemplate_h = TEMPLATE_DIR + 'serverplugin_h.tmpl' self.classtemplate_cpp = TEMPLATE_DIR + 'serverplugin_cpp.tmpl' self.profile = TEMPLATE_DIR + 'serverprofile_xml.tmpl' elif self.configObj['config']['type'] == 'storage': self.classtemplate_h = TEMPLATE_DIR + 'storageplugin_h.tmpl' self.classtemplate_cpp = TEMPLATE_DIR + 'storageplugin_cpp.tmpl' self.profile = TEMPLATE_DIR + 'storageprofile_xml.tmpl' self.projectfile = TEMPLATE_DIR + 'myproj_pro.tmpl' def hasStorages (self): if self.configObj.has_key ('storages') and (len(self.configObj['storages']) > 0): return True else: return False def hasExtConfiguration (self): if self.configObj.has_key ('ext-config') and (len (self.configObj['ext-config']) > 0): return True else: return False def createDirStructure (self): dirExistsFlag = None dir = self.targetDir while dirExistsFlag != False: if os.path.exists (dir): print "Directory '" + dir + "' already exists" dir = raw_input("Enter new directory (Press enter for exiting):") if dir.strip() == None: sys.exit(4) if os.path.exists(dir): dirExistsFlag = True break else: dirExistsFlag = False continue else: dirExistsFlag = False self.targetDir = dir try: os.makedirs(dir + "/" + "xml") if self.configObj['config']['type'] == 'client': os.makedirs(dir + "/xml/client") # Create the sync directory os.makedirs(dir + "/xml/sync") elif self.configObj['config']['type'] == 'server': os.makedirs(dir + "/xml/server") if self.configObj['config']['type'] == 'storage': os.makedirs(dir + "/xml/storage") except OSError as exception: if exception.errno != errno.EEXIST: raise def cleanup (self): inp = 'yn' count = 1 while inp.lower() == 'y' or inp.lower() == 'n' or count < 4: inp = raw_input("Really delete '" + self.targetDir + "' (y/n)?") if inp.lower() == 'y': shutil.rmtree(self.targetDir, False) return elif inp.lower() == 'n': return count = count + 1 def generateClasses (self): classname = self.configObj['config']['classname'] # Generate .h header = open (self.targetDir + "/" + classname + ".h", "w") print >> header, Template (file = self.classtemplate_h, searchList = [{'plugin':self.configObj.get('config')}]) header.close() # Generate .cpp cpp = open (self.targetDir + "/" + classname + ".cpp", "w") print >> cpp, Template (file = self.classtemplate_cpp, searchList = [{'plugin':self.configObj.get('config')}]) cpp.close() def generateStorageProfiles (self, profileXmlName): sl = dict() sl['profile'] = self.configObj['config'] if self.configObj.get('config').has_key('ext-config'): sl['extprops'] = self.configObj['config']['ext-config'] storage_file = open (self.targetDir + "/xml/storage/" + profileXmlName + ".xml", "w") print >> storage_file, Template (file = self.profile, searchList = [sl]) storage_file.close() def generateSyncAgentProfile (self, profileXmlName): sl = dict() sl['name'] = profileXmlName sl['props'] = self.configObj['config']['agent']['props'] agent_file = open (self.targetDir + "/xml/client/" + profileXmlName + ".xml", "w") print >> agent_file, Template (file = self.agent_profile, searchList = [sl]) agent_file.close() def generateServerProfile (self, profileXmlName): sl = dict() sl['profile'] = self.configObj['config'] if self.configObj.get('config').has_key('storages'): sl['storages'] = self.configObj['config']['storages'] if self.configObj.get('config').has_key('ext-config'): sl['extprops'] = self.configObj['config']['ext-config'] server_profile = open (self.targetDir + "/xml/server/" + profileXmlName + ".xml", "w") print >> server_profile, Template (file = self.profile, searchList = [sl]) server_profile.close() def generateSyncProfile (self, profileXmlName): sl = dict() sl['profile'] = self.configObj['config'] if self.configObj.get('config').has_key('storages'): sl['storages'] = self.configObj['config']['storages'] sl['agentname'] = self.configObj['config']['agent']['name'] sl['agentprops'] = self.configObj['config']['agent']['props'] if self.configObj.get('config').has_key('ext-config'): sl['extprops'] = self.configObj['config']['ext-config'] sync_profile_file = open (self.targetDir + "/xml/sync/" + profileXmlName + ".xml", "w") print >> sync_profile_file, Template (file = self.profile, searchList = [sl]) sync_profile_file.close() def generateProfiles (self): profile_name = self.configObj['config']['name'] if self.configObj['config']['type'] == 'client': agent_name = self.configObj['config']['agent']['name'] self.generateSyncProfile(profile_name) self.generateSyncAgentProfile(agent_name) elif self.configObj['config']['type'] == 'server': self.generateServerProfile(profile_name) elif self.configObj['config']['type'] == 'storage': self.generateStorageProfiles(profile_name) self.generateProjectFile() def generateProjectFile (self): sl = dict() sl['projectname'] = self.targetDir + "-" + self.configObj['config']['type'] sl['profile'] = {'type':self.configObj['config']['type'], } sl['classname'] = self.configObj['config']['classname'] project_file = open (self.targetDir + "/" + self.targetDir + ".pro", "w") print >> project_file, Template (file = self.projectfile, searchList = [sl]) project_file.close() # End TemplateGenerator class # Main function if __name__ == "__main__": main() buteo-syncfw-0.7.21+16.04.20151216.1/tools/storage-plugin.cfg0000644000015600001650000000145112634332753023521 0ustar pbuserpbgroup00000000000000# Storage plugin details [config] # Type of the plugin. Possible values: 'client/server/storage' ### MANDATORY ### type=storage #Classname of the plugin ### MANDATORY ### classname=MyStoragePlugin #Name of the storage plugin profile. This name would be used to identify #the binary associated with the plugin. If profile name='mystorageplugin', #then the plugin library should be 'libmystorageplugin.so' ### MANDATORY ### name=mystorageplugin #Use to identify if the storage plugin has been enabled/disable. #Possible values: 'true'/'false' ### MANDATORY ### enabled=true ### OPTIONAL ### mimetype=text/x-abx #You can add any more key/value pairs (the names of which do not conflict #with the above) for your own configuration ### OPTIONAL ### [[ext-config]] mykey1=myvalue1 mykey2=myvalue2 buteo-syncfw-0.7.21+16.04.20151216.1/tools/templates/0000755000015600001650000000000012634333235022071 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/tools/templates/myproj_pro.tmpl0000644000015600001650000000212712634332753025175 0ustar pbuserpbgroup00000000000000TEMPLATE = lib TARGET = $(projectname) DEPENDPATH += . INCLUDEPATH += . CONFIG += link_pkgconfig plugin PKGCONFIG = buteosyncfw5 target.path = /usr/lib/buteo-plugins-qt5 QT += dbus sql network QT -= gui VER_MAJ = 1 VER_MIN = 0 VER_PAT = 0 \#input HEADERS += $(classname).h SOURCES += $(classname).cpp QMAKE_CXXFLAGS = -Wall \ -g \ -Wno-cast-align \ -O2 -finline-functions \#clean QMAKE_CLEAN += \$(TARGET) \$(TARGET0) \$(TARGET1) \$(TARGET2) QMAKE_CLEAN += \$(OBJECTS_DIR)/*.gcda \$(OBJECTS_DIR)/*.gcno \$(OBJECTS_DIR)/*.gcov \$(OBJECTS_DIR)/moc_* \#install $(profile.type).path = /etc/buteo/profiles/$(profile.type) $(profile.type).files = xml/$(profile.type)/* #if $getVar('$profile.type', '') == 'client' sync.path = /etc/buteo/profiles/sync sync.files = xml/sync/* #end if #if $getVar('$profile.type', '') == 'storage' storage.path = /etc/buteo/profiles/storage storage.files = xml/storage/* #end if INSTALLS += target $(profile.type) #if $getVar('$profile.type', '') == 'client' INSTALLS += sync #end if #if $getVar('$profile.type', '') == 'storage' INSTALLS += storage #end if buteo-syncfw-0.7.21+16.04.20151216.1/tools/templates/syncprofile_xml.tmpl0000644000015600001650000000441212634332753026211 0ustar pbuserpbgroup00000000000000 #if $getVar('$profile.remote_url', '') != '' #end if #if $getVar('$profile.username','') != '' #end if #if $getVar('$profile.password','') != '' #end if #if $getVar('$profile.use_accounts','') != '' #end if #if $getVar('$profile.hidden','') != '' #end if #for $key, $value in $agentprops.items() #end for #for $storagename, $props in $storages.items() #for $key, $value in $props.items() #end for #end for #for $key, $value in $extprops.items() #end for buteo-syncfw-0.7.21+16.04.20151216.1/tools/templates/storageplugin_h.tmpl0000644000015600001650000001400512634332753026165 0ustar pbuserpbgroup00000000000000/* * YOUR LICENSE TEXT HERE * */ #ifndef $(plugin.classname)_H #define $(plugin.classname)_H \#include "$(plugin.classname).h" class SimpleItem; // Interface to Storage Plugin towards Sync FW class $(plugin.classname) : public Buteo::StoragePlugin { public: /*! \brief Constructor * */ $(plugin.classname)(const QString& aPluginName); /*! \brief Destructor * */ virtual ~$(plugin.classname)(); //////////////////////////////////////////////////////////////////////////////// // Functions below are derived from storage plugin /////////////// //////////////////////////////////////////////////////////////////////////////// /*! \brief Initializes the plugin * * * @param aProperties Properties that should be set for this plugin * @return True on success, otherwise false */ virtual bool init( const QMap& aProperties ); /*! \brief Uninitializes the plugin * */ virtual bool uninit(); /*! \brief Returns all known items * * @param aItems Array where to place items * @return True on success, otherwise false */ virtual bool getAllItems(QList &aItems); /*! \brief Returns id's of all known items * * @param aItems Array where to place item id's * @return True on success, otherwise false */ virtual bool getAllItemIds( QList& aItems ); /*! \brief Returns all new items since aTime * * @param aNewItems Array where to place items * @param aTime Timestamp * @return True on success, otherwise false */ virtual bool getNewItems( QList& aNewItems, const QDateTime& aTime ); /*! \brief Returns id's of all new items since aTime * * @param aNewItemIds Array where to place item id's * @param aTime Timestamp * @return True on success, otherwise false */ virtual bool getNewItemIds( QList& aNewItemIds, const QDateTime& aTime ); /*! \brief Returns all modified items since aTime * * @param aModifiedItems Array where to place items * @param aTime Timestamp * @return True on success, otherwise false */ virtual bool getModifiedItems( QList& aModifiedItems, const QDateTime& aTime ); /*! \brief Returns id's of all modified items since aTime * * @param aModifiedItemIds Array where to place item id's * @param aTime Timestamp * @return True on success, otherwise false */ virtual bool getModifiedItemIds( QList& aModifiedItemIds, const QDateTime& aTime ); /*! \brief Returns id's of all deleted items since aTime * * @param aDeletedItemIds Array where to place item id's * @param aTime Timestamp * @return True on success, otherwise false */ virtual bool getDeletedItemIds( QList& aDeletedItemIds, const QDateTime& aTime ); /*! \brief Generates a new item * * Returned item is temporary. Therefore returned item ALWAYS has its id * set as empty ID (""). ID will be assigned only after addItem() has been * called for the item. * * @return On success pointer to the item generated, otherwise NULL */ virtual Buteo::StorageItem* newItem(); /*! \brief Returns an item based on id * * @param aItemId Id of the item to return * @return On success pointer to the item, otherwise NULL */ virtual Buteo::StorageItem* getItem( const QString& aItemId ); /*! \brief Returns items based on ids * * @param aItemIdList Ids of the items * @return List of items */ virtual QList getItems( const QStringList& aItemIdList ); /*! \brief Adds an item to the storage * * Upon successful addition, item is updated with its * assigned ID. * * @param aItem Item to add * @return Operation status code */ virtual OperationStatus addItem( Buteo::StorageItem& aItem ); /*! \brief Adds items to the storage * * Upon successful addition, items are updated with its * assigned ID. * * @param aItems Items to add * @return Operation status codes */ virtual QList addItems( const QList& aItems ); /*! \brief Modifies an item in the storage * * @param aItem Item to modify * @return Operation status code */ virtual OperationStatus modifyItem( Buteo::StorageItem& aItem ); /*! \brief Modifies item in the storage * * @param aItems Items to add * @return Operation status codes */ virtual QList modifyItems( const QList& aItems ); /*! \brief Deletes an item from the storage * * @param aItemId Id of the item to be deleted * @return Operation status code */ virtual OperationStatus deleteItem( const QString& aItemId ); /*! \brief Deletes an item from the storage * * @param aItemIds Id's of the item to be deleted * @return Operation status codes */ virtual QList deleteItems( const QList& aItemIds ); private: bool doInitItemAnalysis(); bool doUninitItemAnalysis(); /*! \brief convert list of contacts into vector of storage items * * * @param aList List of contacts * @return list of storage items */ QList getStoreList(QList&aList); QByteArray getCtCaps( const QString& aFilename ) const; $(plugin.classname)::OperationStatus mapErrorStatus(const QContactManager::Error &aContactError) const; // The class that does the actual interaction with the storage backend MyStorageBackend *mBackend; }; /// \brief returns a pointer to new contact storage object extern "C" Buteo::StoragePlugin* createPlugin(const QString& aPluginName); /// \brief destroy contact storage object extern "C" void destroyPlugin(Buteo::StoragePlugin* storage); #endif //$(plugin.classname)_H buteo-syncfw-0.7.21+16.04.20151216.1/tools/templates/serverplugin_cpp.tmpl0000644000015600001650000002447412634332753026375 0ustar pbuserpbgroup00000000000000/* * YOUR LICENSE TEXT HERE */ \#include "$(plugin.classname).h" \#include \#include \#include extern "C" $(plugin.classname)* createPlugin(const QString& pluginName, const Buteo::SyncProfile& profile, Buteo::PluginCbInterface *cbInterface) { return new $(plugin.classname)(pluginName, profile, cbInterface); } extern "C" void destroyPlugin($(plugin.classname) *server) { delete server; } $(plugin.classname)::$(plugin.classname) (const QString& pluginName, const Buteo::Profile profile, Buteo::PluginCbInterface *cbInterface) : ServerPlugin (pluginName, profile, cbInterface), mAgent (0), mConfig (0), mTransport (0), mCommittedItems (0), mConnectionType (Sync::CONNECTIVITY_USB) { FUNCTION_CALL_TRACE; } $(plugin.classname)::~$(plugin.classname) () { FUNCTION_CALL_TRACE; closeSyncAgentConfig (); closeSyncAgent (); } bool $(plugin.classname)::init () { FUNCTION_CALL_TRACE; return true; } bool $(plugin.classname)::uninit () { FUNCTION_CALL_TRACE; closeSyncAgentConfig (); closeSyncAgent (); return true; } void $(plugin.classname)::abortSync (Sync::SyncStatus status) { FUNCTION_CALL_TRACE; MyProtocol::SyncState state = MyProtocol::ABORTED; if (status == Sync::SYNC_ERROR) state = MyProtocol::CONNECTION_ERROR; if (mAgent && mAgent->abort (state)) { LOG_DEBUG ("Signaling SyncML agent abort"); } else { handleSyncFinished (MyProtocol::ABORTED); } } bool $(plugin.classname)::cleanUp () { FUNCTION_CALL_TRACE; // Perform necessary cleanup return true; } Buteo::SyncResults $(plugin.classname)::getSyncResults () const { return mResults; } bool $(plugin.classname)::startListen () { FUNCTION_CALL_TRACE; LOG_DEBUG ("Starting listener"); bool listening = false; if (iCbInterface->isConnectivityAvailable (Sync::CONNECTIVITY_USB)) { // Start listening over USB } else if (iCbInterface->isConnectivityAvailable (Sync::CONNECTIVITY_BT)) { // Start listning over BT } else { mConnectionType = Sync::CONNECTIVITY_INTERNET; // Start listening over IP } return listening; } void $(plugin.classname)::stopListen () { FUNCTION_CALL_TRACE; // Stop all connections } void $(plugin.classname)::suspend () { FUNCTION_CALL_TRACE; // implementing suspend } void $(plugin.classname)::resume () { FUNCTION_CALL_TRACE; // implementing suspend } void $(plugin.classname)::connectivityStateChanged (Sync::ConnectivityType type, bool state) { FUNCTION_CALL_TRACE; LOG_DEBUG ("Connectivity state changed event " << type << ". Connectivity changed to " << state); if (type == Sync::CONNECTIVITY_USB) { // Only connectivity changes would be USB enabled/disabled // Handle USB connectivity state changes } } else if (type == Sync::CONNECTIVITY_BT) { // Handle BT connectivity state changes } else if (type == Sync::CONNECTIVITY_INTERNET) { // Handle IP connectivity state changes } } bool $(plugin.classname)::initSyncAgent () { FUNCTION_CALL_TRACE; LOG_DEBUG ("Creating SyncML agent..."); mAgent = new MyProtocol::SyncAgent (); return true; } void $(plugin.classname)::closeSyncAgent () { delete mAgent; mAgent = 0; } MyProtocol::SyncAgentConfig* $(plugin.classname)::initSyncAgentConfig () { FUNCTION_CALL_TRACE; // Initialize transport mTransport = new MyProtocol::init(); if (!mTransport) return 0; // Initialize any protocol stack configuration mConfig = new MyProtocol::SyncAgentConfig (); // Set the transport used for the configuration mConfig->setTransport (mTransport); return mConfig; } void $(plugin.classname)::closeSyncAgentConfig () { FUNCTION_CALL_TRACE; LOG_DEBUG ("Closing config..."); delete mConfig; mConfig = 0; } bool $(plugin.classname)::startNewSession () { FUNCTION_CALL_TRACE; if (!initSyncAgent () || !initSyncAgentConfig ()) return false; QObject::connect (mAgent, SIGNAL (stateChanged (MyProtocol::SyncState)), this, SLOT (handleStateChanged (MyProtocol::SyncState))); QObject::connect (mAgent, SIGNAL (syncFinished (MyProtocol::SyncState)), this, SLOT (handleSyncFinished (MyProtocol::SyncState))); QObject::connect (mAgent, SIGNAL (storageAccquired (QString)), this, SLOT (handleStorageAccquired (QString))); QObject::connect (mAgent, SIGNAL (itemProcessed (MyProtocol::ModificationType, MyProtocol::ModifiedDatabase, QString, QString, int)), this, SLOT (handleItemProcessed (MyProtocol::ModificationType, MyProtocol::ModifiedDatabase, QString, QString, int))); // Let the agent start listening for incoming connections if (mAgent->listen (*mConfig)) { LOG_DEBUG ("New session started"); return true; } else { return false; } } void $(plugin.classname)::handleStateChanged (MyProtocol::SyncState state) { FUNCTION_CALL_TRACE; LOG_DEBUG ("State changed. New state " << state); } void $(plugin.classname)::handleSyncFinished (MyProtocol::SyncState state) { FUNCTION_CALL_TRACE; LOG_DEBUG ("Sync finished with state " << state); bool errorStatus = true; switch (state) { case MyProtocol::SUSPENDED: case MyProtocol::ABORTED: case MyProtocol::SYNC_FINISHED: { generateResults (true); errorStatus = true; emit success (getProfileName (), QString::number (state)); break; } case MyProtocol::INTERNAL_ERROR: case MyProtocol::DATABASE_FAILURE: case MyProtocol::CONNECTION_ERROR: case MyProtocol::INVALID_SYNCML_MESSAGE: { generateResults (false); emit error (getProfileName (), QString::number (state), 0); break; } default: { LOG_CRITICAL ("Unexpected state change"); generateResults (false); emit error (getProfileName (), QString::number (state), 0); break; } } uninit (); } void $(plugin.classname)::handleItemProcessed (MyProtocol::ModificationType modificationType, MyProtocol::ModifiedDatabase modifiedDb, QString localDb, QString dbType, int committedItems) { FUNCTION_CALL_TRACE; LOG_DEBUG ("Modification type:" << modificationType); LOG_DEBUG ("ModificationType database:" << modifiedDb); LOG_DEBUG ("Local database:" << localDb); LOG_DEBUG ("Database type:" << dbType); LOG_DEBUG ("Committed items:" << committedItems); mCommittedItems++; if (!receivedItems.contains (localDb)) { ReceivedItemDetails details; details.added = details.modified = details.deleted = details.error = 0; details.mime = dbType; receivedItems[localDb] = details; } switch (modificationType) { case MyProtocol::MOD_ITEM_ADDED: { ++receivedItems[localDb].added; break; } case MyProtocol::MOD_ITEM_MODIFIED: { ++receivedItems[localDb].modified; break; } case MyProtocol::MOD_ITEM_DELETED: { ++receivedItems[localDb].deleted; break; } case MyProtocol::MOD_ITEM_ERROR: { ++receivedItems[localDb].error; break; } default: { Q_ASSERT (0); break; } } Sync::TransferDatabase db = Sync::LOCAL_DATABASE; if (modifiedDb == MyProtocol::MOD_LOCAL_DATABASE) db = Sync::LOCAL_DATABASE; else db = Sync::REMOTE_DATABASE; if (mCommittedItems == committedItems) { QMapIterator itr (receivedItems); while (itr.hasNext ()) { itr.next (); if (itr.value ().added) emit transferProgress (getProfileName (), db, Sync::ITEM_ADDED, itr.value ().mime, itr.value ().added); if (itr.value ().modified) emit transferProgress (getProfileName (), db, Sync::ITEM_MODIFIED, itr.value ().mime, itr.value ().modified); if (itr.value ().deleted) emit transferProgress (getProfileName (), db, Sync::ITEM_DELETED, itr.value ().mime, itr.value ().deleted); if (itr.value ().error) emit transferProgress (getProfileName (), db, Sync::ITEM_ERROR, itr.value ().mime, itr.value ().error); } mCommittedItems = 0; receivedItems.clear (); } } void $(plugin.classname)::generateResults (bool success) { FUNCTION_CALL_TRACE; mResults.setMajorCode (success ? Buteo::SyncResults::SYNC_RESULT_SUCCESS : Buteo::SyncResults::SYNC_RESULT_FAILED); mResults.setTargetId (mAgent->getResults().getRemoteDeviceId ()); const QMap* dbResults = mAgent->getResults ().getDatabaseResults (); if (dbResults->isEmpty ()) { LOG_DEBUG("No items transferred"); } else { QMapIterator itr (*dbResults); while (itr.hasNext ()) { itr.next (); const MyProtocol::DatabaseResults& r = itr.value (); Buteo::TargetResults targetResults( itr.key(), // Target name Buteo::ItemCounts (r.iLocalItemsAdded, r.iLocalItemsDeleted, r.iLocalItemsModified), Buteo::ItemCounts (r.iRemoteItemsAdded, r.iRemoteItemsDeleted, r.iRemoteItemsModified)); mResults.addTargetResults (targetResults); LOG_DEBUG("Items for" << targetResults.targetName () << ":"); LOG_DEBUG("LA:" << targetResults.localItems ().added << "LD:" << targetResults.localItems ().deleted << "LM:" << targetResults.localItems ().modified << "RA:" << targetResults.remoteItems ().added << "RD:" << targetResults.remoteItems ().deleted << "RM:" << targetResults.remoteItems ().modified); } } } buteo-syncfw-0.7.21+16.04.20151216.1/tools/templates/clientplugin_h.tmpl0000644000015600001650000001067312634332753026006 0ustar pbuserpbgroup00000000000000/* * YOUR LICENSE TEXT HERE * */ \#ifndef $(plugin.classname)_H \#define $(plugin.classname)_H // Include this header if you are using storage plugins \#include "SyncMLStorageProvider.h" \#include \#include /*! \brief Implementation for client plugin * */ class $plugin.classname : public Buteo::ClientPlugin { Q_OBJECT; public: /*! \brief Constructor * * @param aPluginName Name of this client plugin * @param aProfile Sync profile * @param aCbInterface Pointer to the callback interface */ $plugin.classname ( const QString& pluginName, const Buteo::SyncProfile& profile, Buteo::PluginCbInterface *cbInterface ); /*! \brief Destructor * * Call uninit before destroying the object. */ virtual ~$(plugin.classname)(); //! @see SyncPluginBase::init virtual bool init(); //! @see SyncPluginBase::uninit virtual bool uninit(); //! @see ClientPlugin::startSync virtual bool startSync(); //! @see SyncPluginBase::abortSync virtual void abortSync(Sync::SyncStatus status = Sync::SYNC_ABORTED); //! @see SyncPluginBase::getSyncResults virtual Buteo::SyncResults getSyncResults() const; //! @see SyncPluginBase::cleanUp virtual bool cleanUp(); public slots: //! @see SyncPluginBase::connectivityStateChanged virtual void connectivityStateChanged( Sync::ConnectivityType aType, bool aState ); protected slots: /*! * \brief state change slot for MyProtocol::SyncAgent::stateChanged signal * \param aState - new state */ void syncStateChanged (MyProtocol::SyncState state); /*! * \brief sync Finished slot for MyProtocol::SyncAgent::syncFinished signal * \param aState - final state */ void syncFinished (MyProtocol::SyncState state); /*! * \brief slot for MyProtocol::SyncAgent::storageAcquired signal * \param aMimeType - mimetype of the storage acquired. */ void storageAccquired (QString mimeType); /*! * \brief slot for MyProtocol::SyncAgent::itemProcessed signal * \param aModificationType - modification type * \param aModifiedDatabase - modified database . * \param aLocalDatabase - local database * \param aMimeType - mime type of the database * \param aCommittedItems - No. of items committed for this operation */ void receiveItemProcessed (MyProtocol::ModificationType modificationType, MyProtocol::ModifiedDatabase modifiedDatabase, QString localDatabase, QString mimeType, int committedItems ); private: bool initAgent(); void closeAgent(); bool initTransport(); void closeTransport(); bool initConfig(); void closeConfig(); void generateResults (bool successful); /*! \brief Resolves sync direction from current profile * * @param aInitiator Initiator of the sync * @return Resolved sync direction */ MyProtocol::SyncDirection resolveSyncDirection (const MyProtocol::SyncInitiator& Initiator); /*! \brief Resolves conflict resolution policy from current profile * * @param aInitiator Initiator of the sync * @return Resolved conflict resolution policy */ MyProtocol::ConflictResolutionPolicy resolveConflictResolutionPolicy (const MyProtocol::SyncInitiator& initiator); QMap mProperties; MyProtocol::SyncAgent* mAgent; MyProtocol::AgentConfig* mAgentConfig; MyTransport* mTransport; Buteo::SyncResults mResults; quint32 mCommittedItems; }; /*! \brief Creates $plugin.classname client plugin * * @param aPluginName Name of this client plugin * @param aProfile Profile to use * @param aCbInterface Pointer to the callback interface * @return Client plugin on success, otherwise NULL */ extern "C" $(plugin.classname)* createPlugin (const QString& pluginName, const Buteo::SyncProfile& profile, Buteo::PluginCbInterface *cbInterface ); /*! \brief Destroys $plugin.classname client plugin * * @param aServer $plugin.classname client plugin instance to destroy */ extern "C" void destroyPlugin( $plugin.classname *client ); \#endif // $plugin.classname buteo-syncfw-0.7.21+16.04.20151216.1/tools/templates/serverprofile_xml.tmpl0000644000015600001650000000246712634332753026553 0ustar pbuserpbgroup00000000000000 #if $profile.transport == 'usb' #else if $profile.transport == 'bt' #else if $profile.transport == 'ip' #end if #for $storagename, $props in $storages.items() #for $key, $value in $props.items() #end for #end for #for $key, $value in $extprops.items() #end for buteo-syncfw-0.7.21+16.04.20151216.1/tools/templates/clientplugin_cpp.tmpl0000644000015600001650000004555112634332753026344 0ustar pbuserpbgroup00000000000000/* * YOUR LICENSE TEXT HERE */ \#include "$(plugin.classname).h" \#include \#include extern "C" $(plugin.classname)* createPlugin(const QString& pluginName, const Buteo::SyncProfile& profile, Buteo::PluginCbInterface *cbInterface) { return new $(plugin.classname)(pluginName, profile, cbInterface); } extern "C" void destroyPlugin($(plugin.classname) *client) { delete client; } $(plugin.classname)::$(plugin.classname)(const QString& pluginName, const Buteo::SyncProfile& profile, Buteo::PluginCbInterface *cbInterface) : ClientPlugin(pluginName, profile, cbInterface), mAgent(0), mTransport(0), mConfig(0), mCommittedItems(0) { FUNCTION_CALL_TRACE; } $(plugin.classname)::~$(plugin.classname)() { FUNCTION_CALL_TRACE; } /** * \!brief Initialize the plugin for the actual sync to happen * This method will be invoked by the framework */ bool $(plugin.classname)::init() { FUNCTION_CALL_TRACE; // Fetch all the non-storage keys from the profile XML // The object iProfile is from @see ClientPlugin.h and holds all // the profile information related to this plugin mProperties = iProfile.allNonStorageKeys(); // Initialize protocol agent, transport and the protocol configuration // initConfig can also configure the plugin if necessary if (initAgent() && initTransport() && initConfig()) { return true; } else { // Uninitialize everything that was initialized before failure. // This method will unload the plugin uninit(); return false; } } /** * \!brief Uninitializes the plugin * This method will be invoked by the framework */ bool $(plugin.classname)::uninit() { FUNCTION_CALL_TRACE; // Close the protocol agent closeAgent(); // Close the configuration for the protocol as well as the plugin closeConfig(); // Close the transport closeTransport(); return true; } /** * \!brief Start the actual sync. This method will be invoked by the * framework */ bool $(plugin.classname)::startSync() { FUNCTION_CALL_TRACE; // If any of the initialization fails, abort sync if (mAgent == 0 || mConfig == 0 || mTransport == 0) { return false; } // Connects ths signal for syncFinished connect(mAgent, SIGNAL(syncFinished(MyProtocol::SyncState)), this, SLOT(syncFinished(MyProtocol::SyncState))); // The following signals are optional. But it is good to have such a // structure for your protocol plugin // This signal signifies a protocol state change. Using this signal, // the plugin can take an appropriate action connect(mAgent, SIGNAL(stateChanged(MyProtocol::SyncState)), this, SLOT(syncStateChanged(MyProtocol::SyncState))); // This signal is mainly useful for UI updates to display whenever // items are synchronized connect(mAgent, SIGNAL(itemProcessed(MyProtocol::ModificationType, MyProtocol::ModifiedDatabase, QString, QString,int)), this, SLOT(receiveItemProcessed(MyProtocol::ModificationType, MyProtocol::ModifiedDatabase, QString, QString,int))); // This signal is required for informing that storage plugins (like contacts/ // calendar etc.) have been acquired for sync connect(mAgent, SIGNAL(storageAccquired(QString)), this, SLOT(storageAccquired(QString))); // Set the transport over which the sync has to happen mConfig->setTransport(mTransport); // Start the actual sync return mAgent->startSync(*mConfig); } /** * \!brief Aborts sync. An abort can happen due to protocol errors, * connection failures or by the user (via a UI) */ void $(plugin.classname)::abortSync(Sync::SyncStatus status) { FUNCTION_CALL_TRACE; MyProtocol::SyncState state = MyProtocol::ABORTED; if (status == Sync::SYNC_ERROR) { state = MyProtocol::CONNECTION_ERROR; } if( mAgent ) { if( !mAgent->abort(state) ) { LOG_DEBUG( "Agent not active, aborting immediately" ); syncFinished(MyProtocol::ABORTED); } else { LOG_DEBUG( "Agent active, abort event posted" ); } } else { LOG_WARNING( "abortSync() called before init(), ignoring" ); } } /** * This method is required if a profile has been deleted. The plugin * has to implement the necessary cleanup (like temporary data, anchors etc.) */ bool $(plugin.classname)::cleanUp() { FUNCTION_CALL_TRACE; mProperties = iProfile.allNonStorageKeys(); initAgent(); initConfig(); bool retVal = mAgent->cleanUp(mConfig); closeAgent(); closeConfig(); return retVal; } /** * This method can be used to take an appropriate action whenever the * state changes in the protocol plugin 'MyProtocol' * It is upto the protocol plugin to implement such a signal */ void $(plugin.classname)::syncStateChanged(MyProtocol::SyncState state) { FUNCTION_CALL_TRACE; switch(state) { case MyProtocol::LOCAL_INIT: case MyProtocol::REMOTE_INIT: { emit syncProgressDetail(getProfileName(), Sync::SYNC_PROGRESS_INITIALISING); break; } case MyProtocol::SENDING_ITEMS: { emit syncProgressDetail(getProfileName(), Sync::SYNC_PROGRESS_SENDING_ITEMS); break; } case MyProtocol::RECEIVING_ITEMS: { emit syncProgressDetail(getProfileName(), Sync::SYNC_PROGRESS_RECEIVING_ITEMS); break; } case MyProtocol::FINALIZING: { emit syncProgressDetail(getProfileName(), Sync::SYNC_PROGRESS_FINALISING); break; } default: //do nothing break; }; } /** * This slot can be used to manage the result when sync is finished */ void $(plugin.classname)::syncFinished(MyProtocol::SyncState state) { FUNCTION_CALL_TRACE; switch(state) { // Modify the below states as per the supported states in your // protocol plugin case MyProtocol::INTERNAL_ERROR: case MyProtocol::AUTHENTICATION_FAILURE: case MyProtocol::DATABASE_FAILURE: case MyProtocol::CONNECTION_ERROR: case MyProtocol::INVALID_SYNCML_MESSAGE: case MyProtocol::UNSUPPORTED_SYNC_TYPE: case MyProtocol::UNSUPPORTED_STORAGE_TYPE: { generateResults( false ); emit error( getProfileName(), "", state); break; } case MyProtocol::SUSPENDED: case MyProtocol::ABORTED: case MyProtocol::SYNC_FINISHED: { generateResults( true ); emit success( getProfileName(), QString::number(aState)); break; } case MyProtocol::NOT_PREPARED: case MyProtocol::PREPARED: case MyProtocol::LOCAL_INIT: case MyProtocol::REMOTE_INIT: case MyProtocol::SENDING_ITEMS: case MyProtocol::RECEIVING_ITEMS: case MyProtocol::FINALIZING: case MyProtocol::SUSPENDING: default: { // do nothing. This state should never occur. Probably fail sync break; } } } /** * Slot for storage acquired. If your plugin does not use a separate * storage plugin, then there is no need to have this method */ void $(plugin.classname)::storageAccquired(QString mimeType) { FUNCTION_CALL_TRACE; LOG_DEBUG(" MimeType " << mimeType); emit accquiredStorage(mimeType); } /** * Slot for item processed signal. Keep track of all the added/modified/deleted * items here */ void $(plugin.classname)::receiveItemProcessed( MyProtocol::ModificationType modificationType, MyProtocol::ModifiedDatabase modifiedDatabase, QString localDatabase, QString mimeType, int committedItems) { FUNCTION_CALL_TRACE; LOG_DEBUG("Modification Type " << modificationType); LOG_DEBUG("Modification Database " << modifiedDatabase); LOG_DEBUG("Database " << localDatabase); LOG_DEBUG("MimeType " << mimeType); ++mCommittedItems; // receivedItems is coming from SyncPluginBase.h if(!receivedItems.contains(localDatabase)) { ReceivedItemDetails details; details.added = details.modified = details.deleted = details.error = 0; details.mime = mimeType; receivedItems[localDatabase] = details; } Sync::TransferDatabase db = Sync::LOCAL_DATABASE; switch (modificationType) { case MyProtocol::MOD_ITEM_ADDED: { ++receivedItems[localDatabase].added; break; } case MyProtocol::MOD_ITEM_MODIFIED: { ++receivedItems[localDatabase].modified; break; } case MyProtocol::MOD_ITEM_DELETED: { ++receivedItems[localDatabase].deleted; break; } case MyProtocol::MOD_ITEM_ERROR: { ++receivedItems[localDatabase].error; break; } default: { Q_ASSERT(0); break; } } if (modifiedDatabase == MyProtocol::MOD_LOCAL_DATABASE) { db = Sync::LOCAL_DATABASE; } else { db = Sync::REMOTE_DATABASE; } if( mCommittedItems == committedItems ) { QMapIterator itr(receivedItems); while( itr.hasNext() ) { itr.next(); if( itr.value().added ) emit transferProgress(getProfileName(), db, Sync::ITEM_ADDED, itr.value().mime, itr.value().added); if( itr.value().modified ) emit transferProgress(getProfileName(), db, Sync::ITEM_MODIFIED, itr.value().mime, itr.value().modified); if( itr.value().deleted ) emit transferProgress(getProfileName(), db, Sync::ITEM_DELETED, itr.value().mime, itr.value().deleted); if( itr.value().error ) emit transferProgress(getProfileName(), db, Sync::ITEM_ERROR, itr.value().mime, itr.value().error); } mCommittedItems = 0; receivedItems.clear(); } } /** * Initializes protocol agent */ bool $(plugin.classname)::initAgent() { FUNCTION_CALL_TRACE; LOG_DEBUG("Creating agent..."); mAgent = new MyProtocol::SyncAgent(); if (!mAgent) { LOG_DEBUG("Agent creation failed"); return false; } else { LOG_DEBUG("Agent created"); return true; } } /** * Closes protocol agent */ void $(plugin.classname)::closeAgent() { FUNCTION_CALL_TRACE; LOG_DEBUG("Destroying agent..."); if (iAgent) { delete iAgent; iAgent = 0; } } /** * Initializes transport */ bool $(plugin.classname)::initTransport() { FUNCTION_CALL_TRACE; LOG_DEBUG("Initiating transport..."); bool success = false; // Initialize your transport (could be any like HTTP, Obex, TCP, ...) // mTransport = new MyTransport (); return success; } /** * Closes transport */ void $(plugin.classname)::closeTransport() { FUNCTION_CALL_TRACE; LOG_DEBUG("Closing transport..."); delete mTransport; mTransport = NULL; LOG_DEBUG("Transport closed"); } /** * Initializes config. This configuration can be for both plugin * as well as for the protocol */ bool $(plugin.classname)::initConfig() { FUNCTION_CALL_TRACE; // The following initialization is a sample, but could vary depending // on your specific plugin and protocol LOG_DEBUG("Initiating config..."); // Get the storage names (only if you are using storage plugins) QStringList storageNames = iProfile.subProfileNames( Buteo::Profile::TYPE_STORAGE); if (storageNames.isEmpty()) { LOG_CRITICAL("No storages defined for profile, nothing to sync"); return false; } // Initialize the storages if (!iStorageProvider.init(&iProfile, this, iCbInterface, false)) { LOG_CRITICAL("Could not initialize storage provider"); return false; } mConfig = new MyProtocol::SyncAgentConfig; // ** Read configuration // Two configuration files are being read: first the Meego default config file, // and then possible external config file, which can be used to add additional // configuration, or replace some of the configuration of Meego default config. // ** Set up storage provider mConfig->setStorageProvider(&mStorageProvider); // ** Set up sync parameters QString transportType = mProperties[PROF_SYNC_TRANSPORT]; QString remoteDeviceName; if (transportType == HTTP_TRANSPORT) { // Do HTTP transport initialization } else if (transportType == OBEX_TRANSPORT) { //Do OBEX transport initialization } QString versionProp = iProperties[PROF_SYNC_PROTOCOL]; MyProtocol::SyncInitiator initiator = MyProtocol::INIT_CLIENT; // Resolve the sync direction, "1-way", "2-way", "1-way from client", "1-way from server" // Sync direction comes from profile XML MyProtocol::SyncDirection direction = resolveSyncDirection(initiator); MyProtocol::SyncMode syncMode(direction, initiator); // ** Set up auth parameters // You can fetch the username and password from ASSO MyProtocol::AuthType type = MyProtocol::AUTH_NONE; QString username; QString password; // Read the credentials from asso and if they are not found there, // read the credentials from profile XML (only for testing) if (transportType == HTTP_TRANSPORT) { type = MyProtocol::AUTH_BASIC; username = mProperties[PROF_USERID]; password = mProperties[PROF_PASSWD]; } else if (transportType == OBEX_TRANSPORT) { type = MyProtocol::AUTH_NONE; } mConfig->setAuthParams(type, username, password); // ** Set up other parameters // Conflict resolution policy comes from profile XML // Possible resolution policies: client wins, server wins MyProtocol::ConflictResolutionPolicy policy = resolveConflictResolutionPolicy(initiator); mConfig->setAgentProperty(MyProtocol::CONFLICTRESOLUTIONPOLICYPROP, QString::number(policy)); return true; } void $(plugin.classname)::closeConfig() { FUNCTION_CALL_TRACE; LOG_DEBUG("Closing config..."); delete mConfig; mConfig = NULL; if (!mStorageProvider.uninit()) { LOG_CRITICAL("Could not uninitialize storage provider"); } LOG_DEBUG("Config closed"); } /** * Returns the sync results */ Buteo::SyncResults $(plugin.classname)::getSyncResults() const { FUNCTION_CALL_TRACE; return mResults; } /** * Signal from the protocol engine about connectivity state changes */ void $(plugin.classname)::connectivityStateChanged(Sync::ConnectivityType aType, bool aState) { FUNCTION_CALL_TRACE; LOG_DEBUG("Received connectivity change event:" << aType << " changed to " << aState); } /** * Method to set the sync direction * This method logic need not be changed and should work as is */ MyProtocol::SyncDirection $(plugin.classname)::resolveSyncDirection(const MyProtocol::SyncInitiator& initiator) { FUNCTION_CALL_TRACE; Buteo::SyncProfile::SyncDirection directionFromProfile = iProfile.syncDirection(); MyProtocol::SyncDirection direction = MyProtocol::DIRECTION_TWO_WAY; if (initiator == MyProtocol::INIT_CLIENT) { if (directionFromProfile == Buteo::SyncProfile::SYNC_DIRECTION_FROM_REMOTE) { direction = MyProtocol::DIRECTION_FROM_SERVER; } else if (directionFromProfile == Buteo::SyncProfile::SYNC_DIRECTION_TO_REMOTE) { direction = MyProtocol::DIRECTION_FROM_CLIENT; } } else if (initiator == MyProtocol::INIT_SERVER) { if (directionFromProfile == Buteo::SyncProfile::SYNC_DIRECTION_FROM_REMOTE) { direction = MyProtocol::DIRECTION_FROM_CLIENT; } else if (directionFromProfile == Buteo::SyncProfile::SYNC_DIRECTION_TO_REMOTE) { direction = MyProtocol::DIRECTION_FROM_SERVER; } } return direction; } /** * Method to set the conflict resolution policy. * This method logic need not be changed and can be used as is */ MyProtocol::ConflictResolutionPolicy $(plugin.classname)::resolveConflictResolutionPolicy (const MyProtocol::SyncInitiator& initiator) { FUNCTION_CALL_TRACE; Buteo::SyncProfile::ConflictResolutionPolicy crPolicyFromProfile = iProfile.conflictResolutionPolicy(); /* In case if we have to resolve conflict the choice will be based on the user selection when * creating a sync profile , if to prefer local changes or remote changes. */ MyProtocol::ConflictResolutionPolicy crPolicy = MyProtocol::PREFER_LOCAL_CHANGES; switch (crPolicyFromProfile) { case Buteo::SyncProfile::CR_POLICY_PREFER_LOCAL_CHANGES: { LOG_DEBUG("Buteo::SyncProfile::CR_POLICY_PREFER_LOCAL_CHANGES"); crPolicy = MyProtocol::PREFER_LOCAL_CHANGES; break; } case Buteo::SyncProfile::CR_POLICY_PREFER_REMOTE_CHANGES: { LOG_DEBUG("Buteo::SyncProfile::CR_POLICY_PREFER_REMOTE_CHANGES"); crPolicy = MyProtocol::PREFER_REMOTE_CHANGES; break; } default: { break; } } return crPolicy; } /** * Method to generate the results once the sync is finished * This method logic need not be changed and can be used as is */ void $(plugin.classname)::generateResults (bool successful) { FUNCTION_CALL_TRACE; mResults.setMajorCode( successful ? Buteo::SyncResults::SYNC_RESULT_SUCCESS : Buteo::SyncResults::SYNC_RESULT_FAILED ); mResults.setTargetId(mAgent->getResults().getRemoteDeviceId()); const QMap* dbResults = mAgent->getResults().getDatabaseResults(); if (dbResults->isEmpty()) { LOG_DEBUG("No items transferred"); } else { QMapIterator i( *dbResults ); while ( i.hasNext() ) { i.next(); const MyProtocol::DatabaseResults& r = i.value(); Buteo::TargetResults targetResults( i.key(), // Target name Buteo::ItemCounts( r.iLocalItemsAdded, r.iLocalItemsDeleted, r.iLocalItemsModified ), Buteo::ItemCounts( r.iRemoteItemsAdded, r.iRemoteItemsDeleted, r.iRemoteItemsModified )); mResults.addTargetResults( targetResults ); LOG_DEBUG("Items for" << targetResults.targetName() << ":"); LOG_DEBUG("LA:" << targetResults.localItems().added << "LD:" << targetResults.localItems().deleted << "LM:" << targetResults.localItems().modified << "RA:" << targetResults.remoteItems().added << "RD:" << targetResults.remoteItems().deleted << "RM:" << targetResults.remoteItems().modified); } } } buteo-syncfw-0.7.21+16.04.20151216.1/tools/templates/storageprofile_xml.tmpl0000644000015600001650000000045212634332753026701 0ustar pbuserpbgroup00000000000000 #for $key, $value in $extprops.items() #end for buteo-syncfw-0.7.21+16.04.20151216.1/tools/templates/sample.tmpl0000644000015600001650000000031312634332753024251 0ustar pbuserpbgroup00000000000000This is a stupid test "for" \#ifdef $(plugin.name)_H /* Just want to dump something */ /** * Some more thing */ /** * \!brief writing a brief */ /** * @see somethign */ in quotes "$profile.name" buteo-syncfw-0.7.21+16.04.20151216.1/tools/templates/storageplugin_cpp.tmpl0000644000015600001650000003022112634332753026516 0ustar pbuserpbgroup00000000000000/* * YOUR LICENSE TEXT HERE * */ \#include \#include "$(plugin.classname).h" \#include "SimpleItem.h" extern "C" Buteo::StoragePlugin* createPlugin(const QString& aPluginName) { return new $(plugin.classname)(aPluginName); } extern "C" void destroyPlugin(Buteo::StoragePlugin* storage) { delete storage; } $(plugin.classname)::$(plugin.classname)(const QString& aPluginName) : Buteo::StoragePlugin(aPluginName), mBackend( 0 ) { FUNCTION_CALL_TRACE; } $(plugin.classname)::~$(plugin.classname)() { FUNCTION_CALL_TRACE; if(mBackend) { LOG_WARNING( "Uninit method has not been called!" ); delete mBackend; mBackend = 0; } } //////////////////////////////////////////////////////////////////////////////////// //////////// Below functions are derived from storage plugin /////////////// //////////////////////////////////////////////////////////////////////////////////// bool $(plugin.classname)::init( const QMap& aProperties ) { FUNCTION_CALL_TRACE; // Initialize the MyStorageBackend class and the necessary // initialization for the storage return true; } bool $(plugin.classname)::uninit() { FUNCTION_CALL_TRACE; if( mBackend != NULL) { backendUninitOk = mBackend->uninit(); delete mBackend; mBackend = NULL; } return (backendUninitOk); } bool $(plugin.classname)::getAllItems(QList &aItems) { FUNCTION_CALL_TRACE; bool operationStatus = false; QList list; if(mBackend) { list = mBackend->getAllDbIds(); if(list.size() != 0) { qDebug() << " Number of items retrieved from Contacts " << list.size(); aItems = getStoreList(list); } operationStatus = true; } return operationStatus; } bool $(plugin.classname)::getAllItemIds( QList& aItems ) { FUNCTION_CALL_TRACE; bool operationStatus = false; QList list; if(mBackend) { list = mBackend->getAllDbIds(); qDebug() << " Number of items retrieved from Contacts " << list.size(); foreach(DbId id , list) { aItems.append(QString::number(id)); } operationStatus = true; } return operationStatus; } bool $(plugin.classname)::getNewItems(QList &aItems ,const QDateTime& aTime) { FUNCTION_CALL_TRACE; bool operationStatus = false; QList list; if(mBackend) { qDebug() << "****** getNewItems : Added After: ********" << aTime; list = mBackend->getAllNewDbIds(aTime); if(list.size() != 0) { qDebug() << "New Item List Size is " << list.size(); aItems = getStoreList(list); } operationStatus = true; } return operationStatus; } bool $(plugin.classname)::getNewItemIds( QList& aNewItemIds, const QDateTime& aTime ) { FUNCTION_CALL_TRACE; bool operationStatus = false; QList list; if(mBackend) { qDebug() << "****** getNewItem Ids : Added After: ********" << aTime; list = mBackend->getAllNewDbIds(aTime); foreach(DbId id , list) { aNewItemIds.append(QString::number(id)); } operationStatus = true; } return operationStatus; } bool $(plugin.classname)::getModifiedItems( QList& aModifiedItems, const QDateTime& aTime ) { FUNCTION_CALL_TRACE; QList list; bool operationStatus = false; if(mBackend) { qDebug() << "******* getModifiedItems: From ********" << aTime; list = mBackend->getAllModifiedDbIds(aTime); aModifiedItems = getStoreList(list); operationStatus = true; } return operationStatus; } bool $(plugin.classname)::getModifiedItemIds( QList& aModifiedItemIds, const QDateTime& aTime ) { FUNCTION_CALL_TRACE; QList list; bool operationStatus = false; if(mBackend) { qDebug() << "******* getModifiedItemIds : From ********" << aTime; list = mBackend->getAllModifiedDbIds(aTime); foreach(QContactLocalId id , list) { aModifiedItemIds.append(QString::number(id)); } operationStatus = true; } return operationStatus; } bool $(plugin.classname)::getDeletedItemIds( QList& aDeletedItemIds, const QDateTime& aTime ) { FUNCTION_CALL_TRACE; LOG_DEBUG( "Getting deleted contacts since" << aTime ); // Logic for returning deleted item ids } Buteo::StorageItem* $(plugin.classname)::newItem() { FUNCTION_CALL_TRACE; return new SimpleItem; } QList $(plugin.classname)::getItems( const QStringList& aItemIdList ) { FUNCTION_CALL_TRACE; QList items; QMap textObj; QList ids; QStringListIterator itr( aItemIdList ); if( mBackend ) { foreach (const QString itr, aItemIdList) { ids.append( itr.toUInt() ); } mBackend->getDbObjects ( ids, textObj ); QMapIterator i(textObj); while( i.hasNext() ) { i.next(); if( !i.value().isEmpty() ) { SimpleItem *item = new SimpleItem; item->setId( QString::number( i.key() ) ); item->setType( iProperties[STORAGE_DEFAULT_MIME_PROP] ); item->write( 0, i.value().toUtf8() ); items.append( item ); } else { LOG_WARNING("Db object with id " << i.key() <<" doesn't exist!"); } } } return items; } Buteo::StorageItem* $(plugin.classname)::getItem( const QString& aItemId ) { FUNCTION_CALL_TRACE; if( !mBackend ) { return NULL; } SimpleItem* newItem = NULL; DbId id; id = aItemId.toUInt(); DbObject obj; mBackend->getDbObj( id, obj ); QDateTime creationTime = mBackend->getCreationTime( contact ); if(!contactData.isEmpty()) { newItem = new SimpleItem; newItem->setId(aItemId); newItem->setType(iProperties[STORAGE_DEFAULT_MIME_PROP]); newItem->write(0,contactData.toUtf8()); } else { LOG_WARNING( "Contact does not exist:" << aItemId ); } return newItem; } $(plugin.classname)::OperationStatus $(plugin.classname)::addItem(Buteo::StorageItem& aItem) { FUNCTION_CALL_TRACE; QList items; items.append( &aItem ); QList<$(plugin.classname)::OperationStatus> status = addItems( items ); return status.first(); } QList<$(plugin.classname)::OperationStatus> $(plugin.classname)::addItems( const QList& aItems ) { FUNCTION_CALL_TRACE; QList<$(plugin.classname)::OperationStatus> storageErrorList; QDateTime currentTime = QDateTime::currentDateTime(); if( !mBackend ) { for ( int i = 0; i < aItems.size(); i++) { storageErrorList.append(STATUS_ERROR); } return storageErrorList; } QList dbList; foreach(Buteo::StorageItem *item , aItems) { QByteArray data; item->read(0,item->getSize(),data); dbList.append(QString::fromUtf8(data.data())); } QList dbIdList; QMap dbObjErrorMap; bool retVal = mBackend->addContacts(contactsList, contactsErrorMap); else { QMapIterator i(contactsErrorMap); int j = 0; while (i.hasNext()) { i.next(); Buteo::StorageItem *item = aItems[j]; item->setId(QString::number(i.value().id)); $(plugin.classname)::OperationStatus status = mapErrorStatus(i.value().errorCode); if( status == STATUS_OK ) { // This item was successfully added, so let's add it to the snapshot iSnapshot.insert( i.value().id, currentTime ); } storageErrorList.append(status); j++; } } return storageErrorList; } $(plugin.classname)::OperationStatus $(plugin.classname)::modifyItem(Buteo::StorageItem& aItem) { FUNCTION_CALL_TRACE; $(plugin.classname)::OperationStatus status = STATUS_ERROR; if(mBackend ) { QString strID = aItem.getId(); QByteArray data; aItem.read( 0, aItem.getSize(), data ); QString Contact = QString::fromUtf8( data ); qDebug() << "Modifying an Item with data : " << Contact; qDebug() << "Modifying an Item with ID : " << strID; QContactManager::Error error = mBackend->modifyContact(strID ,Contact); status = mapErrorStatus(error); qDebug() << "After Modification String ID is " << strID; } return status; } QList<$(plugin.classname)::OperationStatus> $(plugin.classname)::modifyItems(const QList &aItems) { FUNCTION_CALL_TRACE; qDebug() << "Items to Modify :" << aItems.size(); QList<$(plugin.classname)::OperationStatus> statusList; if(mBackend) { QStringList dbObjList; QStringList dbIdList; // Convert the string form of the data to a class object foreach(Buteo::StorageItem *item , aItems) { QByteArray data; item->read(0,item->getSize(),data); dbObjList.append(QString::fromUtf8(data.data())); dbIdList.append(item->getId()); } statusList = mBackend->modifyDbObjs(dbObjList, dbIdList); return statusList; } $(plugin.classname)::OperationStatus $(plugin.classname)::deleteItem( const QString& aItemId ) { FUNCTION_CALL_TRACE; QList itemIds; itemIds.append( aItemId ); QList<$(plugin.classname)::OperationStatus> status = deleteItems( itemIds ); return status.first(); } QList<$(plugin.classname)::OperationStatus> $(plugin.classname)::deleteItems(const QList& aItemIds ) { FUNCTION_CALL_TRACE; QList<$(plugin.classname)::OperationStatus> statusList; QDateTime currentTime = QDateTime::currentDateTime(); QMap errorMap = mBackend->deleteDbObjs(aItemIds); if(errorMap.size() != aItemIds.size() ) { LOG_WARNING("Something wrong with batch deletion of database objects"); LOG_DEBUG("dbObjErrroMap.size() " << errorMap.size() ); LOG_DEBUG("dbObjList.size()" << aItemIds.size() ); for ( int i = 0; i < aItemIds.size(); i++) { statusList.append(STATUS_ERROR); } } else { QList itemIds; QList creationTimes; QList deletionTimes; QMapIterator i(errorMap); int j = 0; while( i.hasNext() ) { i.next(); $(plugin.classname)::OperationStatus status = mapErrorStatus( i.value().errorCode ); if( status == STATUS_OK ) { // This item was successfully deleted, so let's remove it from the snapshot and // add it to the deleted items. QString itemId = aItemIds[j]; itemIds.append( itemId ); creationTimes.append( iSnapshot.value( itemId.toUInt()) ); iSnapshot.remove( itemId.toUInt() ); deletionTimes.append( currentTime ); } statusList.append( status ); j++; } if( !itemIds.isEmpty() ) { iDeletedItems.addDeletedItems( itemIds, creationTimes, deletionTimes ); } } return statusList; } QList $(plugin.classname)::getStoreList(QList &aStrIDList) { FUNCTION_CALL_TRACE; QList itemList; if (mBackend != NULL) { QMap idDataMap; mBackend->getDbObjs(aStrIDList, idDataMap); QMapIterator iter(idDataMap); while (iter.hasNext()) { iter.next(); SimpleItem* item = convertStrObjToStorageItem(iter.key(), iter.value()); if (item != NULL) { itemList.append(item); } } } return itemList; } buteo-syncfw-0.7.21+16.04.20151216.1/tools/templates/serverplugin_h.tmpl0000644000015600001650000000404712634332753026034 0ustar pbuserpbgroup00000000000000/* * YOUR LICENSE TEXT HERE */ \#ifndef $(plugin.classname)_H \#define $(plugin.classname)_H \#include \#include \#include namespace Buteo { class ServerPlugin; class Profile; } class $(plugin.classname) : public Buteo::ServerPlugin { Q_OBJECT public: $(plugin.classname) (const QString& pluginName, const Buteo::Profile profile, Buteo::PluginCbInterface *cbInterface); virtual ~$(plugin.classname) (); virtual bool init (); virtual bool uninit (); virtual void abortSync (Sync::SyncStatus status = Sync::SYNC_ABORTED); virtual bool cleanUp (); virtual Buteo::SyncResults getSyncResults () const; virtual bool startListen (); virtual void stopListen (); virtual void suspend (); virtual void resume (); signals: void syncFinished (Sync::SyncStatus); public slots: virtual void connectivityStateChanged (Sync::ConnectivityType type, bool state); protected slots: void handleSyncFinished (DataSync::SyncState state); void handleStateChanged (DataSync::SyncState state); void handleItemProcessed (DataSync::ModificationType modificationType, DataSync::ModifiedDatabase modifiedDb, QString localDb, QString dbType, int committedItems); private: bool initSyncAgent (); void closeSyncAgent (); bool initTransport (); MyProtocol::SyncAgentConfig *initSyncAgentConfig(); void closeSyncAgentConfig (); bool startNewSession (); void generateResults (bool success); QMap mProperties; MyProtocol::SyncAgent* mAgent; MyProtocol::SyncAgentConfig* mConfig; MyProtocol::Transport* mTransport; Buteo::SyncResults mResults; qint32 mCommittedItems; Sync::ConnectivityType mConnectionType; }; \#endif // $(plugin.classname)_H buteo-syncfw-0.7.21+16.04.20151216.1/tools/templates/syncagentprofile_xml.tmpl0000644000015600001650000000026512634332753027232 0ustar pbuserpbgroup00000000000000 #for $key, $value in $props.items() #end for buteo-syncfw-0.7.21+16.04.20151216.1/COPYING0000644000015600001650000006352712634332753020007 0ustar pbuserpbgroup00000000000000GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/0000755000015600001650000000000012634333235021772 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/0000755000015600001650000000000012634333235023776 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/OOPServerPlugin.h0000644000015600001650000000360312634332753027160 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * * Author: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef OOPSERVERPLUGIN_H #define OOPSERVERPLUGIN_H #include #include namespace Buteo { class OOPServerPlugin : public ServerPlugin { Q_OBJECT public: OOPServerPlugin( const QString& aPluginName, const Profile& aProfile, PluginCbInterface* aCbInterface, QProcess& process ); virtual ~OOPServerPlugin(); virtual bool init(); virtual bool uninit(); virtual bool startListen(); virtual void stopListen(); virtual void suspend(); virtual void resume(); virtual bool cleanUp(); public slots: virtual void connectivityStateChanged( Sync::ConnectivityType aType, bool aState ); void onProcessError(QProcess::ProcessError error); void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); void onError(QString aProfileName, QString aMessage, int aErrorCode); void onSuccess(QString aProfileName, QString aMessage); private: bool iDone; }; } #endif // OOPSERVERPLUGIN_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/StorageItem.cpp0000644000015600001650000000304012634332753026726 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "StorageItem.h" using namespace Buteo; StorageItem::StorageItem() { } StorageItem::~StorageItem() { } void StorageItem::setId( const QString& aId ) { iId = aId; } const QString& StorageItem::getId() const { return iId; } void StorageItem::setParentId( const QString& aParentId ) { iParentId = aParentId; } const QString& StorageItem::getParentId() const { return iParentId; } void StorageItem::setType( const QString& aType ) { iType = aType; } const QString& StorageItem::getType() const { return iType; } void StorageItem::setVersion( const QString& aVersion ) { iVersion = aVersion; } const QString& StorageItem::getVersion() const { return iVersion; } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/DeletedItemsIdStorage.h0000644000015600001650000000722712634332753030335 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef DELETEDITEMSIDSTORAGE_H #define DELETEDITEMSIDSTORAGE_H #include #include namespace Buteo { /*! * \brief Persistent storage for storing deleted item IDs */ class DeletedItemsIdStorage{ public: /** * \brief Contructor */ DeletedItemsIdStorage(); /** * \brief Destructor */ ~DeletedItemsIdStorage(); /*! \brief Initializes backend * * * @param aDbFile Path to database to use as persistent storage * @return True on success, otherwise false */ bool init( const QString& aDbFile ); /*! \brief Uninitializes backend * * @return True on success, otherwise false */ bool uninit(); /*! \brief Retrieves persistently stored snapshot of item id's * * @param aItems Items of the snapshot * @param aCreationTimes Creation times of the items * @return True on success, otherwise false */ bool getSnapshot( QList& aItems, QList& aCreationTimes ) const; /*! \brief Store a snapshot of item id's persistently * * @param aItems Item id's to store * @param aCreationTimes Creation times of the items * @return True on success, otherwise false */ bool setSnapshot( const QList& aItems, const QList& aCreationTimes ); /*! \brief Adds a deleted item to backend * * @param aItem Item Id * @param aCreationTime Time when item was initially created * @param aDeleteTime Time of deletion */ void addDeletedItem( const QString& aItem, const QDateTime& aCreationTime, const QDateTime& aDeleteTime ); /*! \brief Adds deleted items to backend * * @param aItems Items Ids * @param aCreationTimes Times when the items were initially created * @param aDeleteTimes Times of deletion */ void addDeletedItems( const QList& aItems, const QList& aCreationTimes, const QList& aDeleteTimes ); /*! \brief Returns the deleted items after given time * * @param aItems Returned deleted items * @param aTime Items deleted after this time are considered deleted * @return True on success, otherwise false */ bool getDeletedItems( QList& aItems, const QDateTime& aTime ); protected: /** * \brief Checks whether snapshot table exists and creates it if needed * @return True on success, otherwise false */ bool ensureItemSnapshotExists(); /** * \brief Checks whether item id table exists and creates it if needed * @return True on success, otherwise false */ bool ensureDeletedItemsExists(); private: QSqlDatabase iDb; ///< Database handle QString iConnectionName; ///< Database connection ID string }; } #endif buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/ClientPlugin.h0000644000015600001650000000405612634332753026555 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef CLIENTPLUGIN_H #define CLIENTPLUGIN_H #include "SyncPluginBase.h" #include "SyncProfile.h" #include namespace Buteo { class PluginCbInterface; /*! \brief Base class for client plugins * */ class ClientPlugin : public SyncPluginBase { Q_OBJECT; public: /*! \brief Constructor * * @param aPluginName Name of this client plugin * @param aProfile Sync profile for the client * @param aCbInterface Pointer to the callback interface */ ClientPlugin( const QString& aPluginName, const SyncProfile& aProfile, PluginCbInterface* aCbInterface ); /*! \brief Destructor * * Call uninit before destroying the client plug-in. */ virtual ~ClientPlugin(); /*! \brief Starts synchronization * * Init must be called before calling this function. * @returns True on success, otherwise false */ virtual bool startSync() = 0; /*! \brief access to profile owned and used by this instance */ SyncProfile &profile() { return iProfile; } protected: //! Sync Profile Object that the plugin is currently operating on SyncProfile iProfile; private: }; } #endif // CLIENTPLUGIN_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/com.buteo.msyncd.baseplugin.xml0000644000015600001650000000502012634332753032040 0ustar pbuserpbgroup00000000000000 buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/StorageChangeNotifierPlugin.h0000644000015600001650000000407612634332753031553 0ustar pbuserpbgroup00000000000000#ifndef STORAGECHANGENOTIFIERPLUGIN_H #define STORAGECHANGENOTIFIERPLUGIN_H #include #include namespace Buteo { /*! \brief Implement this class to notify about changes in * a specific storage - contacts/calendar/sms, or even custom * ones like a facebook storage, if there's such a storage on * the device */ class StorageChangeNotifierPlugin : public QObject { Q_OBJECT public: /*! \brief constructor * @param aStorageName pass the well known sync storage name */ StorageChangeNotifierPlugin(const QString& aStorageName): iStorageName(aStorageName){} /*! \brief destructor */ virtual ~StorageChangeNotifierPlugin(){}; /*! \brief the name should be a well-known name * which buteo sync-fw knows about as a storage that * could be synced, for eg hcontacts for contacts storage * * @return well-known storage name */ virtual QString name() const = 0; /*! \brief Check if this storage has changes since the * last time it was asked for the same * * @return true if there are changes, false otherwise */ virtual bool hasChanges() const = 0; /*! Call this after the change notification has been * received, either via a signal or by calling hasChanges() * manually */ virtual void changesReceived() = 0; /*! \brief Enable listening to storage changes */ virtual void enable() = 0; /*! \brief Disable listening to storage changes * * @param disableAfterNextChange if set to true, then we * disable listening only if the next change occurs and * if enable() hasn't been called before this change is receivied. * This helps to get one notification in case items are added in batches. */ virtual void disable(bool disableAfterNextChange = false) = 0; Q_SIGNALS: /*! \brief emit this signal when there's a change in this * storage. It's upto the plug-in when and how frequently * it wants to emit this signal */ void storageChange(); protected: QString iStorageName; }; } #endif buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/SyncPluginBase.cpp0000644000015600001650000000361412634332753027400 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncPluginBase.h" #include "PluginCbInterface.h" using namespace Buteo; SyncPluginBase::SyncPluginBase( const QString &aPluginName, const QString &aProfileName, PluginCbInterface *aCbInterface ) : iCbInterface(aCbInterface), iPluginName(aPluginName), iProfileName(aProfileName) { // register various metatypes used in DBus arguments qRegisterMetaType("Sync::SyncStatus"); qRegisterMetaType("Sync::TransferDatabase"); qRegisterMetaType("Sync::TransferType"); qRegisterMetaType("Sync::ConnectivityType"); qRegisterMetaType("QProcess::ProcessError"); qRegisterMetaType("QProcess::ExitStatus"); } QString SyncPluginBase::getPluginName() const { return iPluginName; } QString SyncPluginBase::getProfileName() const { return iProfileName; } SyncResults SyncPluginBase::getSyncResults() const { return SyncResults(); } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/DeletedItemsIdStorage.cpp0000644000015600001650000002017512634332753030665 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "DeletedItemsIdStorage.h" #include "LogMacros.h" #include #include #include using namespace Buteo; DeletedItemsIdStorage::DeletedItemsIdStorage() { FUNCTION_CALL_TRACE; } DeletedItemsIdStorage::~DeletedItemsIdStorage() { FUNCTION_CALL_TRACE; } bool DeletedItemsIdStorage::init( const QString& aDbFile ) { FUNCTION_CALL_TRACE; static unsigned connectionNumber = 0; const QString connectionName = "deleteditems"; if( !iDb.isOpen() ) { iConnectionName = connectionName + QString::number( connectionNumber++ ); iDb = QSqlDatabase::addDatabase( "QSQLITE", iConnectionName ); iDb.setDatabaseName( aDbFile ); iDb.open(); } if( !iDb.isOpen() ) { LOG_CRITICAL( "Could open deleted items database file:" << aDbFile ); return false; } if( !ensureItemSnapshotExists() || !ensureDeletedItemsExists() ) { return false; } return true; } bool DeletedItemsIdStorage::uninit() { FUNCTION_CALL_TRACE; if( iDb.isOpen() ) { iDb.close(); iDb = QSqlDatabase(); QSqlDatabase::removeDatabase( iConnectionName ); } return true; } bool DeletedItemsIdStorage::getSnapshot( QList& aItems, QList& aCreationTimes ) const { FUNCTION_CALL_TRACE; const QString queryString( "SELECT * FROM snapshot" ); QSqlQuery query( iDb ); query.prepare( queryString ); if( !query.exec() ) { LOG_WARNING("Could not retrieve item snapshot: " << query.lastError() ); return false; } while( query.next() ) { aItems.append( query.value(0).toString() ); QDateTime t = query.value(1).toDateTime(); t.setTimeSpec(Qt::UTC); aCreationTimes.append( t.toLocalTime() ); } return true; } bool DeletedItemsIdStorage::setSnapshot( const QList& aItems, const QList& aCreationTimes ) { FUNCTION_CALL_TRACE; // Clear existing snapshot const QString deleteQueryString( "DELETE FROM snapshot" ); QSqlQuery deleteQuery( iDb ); deleteQuery.prepare( deleteQueryString ); if( !deleteQuery.exec() ) { LOG_WARNING("Could not clear item snapshot: " << deleteQuery.lastError() ); return false; } if( !aItems.isEmpty() ) { const QString insertQueryString( "INSERT INTO snapshot VALUES (:itemid, :creationtime)" ); bool supportsTransaction = iDb.transaction(); if(!supportsTransaction) { LOG_DEBUG("SQL Db doesn't support transactions"); } QSqlQuery insertQuery( iDb ); insertQuery.prepare( insertQueryString ); QVariantList itemIds; QVariantList creationTimes; for( int i = 0; i < aItems.count(); ++i ) { itemIds << aItems[i]; creationTimes << aCreationTimes[i].toUTC(); } insertQuery.addBindValue( itemIds ); insertQuery.addBindValue( creationTimes ); // Insert new snapshot if( insertQuery.execBatch() ) { LOG_DEBUG( itemIds.count() <<"items set to snapshot" ); } else { LOG_WARNING( "Could not set items snapshot" ); LOG_WARNING( "Reason:" << insertQuery.lastError() ); } if(supportsTransaction) { if( !iDb.commit() ) { LOG_WARNING("Error while commiting : " << iDb.lastError()); } } } return true; } void DeletedItemsIdStorage::addDeletedItem( const QString& aItem, const QDateTime& aCreationTime, const QDateTime& aDeleteTime ) { FUNCTION_CALL_TRACE; const QString queryString( "INSERT INTO deleteditems VALUES(:itemid, :creationtime, :deletetime)"); QSqlQuery query( iDb ); query.prepare( queryString ); query.bindValue( ":itemid", aItem ); query.bindValue( ":creationtime", aCreationTime.toUTC() ); query.bindValue( ":deletetime", aDeleteTime.toUTC() ); if( query.exec() ) { LOG_DEBUG( "Added item" << aItem << "as deleted at time" << aDeleteTime <<", creation time:" << aCreationTime ); } else { LOG_WARNING( "Could not add item as deleted:" << aItem ); LOG_WARNING( "Reason:" << query.lastError() ); } } void DeletedItemsIdStorage::addDeletedItems( const QList& aItems, const QList& aCreationTimes, const QList& aDeleteTimes ) { FUNCTION_CALL_TRACE; const QString queryString( "INSERT INTO deleteditems VALUES(:itemid, :creationtime, :deletetime)"); QSqlQuery query( iDb ); bool supportsTransaction = iDb.transaction(); if(!supportsTransaction) { LOG_DEBUG("SQL Db doesn't support transactions"); } query.prepare( queryString ); QVariantList items; QVariantList creationTimes; QVariantList deleteTimes; for( int i = 0; i < aItems.count(); ++i ) { items << aItems[i]; creationTimes << aCreationTimes[i].toUTC(); deleteTimes << aDeleteTimes[i].toUTC(); } query.addBindValue( items ); query.addBindValue( creationTimes ); query.addBindValue( deleteTimes ); if( query.execBatch() ) { LOG_DEBUG( "Added" << items.count() <<"items as deleted" ); } else { LOG_WARNING( "Could not add items as deleted" ); LOG_WARNING( "Reason:" << query.lastError() ); } if(supportsTransaction) { if( !iDb.commit() ) { LOG_WARNING("Error while commiting : " << iDb.lastError()); } } } bool DeletedItemsIdStorage::getDeletedItems( QList& aItems, const QDateTime& aTime ) { FUNCTION_CALL_TRACE; const QString queryString( "SELECT itemid FROM deleteditems WHERE creationtime < :creationtime AND deletetime > :deletetime" ); LOG_DEBUG(queryString); QSqlQuery query( iDb ); query.prepare( queryString ); query.bindValue( ":creationtime", aTime.toUTC() ); query.bindValue( ":deletetime", aTime.toUTC() ); if( !query.exec() ) { LOG_WARNING("Could not retrieve deleted items:" << query.lastError()); return false; } while( query.next() ) { aItems.append( query.value(0).toString() ); } LOG_DEBUG( "Found" << aItems.count() << "deleted items" ); return true; } bool DeletedItemsIdStorage::ensureItemSnapshotExists() { FUNCTION_CALL_TRACE; const QString queryString( "CREATE TABLE IF NOT EXISTS snapshot(itemid varchar(512) primary key, creationtime timestamp)" ); QSqlQuery query( iDb ); query.prepare( queryString ); if( !query.exec() ) { LOG_WARNING("Query failed: " << query.lastError()); return false; } else { LOG_DEBUG( "Ensured database table: snapshot" ); return true; } } bool DeletedItemsIdStorage::ensureDeletedItemsExists() { FUNCTION_CALL_TRACE; const QString queryString( "CREATE TABLE IF NOT EXISTS deleteditems(itemid varchar(512) primary key, creationtime timestamp, deletetime timestamp)" ); QSqlQuery query( iDb ); query.prepare( queryString ); if( !query.exec() ) { LOG_WARNING("Query failed: " << query.lastError()); return false; } else { LOG_DEBUG( "Ensured database table: deleteditems" ); return true; } } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/SyncPluginBase.h0000644000015600001650000001330112634332753027037 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCPLUGINBASE_H #define SYNCPLUGINBASE_H #include "SyncCommonDefs.h" #include "SyncResults.h" #include "ButeoPluginIface.h" #include #include #include #include #define DBUS_SERVICE_NAME_PREFIX "com.buteo.msyncd.plugin." #define DBUS_SERVICE_OBJ_PATH "/" namespace Buteo { class PluginCbInterface; /*! \brief Base class for client and server plugins. * */ class SyncPluginBase : public QObject { Q_OBJECT; public: /*! \brief Constructor * * @param aPluginName Name of this plugin * @param aProfileName Profile name * @param aCbInterface Pointer to the callback interface */ SyncPluginBase( const QString &aPluginName, const QString &aProfileName, PluginCbInterface *aCbInterface ); /*! \brief Returns the name of this plugin * * @return Name of the plugin */ QString getPluginName() const; /*! \brief Returns profile name * * @return Profile */ QString getProfileName() const; /*! \brief Initializes the plugin. * * It is recommended that the plugin should do not do any thread insecure * initializations inside constructor, instead it should be done inside * this method. * * @return True on success, otherwise false */ virtual bool init() = 0; /*! \brief Uninitializes the plugin * * @return True on success, otherwise false */ virtual bool uninit() = 0; /*! \brief Aborts synchronization * * Derived plug-in should implement this function and abort the sync * session that is in progress when this function is called. A final signal * (success or error) is still expected from the aborted session before * it terminates. */ virtual void abortSync(Sync::SyncStatus aStatus = Sync::SYNC_ABORTED) { Q_UNUSED(aStatus); } /*! \brief Cleans up any sync related stuff (e.g sync anchors etc) when the * profile is deleted * * Derived plug-in should implement this function and perform any cleanup * operations if required when the profile is deleted */ virtual bool cleanUp() = 0; /*! \brief Gets the results of the last completed sync session. * * This function should be called only after the sync session has finished, * after an error or success signal has been emitted. * The default implementation returns empty results, so derived plug-in * should implement this function. * @returns Sync results. */ virtual SyncResults getSyncResults() const; signals: /*! \brief Emitted when progress has been made in synchronization in * transferring items between local and remote database. * * @param aProfileName Name of the profile being synchronized * @param aDatabase Indicates if progress has been made to local or remote database * @param aType Type of progress made (item added, modified or deleted) * @param aMimeType Mime type of the processed item * @param aCommittedItems No. of items committed for this operation */ void transferProgress( const QString &aProfileName, Sync::TransferDatabase aDatabase, Sync::TransferType aType, const QString &aMimeType, int aCommittedItems ); /*! \brief Emitted when error has occurred in synchronization and it * cannot be continued. * * @param aProfileName Name of the profile being synchronized * @param aMessage Message data related to error event * @param aErrorCode Error code */ void error( const QString &aProfileName, const QString &aMessage, int aErrorCode ); /*! \brief Emitted when synchronization has been finished successfully. * * @param aProfileName Name of the profile being synchronized * @param aMessage Message data related to finish event */ void success( const QString &aProfileName, const QString &aMessage ); /*! \brief Emitted when a storage is requested and accquired. * * @param aMimeType Mime type of the processed item */ void accquiredStorage( const QString &aMimeType ); /*! \brief Emitted during Sync Progress to indicate the detail of the current ongoing sync * * @param aProfileName Profile Name * @param aProgressDetail Progress in Detail \see Sync::SyncProgressDetail */ void syncProgressDetail( const QString &aProfileName, int aProgressDetail); public slots: /*! \brief Slot that is invoked by sync framework when changes occur in * connectivity domains * * @param aType Connectivity domain * @param aState True if connectivity domain is now available, otherwise false */ virtual void connectivityStateChanged( Sync::ConnectivityType aType, bool aState ) = 0; protected: //! Pointer to synchronizer PluginCbInterface* iCbInterface; struct ReceivedItemDetails { int added; int deleted; int modified; int error; QString mime; }; QMap receivedItems; ButeoPluginIface *iOopPluginIface; private: QString iPluginName; QString iProfileName; }; } #endif // SYNCPLUGINBASE_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/buteoplugin-dbus-if-gen.sh0000755000015600001650000000043712634332753031000 0ustar pbuserpbgroup00000000000000# For client interface qdbusxml2cpp -v -c ButeoPluginIface -p ButeoPluginIface.h:ButeoPluginIface.cpp com.buteo.msyncd.baseplugin.xml # For server interface qdbusxml2cpp -c ButeoPluginIfaceAdaptor -a ButeoPluginIfaceAdaptor.h:ButeoPluginIfaceAdaptor.cpp com.buteo.msyncd.baseplugin.xml buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/ServerPlugin.cpp0000644000015600001650000000227312634332753027137 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ServerPlugin.h" using namespace Buteo; ServerPlugin::ServerPlugin( const QString& aPluginName, const Profile& aProfile, PluginCbInterface *aCbInterface ) : SyncPluginBase( aPluginName, aProfile.name(), aCbInterface ), iProfile( aProfile ) { } ServerPlugin::~ServerPlugin() { } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/SyncDBusInterface.h0000644000015600001650000003325312634332753027474 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCDBUSINTERFACE_H #define SYNCDBUSINTERFACE_H #include #include #include #include namespace Buteo { /*! * \brief Defines a D-Bus interface for the sync daemon. * * A XML file describing the interface can be generated from this class using * qdbuscpp2xml tool. This XML file can then be used to generate interface * adaptor and proxy classes using qdbusxml2cpp tool. */ class SyncDBusInterface : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "com.meego.msyncd") public: signals: /*! * \brief Notifies about a change in synchronization status. * * \param aProfileName Name of the profile used in the sync session whose * status has changed. * \param aStatus The new status. One of the following: * 0 (QUEUED): Sync request has been queued or was already in the * queue when sync start was requested. * 1 (STARTED): Sync session has been started. * 2 (PROGRESS): Sync session is progressing. * 3 (ERROR): Sync session has encountered an error and has been stopped, * or the session could not be started at all. * 4 (DONE): Sync session was successfully completed. * 5 (ABORTED): Sync session was aborted. * Statuses 3-5 are final, no more status changes will be sent from the * same sync session. * \param aMessage A message describing the status change in detail. This * can for example be shown to the user or written to a log * \param aMoreDetails * When aStatus is ERROR, this parameter contains a specific error code. * When aStatus is PROGRESS, this parameter contains more details about the progress */ void syncStatus(QString aProfileName, int aStatus, QString aMessage, int aMoreDetails); /*! \brief Notifies about progress in transferring items * * \param aProfileName Name of the profile where progress has occurred * \param aTransferDatabase Database to which transfer was made. One of the following: * 0 (LOCAL_DATABASE): Transfer was made from remote database to local database * 1 (REMOTE_DATABASE): Transfer was made from local database to remote database * \param aTransferType Type of transfer that was made. One of the following: * 0 (ADDITION): Addition was made to database * 1 (MODIFICATION): Modification was made to database * 2 (DELETION): Deletion was made to database * 3 (ERROR): Addition/Modification/Deletion was attempted, but it failed * \param aMimeType Mime type of the processed item * \param aCommittedItems No. of Items committed for this operation */ void transferProgress(QString aProfileName, int aTransferDatabase, int aTransferType , QString aMimeType, int aCommittedItems); /*! \brief Notifies about a change in profile. * * This signal is sent when the profile data is modified or when a profile * is added or deleted in msyncd. * \param aProfileName Name of the changed profile. * \param aChangeType * 0 (ADDITION): Profile was added. * 1 (MODIFICATION): Profile was modified. * 2 (DELETION): Profile was deleted. * \param aProfileAsXml Updated Profile Object is sent as xml * */ void signalProfileChanged(QString aProfileName, int aChangeType , QString aProfileAsXml); /*! \brief Notifies about Backup start. * * This signal is sent when the backup framework is backing the sync related * data */ void backupInProgress (); /*! \brief Notifies about Backup done. * * This signal is sent when the backup framework has completed backing the sync related * data. */ void backupDone(); /*! \brief Notifies about Restore start. * * This signal is sent when the backup framework is restoring the sync related * data */ void restoreInProgress(); /*! \brief Notifies about Restore Done. * * This signal is sent when the backup framework has restored the sync related * data */ void restoreDone(); /*! \brief Notifies about the availability of Results for a recent sync * * This signal is sent when the results are available for the last sync * only recent results ( SyncResults object) are sent as xml. * \param aProfileName Name of the profile for which results are available * \param aResultsAsXml results as an xml object */ void resultsAvailable(QString aProfileName , QString aResultsAsXml); /*! \brief Notifies sync status change for a set of account Ids * * This signal is sent when the status of a sync for a particular * account ID changes state Upon receiving this signal, the client is * expected to call the status method to check whether sync is * running/stopped for this account ID * * \param aAccountId The account IDs that changed state * \param aNewStatus The new status of sync for this account * \param aFailedReason This is an out parameter. In case the last sync has * failed, this will contain the code indicating the failure reason (TODO: * Define error codes). In case the last sync has not failed, this must be * ignored * \param aPrevSyncTime This is an out parameter. The previous sync time. * Invalid time is returned if there was no last sync. * \param aNextSyncTime This is an out parameter. The next sync time. */ void statusChanged(unsigned int aAccountId, int aNewStatus, int aFailedReason, qlonglong aPrevSyncTime, qlonglong aNextSyncTime); public slots: /*! * \brief Requests to starts synchronizing using a profile with the given * name. * * A status change signal (QUEUED, STARTED or ERROR) will be sent by the * daemon when the request is processed. If there is a sync already in * progress using the same resources that are needed by the given profile, * adds the sync request to a sync queue. Otherwise a sync session is * started immediately. * * \param aProfileId Id of the profile to use in sync. * \return True if a profile with the given id was found. Otherwise * false and no status change signals will follow from this request. */ virtual bool startSync(QString aProfileId) = 0; /*! * \brief Stops synchronizing the profile with the given name. * * If the sync request is still in queue and not yet started, the queue * entry is removed. * * \param aProfileId Name of the profile to stop syncing. */ virtual Q_NOREPLY void abortSync(QString aProfileId) = 0; /*! * \brief This function should be called when sync profile has to be deleted * * \param aProfileId Id of the profile to be deleted. * \return status of the remove operation */ virtual bool removeProfile(QString aProfileId) = 0; /*! * \brief This function should be called when sync profile information has * been changed by someone else than the sync daemon. * \note If profile does not exist prior to calling this function, a new profile file is created * * \param aProfileAsXml - Modified Profile Object as XML. * \return status of the update operation */ virtual bool updateProfile(QString aProfileAsXml) = 0; /*! * \brief Requests sync daemon to reserve storages for the caller. * * This function must be called if an external sync entity (like Active * Sync engine) wants to use the same storages that the sync daemon uses, * because concurrent access might lead to data corruption. If none of the * requested storages is currently used by the sync daemon, they are all * marked as reserved and can not be used by the daemon until the storages * are freed by calling releaseStorages. If one or more of the requested * storages is already in use, none of them is reserved. * * \param aStorageNames Names of the storages to reserve. * \return Success indicator. True if all requested storages were * successfully reserved. False if request failed and no storages were * reserved. */ virtual bool requestStorages(QStringList aStorageNames) = 0; /*! * \brief Releases the given storages so that sync daemon can again use * them freely. * * This function must be called after a successful requestStorages call, * when the reserved storages are not used by the caller any more. */ virtual Q_NOREPLY void releaseStorages(QStringList aStorageNames) = 0; /*! * \brief Gets the list of profile names of currently running syncs. * * \return Profile name list. */ virtual QStringList runningSyncs() = 0; /*! * \brief This function returns true if backup/restore in progress else * false. */ virtual bool getBackUpRestoreState() = 0; /*! * \brief sets the schedule for a profile * * This Function helps in setting a schedule to profile * this Function is to be used by the SyncInterface Client Library to * expose a user friendly API by abstracting the dbus mechanisms * involved with synchronizer * * \param aProfileId - Id of the profile for which schedule has to be set * \param aScheduleAsXml - Over the dbus the schedule object is transmitted as xml * * \return bool - status of the operation */ virtual bool setSyncSchedule(QString aProfileId , QString aScheduleAsXml) = 0; /*! * \brief Save SyncResults to log.xml file. * \param aProfileId to save result in corresponding file. * \param aSyncResults to save in the \code .log.xml. \endcode * \return status of the saveSyncResults */ virtual bool saveSyncResults(QString aProfileId,QString aSyncResults) = 0; /*! \brief To get lastSyncResult. * \param aProfileId * \return QString of syncResult. */ virtual QString getLastSyncResult(const QString &aProfileId) = 0; /*! \brief Gets all visible sync profiles. * * Returns all sync profiles that should be visible in sync ui. A profile * is visible if it has not been explicitly set as hidden. * \return The list of sync profiles. */ virtual QStringList allVisibleSyncProfiles() = 0; /*! \brief Gets a sync profile. * * Loads and merges also all sub-profiles that are referenced from the * main profile. Loads the log of finished synchronization sessions with * this profile. * \param aProfileId Name of the profile to get. * \return The sync profile as Xml string. */ virtual QString syncProfile(const QString &aProfileId) = 0; /*! \brief Gets a sync profiles matching the key-value. * * Loads and merges also all sub-profiles that are referenced from the * main profile. Loads the log of finished synchronization sessions with * this profile. * \param aKey Key to match for profile. * \param aValue Value to match for profile. * \return The sync profiles as Xml string list. */ virtual QStringList syncProfilesByKey(const QString &aKey, const QString &aValue) = 0; /*! \brief Gets a profiles matching the profile type. * * \param aType Type of the profile service/storage/sync. * \return The sync profile ids as string list. */ virtual QStringList syncProfilesByType(const QString &aType) = 0; /*! \brief Starts sync for all profiles matching the given account ID. * * \param aAccountId The account ID. */ virtual Q_NOREPLY void start(unsigned int aAccountId) = 0; /*! \brief Stops sync for all profiles matching the given account ID. * * \param aAccountId The account ID. */ virtual Q_NOREPLY void stop(unsigned int aAccountId) = 0; /*! \brief Returns the list of account IDs for which sync is ongoing * * \return The list of account IDs currectly syncing. */ virtual QList syncingAccounts() = 0; /*! \brief Returns the status of the sync for the given account Id * * \param aAccountId The account ID. * \param aFailedReason This is an out parameter. In case the last sync has * failed, this will contain the code indicating the failure reason (TODO: * Define error codes). In case the last sync has not failed, this must be * ignored * \param aPrevSyncTime This is an out parameter. The previous sync time. * Invalid time is returned if there was no last sync. * \param aNextSyncTime This is an out parameter. The next sync time. * \return The status of sync: 0 = Sync is running, * 1 = Last sync succeeded, 2 = last sync failed */ virtual int status(unsigned int aAccountId, int &aFailedReason, qlonglong &aPrevSyncTime, qlonglong &aNextSyncTime) = 0; }; } #endif // SYNCDBUSINTERFACE_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/ClientPlugin.cpp0000644000015600001650000000230012634332753027076 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ClientPlugin.h" using namespace Buteo; ClientPlugin::ClientPlugin( const QString& aPluginName, const SyncProfile& aProfile, PluginCbInterface *aCbInterface ) : SyncPluginBase( aPluginName, aProfile.name(), aCbInterface ), iProfile( aProfile ) { } ClientPlugin::~ClientPlugin() { } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/PluginManager.cpp0000644000015600001650000005215412634332753027246 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2014-2015 Jolla Ltd. * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "PluginManager.h" #include #include #include #include #include #include "StoragePlugin.h" #include "ServerPlugin.h" #include "ClientPlugin.h" #include "StorageChangeNotifierPlugin.h" #include "OOPClientPlugin.h" #include "OOPServerPlugin.h" #include "LogMacros.h" using namespace Buteo; PluginManager::PluginManager( const QString &aPluginPath ) : iPluginPath( aPluginPath ) { FUNCTION_CALL_TRACE; if (!iPluginPath.isEmpty() && !iPluginPath.endsWith('/')) { iPluginPath.append('/'); } loadPluginMaps( STORAGECHANGENOTIFIERMAP_LOCATION, iStorageChangeNotifierMaps ); loadPluginMaps( STORAGEMAP_LOCATION, iStorageMaps ); loadPluginMaps( CLIENTMAP_LOCATION, iClientMaps ); loadPluginMaps( SERVERMAP_LOCATION, iServerMaps ); loadOOPPluginMaps( OOP_CLIENT_SUFFIX, iOopClientMaps ); loadOOPPluginMaps( OOP_SERVER_SUFFIX, iOoPServerMaps ); } PluginManager::~PluginManager() { FUNCTION_CALL_TRACE; if( !iLoadedDlls.isEmpty() ) { LOG_WARNING( "Plugin manager: found" << iLoadedDlls.count() << "libraries not properly destroyed:" ); for( int i = 0; i < iLoadedDlls.count(); ++i ) { LOG_WARNING( iLoadedDlls[i].iPath ); } } } StorageChangeNotifierPlugin* PluginManager::createStorageChangeNotifier( const QString& aStorageName ) { FUNCTION_CALL_TRACE; if( ! iStorageChangeNotifierMaps.contains(aStorageName) ) { LOG_CRITICAL( "Library for the storage change notifier" << aStorageName << "does not exist" ); return NULL; } QString libraryName = iStorageChangeNotifierMaps.value(aStorageName); void* handle = loadDll( libraryName ); if( !handle ) { return NULL; } FUNC_CREATE_STORAGECHANGENOTIFIER storageChangeNotifierPointer = ( FUNC_CREATE_STORAGECHANGENOTIFIER)dlsym( handle, CREATE_FUNCTION.toStdString(). c_str() ); if( dlerror() ) { LOG_CRITICAL( "Library" << libraryName << "does not have a create function" ); unloadDll( libraryName ); return NULL; } StorageChangeNotifierPlugin* plugin = (*storageChangeNotifierPointer)(aStorageName); if( plugin ) { return plugin; } else { LOG_CRITICAL( "Could not create plugin instance" ); unloadDll( libraryName ); return NULL; } } void PluginManager::destroyStorageChangeNotifier( StorageChangeNotifierPlugin* aPlugin ) { FUNCTION_CALL_TRACE; if ( aPlugin == 0 ) return; QString storageName = aPlugin->name(); if ( ! iStorageChangeNotifierMaps.contains(storageName) ) { LOG_CRITICAL( "Library for the storage change notifier" << storageName << "does not exist" ); return; } QString path = iStorageChangeNotifierMaps.value(storageName); void* handle = getDllHandle( path ); if( !handle ) { LOG_CRITICAL( "Could not find library for storage plugin" << storageName ); return; } FUNC_DESTROY_STORAGECHANGENOTIFIER storageChangeNotifierDestroyer = (FUNC_DESTROY_STORAGECHANGENOTIFIER)dlsym( handle, DESTROY_FUNCTION.toStdString(). c_str()); if (dlerror()) { unloadDll( path ); LOG_CRITICAL( "Library" << path << "does not have a destroy function" ); } else { (*storageChangeNotifierDestroyer)(aPlugin); unloadDll( path ); } } StoragePlugin* PluginManager::createStorage( const QString& aPluginName ) { FUNCTION_CALL_TRACE; if( ! iStorageMaps.contains(aPluginName) ) { LOG_CRITICAL( "Library for the storage" << aPluginName << "does not exist" ); return NULL; } QString libraryName = iStorageMaps.value(aPluginName); void* handle = loadDll( libraryName ); if( !handle ) { return NULL; } FUNC_CREATE_STORAGE storagePointer = ( FUNC_CREATE_STORAGE)dlsym( handle, CREATE_FUNCTION.toStdString(). c_str() ); if( dlerror() ) { LOG_CRITICAL( "Library" << libraryName << "does not have a create function" ); unloadDll( libraryName ); return NULL; } StoragePlugin* plugin = (*storagePointer)(aPluginName); if( plugin ) { return plugin; } else { LOG_CRITICAL( "Could not create plugin instance" ); unloadDll( libraryName ); return NULL; } } void PluginManager::destroyStorage( StoragePlugin* aPlugin ) { FUNCTION_CALL_TRACE; if ( aPlugin == 0 ) return; QString pluginName = aPlugin->getPluginName(); if ( ! iStorageMaps.contains(pluginName)) { LOG_CRITICAL( "Library for the storage" << pluginName << "does not exist" ); return; } QString path = iStorageMaps.value(pluginName); void* handle = getDllHandle( path ); if( !handle ) { LOG_CRITICAL( "Could not find library for storage plugin" << pluginName ); return; } FUNC_DESTROY_STORAGE storageDestroyer = (FUNC_DESTROY_STORAGE)dlsym( handle, DESTROY_FUNCTION.toStdString(). c_str()); if (dlerror()) { unloadDll( path ); LOG_CRITICAL( "Library" << path << "does not have a destroy function" ); } else { (*storageDestroyer)(aPlugin); unloadDll( path ); } } ClientPlugin* PluginManager::createClient( const QString& aPluginName, const SyncProfile& aProfile, PluginCbInterface *aCbInterface) { FUNCTION_CALL_TRACE; if( ! iClientMaps.contains(aPluginName) && ! iOopClientMaps.contains(aPluginName)) { LOG_CRITICAL( "Library for the client" << aPluginName << "does not exist" ); return NULL; } if ( iClientMaps.contains(aPluginName) ) { QString libraryName = iClientMaps.value(aPluginName); void* handle = loadDll( libraryName ); if( !handle ) { return NULL; } FUNC_CREATE_CLIENT clientPointer = (FUNC_CREATE_CLIENT)dlsym( handle, CREATE_FUNCTION.toStdString().c_str() ); if( dlerror() ) { LOG_CRITICAL( "Library" << libraryName << "does not have a create function" ); unloadDll( libraryName ); return NULL; } ClientPlugin* plugin = (*clientPointer)( aPluginName, aProfile, aCbInterface ); if( plugin ) { return plugin; } else { LOG_CRITICAL( "Could not create plugin instance" ); unloadDll( libraryName ); return NULL; } } else if ( iOopClientMaps.contains(aPluginName) ) { // Start the out of process plugin QString exePath = iOopClientMaps.value( aPluginName ); QProcess* process = startOOPPlugin( exePath, aPluginName, aProfile.name() ); if( process == NULL ) { LOG_CRITICAL( "Could not start process" ); return NULL; } // Create the client plugin interface to talk to the process OOPClientPlugin* plugin = new OOPClientPlugin( aPluginName, aProfile, aCbInterface, *process ); if( plugin ) { return plugin; } else { LOG_CRITICAL( "Could not create plugin instance" ); // Stop the process plugin stopOOPPlugin( exePath ); return NULL; } } return NULL; } void PluginManager::destroyClient( ClientPlugin *aPlugin ) { FUNCTION_CALL_TRACE; if ( aPlugin == 0 ) return; QString pluginName = aPlugin->getPluginName(); if ( ! iClientMaps.contains(pluginName) && ! iOopClientMaps.contains(pluginName)) { LOG_CRITICAL( "Library for the client plugin" << pluginName << "does not exist" ); return; } if ( iClientMaps.contains(pluginName) ) { QString path = iClientMaps.value(pluginName); void* handle = getDllHandle( path ); if( !handle ) { LOG_CRITICAL( "Could not find library for client plugin" << pluginName ); return; } FUNC_DESTROY_CLIENT clientDestroyer = (FUNC_DESTROY_CLIENT)dlsym( handle, DESTROY_FUNCTION.toStdString().c_str() ); if (dlerror()) { unloadDll( path ); LOG_CRITICAL( "Library" << path << "does not have a destroy function" ); } else { (*clientDestroyer)(aPlugin); unloadDll( path ); } } else if ( iOopClientMaps.contains(pluginName) ) { // Stop the OOP process LOG_DEBUG( "Stopping the OOP process for " << pluginName); QString path = iOopClientMaps.value( pluginName ); stopOOPPlugin( path ); delete aPlugin; } } ServerPlugin* PluginManager::createServer( const QString& aPluginName, const Profile& aProfile, PluginCbInterface *aCbInterface ) { FUNCTION_CALL_TRACE; if( ! iServerMaps.contains(aPluginName) && ! iOoPServerMaps.contains(aPluginName) ) { LOG_CRITICAL( "Library for the server" << aPluginName << "does not exist" ); return NULL; } if ( iServerMaps.contains(aPluginName) ) { // Load the plugin library QString libraryName = iServerMaps.value(aPluginName); void* handle = loadDll( libraryName ); if( !handle ) { LOG_CRITICAL("Loading library failed"); return NULL; } FUNC_CREATE_SERVER serverPointer = (FUNC_CREATE_SERVER)dlsym( handle, CREATE_FUNCTION.toStdString().c_str()); if( dlerror() ) { LOG_CRITICAL( "Library" << libraryName << "does not have a create function" ); unloadDll( libraryName ); return NULL; } ServerPlugin* plugin = (*serverPointer)( aPluginName, aProfile, aCbInterface ); if( plugin ) { return plugin; } else { LOG_CRITICAL( "Could not create plugin instance" ); unloadDll( libraryName ); return NULL; } } else if ( iOoPServerMaps.contains(aPluginName) ) { // Start the Oop process plugin QString exePath = iOoPServerMaps.value( aPluginName ); QProcess* process = startOOPPlugin( exePath, aPluginName, aProfile.name() ); if( process == NULL ) { LOG_CRITICAL( "Could not start server plugin process" ); return NULL; } OOPServerPlugin* plugin = new OOPServerPlugin( aPluginName, aProfile, aCbInterface, *process ); if( plugin ) { return plugin; } else { LOG_CRITICAL( "Could not start server plugin" ); stopOOPPlugin( exePath ); return NULL; } } return NULL; } void PluginManager::destroyServer( ServerPlugin *aPlugin ) { FUNCTION_CALL_TRACE; if ( aPlugin == 0 ) return; QString pluginName = aPlugin->getPluginName(); if ( ! iServerMaps.contains(pluginName) && ! iOoPServerMaps.contains(pluginName) ) { LOG_CRITICAL( "Library for the server plugin" << pluginName << "does not exist" ); return; } if ( iServerMaps.contains(pluginName) ) { // Unload the server plugin library QString path = iServerMaps.value(pluginName); void* handle = getDllHandle( path ); if( !handle ) { LOG_CRITICAL( "Could not find library for server plugin" << pluginName ); return; } FUNC_DESTROY_SERVER serverDestroyer = (FUNC_DESTROY_SERVER)dlsym( handle, DESTROY_FUNCTION.toStdString().c_str()); if (dlerror()) { unloadDll( path ); LOG_CRITICAL( "Library" << path << "does not have a destroy function" ); } else { (*serverDestroyer)(aPlugin); unloadDll( path ); } } else if ( iOoPServerMaps.contains(pluginName) ) { // Stop the OOP server process QString path = iOoPServerMaps.value( pluginName ); stopOOPPlugin( path ); delete aPlugin; } } void PluginManager::loadPluginMaps( const QString aFilter, QMap& aTargetMap ) { FUNCTION_CALL_TRACE; QDir pluginDirectory( iPluginPath ); QStringList entries = pluginDirectory.entryList( QDir::Files ); QStringList::const_iterator listIterator = entries.constBegin(); while (listIterator != entries.constEnd()) { QString file = (*listIterator); if (!file.endsWith(aFilter)) { ++listIterator; continue; } // Remove filter from end file.chop( aFilter.length() ); // Remove lib file.remove(0, 3); aTargetMap[file] = iPluginPath + (*listIterator); ++listIterator; } } void PluginManager::loadOOPPluginMaps( const QString aFilter, QMap &aTargetMap ) { FUNCTION_CALL_TRACE; QDir pluginDirectory( iPluginPath + QDir::separator() + "oopp" + QDir::separator()); QStringList entries = pluginDirectory.entryList( QDir::Files ); QStringList::const_iterator listIterator = entries.constBegin(); while (listIterator != entries.constEnd()) { QString file = (*listIterator); if (!file.endsWith(aFilter)) { ++listIterator; continue; } // Remove filter from end file.chop( aFilter.length() ); aTargetMap[file] = iPluginPath + QDir::separator() + "oopp" + QDir::separator() + (*listIterator); ++listIterator; } } void* PluginManager::loadDll( const QString& aPath ) { FUNCTION_CALL_TRACE; iDllLock.lockForWrite(); void* handle = NULL; LOG_DEBUG( "Searching for DLL:" << aPath ); for( int i = 0; i < iLoadedDlls.count(); ++i ) { if( iLoadedDlls[i].iPath == aPath ) { LOG_DEBUG( "DLL already loaded:" << aPath ); handle = iLoadedDlls[i].iHandle; ++iLoadedDlls[i].iRefCount; } } if( !handle ) { LOG_DEBUG( "Opening DLL:" << aPath ); handle = dlopen( aPath.toStdString().c_str(), RTLD_NOW ); if( handle ) { DllInfo info; info.iPath = aPath; info.iHandle = handle; info.iRefCount = 1; iLoadedDlls.append( info ); } else { LOG_CRITICAL( "Cannot load library " << aPath <<":" << dlerror() ); } } iDllLock.unlock(); return handle; } void* PluginManager::getDllHandle( const QString& aPath ) { FUNCTION_CALL_TRACE; iDllLock.lockForRead(); void* handle = NULL; for( int i = 0; i < iLoadedDlls.count(); ++i ) { if( iLoadedDlls[i].iPath == aPath ) { handle = iLoadedDlls[i].iHandle; break; } } iDllLock.unlock(); return handle; } void PluginManager::unloadDll( const QString& aPath ) { FUNCTION_CALL_TRACE; iDllLock.lockForWrite(); for( int i = 0; i < iLoadedDlls.count(); ++i ) { if( iLoadedDlls[i].iPath == aPath ) { --iLoadedDlls[i].iRefCount; #if 0 KLUDGE: Due to NB #169065, crashes are seen in QMetaType if we unload DLLs. Hence commenting this code out for now. if( iLoadedDlls[i].iRefCount == 0 ) { dlclose( iLoadedDlls[i].iHandle ); iLoadedDlls.removeAt( i ); } #endif break; } } iDllLock.unlock(); } bool PluginManager::killProcess( const QString& aPath ) { const QFileInfo pluginFile(aPath); const QDir proc("/proc"); QStringList entries = proc.entryList(QDir::Dirs | QDir::NoDotAndDotDot); foreach (QString entry, entries) { int pid = entry.toInt(); if (pid) { QString exe = QFile::symLinkTarget(proc.filePath(entry).append("/exe")); if (!exe.isEmpty() && QFileInfo(exe) == pluginFile) { if (kill(pid, SIGTERM) == 0) { LOG_DEBUG( "Process" << pid << "has been killed"); return true; } else { LOG_WARNING("Failed to kill" << aPath << "[" << pid << "]" << strerror(errno)); return false; } } } } return false; } QProcess* PluginManager::startOOPPlugin( const QString &aPath, const QString& aPluginName, const QString& aProfileName) { FUNCTION_CALL_TRACE; if (killProcess(aPath)) { LOG_INFO( "Killed runaway plugin" << aProfileName); } LOG_DEBUG( "Starting oop plugin " << aProfileName); bool started = false; QStringList args; args << aPluginName << aProfileName; LOG_DEBUG( "Starting process " << aPath << " with plugin name " << aPluginName << " and profile name " << aProfileName); QProcess *process = new QProcess(); process->setProcessChannelMode( QProcess::ForwardedChannels ); process->start( aPath, args ); // This check is a workaround for the bug https://codereview.qt-project.org/#change,62897 QThread::sleep(1); // The process state does not seem be in a proper state immediately if( process->state() == QProcess::Starting ) { started = process->waitForStarted(); } if (started == true) { DllInfo info; info.iPath = aPath; info.iHandle = (void*)process; info.iRefCount = 1; iDllLock.lockForWrite(); iLoadedDlls.append( info ); iDllLock.unlock(); LOG_DEBUG( "Process " << process->program() << " started with pid " << process->pid() ); connect(process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(onProcessFinished(int,QProcess::ExitStatus))); return process; } else { LOG_CRITICAL( "Unable to start process plugin " << aPath << ". Error " << process->error()); delete process; return NULL; } } void PluginManager::stopOOPPlugin( const QString &aPath ) { FUNCTION_CALL_TRACE; QProcess *process = NULL; iDllLock.lockForWrite(); for( int i = 0; i < iLoadedDlls.size(); ++i ) { if( iLoadedDlls[i].iPath == aPath ) { process = (QProcess*)iLoadedDlls[i].iHandle; break; } } iDllLock.unlock(); // We must terminate the process outside of the locked section because // onProcessFinished handler below will want to acquire the same lock. // It will also schedule the deletion of the QProcess object. if (process) { process->terminate(); if (process->waitForFinished() == false) process->kill(); } } void PluginManager::onProcessFinished( int exitCode, QProcess::ExitStatus ) { FUNCTION_CALL_TRACE; QProcess* process = (QProcess*)sender(); LOG_DEBUG( "Process " << process->program() << " finished with exit code" << exitCode ); iDllLock.lockForWrite(); for( int i = 0; i < iLoadedDlls.size(); ++i ) { if( iLoadedDlls[i].iHandle == (void*)process ) { iLoadedDlls.removeAt( i ); break; } } iDllLock.unlock(); process->deleteLater(); } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/PluginCbImpl.h0000644000015600001650000000441612634332753026505 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * * Author: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef PLUGINCBIMPL_H #define PLUGINCBIMPL_H #include "PluginCbInterface.h" #include "PluginManager.h" #include "SyncDaemonProxy.h" #include "TransportTracker.h" namespace Buteo { class PluginCbImpl : public PluginCbInterface { public: PluginCbImpl(); ~PluginCbImpl(); /// \see PluginCbInterface::requestStorage virtual bool requestStorage(const QString &aStorageName, const SyncPluginBase *aCaller); /// \see PluginCbInterface::releaseStorage virtual void releaseStorage(const QString &aStorageName, const SyncPluginBase *aCaller); /// \see PluginCbInterface::createStorage virtual StoragePlugin* createStorage(const QString &aPluginName); /// \see PluginCbInterface::destroyStorage virtual void destroyStorage(StoragePlugin *aStorage); /// \see PluginCbInterface::isConnectivityAvailable virtual bool isConnectivityAvailable( Sync::ConnectivityType aType ); /// \see PluginCbInterface::getSyncProfileByRemoteAddress virtual Profile* getSyncProfileByRemoteAddress(const QString& aAddress); /// \see PluginCbInterface::getValue virtual QString getValue(const QString& aAddress, const QString& aKey); signals: //! emitted by releaseStorages call void storageReleased(); private: SyncDaemonProxy *imsyncIface; PluginManager iPluginManager; TransportTracker iTransportTracker; }; } #endif // PLUGINCBIMPL_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/PluginCbImpl.cpp0000644000015600001650000000617012634332753027037 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * * Author: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include "PluginCbImpl.h" #include "SyncPluginBase.h" #include "StoragePlugin.h" #include "SyncCommonDefs.h" #include "Profile.h" #include "LogMacros.h" using namespace Buteo; PluginCbImpl::PluginCbImpl() { FUNCTION_CALL_TRACE; imsyncIface = new SyncDaemonProxy( "com.meego.msyncd", "/synchronizer", QDBusConnection::sessionBus()); } PluginCbImpl::~PluginCbImpl() { FUNCTION_CALL_TRACE; if( imsyncIface ) { delete imsyncIface; imsyncIface = 0; } } bool PluginCbImpl::requestStorage(const QString &aStorageName, const SyncPluginBase */*aCaller*/) { FUNCTION_CALL_TRACE; bool requestResult = false; if( imsyncIface ) { QStringList storages; storages << aStorageName; QDBusReply gotStorages = imsyncIface->requestStorages(storages); if( !gotStorages.isValid() ) LOG_WARNING( "Request for storage " << aStorageName << " failed" ); else requestResult = gotStorages.value(); } else { LOG_WARNING( "msyncd dbus interface is NULL" ); } return requestResult; } void PluginCbImpl::releaseStorage(const QString &aStorageName, const SyncPluginBase */*aCaller*/) { FUNCTION_CALL_TRACE; if( imsyncIface ) { QStringList storages; storages << aStorageName; imsyncIface->releaseStorages( storages ); } else { LOG_WARNING( "msyncd dbus interface is NULL" ); } } StoragePlugin* PluginCbImpl::createStorage(const QString &aPluginName) { FUNCTION_CALL_TRACE; StoragePlugin* plugin = NULL; if ( !aPluginName.isEmpty() ) { plugin = iPluginManager.createStorage(aPluginName); } // no else return plugin; } void PluginCbImpl::destroyStorage(StoragePlugin *aStorage) { FUNCTION_CALL_TRACE; iPluginManager.destroyStorage(aStorage); } bool PluginCbImpl::isConnectivityAvailable( Sync::ConnectivityType aType ) { FUNCTION_CALL_TRACE; return iTransportTracker.isConnectivityAvailable(aType); } Profile* PluginCbImpl::getSyncProfileByRemoteAddress(const QString& aAddress) { Q_UNUSED( aAddress ); return NULL; } QString PluginCbImpl::getValue(const QString& aAddress, const QString& aKey) { Q_UNUSED( aAddress ); Q_UNUSED( aKey ); return ""; } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/OOPClientPlugin.cpp0000644000015600001650000001655512634332753027475 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * * Author: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include "OOPClientPlugin.h" #include "LogMacros.h" #include using namespace Buteo; OOPClientPlugin::OOPClientPlugin(const QString& aPluginName, const SyncProfile& aProfile, PluginCbInterface* aCbInterface, QProcess &aProcess ) : ClientPlugin( aPluginName, aProfile, aCbInterface ), iDone( false ) { FUNCTION_CALL_TRACE; // randomly-generated profile names cannot be registered // as dbus service paths due to being purely numeric. QString profileName = aProfile.name(); int numericIdx = profileName.indexOf(QRegExp("[0123456789]")); QString servicePath = numericIdx == 0 ? QString(QLatin1String("%1%2%3")) .arg(DBUS_SERVICE_NAME_PREFIX) .arg("profile-") .arg(profileName) : QString(QLatin1String("%1%2")) .arg(DBUS_SERVICE_NAME_PREFIX) .arg(profileName); // Initialise dbus for client iOopPluginIface = new ButeoPluginIface( servicePath, DBUS_SERVICE_OBJ_PATH, QDBusConnection::sessionBus() ); iOopPluginIface->setTimeout(60000); // one minute. // Chain the signals received over dbus connect(iOopPluginIface, SIGNAL(transferProgress(const QString &, Sync::TransferDatabase, Sync::TransferType, const QString &, int)), this, SIGNAL(transferProgress(const QString &, Sync::TransferDatabase, Sync::TransferType, const QString &, int))); connect(iOopPluginIface, SIGNAL(error(QString,QString,int)), this, SLOT(onError(QString,QString,int))); connect(iOopPluginIface, SIGNAL(success(QString,QString)), this, SLOT(onSuccess(QString,QString))); connect(iOopPluginIface, SIGNAL(accquiredStorage(const QString &)), this, SIGNAL(accquiredStorage(const QString &))); connect(iOopPluginIface,SIGNAL(syncProgressDetail(const QString &,int)), this ,SIGNAL(syncProgressDetail(const QString &,int))); // Handle the signals from the process connect(&aProcess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onProcessError(QProcess::ProcessError))); connect(&aProcess, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(onProcessFinished(int,QProcess::ExitStatus))); } OOPClientPlugin::~OOPClientPlugin() { if( iOopPluginIface ) { delete iOopPluginIface; iOopPluginIface = 0; } } bool OOPClientPlugin::init() { FUNCTION_CALL_TRACE; QDBusPendingReply reply = iOopPluginIface->init(); reply.waitForFinished(); if( !reply.isValid() ) { LOG_WARNING( "Invalid reply for init from plugin" ); return false; } return reply.value(); } bool OOPClientPlugin::uninit() { FUNCTION_CALL_TRACE; QDBusPendingReply reply = iOopPluginIface->uninit(); reply.waitForFinished(); if( !reply.isValid() ) { LOG_WARNING( "Invalid reply for uninit from plugin" ); return false; } return reply.value(); } bool OOPClientPlugin::startSync() { FUNCTION_CALL_TRACE; QDBusPendingReply reply = iOopPluginIface->startSync(); reply.waitForFinished(); if( !reply.isValid() ) { LOG_WARNING( "Invalid reply for startSync from plugin" ); return false; } return reply.value(); } void OOPClientPlugin::abortSync( Sync::SyncStatus aStatus ) { FUNCTION_CALL_TRACE; QDBusPendingReply reply = iOopPluginIface->abortSync( (uchar)aStatus ); reply.waitForFinished(); if( !reply.isValid() ) LOG_WARNING( "Invalid reply for connectivityStateChanged from plugin" ); } bool OOPClientPlugin::cleanUp() { FUNCTION_CALL_TRACE; QDBusPendingReply reply = iOopPluginIface->cleanUp(); reply.waitForFinished(); if( !reply.isValid() ) { LOG_WARNING( "Invalid reply for cleanUp from plugin" ); return false; } return reply.value(); } SyncResults OOPClientPlugin::getSyncResults() const { FUNCTION_CALL_TRACE; SyncResults errorSyncResult( QDateTime(), SyncResults::SYNC_RESULT_INVALID, SyncResults::SYNC_RESULT_INVALID ); QDBusPendingReply reply = iOopPluginIface->getSyncResults(); reply.waitForFinished(); if( !reply.isValid() ) { LOG_WARNING( "Invalid reply for getSyncResults from plugin" ); return errorSyncResult; } QString resultAsXml = reply.value(); QDomDocument doc; if( doc.setContent(resultAsXml, true) ) { SyncResults syncResult( doc.documentElement() ); return syncResult; } else { LOG_CRITICAL( "Invalid sync results returned from plugin" ); return errorSyncResult; } } void OOPClientPlugin::connectivityStateChanged( Sync::ConnectivityType aType, bool aState ) { FUNCTION_CALL_TRACE; QDBusPendingReply reply = iOopPluginIface->connectivityStateChanged( aType, aState ); reply.waitForFinished(); if( !reply.isValid() ) LOG_WARNING( "Invalid reply for connectivityStateChanged from plugin" ); } void OOPClientPlugin::onProcessError( QProcess::ProcessError error ) { if( !iDone ) { onError( iProfile.name(), "Plugin process error:" + QString::number(error), Sync::SYNC_PLUGIN_ERROR ); } } void OOPClientPlugin::onProcessFinished( int exitCode, QProcess::ExitStatus exitStatus ) { if ( !iDone ) { if( (exitCode != 0) || (exitStatus != QProcess::NormalExit) ) { onError( iProfile.name(), "Plugin process exited with error code " + QString::number(exitCode) + " and status " + QString::number(exitStatus), Sync::SYNC_PLUGIN_ERROR ); } else { onError( iProfile.name(), "Plugin process exited unexpectedly", Sync::SYNC_PLUGIN_ERROR ); } } } void OOPClientPlugin::onError(QString aProfileName, QString aMessage, int aErrorCode) { if ( !iDone ) { iDone = true; emit error(aProfileName, aMessage, aErrorCode); } } void OOPClientPlugin::onSuccess(QString aProfileName, QString aMessage) { if ( !iDone ) { iDone = true; emit success(aProfileName, aMessage); } } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/ButeoPluginIface.h0000644000015600001650000000753512634332753027352 0ustar pbuserpbgroup00000000000000/* * This file was generated by qdbusxml2cpp version 0.8 * Command line was: qdbusxml2cpp -v -c ButeoPluginIface -p ButeoPluginIface.h:ButeoPluginIface.cpp com.buteo.msyncd.baseplugin.xml * * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). * * This is an auto-generated file. * Do not edit! All changes made to it will be lost. */ #ifndef BUTEOPLUGINIFACE_H_1391581887 #define BUTEOPLUGINIFACE_H_1391581887 #include #include #include #include #include #include #include #include #include /* * Proxy class for interface com.buteo.msyncd.baseplugin */ class ButeoPluginIface: public QDBusAbstractInterface { Q_OBJECT public: static inline const char *staticInterfaceName() { return "com.buteo.msyncd.baseplugin"; } public: ButeoPluginIface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0); ~ButeoPluginIface(); public Q_SLOTS: // METHODS inline QDBusPendingReply<> abortSync(uchar aStatus) { QList argumentList; argumentList << QVariant::fromValue(aStatus); return asyncCallWithArgumentList(QLatin1String("abortSync"), argumentList); } inline QDBusPendingReply cleanUp() { QList argumentList; return asyncCallWithArgumentList(QLatin1String("cleanUp"), argumentList); } inline QDBusPendingReply<> connectivityStateChanged(int aType, bool aState) { QList argumentList; argumentList << QVariant::fromValue(aType) << QVariant::fromValue(aState); return asyncCallWithArgumentList(QLatin1String("connectivityStateChanged"), argumentList); } inline QDBusPendingReply getSyncResults() { QList argumentList; return asyncCallWithArgumentList(QLatin1String("getSyncResults"), argumentList); } inline QDBusPendingReply init() { QList argumentList; return asyncCallWithArgumentList(QLatin1String("init"), argumentList); } inline QDBusPendingReply<> resume() { QList argumentList; return asyncCallWithArgumentList(QLatin1String("resume"), argumentList); } inline QDBusPendingReply startListen() { QList argumentList; return asyncCallWithArgumentList(QLatin1String("startListen"), argumentList); } inline QDBusPendingReply startSync() { QList argumentList; return asyncCallWithArgumentList(QLatin1String("startSync"), argumentList); } inline QDBusPendingReply<> stopListen() { QList argumentList; return asyncCallWithArgumentList(QLatin1String("stopListen"), argumentList); } inline QDBusPendingReply<> suspend() { QList argumentList; return asyncCallWithArgumentList(QLatin1String("suspend"), argumentList); } inline QDBusPendingReply uninit() { QList argumentList; return asyncCallWithArgumentList(QLatin1String("uninit"), argumentList); } Q_SIGNALS: // SIGNALS void accquiredStorage(const QString &aMimeType); void error(const QString &aProfileName, const QString &aMessage, int aErrorCode); void newSession(const QString &aDestination); void success(const QString &aProfileName, const QString &aMessage); void syncProgressDetail(const QString &aProfileName, int aProgressDetail); void transferProgress(const QString &aProfileName, Sync::TransferDatabase aDatabase, Sync::TransferType aType, const QString &aMimeType, int aCommittedItems); }; namespace com { namespace buteo { namespace msyncd { typedef ::ButeoPluginIface baseplugin; } } } #endif buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/ServerPlugin.h0000644000015600001650000000477112634332753026611 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SERVERPLUGIN_H_4399340943904399349843 #define SERVERPLUGIN_H_4399340943904399349843 #include "SyncPluginBase.h" #include "Profile.h" #include #include namespace Buteo { /*! \brief Base class for server plugins * */ class ServerPlugin : public SyncPluginBase { Q_OBJECT; public: /*! \brief Constructor * * @param aPluginName Name of this plugin * @param aProfile Server profile * @param aCbInterface Pointer to the callback interface */ ServerPlugin( const QString& aPluginName, const Profile& aProfile, PluginCbInterface* aCbInterface ); /*! \brief Destructor * */ virtual ~ServerPlugin(); /*! \brief Start listening for sync requests. * * Init must me called before this function. * @return True on success, otherwise false */ virtual bool startListen() = 0; /*! \brief Stop listening for sync requests * */ virtual void stopListen() = 0; /*! \brief Suspend activity * * Implement this if upon being asked to suspend for some reason, any ongoing * activity can be suspended */ virtual void suspend() = 0; /*! \brief Resume suspended activity * */ virtual void resume() = 0; signals: /*! \brief Signal sent when a new sync session is received by the server * * @param aDestination Sync destination address, for example BT address * or URL. */ void newSession(const QString &aDestination); protected: //! Profile Object that the server plugin operates on Profile iProfile; }; } #endif //SERVERPLUGIN_H_4399340943904399349843 buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/plugin_main.cpp0000644000015600001650000000724012634332753027013 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins package * * Copyright (C) 2013-2015 Jolla Ltd. and/or its subsidiary(-ies). * * Author: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include #include "PluginServiceObj.h" #include "ButeoPluginIfaceAdaptor.h" #include "LogMacros.h" #define DBUS_SERVICE_NAME_PREFIX "com.buteo.msyncd.plugin." #define DBUS_SERVICE_OBJ_PATH "/" int main( int argc, char** argv ) { QCoreApplication app( argc, argv ); // We obtain the plugin name and the profile name from cmdline // One way to pass the arguments is via cmdline, the other way is // to use the method setPluginParams() dbus method. But setting // cmdline arguments is probably cleaner if( (argc != 3) || (argv[1] == NULL) || (argv[2] == NULL) ) { LOG_FATAL( "Plugin name and profile name are not obtained from cmdline" ); } QString pluginName = QString( argv[1] ); QString profileName = QString( argv[2] ); #ifndef CLASSNAME LOG_FATAL( "CLASSNAME value not defined in project file" ); #endif PluginServiceObj *serviceObj = new PluginServiceObj( profileName, pluginName ); if( !serviceObj ) { LOG_FATAL( "Unable to create the service adaptor object" ); } new ButeoPluginIfaceAdaptor( serviceObj ); // randomly-generated profile names cannot be registered // as dbus service paths due to being purely numeric. int numericIdx = profileName.indexOf(QRegExp("[0123456789]")); QString servicePath = numericIdx == 0 ? QString(QLatin1String("%1%2%3")) .arg(DBUS_SERVICE_NAME_PREFIX) .arg("profile-") .arg(profileName) : QString(QLatin1String("%1%2")) .arg(DBUS_SERVICE_NAME_PREFIX) .arg(profileName); int retn; LOG_DEBUG( "attempting to register dbus service:" << servicePath ); QDBusConnection connection = QDBusConnection::sessionBus(); if( connection.registerService( servicePath ) == true ) { if( connection.registerObject(DBUS_SERVICE_OBJ_PATH, serviceObj) == true ) { LOG_DEBUG( "Plugin " << pluginName << " with profile " << profileName << " registered at dbus " << DBUS_SERVICE_NAME_PREFIX + profileName << " and path " << DBUS_SERVICE_OBJ_PATH ); // TODO: Should any unix signals be handled? retn = app.exec(); connection.unregisterObject(DBUS_SERVICE_OBJ_PATH); } else { LOG_WARNING("Unable to register dbus object for service" << servicePath << ", terminating."); retn = -2; } connection.unregisterService(servicePath); } else { LOG_WARNING("Unable to register dbus service" << servicePath << ", terminating."); retn = -1; } delete serviceObj; return retn; } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/PluginCbInterface.h0000644000015600001650000000740312634332753027503 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef PLUGINCBINTERFACE_H #define PLUGINCBINTERFACE_H #include #include "SyncPluginBase.h" namespace Buteo { class StoragePlugin; class Profile; /*! \brief Interface which client and server plugins can use to communicate with * synchronization daemon */ class PluginCbInterface { public: virtual ~PluginCbInterface() {} /*! \brief Tries to reserve the given storage to the caller. * * Server plug-ins must reserve storages before using them. For client * plug-ins this is done automatically by the sync daemon when sync starts, * based on profiles. * Release must be called when the successfully requested storage is not * needed anymore. * @param aStorageName Name of the storage backend to reserve. * @param aCaller Object calling this function * @return Success indicator */ virtual bool requestStorage(const QString &aStorageName, const SyncPluginBase *aCaller) = 0; /*! \brief Releases the given storage. * * Call this function after a storage requested with requestStorage is not * needed anymore by the caller. * \param aStorageName Name of the storage backend to release. * \param aCaller Object calling this function. */ virtual void releaseStorage(const QString &aStorageName, const SyncPluginBase *aCaller) = 0; /*! \brief Creates a storage plug-in instance. * * Server plug-ins must reserve the storage backend before creating a * plug-in that uses it. Otherwise simultaenous access of the same backend * may lead to inconsistent state. * \param aPluginName Name of the storage plug-in to create. * \return Created plug-in instance. NULL if failed. */ virtual StoragePlugin* createStorage(const QString &aPluginName) = 0; /*! \brief Destroys the given storage plug-in instance. * * \param aStorage Storage plug-in to destroy. */ virtual void destroyStorage(StoragePlugin *aStorage) = 0; /*! \brief Returns whether connectivity domain is available * * @param aType Type of connectivity domain * @return True if connectivity domain is available, otherwise false */ virtual bool isConnectivityAvailable( Sync::ConnectivityType aType ) = 0; /*! \brief tries to fetch a profile object based on the remote party's * address (BT address for eg) * * @param aAddress remote party's address * @return profile object - to be deleted by caller */ virtual Profile* getSyncProfileByRemoteAddress(const QString& aAddress) = 0; /*! \brief Get a value for a property describing the remote device * * @param aAddress remote device's address * @param aKey profile key identifying the property * @return value for the property */ virtual QString getValue(const QString& aAddress, const QString& aKey) = 0; }; } #endif buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/ButeoPluginIfaceAdaptor.h0000644000015600001650000001035612634332753030660 0ustar pbuserpbgroup00000000000000/* * This file was generated by qdbusxml2cpp version 0.8 * Command line was: qdbusxml2cpp -c ButeoPluginIfaceAdaptor -a ButeoPluginIfaceAdaptor.h:ButeoPluginIfaceAdaptor.cpp com.buteo.msyncd.baseplugin.xml * * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). * * This is an auto-generated file. * This file may have been hand-edited. Look for HAND-EDIT comments * before re-generating it. */ #ifndef BUTEOPLUGINIFACEADAPTOR_H_1391669724 #define BUTEOPLUGINIFACEADAPTOR_H_1391669724 #include #include QT_BEGIN_NAMESPACE class QByteArray; template class QList; template class QMap; class QString; class QStringList; class QVariant; QT_END_NAMESPACE /* * Adaptor class for interface com.buteo.msyncd.baseplugin */ class ButeoPluginIfaceAdaptor: public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "com.buteo.msyncd.baseplugin") Q_CLASSINFO("D-Bus Introspection", "" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "") public: ButeoPluginIfaceAdaptor(QObject *parent); virtual ~ButeoPluginIfaceAdaptor(); public: // PROPERTIES public Q_SLOTS: // METHODS void abortSync(uchar aStatus); bool cleanUp(); void connectivityStateChanged(int aType, bool aState); QString getSyncResults(); bool init(); void resume(); bool startListen(); bool startSync(); void stopListen(); void suspend(); bool uninit(); Q_SIGNALS: // SIGNALS void accquiredStorage(const QString &aMimeType); void error(const QString &aProfileName, const QString &aMessage, int aErrorCode); void newSession(const QString &aDestination); void success(const QString &aProfileName, const QString &aMessage); void syncProgressDetail(const QString &aProfileName, int aProgressDetail); void transferProgress(const QString &aProfileName, int aTransferDatabase, int aTransferType, const QString &aMimeType, int aCommittedItems); }; #endif buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/StorageItem.h0000644000015600001650000000660112634332753026401 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef STORAGEITEM_H #define STORAGEITEM_H #include #include #include namespace Buteo { /*! \brief Class to describe a storable item * */ class StorageItem { public: /*! \brief Constructor * */ StorageItem(); /*! \brief Destructor * */ virtual ~StorageItem(); /*! \brief Sets the id of the item * * @param aId The id of the item */ void setId( const QString &aId ); /*! \brief Returns the id of the item * * @return Id of the item */ const QString& getId() const; /*! \brief Sets the id of the parent of this item * * @param aParentId The id of the parent of item */ void setParentId( const QString& aParentId ); /*! \brief Returns the id of the parent of this item * * @return Id of the parent of this item */ const QString& getParentId() const; /*! \brief Sets the type of this item * * @param aType Type of this item */ void setType( const QString& aType ); /*! \brief Gets the type of this item * * @return Type of this item */ const QString& getType() const; /*! \brief Sets the version of this item * * @param aVersion Version of this item */ void setVersion( const QString& aVersion ); /*! \brief Gets the version of this item * * @return Version of this item */ const QString& getVersion() const; /*! \brief Write (part of) the item data * * @param aOffset Offset to start writing from * @param aData Data buffer to write. All bytes from buffer are written * @return True on success, otherwise false */ virtual bool write( qint64 aOffset, const QByteArray& aData ) = 0; /*! \brief Read (part of) the item data * * @param aOffset The offset in bytes from where the data is read * @param aLength The number of bytes to read * @param aData Data buffer where to place data * @return True on success, otherwise false */ virtual bool read( qint64 aOffset, qint64 aLength, QByteArray& aData ) const = 0; /*! \brief Sets the length of the item data * * @param aLen Length to set for item data * @return True on success, otherwise false */ virtual bool resize( qint64 aLen ) = 0; /*! \brief Get the size of the item data * * @return The data size in bytes */ virtual qint64 getSize() const = 0; private: QString iId; QString iParentId; QString iType; QString iVersion; }; } #endif buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/PluginServiceObj.cpp0000644000015600001650000001412412634332753027722 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * * Author: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include "PluginServiceObj.h" #include #include #include #include using namespace Buteo; PluginServiceObj::PluginServiceObj( QString aProfileName, QString aPluginName, QObject *parent) : QObject(parent), iPlugin(0), iProfileName(aProfileName), iPluginName(aPluginName) { } PluginServiceObj::~PluginServiceObj() { if( iPlugin ) { delete iPlugin; iPlugin = 0; } } namespace { void initializePlugin(const QString &profileName, const QString &pluginName, Buteo::PluginCbImpl *pluginCb, CLASSNAME **plugin) { ProfileManager pm; #ifdef CLIENT_PLUGIN SyncProfile *syncProfile = pm.syncProfile( profileName ); if( !syncProfile ) { LOG_WARNING( "Profile " << profileName << " does not exist" ); return; } // Create the plugin (client) *plugin = new CLASSNAME( pluginName, *syncProfile, pluginCb ); #else Profile *profile = pm.profile( profileName, Profile::TYPE_SERVER ); if( !profile || !profile->isValid() ) { LOG_WARNING( "Profile " << profileName << " does not exist" ); return; } else { pm.expand( *profile ); } // Create the plugin (server) *plugin = new CLASSNAME( pluginName, *profile, pluginCb ); #endif } } bool PluginServiceObj::init() { FUNCTION_CALL_TRACE; initializePlugin(iProfileName, iPluginName, &iPluginCb, &iPlugin); if (!iPlugin) { LOG_WARNING( "PluginServiceObj::init(): unable to initialize plugin" ); return false; } #ifndef CLIENT_PLUGIN // Server signals QObject::connect(iPlugin, SIGNAL(newSession(const QString&)), this, SIGNAL(newSession(const QString&))); #endif // Chain the signals QObject::connect(iPlugin, SIGNAL(transferProgress(const QString&, Sync::TransferDatabase, Sync::TransferType, const QString&, int)), this, SIGNAL(transferProgress(const QString&, Sync::TransferDatabase, Sync::TransferType, const QString&, int))); QObject::connect(iPlugin, SIGNAL(error(const QString&, const QString&, int)), this, SIGNAL(error(const QString&, const QString&, int))); QObject::connect(iPlugin, SIGNAL(success(const QString&, const QString&)), this, SIGNAL(success(const QString&, const QString&))); QObject::connect(iPlugin, SIGNAL(accquiredStorage(const QString&)), this, SIGNAL(accquiredStorage(const QString&))); QObject::connect(iPlugin, SIGNAL(syncProgressDetail(const QString&, int)), this, SIGNAL(syncProgressDetail(const QString&, int))); return iPlugin->init(); } bool PluginServiceObj::uninit() { FUNCTION_CALL_TRACE; if (!iPlugin) { LOG_WARNING( "PluginServiceObj::uninit(): called on uninitialized plugin" ); return true; } return iPlugin->uninit(); } void PluginServiceObj::abortSync(uchar aStatus) { FUNCTION_CALL_TRACE; if (!iPlugin) { LOG_WARNING( "PluginServiceObj::abortSync(): called on uninitialized plugin" ); return; } iPlugin->abortSync( static_cast(aStatus) ); } bool PluginServiceObj::cleanUp() { FUNCTION_CALL_TRACE; if (!iPlugin) { initializePlugin(iProfileName, iPluginName, &iPluginCb, &iPlugin); if (!iPlugin) { LOG_WARNING( "PluginServiceObj::cleanUp(): unable to initialize plugin" ); return false; } } return iPlugin->cleanUp(); } void PluginServiceObj::connectivityStateChanged(int aType, bool aState) { FUNCTION_CALL_TRACE; if (!iPlugin) { LOG_WARNING( "PluginServiceObj::connectivityStateChanged(): called on uninitialized plugin" ); return; } iPlugin->connectivityStateChanged( static_cast(aType), aState ); } QString PluginServiceObj::getSyncResults() { FUNCTION_CALL_TRACE; if (!iPlugin) { LOG_WARNING( "PluginServiceObj::getSyncResults(): called on uninitialized plugin" ); return QString(); } return iPlugin->getSyncResults().toString(); } #ifdef CLIENT_PLUGIN bool PluginServiceObj::startSync() { FUNCTION_CALL_TRACE; if (!iPlugin) { LOG_WARNING( "PluginServiceObj::startSync(): called on uninitialized plugin" ); return false; } return iPlugin->startSync(); } #else void PluginServiceObj::resume() { FUNCTION_CALL_TRACE; if (!iPlugin) { LOG_WARNING( "PluginServiceObj::resume(): called on uninitialized plugin" ); return; } iPlugin->resume(); } bool PluginServiceObj::startListen() { FUNCTION_CALL_TRACE; if (!iPlugin) { LOG_WARNING( "PluginServiceObj::startListen(): called on uninitialized plugin" ); return false; } return iPlugin->startListen(); } void PluginServiceObj::stopListen() { FUNCTION_CALL_TRACE; if (!iPlugin) { LOG_WARNING( "PluginServiceObj::stopListen(): called on uninitialized plugin" ); return; } return iPlugin->stopListen(); } void PluginServiceObj::suspend() { FUNCTION_CALL_TRACE; if (!iPlugin) { LOG_WARNING( "PluginServiceObj::suspend(): called on uninitialized plugin" ); return; } return iPlugin->suspend(); } #endif buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/OOPServerPlugin.cpp0000644000015600001650000001572212634332753027520 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * * Author: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include "OOPServerPlugin.h" #include "LogMacros.h" #include using namespace Buteo; OOPServerPlugin::OOPServerPlugin( const QString& aPluginName, const Profile& aProfile, PluginCbInterface* aCbInterface, QProcess& aProcess ) : ServerPlugin( aPluginName, aProfile, aCbInterface ), iDone( false ) { FUNCTION_CALL_TRACE; // randomly-generated profile names cannot be registered // as dbus service paths due to being purely numeric. QString profileName = aProfile.name(); int numericIdx = profileName.indexOf(QRegExp("[0123456789]")); QString servicePath = numericIdx == 0 ? QString(QLatin1String("%1%2%3")) .arg(DBUS_SERVICE_NAME_PREFIX) .arg("profile-") .arg(profileName) : QString(QLatin1String("%1%2")) .arg(DBUS_SERVICE_NAME_PREFIX) .arg(profileName); // Initialise dbus for server iOopPluginIface = new ButeoPluginIface( servicePath, DBUS_SERVICE_OBJ_PATH, QDBusConnection::sessionBus() ); iOopPluginIface->setTimeout(60000); // one minute. // Chain the signals received over dbus connect(iOopPluginIface, SIGNAL(transferProgress(const QString &, Sync::TransferDatabase, Sync::TransferType, const QString &, int)), this, SIGNAL(transferProgress(const QString &, Sync::TransferDatabase, Sync::TransferType, const QString &, int))); connect(iOopPluginIface, SIGNAL(error(QString,QString,int)), this, SLOT(onError(QString,QString,int))); connect(iOopPluginIface, SIGNAL(success(QString,QString)), this, SLOT(onSuccess(QString,QString))); connect(iOopPluginIface, SIGNAL(accquiredStorage(const QString &)), this, SIGNAL(accquiredStorage(const QString &))); connect(iOopPluginIface, SIGNAL(newSession(const QString&)), this, SIGNAL(newSession(const QString&))); // Handle the signals from the process connect(&aProcess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onProcessError(QProcess::ProcessError))); connect(&aProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onProcessFinished(int,QProcess::ExitStatus))); } OOPServerPlugin::~OOPServerPlugin() { FUNCTION_CALL_TRACE; if( iOopPluginIface ) { delete iOopPluginIface; iOopPluginIface = 0; } } bool OOPServerPlugin::init() { FUNCTION_CALL_TRACE; QDBusPendingReply reply = iOopPluginIface->init(); reply.waitForFinished(); if( !reply.isValid() ) { LOG_WARNING( "Invalid reply for init from plugin" ); return false; } return reply.value(); } bool OOPServerPlugin::uninit() { FUNCTION_CALL_TRACE; QDBusPendingReply reply = iOopPluginIface->uninit(); reply.waitForFinished(); if( !reply.isValid() ) { LOG_WARNING( "Invalid reply for startSync from plugin" ); return false; } return reply.value(); } bool OOPServerPlugin::startListen() { FUNCTION_CALL_TRACE; QDBusPendingReply reply = iOopPluginIface->startListen(); reply.waitForFinished(); if( !reply.isValid() ) { LOG_WARNING( "Invalid reply for startListen from plugin" ); return false; } return reply.value(); } void OOPServerPlugin::stopListen() { FUNCTION_CALL_TRACE; QDBusPendingReply reply = iOopPluginIface->stopListen(); reply.waitForFinished(); if( !reply.isValid() ) LOG_WARNING( "Invalid reply for stopListen from plugin" ); } void OOPServerPlugin::suspend() { FUNCTION_CALL_TRACE; QDBusPendingReply reply = iOopPluginIface->suspend(); reply.waitForFinished(); if( !reply.isValid() ) LOG_WARNING( "Invalid reply for suspend from plugin" ); } void OOPServerPlugin::resume() { FUNCTION_CALL_TRACE; QDBusPendingReply reply = iOopPluginIface->resume(); reply.waitForFinished(); if( !reply.isValid() ) LOG_WARNING( "Invalid reply for resume from plugin" ); } bool OOPServerPlugin::cleanUp() { FUNCTION_CALL_TRACE; QDBusPendingReply reply = iOopPluginIface->cleanUp(); reply.waitForFinished(); if( !reply.isValid() ) { LOG_WARNING( "Invalid reply for cleanUp from plugin" ); return false; } return reply.value(); } void OOPServerPlugin::connectivityStateChanged( Sync::ConnectivityType aType, bool aState ) { FUNCTION_CALL_TRACE; QDBusPendingReply reply = iOopPluginIface->connectivityStateChanged( aType, aState ); reply.waitForFinished(); if( !reply.isValid() ) LOG_WARNING( "Invalid reply for connectivityStateChanged from plugin" ); } void OOPServerPlugin::onProcessError( QProcess::ProcessError error ) { if( !iDone ) { onError( iProfile.name(), "Plugin process error:" + QString::number(error), Sync::SYNC_PLUGIN_ERROR ); } } void OOPServerPlugin::onProcessFinished( int exitCode, QProcess::ExitStatus exitStatus ) { if ( !iDone ) { if( (exitCode != 0) || (exitStatus != QProcess::NormalExit) ) { onError( iProfile.name(), "Plugin process exited with error code " + QString::number(exitCode) + " and status " + QString::number(exitStatus), Sync::SYNC_PLUGIN_ERROR ); } else { onError( iProfile.name(), "Plugin process exited unexpectedly", Sync::SYNC_PLUGIN_ERROR ); } } } void OOPServerPlugin::onError(QString aProfileName, QString aMessage, int aErrorCode) { if ( !iDone ) { iDone = true; emit error(aProfileName, aMessage, aErrorCode); } } void OOPServerPlugin::onSuccess(QString aProfileName, QString aMessage) { if ( !iDone ) { iDone = true; emit success(aProfileName, aMessage); } } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/StoragePlugin.cpp0000644000015600001650000000267412634332753027302 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "StoragePlugin.h" using namespace Buteo; StoragePlugin::StoragePlugin( const QString& aPluginName ) : iPluginName( aPluginName ) { } StoragePlugin::~StoragePlugin() { } const QString& StoragePlugin::getPluginName() const { return iPluginName; } QString StoragePlugin::getProperty( const QString& aProperty ) const { QString returnValue = ""; if (iProperties.contains(aProperty)) { returnValue = iProperties.value(aProperty); } return returnValue; } void StoragePlugin::getProperties( QMap& aProperties ) const { aProperties = iProperties; } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/PluginManager.h0000644000015600001650000001541012634332753026705 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2014-2015 Jolla Ltd. * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef PLUGINMANAGER_H #define PLUGINMANAGER_H #include #include #include #include namespace Buteo { class StorageChangeNotifierPlugin; class StoragePlugin; class ClientPlugin; class ServerPlugin; class PluginCbInterface; class SyncProfile; class Profile; class ClientPluginTest; class ServerPluginTest; class StoragePluginTest; // Location filters of plugin maps const QString STORAGEMAP_LOCATION = "-storage.so"; const QString CLIENTMAP_LOCATION = "-client.so"; const QString SERVERMAP_LOCATION = "-server.so"; const QString STORAGECHANGENOTIFIERMAP_LOCATION = "-changenotifier.so"; // OOP plugins binary name suffix const QString OOP_CLIENT_SUFFIX = "-client"; const QString OOP_SERVER_SUFFIX = "-server"; // Default directory from which to look for plugins #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) const QString DEFAULT_PLUGIN_PATH = "/usr/lib/buteo-plugins-qt5/"; const QString DEFAULT_OOP_PLUGIN_PATH = "/usr/lib/buteo-plugins-qt5/oopp"; #else const QString DEFAULT_PLUGIN_PATH = "/usr/lib/buteo-plugins/"; #endif // The name of the function which is used to create a plugin const QString CREATE_FUNCTION = "createPlugin"; // The name of the function which is used to destroy a plugin const QString DESTROY_FUNCTION = "destroyPlugin"; typedef ClientPlugin* (*FUNC_CREATE_CLIENT)( const QString&, const SyncProfile&, PluginCbInterface* ); typedef void (*FUNC_DESTROY_CLIENT)( ClientPlugin* ); typedef ServerPlugin* (*FUNC_CREATE_SERVER)( const QString&, const Profile&, PluginCbInterface* ); typedef void (*FUNC_DESTROY_SERVER)( ServerPlugin* ); typedef StoragePlugin* (*FUNC_CREATE_STORAGE)(const QString&); typedef void (*FUNC_DESTROY_STORAGE)(StoragePlugin*); typedef StorageChangeNotifierPlugin* (*FUNC_CREATE_STORAGECHANGENOTIFIER)(const QString&); typedef void (*FUNC_DESTROY_STORAGECHANGENOTIFIER)(StorageChangeNotifierPlugin*); /*! \brief Manages plugins * * Is responsible for creating and destroying storage, * server and client plugins. */ class PluginManager : public QObject { Q_OBJECT public: /*! \brief Constructor * * @param aPluginPath Path where plugins are stored */ PluginManager( const QString &aPluginPath = DEFAULT_PLUGIN_PATH ); /*! \brief Destructor * */ ~PluginManager(); /*! \brief Creates a new storage change notifier plugin * for the storage aStoragName * * @param aStorageName well-known name of the storage */ StorageChangeNotifierPlugin* createStorageChangeNotifier( const QString& aStorageName ); /*! \brief Destroys a storage change notifier plugin instance * * @param aStorageName well-known storage name of the plugin to be destroyed */ void destroyStorageChangeNotifier( StorageChangeNotifierPlugin *aPlugin ); /*! \brief Creates a new storage plugin instance * * @param aPluginName Name of the plugin * @return Storage plugin if success, otherwise NULL */ StoragePlugin* createStorage( const QString& aPluginName ); /*! \brief Destroys a storage plugin instance * * @param aPlugin Plugin to destroy */ void destroyStorage( StoragePlugin* aPlugin ); /*! \brief Creates a new client plugin instance * * @param aPluginName Name of the plugin * @param aProfile Sync profile * @param aCbInterface Callback interface * @return Client plugin on success, otherwise NULL */ ClientPlugin* createClient( const QString& aPluginName, const SyncProfile& aProfile, PluginCbInterface *aCbInterface ); /*! \brief Destroys a client plugin instance * * @param aPlugin Plugin to destroy */ void destroyClient( ClientPlugin* aPlugin ); /*! \brief Creates a new server plugin instance * * @param aPluginName Name of the plugin * @param aProfile Server profile * @param aCbInterface Callback interface * @return Server plugin on success, otherwise NULL */ ServerPlugin* createServer( const QString& aPluginName, const Profile& aProfile, PluginCbInterface *aCbInterface ); /*! \brief Destroys a server plugin * * @param aPlugin Plugin to destroy */ void destroyServer( ServerPlugin *aPlugin ); protected slots: void onProcessFinished( int exitCode, QProcess::ExitStatus exitStatus ); private: struct DllInfo { QString iPath; void* iHandle; int iRefCount; DllInfo() : iHandle( NULL ), iRefCount( 0 ) { } }; void loadPluginMaps( const QString aFilter, QMap& aTargetMap ); void loadOOPPluginMaps( const QString aFilter, QMap& aTargetMap ); void* loadDll( const QString& aPath ); void* getDllHandle( const QString& aPath ); void unloadDll( const QString& aPath ); static bool killProcess( const QString& aPath ); QProcess* startOOPPlugin( const QString& aPath, const QString& aPluginName, const QString& aProfileName ); void stopOOPPlugin( const QString& aPath ); QString iPluginPath; QMap iStorageChangeNotifierMaps; QMap iStorageMaps; QMap iClientMaps; QMap iServerMaps; QMap iOopClientMaps; QMap iOoPServerMaps; QList iLoadedDlls; QReadWriteLock iDllLock; QString iProcBinaryPath; #ifdef SYNCFW_UNIT_TESTS friend class ClientPluginTest; friend class ServerPluginTest; friend class StoragePluginTest; #endif }; } #endif buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/ButeoPluginIfaceAdaptor.cpp0000644000015600001650000000620312634332753031207 0ustar pbuserpbgroup00000000000000/* * This file was generated by qdbusxml2cpp version 0.8 * Command line was: qdbusxml2cpp -c ButeoPluginIfaceAdaptor -a ButeoPluginIfaceAdaptor.h:ButeoPluginIfaceAdaptor.cpp com.buteo.msyncd.baseplugin.xml * * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). * * This is an auto-generated file. * Do not edit! All changes made to it will be lost. */ #include "ButeoPluginIfaceAdaptor.h" #include #include #include #include #include #include #include /* * Implementation of adaptor class ButeoPluginIfaceAdaptor */ ButeoPluginIfaceAdaptor::ButeoPluginIfaceAdaptor(QObject *parent) : QDBusAbstractAdaptor(parent) { // constructor setAutoRelaySignals(true); } ButeoPluginIfaceAdaptor::~ButeoPluginIfaceAdaptor() { // destructor } void ButeoPluginIfaceAdaptor::abortSync(uchar aStatus) { // handle method call com.buteo.msyncd.baseplugin.abortSync QMetaObject::invokeMethod(parent(), "abortSync", Q_ARG(uchar, aStatus)); } bool ButeoPluginIfaceAdaptor::cleanUp() { // handle method call com.buteo.msyncd.baseplugin.cleanUp bool out0; QMetaObject::invokeMethod(parent(), "cleanUp", Q_RETURN_ARG(bool, out0)); return out0; } void ButeoPluginIfaceAdaptor::connectivityStateChanged(int aType, bool aState) { // handle method call com.buteo.msyncd.baseplugin.connectivityStateChanged QMetaObject::invokeMethod(parent(), "connectivityStateChanged", Q_ARG(int, aType), Q_ARG(bool, aState)); } QString ButeoPluginIfaceAdaptor::getSyncResults() { // handle method call com.buteo.msyncd.baseplugin.getSyncResults QString out0; QMetaObject::invokeMethod(parent(), "getSyncResults", Q_RETURN_ARG(QString, out0)); return out0; } bool ButeoPluginIfaceAdaptor::init() { // handle method call com.buteo.msyncd.baseplugin.init bool out0; QMetaObject::invokeMethod(parent(), "init", Q_RETURN_ARG(bool, out0)); return out0; } void ButeoPluginIfaceAdaptor::resume() { // handle method call com.buteo.msyncd.baseplugin.resume QMetaObject::invokeMethod(parent(), "resume"); } bool ButeoPluginIfaceAdaptor::startListen() { // handle method call com.buteo.msyncd.baseplugin.startListen bool out0; QMetaObject::invokeMethod(parent(), "startListen", Q_RETURN_ARG(bool, out0)); return out0; } bool ButeoPluginIfaceAdaptor::startSync() { // handle method call com.buteo.msyncd.baseplugin.startSync bool out0; QMetaObject::invokeMethod(parent(), "startSync", Q_RETURN_ARG(bool, out0)); return out0; } void ButeoPluginIfaceAdaptor::stopListen() { // handle method call com.buteo.msyncd.baseplugin.stopListen QMetaObject::invokeMethod(parent(), "stopListen"); } void ButeoPluginIfaceAdaptor::suspend() { // handle method call com.buteo.msyncd.baseplugin.suspend QMetaObject::invokeMethod(parent(), "suspend"); } bool ButeoPluginIfaceAdaptor::uninit() { // handle method call com.buteo.msyncd.baseplugin.uninit bool out0; QMetaObject::invokeMethod(parent(), "uninit", Q_RETURN_ARG(bool, out0)); return out0; } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/PluginServiceObj.h0000644000015600001650000000441612634332753027372 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * * Author: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef PLUGINSERVICEOBJ_H #define PLUGINSERVICEOBJ_H #include #include #include #include #include #include #include CLASSNAME_H using namespace Buteo; class PluginServiceObj : public QObject { Q_OBJECT public: PluginServiceObj( QString aProfile, QString aPluginName, QObject *parent = 0 ); virtual ~PluginServiceObj(); public: // PROPERTIES public Q_SLOTS: // METHODS void abortSync(uchar aStatus); bool cleanUp(); void connectivityStateChanged(int aType, bool aState); QString getSyncResults(); bool init(); bool uninit(); #ifdef CLIENT_PLUGIN bool startSync(); #else void resume(); bool startListen(); void stopListen(); void suspend(); #endif Q_SIGNALS: // SIGNALS void accquiredStorage(const QString &aMimeType); void error(const QString &aProfileName, const QString &aMessage, int aErrorCode); void newSession(const QString &aDestination); void success(const QString &aProfileName, const QString &aMessage); void syncProgressDetail(const QString &aProfileName, int aProgressDetail); void transferProgress(const QString &aProfileName, Sync::TransferDatabase aDatabase, Sync::TransferType aType, const QString &aMimeType, int aCommittedItems); private: CLASSNAME *iPlugin; QString iProfileName; QString iPluginName; PluginCbImpl iPluginCb; }; #endif // PLUGINSERVICEOBJ_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/ButeoPluginIface.cpp0000644000015600001650000000140112634332753027667 0ustar pbuserpbgroup00000000000000/* * This file was generated by qdbusxml2cpp version 0.8 * Command line was: qdbusxml2cpp -v -c ButeoPluginIface -p ButeoPluginIface.h:ButeoPluginIface.cpp com.buteo.msyncd.baseplugin.xml * * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). * * This is an auto-generated file. * This file may have been hand-edited. Look for HAND-EDIT comments * before re-generating it. */ #include "ButeoPluginIface.h" /* * Implementation of interface class ButeoPluginIface */ ButeoPluginIface::ButeoPluginIface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent) { } ButeoPluginIface::~ButeoPluginIface() { } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/StoragePlugin.h0000644000015600001650000001744312634332753026747 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef STORAGEPLUGIN_H_89943984899843 #define STORAGEPLUGIN_H_89943984899843 #include #include #include #include namespace Buteo { class StorageItem; /*! \brief Base class for storage plugins * */ class StoragePlugin { public: /*! \brief Status of operation performed by storage plugin * */ enum OperationStatus { STATUS_INVALID_FORMAT = -6, /*!< Operation failed because data is in invalid format*/ STATUS_STORAGE_FULL = -5, /*!< Operation failed because storage is full*/ STATUS_OBJECT_TOO_BIG = -4, /*!< Operation failed because object is too big*/ STATUS_ERROR = -3, /*!< General error occurred during operation*/ STATUS_DUPLICATE = -2, /*!< Operation was not performed as object was duplicate*/ STATUS_NOT_FOUND = -1, /*!< Operation failed as object was not found*/ STATUS_OK = 0 /*!< Operation was completed successfully*/ }; /*! \brief Constructor * * @param aPluginName Name of this storage plugin */ StoragePlugin( const QString& aPluginName ); /*! \brief Destructor * */ virtual ~StoragePlugin(); /*! \brief Returns the name of this plugin * * @return Name of the plugin */ const QString& getPluginName() const; /*! \brief Returns the value of the given property * * @param aProperty Property * @return Property value if property found, otherwise empty string */ QString getProperty( const QString& aProperty ) const; /*! \brief Returns the properties set for this plugin * * @param aProperties Properties that are set */ void getProperties( QMap& aProperties ) const; /*! \brief Initializes the plugin * * It is recommended that the plugin should do not do any thread insecure * initializations inside constructor, instead it should do inside this method. * Parameters that were read from storage profile are passed as parameter to this * function. * * @param aProperties Properties that should be set for this plugin */ virtual bool init( const QMap& aProperties ) = 0; /*! \brief Uninitializes the plugin * */ virtual bool uninit() = 0; /*! \brief Returns all known items * * @param aItems Array where to place items * @return True on success, otherwise false */ virtual bool getAllItems( QList& aItems ) = 0; /*! \brief Returns id's of all known items * * @param aItems Array where to place item id's * @return True on success, otherwise false */ virtual bool getAllItemIds( QList& aItems ) = 0; /*! \brief Returns all new items since aTime * * @param aNewItems Array where to place items * @param aTime Items with creation time > aTime are returned * @return True on success, otherwise false */ virtual bool getNewItems( QList& aNewItems, const QDateTime& aTime ) = 0; /*! \brief Returns id's of all new items since aTime (creation time > aTime) * * @param aNewItemIds Array where to place item id's * @param aTime Items with creation time > aTime are returned * @return True on success, otherwise false */ virtual bool getNewItemIds( QList& aNewItemIds, const QDateTime& aTime ) = 0; /*! \brief Returns all modified items since aTime * * @param aModifiedItems Array where to place items * @param aTime Items with modification time > aTime and creation time <= * aTime are returned. * @return True on success, otherwise false */ virtual bool getModifiedItems( QList& aModifiedItems, const QDateTime& aTime ) = 0; /*! \brief Returns id's of all modified items since aTime * * @param aModifiedItemIds Array where to place item id's * @param aTime Items with modification time > aTime and creation time <= * aTime are returned. * @return True on success, otherwise false */ virtual bool getModifiedItemIds( QList& aModifiedItemIds, const QDateTime& aTime ) = 0; /*! \brief Returns id's of all deleted items since aTime * * @param aDeletedItemIds Array where to place item id's * @param aTime Items with deletion time > aTime and creation time <= * aTime are returned. * @return True on success, otherwise false */ virtual bool getDeletedItemIds( QList& aDeletedItemIds, const QDateTime& aTime ) = 0; /*! \brief Generates a new item * * Returned item is temporary. Therefore returned item ALWAYS has its id * set as empty ID (""). ID will be assigned only after addItem() has been * called for the item. * * @return On success pointer to the item generated, otherwise NULL */ virtual StorageItem* newItem() = 0; /*! \brief Returns an item based on id * * @param aItemId Id of the item to return * @return On success pointer to the item, otherwise NULL */ virtual StorageItem* getItem( const QString& aItemId ) = 0; /*! \brief Returns items based on ids * * @param aItemIdList List of id's * @return List of items */ virtual QList getItems( const QStringList& aItemIdList ) = 0; /*! \brief Adds an item to the storage * * Upon successful addition, item is updated with its * assigned ID. * * @param aItem Item to add * @return Operation status code */ virtual OperationStatus addItem( StorageItem& aItem ) = 0; /*! \brief Adds items to the storage * * Upon successful addition, items are updated with its * assigned ID. * * @param aItems Items to add * @return Operation status codes */ virtual QList addItems( const QList& aItems ) = 0; /*! \brief Modifies an item in the storage * * @param aItem Item to modify * @return Operation status code */ virtual OperationStatus modifyItem( StorageItem& aItem ) = 0; /*! \brief Modifies item in the storage * * @param aItems Items to add * @return Operation status codes */ virtual QList modifyItems( const QList& aItems ) = 0; /*! \brief Deletes an item from the storage * * @param aItemId Id of the item to be deleted * @return Operation status code */ virtual OperationStatus deleteItem( const QString& aItemId ) = 0; /*! \brief Deletes an item from the storage * * @param aItemIds Id's of the item to be deleted * @return Operation status codes */ virtual QList deleteItems( const QList& aItemIds ) = 0; protected: //! Name of the plugin QString iPluginName; //! Properties of the plugin as read from profile xml QMap iProperties; }; } #endif // STORAGEPLUGIN_H_89943984899843 buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/pluginmgr/OOPClientPlugin.h0000644000015600001650000000367612634332753027142 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * * Author: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef OOPCLIENTPLUGIN_H #define OOPCLIENTPLUGIN_H #include #include namespace Buteo { class OOPClientPlugin : public ClientPlugin { Q_OBJECT public: OOPClientPlugin( const QString& aPluginName, const Buteo::SyncProfile& aProfile, Buteo::PluginCbInterface* aCbInterface, QProcess& aProcess); virtual ~OOPClientPlugin(); virtual bool init(); virtual bool uninit(); virtual bool startSync(); virtual void abortSync(Sync::SyncStatus aStatus = Sync::SYNC_ABORTED); virtual Buteo::SyncResults getSyncResults() const; virtual bool cleanUp(); public slots: virtual void connectivityStateChanged(Sync::ConnectivityType aType, bool aState); void onProcessError(QProcess::ProcessError error); void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); void onError(QString aProfileName, QString aMessage, int aErrorCode); void onSuccess(QString aProfileName, QString aMessage); private: bool iDone; }; } #endif // OOPCLIENTPLUGIN_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/clientfw/0000755000015600001650000000000012634333235023605 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/clientfw/SyncClientInterface.cpp0000644000015600001650000000537012634332753030216 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncClientInterface.h" #include "SyncClientInterfacePrivate.h" using namespace Buteo; SyncClientInterface::SyncClientInterface(): d_ptr(new SyncClientInterfacePrivate(this)) { } SyncClientInterface::~SyncClientInterface() { delete d_ptr; d_ptr = NULL; } bool SyncClientInterface::startSync(const QString &aProfileId) const { return d_ptr->startSync(aProfileId); } void SyncClientInterface::abortSync(const QString &aProfileId) const { d_ptr->abortSync(aProfileId); } QStringList SyncClientInterface::getRunningSyncList() { return d_ptr->getRunningSyncList(); } bool SyncClientInterface::removeProfile(QString &aProfileId) { return d_ptr->removeProfile(aProfileId); } bool SyncClientInterface::updateProfile(Buteo::SyncProfile &aProfile) { return d_ptr->updateProfile(aProfile); } bool SyncClientInterface::setSyncSchedule(QString &aProfileId,SyncSchedule &aSchedule) { return d_ptr->setSyncSchedule(aProfileId,aSchedule); } bool SyncClientInterface::saveSyncResults(const QString &aProfileId,const Buteo::SyncResults &aSyncResults) { return d_ptr->saveSyncResults(aProfileId,aSyncResults); } bool SyncClientInterface::getBackUpRestoreState() { return d_ptr->getBackUpRestoreState(); } bool SyncClientInterface::isValid() { return d_ptr->isValid(); } Buteo::SyncResults SyncClientInterface::getLastSyncResult(const QString &aProfileId) { return d_ptr->getLastSyncResult(aProfileId); } QList SyncClientInterface::allVisibleSyncProfiles() { return d_ptr->allVisibleSyncProfiles(); } QString SyncClientInterface::syncProfile(const QString &aProfileId) { return d_ptr->syncProfile(aProfileId); } QStringList SyncClientInterface::syncProfilesByKey(const QString &aKey, const QString &aValue) { return d_ptr->syncProfilesByKey(aKey, aValue); } QStringList SyncClientInterface::syncProfilesByType(const QString &aType) { return d_ptr->syncProfilesByType(aType); } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/clientfw/SyncClientInterface.h0000644000015600001650000002414512634332753027664 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCCLIENTINTERFACE_H #define SYNCCLIENTINTERFACE_H #include #include #include #include #include #include namespace Buteo { class SyncClientInterfacePrivate; /*! \brief * SyncInterface Class - Main Entry Point for SyncFW Clients * * This Class Provides Interface API towards SyncFW. * This Class has Methods to start and abort a sync , set schedule for a profile * and use the fw functionality to automatically schedule it, get a list of * running syncs( to display in UI ) and get status of the synchronization. * It also has Signals that the clients can connect to know the last sync results , * to keep track of profile changes by SyncFw. */ class SyncClientInterface: public QObject { Q_OBJECT public: /*! * \brief Constructor */ SyncClientInterface(); /*! * \brief Destructor */ ~SyncClientInterface(); /*! * \brief Requests to starts synchronizing using a profile Id * * A status change signal (QUEUED, STARTED or ERROR) will be sent by the * daemon when the request is processed. If there is a sync already in * progress using the same resources that are needed by the given profile, * adds the sync request to a sync queue. Otherwise a sync session is * started immediately. * * \param aProfileId Id of the profile to use in sync. * \return True if a profile with the Id was found. Otherwise * false and no status change signals will follow from this request. */ bool startSync(const QString &aProfileId) const; /*! * \brief Stops synchronizing the profile with the given Id. * * If the sync request is still in queue and not yet started, the queue * entry is removed. * * \param aProfileId Id of the profile to stop syncing. */ void abortSync(const QString &aProfileId) const; /*! * \brief Gets the list of profile names of currently running syncs. * * \return Profile name list. */ QStringList getRunningSyncList(); /*! * \brief Sets Sync Schedule to the profile * * This function does the following * 1. sets the sync type of the profile to schedule * 2. Adds the schedule to the profile * 3. saves the profile. * 4. schedules the synchronization , so that sync automatically starts next time * * \return status of the operation */ bool setSyncSchedule(QString &aProfileId,SyncSchedule &aSchedule); /*! * \brief Save SyncResults to log.xml file. * \param aProfileId to save result in corresponding file. * \param aSyncResults to save in the \code .log.xml \endcode * \return status of the saveSyncResults */ bool saveSyncResults(const QString &aProfileId,const Buteo::SyncResults &aSyncResults); /*! * \brief This function should be called when sync profile has to be deleted * * \param aProfileId Id of the profile to be deleted. * \return status of the remove operation */ bool removeProfile(QString &aProfileId); /*! * \brief This function should be called when sync profile information has * been changed by the client * * \param aSyncProfile Modified Profile Object.If same profile already exists it * will be overwritten with the changes from this object. * \return status of the update operation * */ bool updateProfile(Buteo::SyncProfile &aSyncProfile); /*! * \brief This function returns true if backup/restore in progress else * false. */ bool getBackUpRestoreState(); /*! * \brief Use this function to understand if the creation of dbus connection to msyncd * succeeded or not. * \return - status of the dbus object created for msyncd */ bool isValid(); /*! \brief To get lastSyncResult. * \param aProfileId * \return SyncResults of syncLastResult. */ Buteo::SyncResults getLastSyncResult(const QString &aProfileId); /*! \brief Gets all visible sync profiles. * * Returns all sync profiles that should be visible in sync ui. A profile * is visible if it has not been explicitly set as hidden. * \return The list of sync profiles. */ QList allVisibleSyncProfiles(); /*! \brief Gets a sync profile. * * Loads and merges also all sub-profiles that are referenced from the * main profile. Loads the log of finished synchronization sessions with * this profile. * \param aProfileId Name of the profile to get. * \return The sync profile as Xml string. */ QString syncProfile(const QString &aProfileId); /*! \brief Gets a sync profiles which matches the key-value. * * Loads and merges also all sub-profiles that are referenced from the * main profile. Loads the log of finished synchronization sessions with * this profile. * \param aKey Key to match for profile. * \param aValue Value to match for profile. * \return The sync profiles as Xml string list. */ QStringList syncProfilesByKey(const QString &aKey, const QString &aValue); /*! \brief Gets a profiles matching the profile type. * * \param aType Type of the profile service/storage/sync. * \return The sync profile ids as string list. */ QStringList syncProfilesByType(const QString &aType); signals: /*! \brief Notifies about Backup start. * * This signal is sent when the backup framework is backing the sync related * data */ void backupInProgress (); /*! \brief Notifies about Backup done. * * This signal is sent when the backup framework has completed backing the sync related * data. */ void backupDone(); /*! \brief Notifies about Restore start. * * This signal is sent when the backup framework is restoring the sync related * data */ void restoreInProgress(); /*! \brief Notifies about Restore Done. * * This signal is sent when the backup framework has restored the sync related * data */ void restoreDone(); /*! \brief Notifies about a change in profile. * * This signal is sent when the profile data is modified or when a profile * is added or deleted in msyncd. * \param aProfileId Id of the changed profile. * \param aChangeType * 0 (ADDITION): Profile was added. * 1 (MODIFICATION): Profile was modified. * 2 (DELETION): Profile was deleted. * \param aChangedProfile changed sync profie as XMl string. * */ void profileChanged(QString aProfileId,int aChangeType, QString aChangedProfile); /*! \brief Notifies about the results of a recent sync for a profile * * This signal is sent after the sync has completed for a profile. * \param aProfileId Id of the changed profile. * \param aResults - Results of the sync * */ void resultsAvailable(QString aProfileId , Buteo::SyncResults aResults); /*! * \brief Notifies about a change in synchronization status. * * \param aProfileId Id of the profile used in the sync session whose * status has changed. * \param aStatus The new status. One of the following: * 0 (QUEUED): Sync request has been queued or was already in the * queue when sync start was requested. * 1 (STARTED): Sync session has been started. * 2 (PROGRESS): Sync session is progressing. * 3 (ERROR): Sync session has encountered an error and has been stopped, * or the session could not be started at all. * 4 (DONE): Sync session was successfully completed. * 5 (ABORTED): Sync session was aborted. * Statuses 3-5 are final, no more status changes will be sent from the * same sync session. * \param aMessage A message describing the status change in detail. This * can for example be shown to the user or written to a log * \param aStatusDetails * When aStatus is ERROR, this parameter contains a specific error code. * When aStatus is PROGRESS, this parameter contains more details about the progress * \see SyncCommonDefs::SyncProgressDetails */ void syncStatus(QString aProfileId, int aStatus, QString aMessage, int aStatusDetails); /*! \brief Notifies about progress in transferring items * * \param aProfileId Id of the profile where progress has occurred * \param aTransferDatabase Database to which transfer was made. One of the following: * 0 (LOCAL_DATABASE): Transfer was made from remote database to local database * 1 (REMOTE_DATABASE): Transfer was made from local database to remote database * \param aTransferType Type of transfer that was made. One of the following: * 0 (ADDITION): Addition was made to database * 1 (MODIFICATION): Modification was made to database * 2 (DELETION): Deletion was made to database * 3 (ERROR): Addition/Modification/Deletion was attempted, but it failed * \param aMimeType Mime type of the processed item * \param aCommittedItems No. of items committed for this operation */ void transferProgress(QString aProfileId, int aTransferDatabase, int aTransferType , QString aMimeType, int aCommittedItems ); private: SyncClientInterfacePrivate *d_ptr; }; }; #endif buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/clientfw/SyncClientInterfacePrivate.h0000644000015600001650000001624712634332753031223 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCCLIENTINTERFACEPRIVATE_H #define SYNCCLIENTINTERFACEPRIVATE_H #include #include "SyncDaemonProxy.h" #include namespace Buteo { class SyncProfile; class SyncSchedule; class SyncResults; class SyncClientInterface; //! Private implementation class for SyncClientInterface. class SyncClientInterfacePrivate:public QObject { Q_OBJECT public: /*!\brief Constructor * *@param aParent - pointer to the parent object */ SyncClientInterfacePrivate(SyncClientInterface *aParent); /*! \brief Destructor */ ~SyncClientInterfacePrivate(); /*! \brief function to start the sync * * @param aProfileId - id of the profile to start the sync */ bool startSync(const QString &aProfileId) const; /*! \brief function to abort the sync * * @param aProfileId - id of the profile to abort the sync */ void abortSync(const QString &aProfileId) const; /*! \brief function to get Running sync list * * @return - list of running sync profile ids */ QStringList getRunningSyncList(); /*! \brief function to remove a profile * * @param aProfileId id of the profule to remove to add */ bool removeProfile(QString &aProfileId); /*! \brief function to update an existing profile * * @param aProfile profile object to add */ bool updateProfile(Buteo::SyncProfile &aProfile); /*! \brief function to add a profile * * @return returns the backup restore state. */ bool getBackUpRestoreState(); /*! \brief function to check if the interface is valid * * @return status of the validity of the interface */ bool isValid(); /*! \brief this function converts the SyncSchedule object to an xml * file of the below format * \code * * \endcode * * @param aProfileId - profile id * @param aSchedule - schedule object */ bool setSyncSchedule(QString &aProfileId,SyncSchedule &aSchedule); /*! \brief this function converts the save the syncResults into * log.xml file corresponding to profileName. * \code * * \endcode */ bool saveSyncResults(const QString &aProfileId,const Buteo::SyncResults &aSyncResults); /*! \brief To get lastSyncResult. * \param aProfileId * \return SyncResults of syncLastResult. */ Buteo::SyncResults getLastSyncResult(const QString &aProfileId); /*! \brief Gets all visible sync profiles. * * Returns all sync profiles that should be visible in sync ui. A profile * is visible if it has not been explicitly set as hidden. * \return The list of sync profiles. */ QList allVisibleSyncProfiles(); /*! \brief Gets a sync profile. * * Loads and merges also all sub-profiles that are referenced from the * main profile. Loads the log of finished synchronization sessions with * this profile. * \param aProfileId Name of the profile to get. * \return The sync profile as Xml string. */ QString syncProfile(const QString &aProfileId); /*! \brief Gets a sync profiles which matches the key-value. * * Loads and merges also all sub-profiles that are referenced from the * main profile. Loads the log of finished synchronization sessions with * this profile. * \param aKey Key to match for profile. * \param aValue Value to match for profile. * \return The sync profiles as Xml string list. */ QStringList syncProfilesByKey(const QString &aKey, const QString &aValue); /*! \brief Gets a profiles matching the profile type. * * \param aType Type of the profile service/storage/sync. * \return The sync profile ids as string list. */ QStringList syncProfilesByType(const QString &aType); public slots: /*! \brief this is the slot where we will receive the xml data for profile from msyncd. * The XML Data received will be of the following format * \code * * * * * * * * * * * * * * * * \endcode * @param aProfileId - id of the profile * @param aChangeType - change type whether addition , deletion or modification * @param aChangedProfileAsXml - changed profile arrives as xml * */ void slotProfileChanged(QString aProfileId , int aChangeType , QString aChangedProfileAsXml); /*! \brief this is the slot where we will receive the xml data for results from msyncd * the xml looks like this * \code * * * * * *\endcode * @param aProfileId - id of the profile * @param aLastSyncResultAsXml - last sync result as xml */ void resultsAvailable(QString aProfileId , QString aLastSyncResultAsXml); signals: /*! \brief Signal that gets emitted on receiving profileChanged from msyncd * * @param aProfileId - id of the profile * @param aChangeType - change type whether addition , deletion or modification * @param aChangedProfile - changed sync profie as XMl string. */ void profileChanged(QString aProfileId,int aChangeType, QString aChangedProfile); /*! \brief Signal that gets emitted on receiving resultsAvailable from msyncd * * @param aProfileId - id of the profile * @param aLastResults - last results as SyncResults Object */ void resultsAvailable(QString aProfileId,Buteo::SyncResults aLastResults); private: SyncDaemonProxy *iSyncDaemon; Buteo::SyncClientInterface *iParent; }; }; #endif buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/clientfw/SyncClientInterfacePrivate.cpp0000644000015600001650000001733112634332753031551 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include #include #include #include "SyncClientInterfacePrivate.h" #include "SyncClientInterface.h" #include "LogMacros.h" using namespace Buteo; static const QString SYNC_DBUS_OBJECT = "/synchronizer"; static const QString SYNC_DBUS_SERVICE = "com.meego.msyncd"; SyncClientInterfacePrivate::SyncClientInterfacePrivate(SyncClientInterface *aParent) : iParent(aParent) { FUNCTION_CALL_TRACE; iSyncDaemon = new SyncDaemonProxy(SYNC_DBUS_SERVICE, SYNC_DBUS_OBJECT, QDBusConnection::sessionBus(), this); if (iSyncDaemon) { connect(iSyncDaemon,SIGNAL(signalProfileChanged(QString,int,QString)), this,SLOT(slotProfileChanged(QString,int,QString))); connect(iSyncDaemon, SIGNAL(resultsAvailable(QString, QString)), this, SLOT(resultsAvailable(QString, QString))); connect(this,SIGNAL(profileChanged(QString, int, QString)), iParent,SIGNAL(profileChanged(QString, int, QString))); connect(this,SIGNAL(resultsAvailable(QString,Buteo::SyncResults)), iParent,SIGNAL(resultsAvailable(QString,Buteo::SyncResults))); connect (iSyncDaemon,SIGNAL(syncStatus(QString,int,QString,int)), iParent,SIGNAL(syncStatus(QString,int,QString,int))); connect(iSyncDaemon,SIGNAL(transferProgress(QString,int,int,QString,int)), iParent,SIGNAL(transferProgress(QString,int,int,QString,int))); connect(iSyncDaemon, SIGNAL(backupInProgress()), iParent, SIGNAL(backupInProgress())); connect(iSyncDaemon, SIGNAL(backupDone()), iParent, SIGNAL(backupDone())); connect(iSyncDaemon, SIGNAL(restoreInProgress()), iParent, SIGNAL(restoreInProgress())); connect(iSyncDaemon, SIGNAL(restoreDone()), iParent, SIGNAL(restoreDone())); } qRegisterMetaType("Buteo::Profile"); qRegisterMetaType("Buteo::SyncResults"); } SyncClientInterfacePrivate::~SyncClientInterfacePrivate() { FUNCTION_CALL_TRACE; delete iSyncDaemon; iSyncDaemon = NULL; } bool SyncClientInterfacePrivate::startSync(const QString &aProfileId) const { FUNCTION_CALL_TRACE; bool syncStatus = false; if (iSyncDaemon && !aProfileId.isEmpty()) { syncStatus = iSyncDaemon->startSync(aProfileId); } return syncStatus; } void SyncClientInterfacePrivate::abortSync(const QString &aProfileId) const { FUNCTION_CALL_TRACE; if (iSyncDaemon && !aProfileId.isEmpty()) { iSyncDaemon->abortSync(aProfileId); } } QStringList SyncClientInterfacePrivate::getRunningSyncList() { FUNCTION_CALL_TRACE; QStringList runningSyncList; if (iSyncDaemon) { runningSyncList = iSyncDaemon->runningSyncs(); } return runningSyncList; } bool SyncClientInterfacePrivate::removeProfile(QString &aProfileId) { FUNCTION_CALL_TRACE; bool status = false; if (iSyncDaemon) { status = iSyncDaemon->removeProfile(aProfileId); } return status; } bool SyncClientInterfacePrivate::updateProfile(Buteo::SyncProfile &aProfile) { FUNCTION_CALL_TRACE; bool status = false; if (iSyncDaemon) { QString profileAsXmlString = aProfile.toString(); status = iSyncDaemon->updateProfile(profileAsXmlString); } return status; } void SyncClientInterfacePrivate::slotProfileChanged(QString aProfileId,int aChangeType, QString aProfileAsXml) { FUNCTION_CALL_TRACE; emit profileChanged(aProfileId,aChangeType,aProfileAsXml); } void SyncClientInterfacePrivate::resultsAvailable(QString aProfileId, QString aLastResultsAsXml) { FUNCTION_CALL_TRACE; QDomDocument doc; if (doc.setContent(aLastResultsAsXml, true)) { Buteo::SyncResults results(doc.documentElement()); emit resultsAvailable(aProfileId, results); } else { LOG_DEBUG("Invalid Profile Xml Received from msyncd"); } } bool SyncClientInterfacePrivate::setSyncSchedule(QString &aProfileId, SyncSchedule &aSchedule) { FUNCTION_CALL_TRACE; bool status = false; if (iSyncDaemon) { QString scheduleAsXmlString = aSchedule.toString(); if (!scheduleAsXmlString.isEmpty()) { status = iSyncDaemon->setSyncSchedule(aProfileId, scheduleAsXmlString); } } return status; } bool SyncClientInterfacePrivate::saveSyncResults(const QString &aProfileId, const Buteo::SyncResults &aSyncResults) { FUNCTION_CALL_TRACE; bool status = false; if (iSyncDaemon) { QString resultsAsXmlString = aSyncResults.toString(); if (!resultsAsXmlString.isEmpty()) { status = iSyncDaemon->saveSyncResults(aProfileId, resultsAsXmlString); } } return status; } bool SyncClientInterfacePrivate::getBackUpRestoreState() { FUNCTION_CALL_TRACE; bool status = false; if (iSyncDaemon) { status = iSyncDaemon->getBackUpRestoreState(); } return status; } bool SyncClientInterfacePrivate::isValid() { return(iSyncDaemon && iSyncDaemon->isValid()); } Buteo::SyncResults SyncClientInterfacePrivate::getLastSyncResult(const QString &aProfileId) { FUNCTION_CALL_TRACE; // Default construct with invalid values // Using default constructor for QDateTime() creates "null" date. Buteo::SyncResults syncResult(QDateTime(), SyncResults::SYNC_RESULT_INVALID, Buteo::SyncResults::SYNC_RESULT_INVALID); if (iSyncDaemon) { QString resultASXmlString = iSyncDaemon->getLastSyncResult(aProfileId); QDomDocument doc; if (doc.setContent(resultASXmlString, true)) { Buteo::SyncResults result(doc.documentElement()); return result; } else { LOG_CRITICAL("Invalid Profile Xml Received from msyncd"); } } return syncResult; } QList SyncClientInterfacePrivate::allVisibleSyncProfiles() { FUNCTION_CALL_TRACE; QList profilesAsXml; if (iSyncDaemon) { QStringList profilesList = iSyncDaemon->allVisibleSyncProfiles(); if (!profilesList.isEmpty()) { foreach(QString profileAsXml, profilesList) { profilesAsXml.append(profileAsXml); } } } LOG_DEBUG("allVisibleSyncProfiles "<syncProfile(aProfileId); } LOG_DEBUG("syncProfile "<syncProfilesByKey(aKey, aValue); } return profileAsXml; } QStringList SyncClientInterfacePrivate::syncProfilesByType(const QString &aType) { FUNCTION_CALL_TRACE; QStringList profileIds; if (iSyncDaemon) { profileIds = iSyncDaemon->syncProfilesByType(aType); } return profileIds; } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/clientfw/SyncDaemonProxy.cpp0000644000015600001650000000307312634332753027422 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ /* * This file was generated by qdbusxml2cpp version 0.7 * Command line was: qdbusxml2cpp -p SyncDaemonProxy -N -c SyncDaemonProxy com.meego.msyncd.xml * * qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * This is an auto-generated file. * This file may have been hand-edited. Look for HAND-EDIT comments * before re-generating it. */ #include "SyncDaemonProxy.h" /* * Implementation of interface class SyncDaemonProxy */ SyncDaemonProxy::SyncDaemonProxy(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent) { } SyncDaemonProxy::~SyncDaemonProxy() { } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/clientfw/SyncDaemonProxy.h0000644000015600001650000002242112634332753027065 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ /* * This file was generated by qdbusxml2cpp version 0.7 * Command line was: qdbusxml2cpp -p SyncDaemonProxy -N -c SyncDaemonProxy com.meego.msyncd.xml * * qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * This is an auto-generated file. * Do not edit! All changes made to it will be lost. */ #ifndef SYNCDAEMONPROXY_H_1280213538 #define SYNCDAEMONPROXY_H_1280213538 #include #include #include #include #include #include #include #include /*! \brief Proxy class for interface com.meego.msyncd */ class SyncDaemonProxy: public QDBusAbstractInterface { Q_OBJECT public: //! \brief returns Interface Name static inline const char *staticInterfaceName() { return "com.meego.msyncd"; } public: //! \see SyncDBusInterface::SyncDBusInterface() SyncDaemonProxy(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0); //! \see SyncDBusInterface::SyncDBusInterface() ~SyncDaemonProxy(); public Q_SLOTS: // METHODS //! \see SyncDBusInterface::abortSync() inline Q_NOREPLY void abortSync(const QString &aProfileId) { QList argumentList; argumentList << qVariantFromValue(aProfileId); callWithArgumentList(QDBus::NoBlock, QLatin1String("abortSync"), argumentList); } //! \see SyncDBusInterface::addProfile() inline QDBusPendingReply addProfile(const QString &aProfileAsXml) { QList argumentList; argumentList << qVariantFromValue(aProfileAsXml); return asyncCallWithArgumentList(QLatin1String("addProfile"), argumentList); } //! \see SyncDBusInterface::allVisibleSyncProfiles() inline QDBusPendingReply allVisibleSyncProfiles() { QList argumentList; return callWithArgumentList(QDBus::Block, QLatin1String("allVisibleSyncProfiles"), argumentList); } //! \see SyncDBusInterface::getBackUpRestoreState() inline QDBusPendingReply getBackUpRestoreState() { QList argumentList; return asyncCallWithArgumentList(QLatin1String("getBackUpRestoreState"), argumentList); } //! \see SyncDBusInterface::getLastSyncResult() inline QDBusPendingReply getLastSyncResult(const QString &aProfileId) { QList argumentList; argumentList << qVariantFromValue(aProfileId); return asyncCallWithArgumentList(QLatin1String("getLastSyncResult"), argumentList); } //! \see SyncDBusInterface::isLastSyncScheduled() inline QDBusPendingReply isLastSyncScheduled(const QString &aProfileId) { QList argumentList; argumentList << qVariantFromValue(aProfileId); return asyncCallWithArgumentList(QLatin1String("isLastSyncScheduled"), argumentList); } //! \see SyncDBusInterface::lastSyncMajorCode() inline QDBusPendingReply lastSyncMajorCode(const QString &aProfileId) { QList argumentList; argumentList << qVariantFromValue(aProfileId); return asyncCallWithArgumentList(QLatin1String("lastSyncMajorCode"), argumentList); } //! \see SyncDBusInterface::lastSyncMinorCode() inline QDBusPendingReply lastSyncMinorCode(const QString &aProfileId) { QList argumentList; argumentList << qVariantFromValue(aProfileId); return asyncCallWithArgumentList(QLatin1String("lastSyncMinorCode"), argumentList); } //! \see SyncDBusInterface::lastSyncTime() inline QDBusPendingReply lastSyncTime(const QString &aProfileId) { QList argumentList; argumentList << qVariantFromValue(aProfileId); return asyncCallWithArgumentList(QLatin1String("lastSyncTime"), argumentList); } //! \see SyncDBusInterface::releaseStorages() inline Q_NOREPLY void releaseStorages(const QStringList &aStorageNames) { QList argumentList; argumentList << qVariantFromValue(aStorageNames); callWithArgumentList(QDBus::NoBlock, QLatin1String("releaseStorages"), argumentList); } //! \see SyncDBusInterface::removeProfile() inline QDBusPendingReply removeProfile(const QString &aProfileId) { QList argumentList; argumentList << qVariantFromValue(aProfileId); return asyncCallWithArgumentList(QLatin1String("removeProfile"), argumentList); } //! \see SyncDBusInterface::requestStorages() inline QDBusPendingReply requestStorages(const QStringList &aStorageNames) { QList argumentList; argumentList << qVariantFromValue(aStorageNames); return asyncCallWithArgumentList(QLatin1String("requestStorages"), argumentList); } //! \see SyncDBusInterface::runningSyncs() inline QDBusPendingReply runningSyncs() { QList argumentList; return asyncCallWithArgumentList(QLatin1String("runningSyncs"), argumentList); } //! \see SyncDBusInterface::saveSyncResults() inline QDBusPendingReply saveSyncResults(const QString &aProfileId, const QString &aSyncResults) { QList argumentList; argumentList << qVariantFromValue(aProfileId) << qVariantFromValue(aSyncResults); return asyncCallWithArgumentList(QLatin1String("saveSyncResults"), argumentList); } //! \see SyncDBusInterface::setSyncSchedule() inline QDBusPendingReply setSyncSchedule(const QString &aProfileId, const QString &aScheduleAsXml) { QList argumentList; argumentList << qVariantFromValue(aProfileId) << qVariantFromValue(aScheduleAsXml); return asyncCallWithArgumentList(QLatin1String("setSyncSchedule"), argumentList); } //! \see SyncDBusInterface::startSync() inline QDBusPendingReply startSync(const QString &aProfileId) { QList argumentList; argumentList << qVariantFromValue(aProfileId); return asyncCallWithArgumentList(QLatin1String("startSync"), argumentList); } //! \see SyncDBusInterface::syncProfile() inline QDBusPendingReply syncProfile(const QString &aProfileId) { QList argumentList; argumentList << qVariantFromValue(aProfileId); return callWithArgumentList(QDBus::Block, QLatin1String("syncProfile"), argumentList); } //! \see SyncDBusInterface::syncProfilesByKey inline QDBusPendingReply syncProfilesByKey(const QString &aKey, const QString &aValue) { QList argumentList; argumentList << qVariantFromValue(aKey) << qVariantFromValue(aValue); return asyncCallWithArgumentList(QLatin1String("syncProfilesByKey"), argumentList); } //! \see SyncDBusInterface::syncProfilesByType inline QDBusPendingReply syncProfilesByType(const QString &aType) { QList argumentList; argumentList << qVariantFromValue(aType); return asyncCallWithArgumentList(QLatin1String("syncProfilesByType"), argumentList); } //! \see SyncDBusInterface::updateProfile() inline QDBusPendingReply updateProfile(const QString &aProfileAsXml) { QList argumentList; argumentList << qVariantFromValue(aProfileAsXml); return asyncCallWithArgumentList(QLatin1String("updateProfile"), argumentList); } Q_SIGNALS: // SIGNALS //! \see SyncDBusInterface::backupDone() void backupDone(); //! \see SyncDBusInterface::backupInProgress() void backupInProgress(); //! \see SyncDBusInterface::restoreDone() void restoreDone(); //! \see SyncDBusInterface::restoreInProgress() void restoreInProgress(); //! \see SyncDBusInterface::resultsAvailable() void resultsAvailable(const QString &aProfileName, const QString &aResultsAsXml); //! \see SyncDBusInterface::signalProfileChanged() void signalProfileChanged(const QString &aProfileName, int aChangeType, const QString &aProfileAsXml); //! \see SyncDBusInterface::syncStatus() void syncStatus(const QString &aProfileName, int aStatus, const QString &aMessage, int aErrorCode); //! \see SyncDBusInterface::transferProgress() void transferProgress(const QString &aProfileName, int aTransferDatabase, int aTransferType, const QString &aMimeType, int aCommittedItems); }; #endif buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/libbuteosyncfw.pro0000644000015600001650000001364012634332753025563 0ustar pbuserpbgroup00000000000000TEMPLATE = lib equals(QT_MAJOR_VERSION, 4): TARGET = buteosyncfw equals(QT_MAJOR_VERSION, 5): TARGET = buteosyncfw5 DEPENDPATH += . clientfw common pluginmgr profile INCLUDEPATH += . clientfw common pluginmgr profile VER_MAJ = 0 VER_MIN = 1 VER_PAT = 0 QT += sql xml dbus network QT -= gui CONFIG += dll \ create_pc \ create_prl #DEFINES += BUTEO_ENABLE_DEBUG # Input HEADERS += common/Logger.h \ common/LogMacros.h \ common/SyncCommonDefs.h \ common/TransportTracker.h \ common/NetworkManager.h \ clientfw/SyncClientInterface.h \ clientfw/SyncClientInterfacePrivate.h \ clientfw/SyncDaemonProxy.h \ pluginmgr/ClientPlugin.h \ pluginmgr/DeletedItemsIdStorage.h \ pluginmgr/PluginCbInterface.h \ pluginmgr/PluginManager.h \ pluginmgr/ServerPlugin.h \ pluginmgr/StorageChangeNotifierPlugin.h \ pluginmgr/StorageItem.h \ pluginmgr/StoragePlugin.h \ pluginmgr/SyncPluginBase.h \ profile/BtHelper.h \ profile/Profile.h \ profile/Profile_p.h \ profile/ProfileEngineDefs.h \ profile/ProfileFactory.h \ profile/ProfileField.h \ profile/ProfileManager.h \ profile/StorageProfile.h \ profile/SyncLog.h \ profile/SyncProfile.h \ profile/SyncResults.h \ profile/SyncSchedule.h \ profile/SyncSchedule_p.h \ profile/TargetResults.h \ pluginmgr/OOPClientPlugin.h \ pluginmgr/OOPServerPlugin.h \ pluginmgr/ButeoPluginIface.h SOURCES += common/Logger.cpp \ common/TransportTracker.cpp \ common/NetworkManager.cpp \ clientfw/SyncClientInterface.cpp \ clientfw/SyncClientInterfacePrivate.cpp \ clientfw/SyncDaemonProxy.cpp \ pluginmgr/ClientPlugin.cpp \ pluginmgr/DeletedItemsIdStorage.cpp \ pluginmgr/PluginManager.cpp \ pluginmgr/ServerPlugin.cpp \ pluginmgr/StorageItem.cpp \ pluginmgr/StoragePlugin.cpp \ pluginmgr/SyncPluginBase.cpp \ profile/BtHelper.cpp \ profile/Profile.cpp \ profile/ProfileFactory.cpp \ profile/ProfileField.cpp \ profile/ProfileManager.cpp \ profile/StorageProfile.cpp \ profile/SyncLog.cpp \ profile/SyncProfile.cpp \ profile/SyncResults.cpp \ profile/SyncSchedule.cpp \ profile/TargetResults.cpp \ pluginmgr/OOPClientPlugin.cpp \ pluginmgr/OOPServerPlugin.cpp \ pluginmgr/ButeoPluginIface.cpp usb-moded { message("Building with usb-moded") DEFINES += __USBMODED__ HEADERS += common/USBModedProxy.h SOURCES += common/USBModedProxy.cpp } QMAKE_CXXFLAGS = -Wall \ -g \ -Wno-cast-align \ -O2 -finline-functions # clean QMAKE_CLEAN += $(TARGET) $(TARGET0) $(TARGET1) $(TARGET2) QMAKE_CLEAN += $(OBJECTS_DIR)/moc_* QMAKE_CLEAN += lib$${TARGET}.prl pkgconfig/* # install target.path = /usr/lib/ equals(QT_MAJOR_VERSION, 4): headers.path = /usr/include/buteosyncfw equals(QT_MAJOR_VERSION, 5): headers.path = /usr/include/buteosyncfw5 sources.path = /usr/include/buteosyncfw5/ sources.files = pluginmgr/plugin_main.cpp \ pluginmgr/PluginServiceObj.cpp \ pluginmgr/ButeoPluginIfaceAdaptor.cpp \ pluginmgr/PluginCbImpl.cpp headers.files = common/Logger.h \ common/LogMacros.h \ common/SyncCommonDefs.h \ common/TransportTracker.h \ common/NetworkManager.h \ clientfw/SyncClientInterface.h \ clientfw/SyncClientInterfacePrivate.h \ clientfw/SyncDaemonProxy.h \ pluginmgr/ClientPlugin.h \ pluginmgr/DeletedItemsIdStorage.h \ pluginmgr/PluginCbInterface.h \ pluginmgr/PluginManager.h \ pluginmgr/ServerPlugin.h \ pluginmgr/StorageChangeNotifierPlugin.h \ pluginmgr/StorageItem.h \ pluginmgr/StoragePlugin.h \ pluginmgr/SyncPluginBase.h \ pluginmgr/PluginServiceObj.h \ pluginmgr/ButeoPluginIfaceAdaptor.h \ pluginmgr/ButeoPluginIface.h \ pluginmgr/PluginCbImpl.h \ profile/BtHelper.h \ profile/Profile.h \ profile/Profile_p.h \ profile/ProfileEngineDefs.h \ profile/ProfileFactory.h \ profile/ProfileField.h \ profile/ProfileManager.h \ profile/StorageProfile.h \ profile/SyncLog.h \ profile/SyncProfile.h \ profile/SyncResults.h \ profile/SyncSchedule.h \ profile/SyncSchedule_p.h \ profile/TargetResults.h utility.path = /opt/tests/buteo-syncfw utility.files = ../bin/*.pl \ ../bin/*.sh INSTALLS += target headers utility sources QMAKE_PKGCONFIG_DESTDIR = pkgconfig QMAKE_PKGCONFIG_LIBDIR = $$target.path QMAKE_PKGCONFIG_INCDIR = $$headers.path pkgconfig.files = $${TARGET}.pc # ##################################################################### # make coverage (debug) # ##################################################################### coverage.CONFIG += recursive QMAKE_EXTRA_TARGETS += coverage CONFIG(debug,debug|release){ QMAKE_EXTRA_TARGETS += cov_cxxflags \ cov_lflags cov_cxxflags.target = coverage cov_cxxflags.depends = CXXFLAGS \ += \ -fprofile-arcs \ -ftest-coverage cov_lflags.target = coverage cov_lflags.depends = LFLAGS \ += \ -fprofile-arcs \ -ftest-coverage coverage.commands = @echo \ "Built with coverage support..." build_pass|!debug_and_release : coverage.depends = all QMAKE_CLEAN += $(OBJECTS_DIR)/*.gcda \ $(OBJECTS_DIR)/*.gcno \ $(OBJECTS_DIR)/*.gcov } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/common/0000755000015600001650000000000012634333235023262 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/common/TransportTracker.h0000644000015600001650000000725712634332753026762 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef TRANSPORTTRACKER_H_ #define TRANSPORTTRACKER_H_ #include "SyncCommonDefs.h" #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #include #endif namespace Buteo { class USBModedProxy; class NetworkManager; /*! \brief Class for tracking transport states * * USB state is tracked with HAL, BT with Context Framework and Internet states with Buteo::NetworkManager. */ class TransportTracker : public QObject { Q_OBJECT public: /*! \brief Constructor * * @param aParent Parent object */ TransportTracker(QObject *aParent = 0); //! \brief Destructor virtual ~TransportTracker(); /*! \brief Checks the state of the given connectivity type * * @param aType Connectivity type * @return True if available, false if not */ bool isConnectivityAvailable(Sync::ConnectivityType aType) const; signals: /*! \brief Signal emitted when a connectivity state changes * * @param aType Connectivity type whose state has changed * @param aState New state. True if available, false if not. */ void connectivityStateChanged(Sync::ConnectivityType aType, bool aState); /*! \brief Signal emitted when a n/w state changes * * @param aState New state. True if available, false if not. * @param aType Connection type. The type of connetcion with the Internet. */ void networkStateChanged(bool aState, Sync::InternetConnectionType aType); /*! \brief Signal emitted when a network session is successfully opened */ void sessionConnected(); /*! \brief Signal emitted when opening a network session fails */ void sessionError(); private slots: void onUsbStateChanged(bool aConnected); #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) void onBtStateChanged(bool aState); #else void onBtStateChanged(QString aKey, QDBusVariant aValue); #endif void onInternetStateChanged(bool aConnected, Sync::InternetConnectionType aType); private: QMap iTransportStates; USBModedProxy *iUSBProxy; #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) QtMobility::QSystemDeviceInfo iDeviceInfo; #endif NetworkManager *iInternet; QDBusConnection *iSystemBus; mutable QMutex iMutex; /*! \brief updates the state of the given connectivity type to input value * * @param aType Connectivity type * @param aState Connectivity State */ void updateState(Sync::ConnectivityType aType, bool aState); #ifdef SYNCFW_UNIT_TESTS friend class TransportTrackerTest; friend class SynchronizerTest; #endif bool btConnectivityStatus(); }; } #endif /* TRANSPORTTRACKER_H_ */ buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/common/NetworkManager.cpp0000644000015600001650000002316712634332753026727 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include #include "NetworkManager.h" #include "LogMacros.h" using namespace Buteo; int NetworkManager::m_refCount = 0; bool NetworkManager::m_isSessionActive = false; NetworkManager::NetworkManager(QObject *parent /* = 0*/) : QObject(parent), m_networkConfigManager(0), m_networkSession(0), m_isOnline(false), m_errorEmitted(false), m_sessionTimer(0), m_connectionType(Sync::INTERNET_CONNECTION_UNKNOWN) { FUNCTION_CALL_TRACE; m_networkConfigManager = new QNetworkConfigurationManager(); Q_ASSERT(m_networkConfigManager); // check for network status and configuration change (switch wifi, ethernet, mobile) a connect(m_networkConfigManager, SIGNAL(onlineStateChanged(bool)), SLOT(slotConfigurationChanged()), Qt::QueuedConnection); connect(m_networkConfigManager, SIGNAL(configurationAdded(QNetworkConfiguration)), SLOT(slotConfigurationChanged()), Qt::QueuedConnection); connect(m_networkConfigManager, SIGNAL(configurationChanged(QNetworkConfiguration)), SLOT(slotConfigurationChanged()), Qt::QueuedConnection); connect(m_networkConfigManager, SIGNAL(configurationRemoved(QNetworkConfiguration)), SLOT(slotConfigurationChanged()), Qt::QueuedConnection); connect(m_networkConfigManager, SIGNAL(updateCompleted()), SLOT(slotConfigurationChanged()), Qt::QueuedConnection); connect(&m_idleRefreshTimer, SIGNAL(timeout()), SLOT(idleRefresh()), Qt::QueuedConnection); m_idleRefreshTimer.setSingleShot(true); m_isOnline = m_networkConfigManager->isOnline(); LOG_DEBUG("Online status::" << m_isOnline); m_sessionTimer = new QTimer(this); m_sessionTimer->setSingleShot(true); m_sessionTimer->setInterval(10000); connect(m_sessionTimer,SIGNAL(timeout()),this,SLOT(sessionConnectionTimeout())); } NetworkManager::~NetworkManager() { FUNCTION_CALL_TRACE; if(m_networkSession) { delete m_networkSession; m_networkSession = 0; } if(m_networkConfigManager) { delete m_networkConfigManager; m_networkConfigManager = 0; } } bool NetworkManager::isOnline() { FUNCTION_CALL_TRACE; return m_isOnline; } Sync::InternetConnectionType NetworkManager::connectionType() const { return m_connectionType; } void NetworkManager::connectSession(bool connectInBackground /* = false*/) { FUNCTION_CALL_TRACE; if(m_isSessionActive) { LOG_DEBUG("Network session already active, ignoring connect call"); m_refCount++; emit connectionSuccess(); return; } else if(!m_networkSession) { QNetworkConfiguration netConfig = m_networkConfigManager->defaultConfiguration(); m_networkSession = new QNetworkSession(netConfig); m_errorEmitted = false; Q_ASSERT(m_networkSession); connect(m_networkSession, SIGNAL(error(QNetworkSession::SessionError)), SLOT(slotSessionError(QNetworkSession::SessionError))); connect(m_networkSession, SIGNAL(stateChanged(QNetworkSession::State)), SLOT(slotSessionState(QNetworkSession::State))); connect(m_networkSession, SIGNAL(opened()), SIGNAL(connectionSuccess())); } m_networkSession->setSessionProperty("ConnectInBackground", connectInBackground); if(!m_networkSession->isOpen()) { m_networkSession->open(); // Fail after 10 sec if no network reply is received m_sessionTimer->start(); } else { slotSessionState(m_networkSession->state()); } } void NetworkManager::sessionConnectionTimeout() { if (!m_errorEmitted && m_networkSession) { if (!m_networkSession->isOpen()) { LOG_WARNING("No network reply received after 10 seconds, emitting session error."); slotSessionError(m_networkSession->error()); } } } void NetworkManager::slotConfigurationChanged() { // wait for 3 secs before update connection status // this avoid problems with connections that take a while to be stabilished m_idleRefreshTimer.start(3000); } void NetworkManager::idleRefresh() { FUNCTION_CALL_TRACE; QList activeConfigs = m_networkConfigManager->allConfigurations(QNetworkConfiguration::Active); QNetworkConfiguration::BearerType connectionType = QNetworkConfiguration::BearerUnknown; QString bearerTypeName; bool isOnline = activeConfigs.size() > 0; if (isOnline) { // FIXME: due this bug lp:#1444162 on nm the QNetworkConfigurationManager // returns the wrong default connection. // We will consider the connection with the smallest bearer as the // default connection, with that wifi and ethernet will be the first one // https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1444162 connectionType = activeConfigs.first().bearerType(); bearerTypeName = activeConfigs.first().bearerTypeName(); foreach(const QNetworkConfiguration &conf, activeConfigs) { if (conf.bearerType() < connectionType) { connectionType = conf.bearerType(); bearerTypeName = conf.bearerTypeName(); } } } LOG_DEBUG("New state:" << isOnline << " New type: " << bearerTypeName << "(" << connectionType << ")"); if ((isOnline != m_isOnline) || ((Sync::InternetConnectionType)connectionType != m_connectionType)) { m_isOnline = isOnline; m_connectionType = (Sync::InternetConnectionType) connectionType; emit statusChanged(m_isOnline, m_connectionType); } } void NetworkManager::disconnectSession() { FUNCTION_CALL_TRACE; if(m_refCount > 0) { m_refCount--; } if(m_networkSession && 0 == m_refCount) { if (m_sessionTimer->isActive()) m_sessionTimer->stop(); m_networkSession->close(); delete m_networkSession; m_networkSession = NULL; } } void NetworkManager::slotSessionState(QNetworkSession::State status) { FUNCTION_CALL_TRACE; switch(status) { case QNetworkSession::Invalid: LOG_WARNING("QNetworkSession::Invalid"); m_isSessionActive = false; break; case QNetworkSession::NotAvailable: LOG_WARNING("QNetworkSession::NotAvailable"); m_isSessionActive = false; emit connectionError(); break; case QNetworkSession::Connecting: LOG_DEBUG("QNetworkSession::Connecting"); m_isSessionActive = false; break; case QNetworkSession::Connected: LOG_WARNING("QNetworkSession::Connected"); if (m_networkSession->isOpen() && m_networkSession->state() == QNetworkSession::Connected) { m_isSessionActive = true; emit connectionSuccess(); } else { emit connectionError(); } break; case QNetworkSession::Closing: LOG_WARNING("QNetworkSession::Closing"); m_isSessionActive = false; break; case QNetworkSession::Disconnected: LOG_DEBUG("QNetworkSession::Disconnected"); m_isSessionActive = false; break; case QNetworkSession::Roaming: LOG_WARNING("QNetworkSession::Roaming"); m_isSessionActive = false; break; default: LOG_WARNING("QNetworkSession:: Unknown status change"); m_isSessionActive = false; break; } } void NetworkManager::slotSessionError(QNetworkSession::SessionError error) { FUNCTION_CALL_TRACE; // Emit network errors only once per request if (m_errorEmitted) { m_errorEmitted = false; return; } else { m_errorEmitted = true; } switch(error) { case QNetworkSession::UnknownSessionError: LOG_WARNING("QNetworkSession::UnknownSessionError"); emit connectionError(); break; case QNetworkSession::SessionAbortedError: LOG_DEBUG("QNetworkSession::SessionAbortedError"); emit connectionError(); break; case QNetworkSession::RoamingError: LOG_WARNING("QNetworkSession::RoamingError"); emit connectionError(); break; case QNetworkSession::OperationNotSupportedError: LOG_WARNING("QNetworkSession::OperationNotSupportedError"); emit connectionError(); break; case QNetworkSession::InvalidConfigurationError: LOG_WARNING("QNetworkSession::InvalidConfigurationError"); emit connectionError(); break; default: LOG_WARNING("QNetworkSession:: Invalid error code"); emit connectionError(); break; } } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/common/LogMacros.h0000644000015600001650000000577112634332753025337 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef LOGMACROS_H #define LOGMACROS_H #include #include #include #include #include "Logger.h" //! Helper macro for writing log messages. Avoid using directly. #define LOG_MSG_L(level, msg) if(Buteo::Logger::instance()->enabled())(QDebug((QtMsgType)(level)) << __FILE__ << __LINE__ << ":" << msg) #define LOG_MSG_L_PLAIN(level, msg) if(Buteo::Logger::instance()->enabled())(QDebug((QtMsgType)(level)) << msg) //! Macros for writing log messages. Use these. //! Messages with level below warning are enabled only in debug builds. #define LOG_FATAL(msg) qFatal(msg) #define LOG_CRITICAL(msg) qCritical() << msg #define LOG_WARNING(msg) qWarning() << msg // use relevant logging level numbers from syslog.h where possible # define LOG_PROTOCOL(msg) if (Buteo::Logger::instance()->getLogLevel() >= 6) qDebug() << msg # define LOG_INFO(msg) if (Buteo::Logger::instance()->getLogLevel() >= 6) qDebug() << msg # define LOG_DEBUG(msg) if (Buteo::Logger::instance()->getLogLevel() >= 7) qDebug() << msg # define LOG_TRACE(msg) if (Buteo::Logger::instance()->getLogLevel() >= 8) qDebug() << msg # define LOG_TRACE_PLAIN(msg) if (Buteo::Logger::instance()->getLogLevel() >= 8) qDebug() << msg /*! * Creates a trace message to log when the function is entered and exited. * Logs also to time spent in the function. */ # define FUNCTION_CALL_TRACE if (Buteo::Logger::instance()->getLogLevel() >= 9) Buteo::LogTimer timerDebugVariable(QString(__PRETTY_FUNCTION__)); namespace Buteo { /*! * \brief Helper class for timing function execution time. */ class LogTimer { public: /*! * \brief Constructor. Creates an entry message to the log. * * @param aFunc Name of the function. */ LogTimer(const QString &aFunc) : iFunc(aFunc) { LOG_TRACE_PLAIN(iFunc << ":Entry"); iTimer.start(); } /*! * \brief Destructor. Creates an exit message to the log, including * function execution time. */ ~LogTimer() { LOG_TRACE_PLAIN(iFunc << ":Exit, execution time:" << iTimer.elapsed() << "ms"); } private: QTime iTimer; QString iFunc; }; } #endif // LOGMACROS_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/common/USBModedProxy.h0000644000015600001650000000623412634332753026110 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ /* * This file was generated by qdbusxml2cpp version 0.7 * Command line was: qdbusxml2cpp -v -p USBModedProxy -c USBModedProxy usb_moded.xml * * qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * This is an auto-generated file, with several edits. * If this file is to be regenerated, the changes must be backed up and merged */ #ifndef USBMODEDPROXY_H_1272105195 #define USBMODEDPROXY_H_1272105195 #include #include #include #include #include #include #include #include namespace Buteo { /*! \brief - Proxy class for interface com.meego.usb_moded */ class USBModedProxy: public QDBusAbstractInterface { Q_OBJECT public: /*! \brief - returns the static interface name */ static inline const char *staticInterfaceName() { return "com.meego.usb_moded"; } public: /*! \brief - Constructor * *@param parent - pointer to parent object */ USBModedProxy(QObject *parent = 0); /*! \brief - Destructor * */ ~USBModedProxy(); /*! \brief - function to check if usb is connected or not * *@return - Returns true if the USB cable is connected in * the Ovi Suite mode,false, if it's in any other mode, * or if it isn't connected */ bool isUSBConnected(); public Q_SLOTS: // METHODS /*! \brief - connected to usbmoded proxy's sig_usb_state_ind signal * * @param mode - new mode to which usb has changed to * */ void slotModeChanged(const QString &mode); /*! \brief - method to make a DBUS call to USB moded daemon * *@return - result of the request as QString */ inline QDBusPendingReply mode_request() { QList argumentList; return asyncCallWithArgumentList(QLatin1String("mode_request"), argumentList); } Q_SIGNALS: // SIGNALS /*! \brief - overridden signal from usb moded proxy. * *@param mode new mode */ void sig_usb_state_ind(const QString &mode); /*! \brief - this is emitted on receiving sig_usb_state_ind * from usb moded daemon * * @param bConnected - boolean flag to indicate whether the connection is successful or not */ void usbConnection(bool bConnected); }; } namespace com { namespace meego { typedef Buteo::USBModedProxy usb_moded; } } #endif buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/common/usb_moded.xml0000644000015600001650000000236212634332753025754 0ustar pbuserpbgroup00000000000000 buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/common/Logger.h0000644000015600001650000001067412634332753024666 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef LOGGER_H #define LOGGER_H #include #include #include #include #include #include #include namespace Buteo { /*! * \brief A logger singleton class. * * Using the logger is thread safe, but creating the instance is not. * Make sure that the instance is created before any threads that use the * logger are started. This can be done by calling createInstance explicitly, * or by simply logging some message, which will automatically create the * logger instance with default parameters. */ class Q_DECL_EXPORT Logger { public: static const int NUM_LEVELS = 4; //! Default indent size. static const int DEFAULT_INDENT_SIZE; /*! * \brief Returns the logger instance. * * If the instance is not yet created, creates it with default parameters. * @return The instance. */ static Logger *instance(); //! Destructor. ~Logger(); /*! * \brief Creates a logger instance. * * If an instance already exists, deletes the old instance first. * This function should be called in the beginning of the program * before any threads using the logger are created, because creating * the log instance is not thread safe. * @param aLogFileName Name of the file where log messages are written. * If this is empty, messages are not written to a file. * @param aUseStdOut Should messages be written to standard output. * @param aIndentSize Number of spaces that each indent level inserts. */ static void createInstance(const QString &aLogFileName = "", bool aUseStdOut = false, int aIndentSize = DEFAULT_INDENT_SIZE); //! Deletes the logger instance. Closes the log file in a controlled way. static void deleteInstance(); /*! * \brief Enables given log levels. * @param aLevels Log levels to enable. Default enables all levels. */ void enable(const QBitArray &aLevels = QBitArray(NUM_LEVELS, true)); /*! * \brief Disables given log levels. * @param aLevels Log levels to disable. Default disables all levels. */ void disable(const QBitArray &aLevels = QBitArray(NUM_LEVELS, true)); /*! * \brief Adds one indent level. */ void push(); /*! * \brief Removes one indent level. */ void pop(); /*! * \brief Writes a message to the log. * * @param aLevel Message level. * @param aMsg Message. */ void write(int aLevel, const char *aMsg); /*! * \brief Sets logging level. * * Messages with the given level and levels more severe than it will be * enabled. Qt built-in log levels will also be enabled. * @param aLevel Logging level. */ bool setLogLevel(int aLevel); /*! * \brief Gets logging level BitArray * * Use this API to count the levels the BitArray has been set to true */ QBitArray getLogLevelArray(); /*! * \brief Gets the logging level as a single number. */ int getLogLevel() const; bool enabled(){return iEnabled;} private: Logger(const QString &aLogFileName, bool aUseStdOut, int aIndentSize); static int defaultLogLevel(); static Logger *sInstance; QBitArray iEnabledLevels; int iIndentLevel; int iIndentSize; QFile iFile; QTextStream *iFileStream; QTextStream *iStdOutStream; QTextStream *iStdErrStream; QMutex iMutex; bool iEnabled; int iLogLevel; }; } #endif // LOGGER_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/common/NetworkManager.h0000644000015600001650000001151612634332753026367 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef NETWORKMANAGER_H_ #define NETWORKMANAGER_H_ #include #include #include "SyncCommonDefs.h" class QNetworkConfigurationManager; namespace Buteo { /*! \brief Class for managing network sessions * * This class provides APIs to open and close network sessions. It internally * uses QNetworkSession set of classes to manage network sessions. The user * while creating a new session can choose whether to pop-up the internet * connectivity dialog or not. The class also signals about online status of * the device. */ class NetworkManager : public QObject { Q_OBJECT public: /*! \brief Constructor * * @param parent Parent object */ NetworkManager(QObject *parent = 0); /*! \brief Destructor * */ ~NetworkManager(); /*! \brief Returns if the device is currently online, i.e, a data * sessions is possible. * * @return True if device is online, false if not */ bool isOnline(); /*! \brief Returns the type of connection used by the device. * * @return Sync::InternetConnectionType the type of connection. */ Sync::InternetConnectionType connectionType() const; /*! \brief Connects a new network session. If a session was already * open, the signal connectionSuccess will be emitted immediately, * else the function will return and the signal connectionSuccess or * connectionError will be emitted accordingly. The caller can * choose whether to show the network connectivity dialog, or just * open the default network configuration in the background using * the parameter connetInBackground. * * @param connectInBackground If true, the connection is opened * without popping up the connetion dialog */ void connectSession(bool connectInBackground = false); /*! \brief Disconnects an open session * */ void disconnectSession(); signals: /*! \brief This signal is emitted when the device's online status * changes * * @param aConnected If true, the device is online */ void statusChanged(bool aConnected, Sync::InternetConnectionType aType); /*! \brief This signal is emitted when a network session gets * connected * */ void connectionSuccess(); /*! \brief This signal is emitted when opening a network session * fails * */ void connectionError(); private: QNetworkConfigurationManager *m_networkConfigManager; // QT network configuration manager QNetworkSession *m_networkSession; // QT network session static bool m_isSessionActive; // Flag to indicate if a network session is active bool m_isOnline; // Flag to indicate if the device is online static int m_refCount; // Reference counter for number of open connections bool m_errorEmitted; // Network error emited flag QTimer *m_sessionTimer; Sync::InternetConnectionType m_connectionType; QTimer m_idleRefreshTimer; private slots: void slotSessionState(QNetworkSession::State status); void slotSessionError(QNetworkSession::SessionError error); void sessionConnectionTimeout(); void slotConfigurationChanged(); void idleRefresh(); }; } #endif//NETWORKMANAGER_H_ buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/common/USBModedProxy.cpp0000644000015600001650000000531212634332753026437 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ /* * This file was generated by qdbusxml2cpp version 0.7 * Command line was: qdbusxml2cpp -v -p USBModedProxy -c USBModedProxy usb_moded.xml * * qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * This is an auto-generated file, with several edits. * If this file is to be regenerated, the changes must be backed up and merged */ #include #include "USBModedProxy.h" #include "LogMacros.h" using namespace Buteo; static const QString USB_MODE_SERVICE("com.meego.usb_moded"); static const QString USB_MODE_OBJECT("/com/meego/usb_moded"); static const QString SYNC_MODE_NAME("pc_suite"); /* * Implementation of interface class USBModedProxy */ USBModedProxy::USBModedProxy(QObject *parent) : QDBusAbstractInterface(USB_MODE_SERVICE, USB_MODE_OBJECT, staticInterfaceName(), QDBusConnection::systemBus(), parent) { FUNCTION_CALL_TRACE; if(false == QObject::connect(this, SIGNAL(sig_usb_state_ind(const QString&)), this, SLOT(slotModeChanged(const QString&)))) { LOG_CRITICAL("Failed to connect to USB moded signal! USB notifications will not be available."); } } USBModedProxy::~USBModedProxy() { } void USBModedProxy::slotModeChanged(const QString &mode) { FUNCTION_CALL_TRACE; bool isConnected = false; if(SYNC_MODE_NAME == mode) { isConnected = true; } emit usbConnection(isConnected); } bool USBModedProxy::isUSBConnected() { FUNCTION_CALL_TRACE; bool isConnected = false; QDBusPendingReply reply = this->mode_request(); reply.waitForFinished(); if(reply.isError()) { LOG_CRITICAL("Get mode returns::" << reply.error()); } else { LOG_INFO("USB connected in mode::" << reply.value()); if(SYNC_MODE_NAME == reply.value()) { isConnected = true; } } return isConnected; } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/common/SyncCommonDefs.h0000644000015600001650000000642312634332753026333 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCCOMMONDEFS_H #define SYNCCOMMONDEFS_H #include #include #include namespace Sync { #ifdef __GNUC__ static const QString syncCacheDir() __attribute__ ((unused)); #endif static const QString syncCacheDir() { const QString HOME_PATH = (::getenv("XDG_CACHE_HOME") == NULL) ? QDir::homePath() + QDir::separator() + ".cache" : ::getenv("XDG_CACHE_HOME"); return HOME_PATH + QDir::separator() + "msyncd"; } enum SyncStatus { SYNC_QUEUED = 0, SYNC_STARTED, SYNC_PROGRESS, SYNC_ERROR, SYNC_DONE, SYNC_ABORTED, SYNC_CANCELLED, SYNC_STOPPING, SYNC_NOTPOSSIBLE, SYNC_AUTHENTICATION_FAILURE, SYNC_DATABASE_FAILURE, SYNC_CONNECTION_ERROR, SYNC_SERVER_FAILURE, SYNC_BAD_REQUEST, SYNC_PLUGIN_ERROR, SYNC_PLUGIN_TIMEOUT }; // UI needs to display a detailed Progress for the Current ongoing sync enum SyncProgressDetail { SYNC_PROGRESS_INITIALISING = 201, SYNC_PROGRESS_SENDING_ITEMS , SYNC_PROGRESS_RECEIVING_ITEMS, SYNC_PROGRESS_FINALISING }; enum TransferDatabase { LOCAL_DATABASE = 0, REMOTE_DATABASE }; enum TransferType { ITEM_ADDED = 0, ITEM_MODIFIED, ITEM_DELETED, ITEM_ERROR }; enum ConnectivityType { CONNECTIVITY_USB, CONNECTIVITY_BT, CONNECTIVITY_INTERNET }; enum InternetConnectionType { INTERNET_CONNECTION_UNKNOWN = QNetworkConfiguration::BearerUnknown, INTERNET_CONNECTION_ETHERNET = QNetworkConfiguration::BearerEthernet, INTERNET_CONNECTION_WLAN = QNetworkConfiguration::BearerWLAN, INTERNET_CONNECTION_2G = QNetworkConfiguration::Bearer2G, INTERNET_CONNECTION_3G = QNetworkConfiguration::Bearer3G, INTERNET_CONNECTION_4G = QNetworkConfiguration::Bearer4G, INTERNET_CONNECTION_CDMA2000 = QNetworkConfiguration::BearerCDMA2000, INTERNET_CONNECTION_WCDMA = QNetworkConfiguration::BearerWCDMA, INTERNET_CONNECTION_HSPA = QNetworkConfiguration::BearerHSPA, INTERNET_CONNECTION_BLUETOOTH = QNetworkConfiguration::BearerBluetooth, INTERNET_CONNECTION_WIMAX = QNetworkConfiguration::BearerWiMAX, INTERNET_CONNECTION_EVDO = QNetworkConfiguration::BearerEVDO, INTERNET_CONNECTION_LTE = QNetworkConfiguration::BearerLTE }; } // namespace Sync Q_DECLARE_METATYPE( Sync::SyncStatus ); Q_DECLARE_METATYPE( Sync::TransferDatabase ); Q_DECLARE_METATYPE( Sync::TransferType ); Q_DECLARE_METATYPE( Sync::ConnectivityType ); #endif // SYNCCOMMONDEFS_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/common/Logger.cpp0000644000015600001650000001340612634332753025215 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "Logger.h" #include #include #include #include #include #include using namespace Buteo; const int Logger::DEFAULT_INDENT_SIZE = 4; Logger *Logger::sInstance = NULL; Logger *Logger::instance() { if (NULL == sInstance) { createInstance(); } return sInstance; } int Logger::defaultLogLevel() { QByteArray levelByteArray = qgetenv("MSYNCD_LOGGING_LEVEL"); QString levelStr = QString::fromLocal8Bit(levelByteArray.constData()); bool ok = false; int level = levelStr.toInt(&ok); if (!ok) { level = 4; // warning level } return level; } void Logger::createInstance(const QString &aLogFileName, bool aUseStdOut, int aIndentSize) { deleteInstance(); sInstance = new Logger(aLogFileName, aUseStdOut, aIndentSize); } void Logger::deleteInstance() { delete sInstance; sInstance = NULL; } Logger::Logger(const QString &aLogFileName, bool aUseStdOut, int aIndentSize) : iEnabledLevels(NUM_LEVELS), iIndentLevel(0), iIndentSize(aIndentSize), iFileStream(0), iStdOutStream(0), iStdErrStream(0), iEnabled(false), iLogLevel(0) { iLogLevel = defaultLogLevel(); if (aUseStdOut) { // Create a stream for writing log messages to standard output. iStdOutStream = new QTextStream(stdout); } // no else iStdErrStream = new QTextStream(stderr); if (!aLogFileName.isEmpty()) { // Create stream for writing log messages to a file. iFile.setFileName(aLogFileName); QFileInfo fileInfo(iFile); QDir dir; dir.mkpath(fileInfo.absolutePath()); if (iFile.open(QIODevice::WriteOnly | QIODevice::Append)) { iFileStream = new QTextStream(&iFile); } // no else } // no else } Logger::~Logger() { delete iFileStream; iFileStream = 0; delete iStdOutStream; iStdOutStream = 0; delete iStdErrStream; iStdErrStream = 0; } bool Logger::setLogLevel(int aLevel) { bool retVal = false; if ((aLevel > 0) && (aLevel <= NUM_LEVELS)) { iLogLevel = aLevel; disable(iEnabledLevels); QBitArray iLevels(NUM_LEVELS, false); for(int i = aLevel; i > 0; i--) { iLevels.setBit(NUM_LEVELS - i); } enable(iLevels); retVal = true; } // no else return retVal; } QBitArray Logger::getLogLevelArray() { return iEnabledLevels; } int Logger::getLogLevel() const { return iLogLevel; } void Logger::enable(const QBitArray &aLevels) { QMutexLocker lock(&iMutex); iEnabledLevels |= aLevels; iEnabled = true; } void Logger::disable(const QBitArray &aLevels) { QMutexLocker lock(&iMutex); iEnabledLevels &= ~(aLevels); iEnabled = false; } void Logger::push() { QMutexLocker lock(&iMutex); iIndentLevel += iIndentSize; } void Logger::pop() { QMutexLocker lock(&iMutex); iIndentLevel -= iIndentSize; if (iIndentLevel < 0) iIndentLevel = 0; } void Logger::write(int aLevel, const char *aMsg) { static const char* levelTexts[NUM_LEVELS] = { "Debug: ", "Warning: ", "Critical: ", "Fatal: ", }; int syslogLevel[NUM_LEVELS] = { LOG_DEBUG, LOG_WARNING, LOG_CRIT, LOG_CRIT, } ; QMutexLocker lock(&iMutex); // Verify that the log message can and should be written. if (aLevel < QtDebugMsg || aLevel > QtFatalMsg ) { return; } // We don't make use of format specifiers in our logs, but syslog (vprintf) might interpret any % as part of // a format specifier and might fail (crash) when it doesn't find values corresponding to the format, prevent that. QString sysLogMsg = QString::fromLocal8Bit(aMsg); sysLogMsg = sysLogMsg.remove("%"); if(!iEnabledLevels.count(true)) { if(aLevel >= QtCriticalMsg) { syslog(LOG_CRIT, "%s", sysLogMsg.toLocal8Bit().data()); } return; } // Verify that the log message can and should be written. if (!iEnabledLevels.testBit(aLevel)) { return; } syslog(syslogLevel[aLevel], "%s", sysLogMsg.toLocal8Bit().data()); if (iFileStream != 0) { *iFileStream << QString(iIndentLevel, ' ') << levelTexts[aLevel] << aMsg << "\n"; iFileStream->flush(); } if (aLevel != QtDebugMsg) { if (iStdErrStream != 0) { *iStdErrStream << QString(iIndentLevel, ' ') << levelTexts[aLevel] << aMsg << "\n"; iStdErrStream->flush(); } } else if (iStdOutStream != 0) { *iStdOutStream << QString(iIndentLevel, ' ') << levelTexts[aLevel] << aMsg << "\n"; iStdOutStream->flush(); } if (QtFatalMsg == aLevel) { // Exit on fatal message. abort(); } // no else } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/common/TransportTracker.cpp0000644000015600001650000001727212634332753027313 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "TransportTracker.h" #if __USBMODED__ #include "USBModedProxy.h" #endif #include "NetworkManager.h" #include "LogMacros.h" #include #include #include #include using namespace Buteo; TransportTracker::TransportTracker(QObject *aParent) : QObject(aParent), iUSBProxy(0), iInternet(0), iSystemBus(0) { FUNCTION_CALL_TRACE; iTransportStates[Sync::CONNECTIVITY_USB] = false; iTransportStates[Sync::CONNECTIVITY_BT] = false; iTransportStates[Sync::CONNECTIVITY_INTERNET] = false; #if __USBMODED__ // USB iUSBProxy = new USBModedProxy(this); if (!iUSBProxy->isValid()) { LOG_CRITICAL("Failed to connect to USB moded D-Bus interface"); delete iUSBProxy; iUSBProxy = NULL; } else { QObject::connect(iUSBProxy, SIGNAL(usbConnection(bool)), this, SLOT(onUsbStateChanged(bool))); iTransportStates[Sync::CONNECTIVITY_USB] = iUSBProxy->isUSBConnected(); } #endif // BT #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) // Set the bluetooth state iTransportStates[Sync::CONNECTIVITY_BT] = btConnectivityStatus(); // Add signal to track the bluetooth state changes iSystemBus = new QDBusConnection(QDBusConnection::connectToBus(QDBusConnection::SystemBus, QStringLiteral("buteo_system_bus"))); if (!iSystemBus->connect("org.bluez", "", "org.bluez.Adapter", "PropertyChanged", this, SLOT(onBtStateChanged(QString, QDBusVariant) ))) { LOG_WARNING("Unable to connect to system bus for org.bluez.Adapter"); } #else iTransportStates[Sync::CONNECTIVITY_BT] = iDeviceInfo.currentBluetoothPowerState(); QObject::connect(&iDeviceInfo, SIGNAL(bluetoothStateChanged(bool)), this, SLOT(onBtStateChanged(bool))); LOG_DEBUG("Current bluetooth power state"<isOnline(); connect(iInternet, SIGNAL(statusChanged(bool, Sync::InternetConnectionType)), SLOT(onInternetStateChanged(bool, Sync::InternetConnectionType)) /*, Qt::QueuedConnection*/); } else { LOG_WARNING("Failed to listen for Internet state changes"); } } TransportTracker::~TransportTracker() { FUNCTION_CALL_TRACE; delete iSystemBus; } bool TransportTracker::isConnectivityAvailable(Sync::ConnectivityType aType) const { FUNCTION_CALL_TRACE; QMutexLocker locker(&iMutex); return iTransportStates[aType]; } void TransportTracker::onUsbStateChanged(bool aConnected) { FUNCTION_CALL_TRACE; LOG_DEBUG("USB state changed:" << aConnected); updateState(Sync::CONNECTIVITY_USB, aConnected); } #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) void TransportTracker::onBtStateChanged(bool aState) { FUNCTION_CALL_TRACE; Q_UNUSED(aState); bool btPowered = iDeviceInfo.currentBluetoothPowerState(); LOG_DEBUG("BT power state" << btPowered); updateState(Sync::CONNECTIVITY_BT, btPowered); } #else void TransportTracker::onBtStateChanged(QString aKey, QDBusVariant aValue) { FUNCTION_CALL_TRACE; if (aKey == "Powered") { bool btPowered = aValue.variant().toBool(); LOG_DEBUG("BT power state " << btPowered); updateState(Sync::CONNECTIVITY_BT, btPowered); } } #endif void TransportTracker::onInternetStateChanged(bool aConnected, Sync::InternetConnectionType aType) { FUNCTION_CALL_TRACE; LOG_DEBUG("Internet state changed:" << aConnected); updateState(Sync::CONNECTIVITY_INTERNET, aConnected); emit networkStateChanged(aConnected, aType); } void TransportTracker::updateState(Sync::ConnectivityType aType, bool aState) { FUNCTION_CALL_TRACE; bool oldState = false; { QMutexLocker locker(&iMutex); oldState = iTransportStates[aType]; iTransportStates[aType] = aState; } if(oldState != aState) { if (aType != Sync::CONNECTIVITY_INTERNET) { emit connectivityStateChanged(aType, aState); } } } bool TransportTracker::btConnectivityStatus() { FUNCTION_CALL_TRACE; bool btOn = false; QDBusConnection *systemBus = new QDBusConnection(QDBusConnection::connectToBus(QDBusConnection::SystemBus, QStringLiteral("buteo_system_bus2"))); QDBusMessage methodCallMsg = QDBusMessage::createMethodCall("org.bluez", "/", "org.bluez.Manager", "DefaultAdapter"); QDBusMessage reply = systemBus->call(methodCallMsg); if (reply.type() == QDBusMessage::ErrorMessage) { LOG_WARNING("This device does not have a BT adapter"); delete systemBus; return btOn; } QList adapterList = reply.arguments(); // We will take the first adapter in the list QString adapterPath = qdbus_cast(adapterList.at(0)).path(); if (!adapterPath.isEmpty() || !adapterPath.isNull()) { // Retrive the properties of the adapter and check for "Powered" key methodCallMsg = QDBusMessage::createMethodCall("org.bluez", adapterPath, "org.bluez.Adapter", "GetProperties"); reply = systemBus->call(methodCallMsg); if (reply.type() == QDBusMessage::ErrorMessage) { LOG_WARNING("Error in retrieving bluetooth properties"); delete systemBus; return btOn; } QDBusArgument arg = reply.arguments().at(0).value(); if (arg.currentType() == QDBusArgument::MapType) { // Scan through the dict returned and check for "Powered" entry QMap dict = qdbus_cast >(arg); QMap::iterator iter; for(iter = dict.begin(); iter != dict.end(); ++iter) { if (iter.key() == "Powered") { btOn = iter.value().toBool(); LOG_DEBUG ("Bluetooth powered on? " << btOn); break; } } } } delete systemBus; return btOn; } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/0000755000015600001650000000000012634333235023432 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/ProfileFactory.h0000644000015600001650000000463412634332753026546 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef PROFILEFACTORY_H #define PROFILEFACTORY_H #include "Profile.h" namespace Buteo { /*! \brief ProfileFactory handles creating Profile instances. * * ProfileFactory knows all the classed derived from the Profile class and * creates an instance of a correct class based on the profile type given as a * parameter for the create function. By using the ProfileFactory it is * possible to create profiles from a component that is not aware of the * different derived profile classes, especially from the Profile class itself. */ class ProfileFactory { public: //! \brief Constructor. ProfileFactory(); /*! \brief Creates an empty profile with the given name and type. * * An instance of the correct class derived from Profile is created based * on the given profile type. If the type is not recognizer as a specific * derived class, an instance of the Profile base class is created. * \param aName Name of the profile. * \param aType Type of the profile. * \return The created profile. */ Profile *createProfile(const QString &aName, const QString &aType); /*! \brief Creates a profile from XML. * * An instance of the correct class derived from Profile is created based * on the profile type read from XML. If the type is not recognizer as a * specific derived class, an instance of the Profile base class is * created. * \param aRoot Root element of the profile XML. * \return Created profile. */ Profile *createProfile(const QDomElement &aRoot); }; } #endif // PROFILEFACTORY_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/StorageProfile.h0000644000015600001650000000421212634332753026533 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef STORAGEPROFILE_H #define STORAGEPROFILE_H #include "Profile.h" namespace Buteo { class StorageProfilePrivate; /*! \brief Storage Profile Class */ class StorageProfile : public Profile { public: /*! \brief Constructs an empty StorageProfile with the given name. * * \param aName Name of the profile to create. */ explicit StorageProfile(const QString &aName); /*! \brief Constructs a profile from the given XML. * * \param aRoot Root element of the XML node tree. */ explicit StorageProfile(const QDomElement &aRoot); /*! \brief Copy constructor. * * \param aSource Copy source. */ StorageProfile(const StorageProfile &aSource); /*! \brief Destructor. */ ~StorageProfile(); /*! \brief Creates a clone of the profile. * * \return The clone. */ virtual StorageProfile *clone() const; /*! \brief Returns if the profile is enabled. * * Storage profile is disabled by default. This means that if there is no * key for the enabled status, the profile will be disabled. * \return Is the profile enabled. */ virtual bool isEnabled() const; private: StorageProfile& operator=(const StorageProfile &aRhs); StorageProfilePrivate *d_ptr; }; } #endif // STORAGEPROFILE_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/SyncLog.h0000644000015600001650000000641312634332753025171 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCLOG_H #define SYNCLOG_H #include #include #include "SyncResults.h" class QDomDocument; class QDomElement; namespace Buteo { class SyncLogPrivate; class SyncLogTest; /*! \brief History of completed synchronization sessions and their results. * * Each SyncProfile has its own SyncLog associated to it. Loading and saving of * SyncLog objects is handled by the ProfileManager. SyncLog is composed of * SyncResults objects, one for each completed sync session. */ class SyncLog { public: /*! \brief Constructs an empty log with the given profile name. * * \param aProfileName Name of the profile this log is related to. */ explicit SyncLog(const QString &aProfileName); /*! \brief Constructs a SyncLog from XML. * * \param aRoot Root element of the XML representation of the log. */ explicit SyncLog(const QDomElement &aRoot); /*! \brief Copy constructor. * * \param aSource Copy source. */ SyncLog(const SyncLog &aSource); /*! \brief Destructor. * */ ~SyncLog(); /*! \brief Sets the name of the profile that owns this log. */ void setProfileName(const QString& aProfileName); /*! \brief Gets the name of the profile that owns this log. * * \return Profile name. */ QString profileName() const; /*! \brief Exports the log to XML. * * \param aDoc Parent document for the created XML elements. The created * elements are not inserted to the document by this function, but the * document is still required for creating the elements. * \return Root element of the created XML. */ QDomElement toXml(QDomDocument &aDoc) const; /*! \brief Gets the most recent results in the sync log. * * \return The results. NULL if the log is empty. */ const SyncResults *lastResults() const; /*! \brief Gets all results in the sync log. * * \return List of results. The results are ordered by time so that * the oldest results object is first in the list. */ QList allResults() const; /*! \brief Adds results to the sync log. * Also makes sure that log size doesn't exceed given size limit * * \param aResults Results to add. */ void addResults(const SyncResults &aResults); private: SyncLog& operator=(const SyncLog &aRhs); SyncLogPrivate *d_ptr; }; } #endif // SYNCLOG_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/SyncResults.h0000644000015600001650000001464012634332753026112 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCRESULTS_H_2 #define SYNCRESULTS_H_2 #include #include #include "TargetResults.h" class QDomDocument; class QDomElement; namespace Buteo { struct DatabaseResults { int iLocalItemsAdded; /*! targetResults() const; /*! \brief Adds target results to this object. * * \param aResults The target results to add. */ void addTargetResults(const TargetResults &aResults); /*! \brief Gets the sync time. * * \return Sync time. */ QDateTime syncTime() const; /*! \brief Gets the result code. * * \return major code. */ int majorCode() const; /*! \brief Sets the result code. * * \param aMajorCode The result code. */ void setMajorCode(int aMajorCode); /*! \brief Gets the failed reason. * * \return failed Reason. */ int minorCode() const; /*! \brief Sets the failed Reason. * * \param aMinorCode - minor code or the reason */ void setMinorCode(int aMinorCode); /*! \brief Sets the remote target Id. * * \param aTargetId The remote device Id. */ void setTargetId(const QString& aTargetId) ; /*! \brief Gets the remote target Id. */ QString getTargetId() const; /*! \brief Compares two results objects by sync time. * * The object with earlier sync time is smaller. * \param aOther Point of comparison. */ bool operator<(const SyncResults &aOther) const; /*! \brief Sets if the results are from a scheduled sync. * * \param aScheduled True if this is a scheduled sync. */ void setScheduled(bool aScheduled); /*! \brief Checks if the results are from a scheduled sync. * * \return True if scheduled. */ bool isScheduled() const; private: SyncResultsPrivate *d_ptr; #ifdef SYNCFW_UNIT_TESTS friend class ClientThreadTest; #endif }; } #endif // SYNCRESULTS_H_2 buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/ProfileFactory.cpp0000644000015600001650000000377512634332753027106 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ProfileFactory.h" #include #include "SyncProfile.h" #include "StorageProfile.h" #include "ProfileEngineDefs.h" using namespace Buteo; ProfileFactory::ProfileFactory() { } Profile *ProfileFactory::createProfile(const QString &aName, const QString &aType) { if (aType.isEmpty()) return NULL; Profile *p = NULL; if (aType == Profile::TYPE_SYNC) { p = new SyncProfile(aName); } else if (aType == Profile::TYPE_STORAGE) { p = new StorageProfile(aName); } // Entries for each class derived from Profile can be added here. else { p = new Profile(aName, aType); } return p; } Profile *ProfileFactory::createProfile(const QDomElement &aRoot) { Profile *p = NULL; QString type = aRoot.attribute(ATTR_TYPE); if (type == Profile::TYPE_SYNC) { p = new SyncProfile(aRoot); } else if (type == Profile::TYPE_STORAGE) { p = new StorageProfile(aRoot); } // Entries for each class derived from Profile can be added here. else { p = new Profile(aRoot); } return p; } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/SyncProfile.h0000644000015600001650000002712712634332753026055 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2014-2015 Jolla Ltd * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCPROFILE_H #define SYNCPROFILE_H #include "Profile.h" #include "SyncLog.h" #include "SyncSchedule.h" namespace Buteo { class SyncProfilePrivate; /*! \brief A top level synchronization profile. * * SyncProfile is derived from Profile. It represents a top level * synchronization profile, which contains all settings needed for a * synchronization session. A SyncProfile typically has sub-profiles for the * used service, client/server plug-in and storage plug-ins. SyncProfile * extends the Profile inteface with functions for accessing information about * synchronization schedule and history of finished synchronization sessions * with this profile. */ class SyncProfile : public Profile { public: //! Synchronization types. enum SyncType { //! Synchronization is started manually. SYNC_MANUAL, //! Synchronization is triggered automatically based on a defined //! schedule. SYNC_SCHEDULED }; //! Sync destination type. enum DestinationType { //! Destination is a device (N95, Harmattan, OviSuite etc.) DESTINATION_TYPE_DEVICE, //! Destination is an online service. DESTINATION_TYPE_ONLINE, //! Destination type is not defined. DESTINATION_TYPE_UNDEFINED }; //! Sync direction for device-to-device syncs. enum SyncDirection { //! Two way sync. SYNC_DIRECTION_TWO_WAY, //! Data is copied from remote device only. SYNC_DIRECTION_FROM_REMOTE, //! Data is copied to remote device only. SYNC_DIRECTION_TO_REMOTE, //! Sync direction is not defined. SYNC_DIRECTION_UNDEFINED }; //! Conflict resolution policy for device-to-device syncs. enum ConflictResolutionPolicy { //! Prefer local data in conflict situation. CR_POLICY_PREFER_LOCAL_CHANGES, //! Prefer remote data in conflict situation. CR_POLICY_PREFER_REMOTE_CHANGES, //! Conflict resolution policy is undefined. CR_POLICY_UNDEFINED }; //! Current status enum enum CurrentSyncStatus { //! NOT_SYNCED - no sync has been done for the profile yet SYNC_NEVER_HAPPENED, //! SYNC_SUCCESS - the last sync has been successful SYNC_SUCCESS, //! SYNC_FAILED - the last sync has failed SYNC_FAILED, //! SYNC_CANCELLED - the last sync has been cancelled SYNC_CANCLLED }; /*! \brief Constructs an empty SyncProfile with the given name. * * \param aName Name of the profile to create. */ explicit SyncProfile(const QString &aName); /*! \brief Constructs a SyncProfile from the given XML. * * \param aRoot Root element of the XML node tree. */ explicit SyncProfile(const QDomElement &aRoot); /*! \brief Copy constructor. * * \param aSource Copy source. */ SyncProfile(const SyncProfile &aSource); //! \brief Destructor. ~SyncProfile(); /*! \brief Creates a clone of the sync profile. * * \return The clone. */ virtual SyncProfile *clone() const; /*! \brief Sets the name for the profile and associated log. */ virtual void setName(const QString &aName); /*! \brief Sets the name for the profile and associated log. */ virtual void setName(const QStringList &aKeys); //! \see Profile::toXml virtual QDomElement toXml(QDomDocument &aDoc, bool aLocalOnly = true) const; /*! \brief Checks if schedule is controlled by a external process (e.g always-up-to-date). * * \return True if schedule is controlled by a external process. External process will control the sync, * buteo schedule is disabled in this case. */ virtual bool syncExternallyEnabled() const; /*! \brief Checks if rush/off-rush schedule is enabled. * * \return True if rush/off-rush schedule is enabled. False, if rush/off-rush scheduling is off. */ virtual bool rushEnabled() const; /*! \brief Checks if external rush schedule is to be obeyed. * * \return True if rush hour schedule is to be used by a external process, The external process will control the sync, buteo will just call * the corresponding plugins when a switch from rush to offRush or vice-versa is necessary, corresponding plugins should be prepared to do any needed * changes. * False, if rush hour scheduling is controlled by this process or if rush hour scheduling is off (i.e. manual mode). */ virtual bool syncExternallyDuringRush() const; /*! \brief Checks if a given time is inside rush hour and if the sync is controlled by a external process. * * \param aDateTime DateTime to check, current DateTime used by default. */ virtual bool inExternalSyncRushPeriod(QDateTime aDateTime = QDateTime::currentDateTime()) const; /*! \brief Gets the time of last completed sync session with this profile. * * \return Last sync time. Null object if this could not be determined. */ QDateTime lastSyncTime() const; /*! \brief Gets the time of the last successful sync session for * this profile * \return Sync time of the last successful session. Null if this * could not be determined */ QDateTime lastSuccessfulSyncTime() const; /*! \brief Gets the next scheduled sync time. * * \return Next sync time. Null object if the sync type is manual or the * time could not be determined for some other reason. */ virtual QDateTime nextSyncTime(QDateTime aDateTime = QDateTime::currentDateTime()) const; /*! \brief Gets next time to switch rush/off-rush schedule intervals. * * \param aFromTime From time to calculate next switch, usually current time. * \return Next time to switch rush/off-rush schedule intervals. Null object if schedule is not defined for rush/off-rush * or if the rush and off-rush intervals are the same. */ QDateTime nextRushSwitchTime(const QDateTime& aFromTime) const; /*! \brief Gets the results of the last sync from the sync log. * * \return The results. NULL if not available. */ const SyncResults *lastResults() const; /*! \brief Gets the synchronization log associated with this profile. * * \return The sync log. NULL if no log is set. */ SyncLog *log() const; /*! \brief Sets the synchronization log for this profile. * * The ownership of the given log object is transferred to this object. * If a log is already set, the old log object is deleted first. * \param aLog The log. */ void setLog(SyncLog *aLog); /*! \brief Adds synchronization results to the log. * * If a log does not exist yet, an empty log is created first. * \param aResults Results to add. */ void addResults(const SyncResults &aResults); /*! \brief Gets the sync type of this profile. * * \return The sync type. */ SyncType syncType() const; /*! \brief Sets the sync type of this profile (manual/scheduled). * * \param aType The new sync type. */ void setSyncType(SyncType aType); /*! \brief Gets the names of storage backends used by this profile. * * \return List of storage backend names. */ QStringList storageBackendNames() const; /*! \brief Gets sync schedule settings. * * \return Sync schedule. */ SyncSchedule syncSchedule() const; /*! \brief Sets sync schedule settings. * * \param aSchedule New schedule. */ void setSyncSchedule(const SyncSchedule &aSchedule); /*! \brief Get the first service sub-profile. * * \return Service profile. NULL if not found. */ //const Profile *serviceProfile() const; /*! \brief Get the first service sub-profile. * * \return Service profile. NULL if not found. */ //Profile *serviceProfile(); /*! \brief Get the first client sub-profile. * * \return Client profile. NULL if not found. */ const Profile *clientProfile() const; /*! \brief Get the first client sub-profile. * * \return Client profile. NULL if not found. */ Profile *clientProfile(); /*! \brief Get the first server sub-profile. * * \return Server profile. NULL if not found. */ const Profile *serverProfile() const; /*! \brief Get the first server sub-profile. * * \return Server profile. NULL if not found. */ Profile *serverProfile(); /*! \brief Get the storage sub-profiles. * * \return Storage profiles. */ QList storageProfiles() const; /*! \brief Get the storage sub-profiles. * * \return Storage profiles. */ QList storageProfilesNonConst(); /*! \brief Gets sync destination type (device or online). * * \return Destination type. */ DestinationType destinationType() const; /*! \brief Gets sync direction (two way, to destination, from destination). * * \return Sync direction. */ SyncDirection syncDirection() const; /*! \brief Sets sync direction. * * \return New sync direction. */ void setSyncDirection(SyncDirection aDirection); /*! \brief Gets conflict resolution policy. * * \return Conflict resolution policy. */ ConflictResolutionPolicy conflictResolutionPolicy() const; /*! \brief Set conflict resolution policy. * * \return Conflict resolution policy. */ void setConflictResolutionPolicy(ConflictResolutionPolicy aPolicy); /*! \brief Get the service name of profile. * * \return Service name associated with profile. */ QString serviceName() const; /*! \brief If a profiles is interested in SOC, this * gets the the SOC after time from that profile. * The time should be in seconds and a value of 0 means * sync immediately afer change * * @return SOC after time or -1 if none is specified */ quint32 syncOnChangeAfter() const; /*! \brief checks if a profile has SOC enabled * * @return true if SOC enabled for this profile, false otherwise */ bool isSOCProfile() const; bool hasRetries() const; QList retryIntervals() const; /*! \brief Gives the current status of the sync as an enum value * If the current status of ongoing syncs is required, check the * d-bus API "runningSyncs" which returns the list of currently running * sync sessions. The current sync sessions cannot be part of a profiel, * * @return CurrentSyncStatus */ CurrentSyncStatus currentSyncStatus() const; private: SyncProfile& operator=(const SyncProfile &aRhs); SyncProfilePrivate *d_ptr; }; } #endif // SYNCPROFILE_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/SyncProfile.cpp0000644000015600001650000004166512634332753026413 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2014-2015 Jolla Ltd * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncProfile.h" #include "ProfileEngineDefs.h" #include "LogMacros.h" #include namespace Buteo { // Private implementation class for SyncProfile. class SyncProfilePrivate { public: SyncProfilePrivate(); SyncProfilePrivate(const SyncProfilePrivate &aSource); ~SyncProfilePrivate(); SyncLog *iLog; SyncSchedule iSchedule; struct SyncRetriesInfo { QList iRetryIntervals; quint32 iIntervalIndex; void init() { iIntervalIndex = 0; } void addInterval(quint32 interval) { iRetryIntervals.append(interval); } quint32 retries() { return iRetryIntervals.count(); } qint32 nextInterval() { qint32 next = -1; if(iIntervalIndex < retries()) { next = iRetryIntervals.at(iIntervalIndex); ++iIntervalIndex; } return next; } QList intervals() { return iRetryIntervals; } SyncRetriesInfo& operator=(const SyncRetriesInfo& rhs) { if(this != &rhs) { iIntervalIndex = rhs.iIntervalIndex; iRetryIntervals = rhs.iRetryIntervals; } return *this; } }iSyncRetriesInfo; }; } using namespace Buteo; const quint32 DEFAULT_SOC_AFTER_TIME(5*60); SyncProfilePrivate::SyncProfilePrivate() : iLog(0) { iSyncRetriesInfo.init(); } SyncProfilePrivate::SyncProfilePrivate(const SyncProfilePrivate &aSource) : iLog(0), iSchedule(aSource.iSchedule) { if (aSource.iLog != 0) { iLog = new SyncLog(*aSource.iLog); } // no else iSyncRetriesInfo = aSource.iSyncRetriesInfo; } SyncProfilePrivate::~SyncProfilePrivate() { delete iLog; iLog = 0; } SyncProfile::SyncProfile(const QString &aName) : Profile(aName, Profile::TYPE_SYNC), d_ptr(new SyncProfilePrivate()) { } SyncProfile::SyncProfile(const QDomElement &aRoot) : Profile(aRoot), d_ptr(new SyncProfilePrivate()) { QDomElement schedule = aRoot.firstChildElement(TAG_SCHEDULE); if (!schedule.isNull()) { d_ptr->iSchedule = SyncSchedule(schedule); } // no else QDomElement retriesElement = aRoot.firstChildElement(TAG_ERROR_ATTEMPTS); if (!retriesElement.isNull()) { QDomElement timeElement = retriesElement.firstChildElement(TAG_ATTEMPT_DELAY); while (!timeElement.isNull()) { bool ok = false; int parsedTime = timeElement.attribute(ATTR_VALUE, "-1").toUInt(&ok); if ( ok && parsedTime > 0 ) { d_ptr->iSyncRetriesInfo.addInterval(parsedTime); } timeElement = timeElement.nextSiblingElement(TAG_ATTEMPT_DELAY); } } } SyncProfile::SyncProfile(const SyncProfile &aSource) : Profile(aSource), d_ptr(new SyncProfilePrivate(*aSource.d_ptr)) { } SyncProfile::~SyncProfile() { delete d_ptr; d_ptr = 0; } SyncProfile *SyncProfile::clone() const { return new SyncProfile(*this); } QDomElement SyncProfile::toXml(QDomDocument &aDoc, bool aLocalOnly) const { QDomElement root = Profile::toXml(aDoc, aLocalOnly); QDomElement schedule = d_ptr->iSchedule.toXml(aDoc); if (!schedule.isNull()) { root.appendChild(schedule); } // no else if (d_ptr->iSyncRetriesInfo.retries()) { QDomElement retries = aDoc.createElement(TAG_ERROR_ATTEMPTS); for (quint32 i = 0; i < d_ptr->iSyncRetriesInfo.retries(); ++i) { QDomElement retryInterval = aDoc.createElement(TAG_ATTEMPT_DELAY); qint32 nextInt = d_ptr->iSyncRetriesInfo.nextInterval(); if(-1 != nextInt) { retryInterval.setAttribute(ATTR_VALUE, nextInt); retries.appendChild(retryInterval); } } root.appendChild(retries); d_ptr->iSyncRetriesInfo.init(); } return root; } void SyncProfile::setName(const QString &aName) { // sets the name in the super class Profile. Profile::setName(aName); // Here we also must set name for the log associated to this profile, // as that is only set during log construction. Changing SyncProfile name does not // reflect there if (d_ptr->iLog) d_ptr->iLog->setProfileName(aName); } void SyncProfile::setName(const QStringList &aKeys) { // sets the name in the super class Profile. Profile::setName(aKeys); // Here we also must set name for the log associated to this profile, // as that is only set during log construction. Changing SyncProfile name does not // reflect there if (d_ptr->iLog) d_ptr->iLog->setProfileName(Profile::name()); } bool SyncProfile::syncExternallyEnabled() const { return boolKey(KEY_SYNC_EXTERNALLY, false); } bool SyncProfile::rushEnabled() const { return d_ptr->iSchedule.rushEnabled() && d_ptr->iSchedule.scheduleEnabled(); } bool SyncProfile::syncExternallyDuringRush() const { return d_ptr->iSchedule.scheduleEnabled() && d_ptr->iSchedule.rushEnabled() && d_ptr->iSchedule.syncExternallyDuringRush(); } bool SyncProfile::inExternalSyncRushPeriod(QDateTime aDateTime) const { return d_ptr->iSchedule.inExternalSyncRushPeriod(aDateTime); } QDateTime SyncProfile::lastSyncTime() const { QDateTime lastSync; if (d_ptr->iLog != 0 && d_ptr->iLog->lastResults() != 0) { lastSync = d_ptr->iLog->lastResults()->syncTime(); } // no else LOG_DEBUG("lastSync:"<iLog) { QList allResults = d_ptr->iLog->allResults(); foreach (const SyncResults* result, allResults) { if ((result->majorCode () == SyncResults::SYNC_RESULT_SUCCESS) && (result->minorCode () == SyncResults::NO_ERROR) && (lastSyncTime().isNull() || (result->syncTime () > lastSuccessSyncTime))) { lastSuccessSyncTime = result->syncTime (); } } } return lastSuccessSyncTime; } QDateTime SyncProfile::nextSyncTime(QDateTime aDateTime) const { QDateTime nextSync; if(syncType() == SYNC_SCHEDULED) { if (aDateTime.isValid()) { nextSync = d_ptr->iSchedule.nextSyncTime(aDateTime); } else { nextSync = d_ptr->iSchedule.nextSyncTime(lastSyncTime()); } } return nextSync; } QDateTime SyncProfile::nextRushSwitchTime(const QDateTime &aFromTime) const { QDateTime nextSwitch; if(syncType() == SYNC_SCHEDULED) { nextSwitch = d_ptr->iSchedule.nextRushSwitchTime(aFromTime); } return nextSwitch; } const SyncResults *SyncProfile::lastResults() const { if (d_ptr->iLog != 0) { return d_ptr->iLog->lastResults(); } else { return 0; } } SyncLog *SyncProfile::log() const { return d_ptr->iLog; } void SyncProfile::setLog(SyncLog *aLog) { delete d_ptr->iLog; d_ptr->iLog = aLog; } void SyncProfile::addResults(const SyncResults &aResults) { if (0 == d_ptr->iLog) { d_ptr->iLog = new SyncLog(name()); } // no else d_ptr->iLog->addResults(aResults); } SyncProfile::SyncType SyncProfile::syncType() const { // Sync schedule is enabled for peak or manual -> it is scheduled type. return !syncExternallyEnabled() && (d_ptr->iSchedule.scheduleEnabled() || d_ptr->iSchedule.rushEnabled()) ? SYNC_SCHEDULED : SYNC_MANUAL; } // TODO: seems effectless since d6d974e (Added functions to enable/disable normal scheduling.) void SyncProfile::setSyncType(SyncType aType) { setBoolKey(KEY_SYNC_SCHEDULED, aType == SYNC_SCHEDULED); } SyncSchedule SyncProfile::syncSchedule() const { return d_ptr->iSchedule; } void SyncProfile::setSyncSchedule(const SyncSchedule &aSchedule) { d_ptr->iSchedule = aSchedule; } QStringList SyncProfile::storageBackendNames() const { QStringList enabledStorageBackends; QStringList storageNames = subProfileNames(Profile::TYPE_STORAGE); foreach (QString storage, storageNames) { const Profile *p = subProfile(storage, Profile::TYPE_STORAGE); if (p->isEnabled()) { // Get backend name from the storage profile. If the backend name // is not defined, use profile name as the backend name. enabledStorageBackends.append(p->key(KEY_BACKEND, p->name())); } // no else } return enabledStorageBackends; } /* QString SyncProfile::serviceName() const { QStringList serviceNameList = subProfileNames(Profile::TYPE_SERVICE); if (serviceNameList.isEmpty()) return QString(); return serviceNameList.first(); } const Profile *SyncProfile::serviceProfile() const { QList subProfiles = allSubProfiles(); foreach (const Profile *p, subProfiles) { if (p->type() == TYPE_SERVICE) { return p; } // no else } return 0; } Profile *SyncProfile::serviceProfile() { QList subProfiles = allSubProfiles(); foreach (Profile *p, subProfiles) { if (p->type() == TYPE_SERVICE) { return p; } // no else } return 0; } */ const Profile *SyncProfile::clientProfile() const { QList subProfiles = allSubProfiles(); foreach (const Profile *p, subProfiles) { if (p->type() == TYPE_CLIENT) { return p; } // no else } return 0; } Profile *SyncProfile::clientProfile() { QList subProfiles = allSubProfiles(); foreach (Profile *p, subProfiles) { if (p->type() == TYPE_CLIENT) { return p; } // no else } return 0; } const Profile *SyncProfile::serverProfile() const { QList subProfiles = allSubProfiles(); foreach (const Profile *p, subProfiles) { if (p->type() == TYPE_SERVER) { return p; } // no else } return 0; } Profile *SyncProfile::serverProfile() { QList subProfiles = allSubProfiles(); foreach (Profile *p, subProfiles) { if (p->type() == TYPE_SERVER) { return p; } // no else } return 0; } QList SyncProfile::storageProfiles() const { QList storages; QList subProfiles = allSubProfiles(); foreach (const Profile *p, subProfiles) { if (p->type() == TYPE_STORAGE) { storages.append(p); } // no else } return storages; } QList SyncProfile::storageProfilesNonConst() { QList storages; QList subProfiles = allSubProfiles(); foreach (Profile *p, subProfiles) { if (p->type() == TYPE_STORAGE) { storages.append(p); } // no else } return storages; } SyncProfile::DestinationType SyncProfile::destinationType() const { DestinationType type = DESTINATION_TYPE_UNDEFINED; QString typeStr; //const Profile *service = serviceProfile(); //if (service) //{ typeStr = this->key(KEY_DESTINATION_TYPE); //} // no else if (typeStr == VALUE_ONLINE) { type = DESTINATION_TYPE_ONLINE; } else if (typeStr == VALUE_DEVICE) { type = DESTINATION_TYPE_DEVICE; } else { type = DESTINATION_TYPE_UNDEFINED; } return type; } SyncProfile::SyncDirection SyncProfile::syncDirection() const { SyncDirection dir = SYNC_DIRECTION_UNDEFINED; QString dirStr; const Profile *client = clientProfile(); if (client) { dirStr = client->key(KEY_SYNC_DIRECTION); } // no else if (dirStr == VALUE_TWO_WAY) { dir = SYNC_DIRECTION_TWO_WAY; } else if (dirStr == VALUE_FROM_REMOTE) { dir = SYNC_DIRECTION_FROM_REMOTE; } else if (dirStr == VALUE_TO_REMOTE) { dir = SYNC_DIRECTION_TO_REMOTE; } else { dir = SYNC_DIRECTION_UNDEFINED; } return dir; } bool SyncProfile::isSOCProfile() const { bool aSOCProfile = false; //const Profile *service = serviceProfile(); //if (service) //{ QString enabled = this->key(KEY_SOC); enabled = enabled.trimmed(); if("true" == enabled) { aSOCProfile = true; } //} return aSOCProfile; } quint32 SyncProfile::syncOnChangeAfter() const { quint32 syncOnChangeAfterTime = DEFAULT_SOC_AFTER_TIME; //const Profile *service = serviceProfile(); //if (service) //{ QString time = this->key(KEY_SOC_AFTER); if(!time.isEmpty()) { bool ok = false; syncOnChangeAfterTime = time.toUInt(&ok); if(false == ok) { syncOnChangeAfterTime = DEFAULT_SOC_AFTER_TIME; } } //} LOG_DEBUG("Sync on change after time from profile :" << syncOnChangeAfterTime); return syncOnChangeAfterTime; } void SyncProfile::setSyncDirection(SyncDirection aDirection) { QString dirStr; switch (aDirection) { case SYNC_DIRECTION_TWO_WAY: dirStr = VALUE_TWO_WAY; break; case SYNC_DIRECTION_FROM_REMOTE: dirStr = VALUE_FROM_REMOTE; break; case SYNC_DIRECTION_TO_REMOTE: dirStr = VALUE_TO_REMOTE; break; case SYNC_DIRECTION_UNDEFINED: default: // Value string is left as null. Key gets deleted when the value is set. break; } Profile *client = clientProfile(); if (client) { client->setKey(KEY_SYNC_DIRECTION, dirStr); } else { LOG_WARNING("Profile" << name() << "has no client profile"); LOG_WARNING("Failed to set sync direction"); } } SyncProfile::ConflictResolutionPolicy SyncProfile::conflictResolutionPolicy() const { ConflictResolutionPolicy policy = CR_POLICY_UNDEFINED; QString policyStr; const Profile *client = clientProfile(); if (client) { policyStr = client->key(KEY_CONFLICT_RESOLUTION_POLICY); } // no else if (policyStr == VALUE_PREFER_REMOTE) { policy = CR_POLICY_PREFER_REMOTE_CHANGES; } else if (policyStr == VALUE_PREFER_LOCAL) { policy = CR_POLICY_PREFER_LOCAL_CHANGES; } else { policy = CR_POLICY_UNDEFINED; } return policy; } void SyncProfile::setConflictResolutionPolicy(ConflictResolutionPolicy aPolicy) { QString policyStr; switch (aPolicy) { case CR_POLICY_PREFER_REMOTE_CHANGES: policyStr = VALUE_PREFER_REMOTE; break; case CR_POLICY_PREFER_LOCAL_CHANGES: policyStr = VALUE_PREFER_LOCAL; break; case CR_POLICY_UNDEFINED: default: // Value string is left as null. Key gets deleted when the value is set. break; } Profile *client = clientProfile(); if (client) { client->setKey(KEY_CONFLICT_RESOLUTION_POLICY, policyStr); } else { LOG_WARNING("Profile" << name() << "has no client profile"); LOG_WARNING("Failed to set conflict resolution policy"); } } bool SyncProfile::hasRetries() const { return d_ptr->iSyncRetriesInfo.retries() ? true : false; } QList SyncProfile::retryIntervals() const { return d_ptr->iSyncRetriesInfo.intervals(); } SyncProfile::CurrentSyncStatus SyncProfile::currentSyncStatus() const { //Fetch the last sync result const SyncResults *syncResult = lastResults(); SyncProfile::CurrentSyncStatus syncStatus = SyncProfile::SYNC_NEVER_HAPPENED; if (syncResult) { if ((syncResult->majorCode() == SyncResults::SYNC_RESULT_SUCCESS) && (syncResult->minorCode() == SyncResults::NO_ERROR)) syncStatus = SyncProfile::SYNC_SUCCESS; else if (syncResult->majorCode() == SyncResults::SYNC_RESULT_FAILED) syncStatus = SyncProfile::SYNC_FAILED; else if (syncResult->majorCode() == SyncResults::SYNC_RESULT_CANCELLED) syncStatus = SyncProfile::SYNC_CANCLLED; } return syncStatus; } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/BtHelper.h0000644000015600001650000000631712634332753025323 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef BTHELPER_H #define BTHELPER_H #include #include #include /*! \brief Strings used for DBus communication with bluetooth daemon are grouped using this structure. */ struct BT { /// Destination for Dbus command to bluez static const QString BLUEZ_DEST; /// Bluez manager interface name static const QString BLUEZ_MANAGER_INTERFACE; /// Bluez adapter interface name static const QString BLUEZ_ADAPTER_INTERFACE; /// Bluez Device interface name static const QString BLUEZ_DEVICE_INTERFACE; /// Method name for retrieving default adapter static const QString GET_DEFAULT_ADAPTER; /// Method name for finding the device static const QString FIND_DEVICE; /// Method name for discovering services static const QString DISCOVERSERVICES; /// Method name for discovering services static const QString GETPROPERTIES; }; /*! \brief Implementation for bluetooth helper utils. */ class BtHelper : public QObject { Q_OBJECT private: QString m_deviceAddress; // Private methods /*! \brief Fetches the default adapter path */ QString getDefaultAdapterPath(); /*! \brief Fetches the device path * \param defaultAdapterPath Default adapter path */ QString getDevicePath(QString& defaultAdapterPath); /* \brief Open the serial port and get file descriptor for the port. * \return File descriptor if opening port was success, otherwise -1 */ public: /*! \brief Constructor. * \param deviceAddess Bluetooth address of remote device * \param parent Parent object */ BtHelper(const QString& deviceAddess, QObject* parent = 0); /*! \brief Destructor */ ~BtHelper(); /*! \brief Fetch the bluetooth services supported by remote device. * \param servicesList outparam which will be populated with the services. */ bool getServiceRecords(QList& servicesList); /*! \brief To find if a specific service is supported by remote device. * \param servicesList List of remote device sdp records * \param serviceUUID UUID of the service to be connected */ bool isServiceSupported (const QList& servicesList, const QString& serviceUUID); /*! \brief To find remote device BT properties. */ QMap getDeviceProperties(); }; #endif // BTHELPER_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/ProfileField.h0000644000015600001650000001062012634332753026152 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef PROFILEFIELD_H #define PROFILEFIELD_H #include #include class QDomDocument; class QDomElement; namespace Buteo { class ProfileFieldPrivate; /*! \brief This class represents a profile field. * * Profile field is a bunch of information about a setting whose value must * be defined as a separate key/value pair in some profile. The key name must * be same as the profile field name. * The class includes functions for accessing the name, * type, description and possible values of the setting. Only the name is a * mandatory field. The class also has a function for validating a given value * against the possible values defined by the field. A ProfileField can be * constructed from XML and exported to XML. */ class ProfileField { public: //! Field should be always visible in UI. static const QString VISIBLE_ALWAYS; //! Field should never be visible in UI. static const QString VISIBLE_NEVER; //! Field should be visible in UI if a value for the field has not // been pre-defined in the sub-profiles loaded by the main profile. static const QString VISIBLE_USER; //! Field type for boolean fields. static const QString TYPE_BOOLEAN; /*! \brief Constructs a ProfileField from XML. * * \param aRoot Root element of the field XML. */ explicit ProfileField(const QDomElement &aRoot); /*! \brief Copy constructor. * * \param aSource Copy source. */ ProfileField(const ProfileField &aSource); /*! \brief Destructor. */ ~ProfileField(); /*! \brief Gets the field name. * * \return Field name. */ QString name() const; /*! \brief Get the field type. * * \return Field type. */ QString type() const; /*! \brief Gets the field default value. * * \return Field default value. */ QString defaultValue() const; /*! \brief Gets the allowed values for the field. * * \return List of valid values. */ QStringList options() const; /*! \brief Gets the field label. * * The label can be for example displayed in the UI that asks for the field * value. * \return Field label. */ QString label() const; /*! \brief Checks if the given value is in the list of allowed values. * * If allowed values have not been defined, any value is accepted. * \param aValue The value to validate. * \return Is the given value in the list of allowed values (options). */ bool validate(const QString &aValue) const; /*! \brief Exports the field to XML. * * \param aDoc Parent document for the created XML elements. The created * elements are not inserted to the document by this function, but the * document is still required for creating the elements. * \return The root element of the created XML node tree. */ QDomElement toXml(QDomDocument &aDoc) const; /*! \brief Gets the visibility of the field. * * \return String defining the visibility. See VISIBLE_ constants for * predefined values. */ QString visible() const; /*! \brief Checks if the field is read only. * * UI should not allow modifying the value of a read only field. * \return True if readonly. */ bool isReadOnly() const; private: ProfileField& operator=(const ProfileField &aRhs); ProfileFieldPrivate *d_ptr; }; } #endif // PROFILEFIELD_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/ProfileEngineDefs.h0000644000015600001650000001136212634332753027142 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2014-2015 Jolla Ltd * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef COMMONDEFS_H #define COMMONDEFS_H #include // Define string constants for different XML elements. namespace Buteo { const QString ATTR_NAME("name"); const QString ATTR_TYPE("type"); const QString ATTR_VALUE("value"); const QString ATTR_DEFAULT("default"); const QString ATTR_LABEL("label"); const QString ATTR_VISIBLE("visible"); const QString ATTR_READONLY("readonly"); const QString ATTR_ADDED("added"); const QString ATTR_DELETED("deleted"); const QString ATTR_MODIFIED("modified"); const QString ATTR_TIME("time"); const QString ATTR_INTERVAL("interval"); const QString ATTR_BEGIN("begin"); const QString ATTR_END("end"); const QString ATTR_DAYS("days"); const QString ATTR_MAJOR_CODE("majorcode"); const QString ATTR_MINOR_CODE("minorcode"); const QString ATTR_ENABLED("enabled"); const QString ATTR_SYNC_CONFIGURE("syncconfiguredtime"); const QString ATTR_EXTERNAL_SYNC("externalsync"); const QString TAG_FIELD("field"); const QString TAG_PROFILE("profile"); const QString TAG_KEY("key"); const QString TAG_OPTION("option"); const QString TAG_TARGET_RESULTS("target"); const QString TAG_SYNC_RESULTS("syncresults"); const QString TAG_SYNC_LOG("synclog"); const QString TAG_LOCAL("local"); const QString TAG_REMOTE("remote"); const QString TAG_SCHEDULE("schedule"); const QString TAG_RUSH("rush"); const QString TAG_ERROR_ATTEMPTS("attempts"); const QString TAG_ATTEMPT_DELAY("attemptdelay"); const QString KEY_ENABLED("enabled"); const QString KEY_DISPLAY_NAME("displayname"); const QString KEY_ACTIVE("active"); const QString KEY_USE_ACCOUNTS("use_accounts"); const QString KEY_SYNC_SCHEDULED("scheduled"); const QString KEY_PLUGIN("plugin"); const QString KEY_BACKEND("backend"); const QString KEY_ACCOUNT_ID("accountid"); const QString KEY_USERNAME("Username"); const QString KEY_PASSWORD("Password"); const QString KEY_HIDDEN("hidden"); const QString KEY_PROTECTED("protected"); const QString KEY_DESTINATION_TYPE("destinationtype"); const QString KEY_SYNC_DIRECTION("Sync Direction"); const QString KEY_FORCE_SLOW_SYNC("force_slow_sync"); const QString KEY_CONFLICT_RESOLUTION_POLICY("conflictpolicy"); const QString KEY_BT_ADDRESS("bt_address"); const QString KEY_REMOTE_ID("remote_id"); const QString KEY_REMOTE_DATABASE("Remote database"); const QString KEY_BT_NAME("bt_name"); const QString KEY_BT_TRANSPORT("bt_transport"); const QString KEY_USB_TRANSPORT("usb_transport"); const QString KEY_INTERNET_TRANSPORT("internet_transport"); const QString KEY_LOAD_WITHOUT_TRANSPORT("load_without_transport"); const QString KEY_CAPS_MODIFIED("caps_modified"); const QString KEY_SYNC_SINCE_DAYS_PAST("sync_since_days_past"); // sync from this many days before the current date const QString KEY_SYNC_ALWAYS_UP_TO_DATE("sync_always_up_to_date"); const QString KEY_SYNC_EXTERNALLY("sync_externally"); const QString KEY_SOC("sync_on_change"); const QString KEY_SOC_AFTER("sync_on_change_after"); const QString KEY_LOCAL_URI("Local URI"); const QString KEY_ALWAYS_ON_ENABLED("always_on_enabled"); const QString KEY_REMOTE_NAME("remote_name"); const QString KEY_UUID("uuid"); const QString KEY_NOTES_UUID("notes_uuid"); const QString KEY_STORAGE_UPDATED("storage_updated"); const QString KEY_HTTP_PROXY_HOST("http_proxy_host"); const QString KEY_HTTP_PROXY_PORT("http_proxy_port"); const QString KEY_PROFILE_ID("profile_id"); const QString BOOLEAN_TRUE("true"); const QString BOOLEAN_FALSE("false"); const QString VALUE_ONLINE("online"); const QString VALUE_DEVICE("device"); const QString VALUE_TWO_WAY("two-way"); const QString VALUE_FROM_REMOTE("from-remote"); const QString VALUE_TO_REMOTE("to-remote"); const QString VALUE_PREFER_REMOTE("prefer remote"); const QString VALUE_PREFER_LOCAL("prefer local"); // Indent size for profile XML output. const int PROFILE_INDENT = 4; const QString PC_SYNC("PC-SYNC"); //For account online_template const QString SYNC_ONLINE_TEMPLATE("online_template"); } #endif // COMMONDEFS_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/bin/0000755000015600001650000000000012634333235024202 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/bin/get_address.pl0000644000015600001650000000307112634332753027030 0ustar pbuserpbgroup00000000000000#!/usr/bin/perl #/* # * This file is part of buteo-syncfw package # * # * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). # * # * Contact: Sateesh Kavuri # * # * This library is free software; you can redistribute it and/or # * modify it under the terms of the GNU Lesser General Public License # * version 2.1 as published by the Free Software Foundation. # * # * This library is distributed in the hope that it will be useful, but # * WITHOUT ANY WARRANTY; without even the implied warranty of # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # * Lesser General Public License for more details. # * # * You should have received a copy of the GNU Lesser General Public # * License along with this library; if not, write to the Free Software # * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # * 02110-1301 USA # * # */ # Author : Srikanth Kavoori # Not Tested fully yet. There could be some issues. # If you see any issues Let me know and i shall fix it :) open(INPUTFILE, "<$ARGV[0]"); open(EXTRACTED_INFO,">$ARGV[1]"); $search_string = $ARGV[2]; $address= ""; $string_found=0; while() { if(/$search_string/) { print "Found address for the device $search_string \n" ; print $_; $address = substr($_,0,18); # print "address of the device is $address \n" ; $string_found=1; } #write the address to EXTRACTED_INFO file } open(EXTRACTED_INFO,">$ARGV[1]"); print EXTRACTED_INFO "$address" ; close(EXTRACTED_INFO); if($string_found == 0) { print "address not found"; exit 1; } else { exit 0 } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/bin/add_address.pl0000644000015600001650000000514112634332753027001 0ustar pbuserpbgroup00000000000000#!/usr/bin/perl #/* # * This file is part of buteo-syncfw package # * # * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). # * # * Contact: Sateesh Kavuri # * # * This library is free software; you can redistribute it and/or # * modify it under the terms of the GNU Lesser General Public License # * version 2.1 as published by the Free Software Foundation. # * # * This library is distributed in the hope that it will be useful, but # * WITHOUT ANY WARRANTY; without even the implied warranty of # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # * Lesser General Public License for more details. # * # * You should have received a copy of the GNU Lesser General Public # * License along with this library; if not, write to the Free Software # * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # * 02110-1301 USA # * # */ # Author : Srikanth Kavoori # Not Tested fully yet. There could be some issues. # If you see any issues Let me know and i shall fix it :) print "Second Argument Passed to add_address script is $ARGV[2] "; if ( $ARGV[2] eq "root" ) { print "ADDING THE ADDRESS ONLY TO /etc/profiles. Please run this as USER if you want to update /home/user \n"; $temp="/etc/buteo/profiles/sync/temp"; @files = ; foreach $file (@files) { print $file . "\n"; open(INPUTFILE, '<', $file) or die "Cannot open $file"; open(OUTPUTFILE, '>', $temp) or die "Cannot open new "; $string="bt_address"; $string2="bt_name"; while() { if(/$string/) { print OUTPUTFILE " \n"; next; } if(/$string2/) { print OUTPUTFILE " \n"; next; } print OUTPUTFILE; } close(INPUTFILE); close(OUTPUTFILE); rename($temp,$file) or die "Cannot rename temp to $file"; } } else { print "ADDING THE ADDRESS ONLY TO /home/user. Please run this as ROOT if you want to update /etc/buteo \n"; $temp="/home/user/.cache/msyncd/profiles/sync/temp"; @files = ; foreach $file (@files) { print $file . "\n"; open(INPUTFILE, '<', $file) or die "Cannot open $file"; open(OUTPUTFILE, '>', $temp) or die "Cannot open new "; $string="bt_address"; $string2="bt_name"; while() { if(/$string/) { print OUTPUTFILE " \n"; next; } if(/$string2/) { print OUTPUTFILE " \n"; next; } print OUTPUTFILE; } close(INPUTFILE); close(OUTPUTFILE); rename($temp,$file) or die "Cannot rename temp to $file"; } } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/bin/bt-connect.sh0000755000015600001650000000605512634332753026607 0ustar pbuserpbgroup00000000000000#!/bin/sh #/* # * This file is part of buteo-syncfw package # * # * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). # * # * Contact: Sateesh Kavuri # * # * This library is free software; you can redistribute it and/or # * modify it under the terms of the GNU Lesser General Public License # * version 2.1 as published by the Free Software Foundation. # * # * This library is distributed in the hope that it will be useful, but # * WITHOUT ANY WARRANTY; without even the implied warranty of # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # * Lesser General Public License for more details. # * # * You should have received a copy of the GNU Lesser General Public # * License along with this library; if not, write to the Free Software # * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # * 02110-1301 USA # * # */ #this script makes it easy to connect to a BT device from command line on the device # check for hcitool availability CWD=$PWD WHOAMI=$USER echo "Current working directory is $CWD and you are running this script as $USER" if [ $# == 0 ]; then echo "Usage: bt-connect [DEVICe_NAME]" exit 0 fi; if [ -f /usr/bin/bluez-simple-agent ]; then echo "Found bluez simple agent " else echo "Bluez-test package is not installed. this script needs that package" echo "This script needs that package to run. install that package and re-run this script" exit 0 fi if [ -f /usr/bin/hcitool ]; then echo "Scanning and Getting the information for the device $1" hcitool scan > temp_file.txt else echo "hcitool is not installed. Please Install Bluez-test package and then rerun the script" exit 0 fi /usr/bin/perl /opt/tests/buteo-syncfw/get_address.pl $CWD/temp_file.txt $CWD/temp_file2.txt $1 if [ $? -eq 0 ]; then echo "Perl Script exit normal address found" else echo "ADDRESS for the device not found" rm $CWD/temp_file.txt $CWD/temp_file2.txt exit 0 fi #echo "Making pairing with $RET_VAL" #get address for the string matching the argument address=`cat temp_file2.txt` echo "$address is the device's address" /usr/bin/bluez-simple-agent hcio $address if [ $? -eq 0 ]; then echo "Bluetooth pairing done" else echo "Bluetooth pairing failed exiting " rm $CWD/temp_file.txt $CWD/temp_file2.txt exit 0 fi name=$1 rm $CWD/temp_file.txt $CWD/temp_file2.txt if [ $USER == "root" ]; then echo "Home Directory is $HOME" echo "You are running the script as $USER .So Adding the bluetooth address to default profiles in /etc/buteo/profiles/ " /usr/bin/perl /opt/tests/buteo-syncfw/add_address.pl $address $name root else echo "Home Directory is $HOME " echo "Adding the bluetooth address to user profiles in /home/user/.sync/profiles/" /usr/bin/perl /opt/tests/buteo-syncfw/add_address.pl $address $name user chmod -R 777 /home/user/.cache/msyncd/profiles fi if [ $? -eq 0 ]; then echo "Added BT information in all the sync profile files where bt_address field was found" else echo "Error while adding bt_address to the profiles file. Please add it manually " exit 0 fi buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/Profile.cpp0000644000015600001650000003277612634332753025561 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "Profile.h" #include "Profile_p.h" #include #include "ProfileFactory.h" #include "ProfileEngineDefs.h" #include "LogMacros.h" using namespace Buteo; const QString Profile::TYPE_CLIENT("client"); const QString Profile::TYPE_SERVER("server"); const QString Profile::TYPE_STORAGE("storage"); //const QString Profile::TYPE_SERVICE("service"); const QString Profile::TYPE_SYNC("sync"); Profile::Profile() : d_ptr(new ProfilePrivate()) { } Profile::Profile(const QString &aName, const QString &aType) : d_ptr(new ProfilePrivate()) { d_ptr->iName = aName; d_ptr->iType = aType; } Profile::Profile(const QDomElement &aRoot) : d_ptr(new ProfilePrivate()) { d_ptr->iName = aRoot.attribute(ATTR_NAME); d_ptr->iType = aRoot.attribute(ATTR_TYPE); // Get keys. QDomElement key = aRoot.firstChildElement(TAG_KEY); for (; !key.isNull(); key = key.nextSiblingElement(TAG_KEY)) { QString name = key.attribute(ATTR_NAME); QString value = key.attribute(ATTR_VALUE); if (!name.isEmpty() && !value.isNull()) { d_ptr->iLocalKeys.insertMulti(name, value); } else { // Invalid key } } // Get fields. QDomElement field = aRoot.firstChildElement(TAG_FIELD); for (; !field.isNull(); field = field.nextSiblingElement(TAG_FIELD)) { d_ptr->iLocalFields.append(new ProfileField(field)); } // Get sub-profiles. ProfileFactory pf; QDomElement prof = aRoot.firstChildElement(TAG_PROFILE); for (; !prof.isNull(); prof = prof.nextSiblingElement(TAG_PROFILE)) { Profile *subProfile = pf.createProfile(prof); if (subProfile != 0) { d_ptr->iSubProfiles.append(subProfile); } // no else } } Profile::Profile(const Profile &aSource) : d_ptr(new ProfilePrivate(*aSource.d_ptr)) { } Profile *Profile::clone() const { return new Profile(*this); } Profile::~Profile() { delete d_ptr; d_ptr = 0; } QString Profile::name() const { return d_ptr->iName; } void Profile::setName(const QString &aName) { d_ptr->iName = aName; } void Profile::setName(const QStringList &aKeys) { d_ptr->iName = generateProfileId(aKeys); } QString Profile::type() const { return d_ptr->iType; } QString Profile::key(const QString &aName, const QString &aDefault) const { QString value; if (d_ptr->iLocalKeys.contains(aName)) { value = d_ptr->iLocalKeys[aName]; } else if (d_ptr->iMergedKeys.contains(aName)) { value = d_ptr->iMergedKeys[aName]; } else { value = aDefault; } return value; } QMap Profile::allKeys() const { QMap keys(d_ptr->iMergedKeys); keys.unite(d_ptr->iLocalKeys); return keys; } QMap Profile::allNonStorageKeys() const { QMap keys; foreach (Profile *p, d_ptr->iSubProfiles) { if (p != 0 && p->type() != Profile::TYPE_STORAGE) { keys.unite(p->allKeys()); } // no else } keys.unite(allKeys()); return keys; } bool Profile::boolKey(const QString &aName, bool aDefault) const { QString value = key(aName); if (!value.isNull()) { return (key(aName).compare(BOOLEAN_TRUE, Qt::CaseInsensitive) == 0); } else { return aDefault; } } QStringList Profile::keyValues(const QString &aName) const { return (d_ptr->iLocalKeys.values(aName) + d_ptr->iMergedKeys.values(aName)); } QStringList Profile::keyNames() const { return d_ptr->iLocalKeys.uniqueKeys() + d_ptr->iMergedKeys.uniqueKeys(); } void Profile::setKey(const QString &aName, const QString &aValue) { if (aName.isEmpty()) return; // Value is not checked, because it is allowed to have a key with empty // value. if (aValue.isNull()) { // Setting a key value to null removes the key. d_ptr->iLocalKeys.remove(aName); d_ptr->iMergedKeys.remove(aName); } else { d_ptr->iLocalKeys.insert(aName, aValue); } } void Profile::setKeyValues(const QString &aName, const QStringList &aValues) { d_ptr->iLocalKeys.remove(aName); d_ptr->iMergedKeys.remove(aName); if (aValues.size() == 0) return; unsigned i = aValues.size(); do { i--; d_ptr->iLocalKeys.insertMulti(aName, aValues[i]); } while (i > 0); } void Profile::setBoolKey(const QString &aName, bool aValue) { d_ptr->iLocalKeys.insert(aName, aValue ? BOOLEAN_TRUE : BOOLEAN_FALSE); } void Profile::removeKey(const QString &aName) { d_ptr->iLocalKeys.remove(aName); d_ptr->iMergedKeys.remove(aName); } const ProfileField *Profile::field(const QString &aName) const { QList fields = allFields(); foreach (const ProfileField *f, fields) { if (f->name() == aName) return f; } return 0; } QList Profile::allFields() const { QList fields = d_ptr->iLocalFields + d_ptr->iMergedFields; return fields; } QList Profile::visibleFields() const { QList fields = allFields(); QList visibleFields; foreach (const ProfileField *f, fields) { // A field with VISIBLE_USER status is visible if a value for the field // does not come from a merged sub-profile, but from the top level // profile. This is the default visibility for a field. // In practice this means that the field value is not hard coded and // it should be possible for the user to modify it. if (f->visible() == ProfileField::VISIBLE_ALWAYS || (f->visible() == ProfileField::VISIBLE_USER && !d_ptr->iMergedKeys.contains(f->name()))) { visibleFields.append(f); } // no else } return visibleFields; } QDomElement Profile::toXml(QDomDocument &aDoc, bool aLocalOnly) const { // Set profile name and type attributes. QDomElement root = aDoc.createElement(TAG_PROFILE); root.setAttribute(ATTR_NAME, d_ptr->iName); root.setAttribute(ATTR_TYPE, d_ptr->iType); // Set local keys. QMap::const_iterator i; for (i = d_ptr->iLocalKeys.begin(); i != d_ptr->iLocalKeys.end(); i++) { QDomElement key = aDoc.createElement(TAG_KEY); key.setAttribute(ATTR_NAME, i.key()); key.setAttribute(ATTR_VALUE, i.value()); root.appendChild(key); } // Set local fields. const ProfileField *field = 0; foreach (field, d_ptr->iLocalFields) { root.appendChild(field->toXml(aDoc)); } if (!aLocalOnly) { // Set merged keys. for (i = d_ptr->iMergedKeys.begin(); i != d_ptr->iMergedKeys.end(); i++) { QDomElement key = aDoc.createElement(TAG_KEY); key.setAttribute(ATTR_NAME, i.key()); key.setAttribute(ATTR_VALUE, i.value()); root.appendChild(key); } // Set merged fields. foreach (field, d_ptr->iMergedFields) { root.appendChild(field->toXml(aDoc)); } } // no else // Set sub-profiles. foreach (Profile *p, d_ptr->iSubProfiles) { if (!p->d_ptr->iMerged || !p->d_ptr->iLocalKeys.isEmpty() || !p->d_ptr->iLocalFields.isEmpty()) { root.appendChild(p->toXml(aDoc, aLocalOnly)); } // no else } return root; } QString Profile::toString() const { QDomDocument doc; QDomProcessingInstruction xmlHeading = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""); doc.appendChild(xmlHeading); QDomElement root = toXml(doc, false); doc.appendChild(root); return doc.toString(PROFILE_INDENT); } bool Profile::isValid() const { // Profile name and type must be set. if (d_ptr->iName.isEmpty() ) { LOG_DEBUG( "Error: Profile name is empty" ); return false; } if(d_ptr->iType.isEmpty() ) { LOG_DEBUG( "Error: Profile type is empty" ); return false; } // For each field a key with the same name must exist, and the // key values must be valid for the field. QList fields = allFields(); foreach (const ProfileField *f, fields) { QStringList values = keyValues(f->name()); if (values.isEmpty() ) { LOG_DEBUG( "Error: Cannot find value for field" << f->name() << "for profile" << d_ptr->iName ); return false; } foreach (QString value, values) { if (!f->validate(value)) { LOG_DEBUG( "Error: Value" << value << "is not valid for profile" << d_ptr->iName ); return false; } } } // Enabled sub-profiles must be valid. foreach (Profile *p, d_ptr->iSubProfiles) { if (p->isEnabled() && !p->isValid()) return false; } return true; } QStringList Profile::subProfileNames(const QString &aType) const { QStringList names; bool checkType = !aType.isEmpty(); foreach (Profile *p, d_ptr->iSubProfiles) { if (!checkType || aType == p->type()) { names.append(p->name()); } // no else } return names; } Profile* Profile::subProfile(const QString &aName, const QString &aType) { bool checkType = !aType.isEmpty(); foreach (Profile *p, d_ptr->iSubProfiles) { if (aName == p->name() && (!checkType || aType == p->type())) { return p; } // no else } return 0; } const Profile* Profile::subProfile(const QString &aName, const QString &aType) const { bool checkType = !aType.isEmpty(); foreach (Profile *p, d_ptr->iSubProfiles) { if (aName == p->name() && (!checkType || aType == p->type())) { return p; } // no else } return 0; } const Profile *Profile::subProfileByKeyValue(const QString &aKey, const QString &aValue, const QString &aType, bool aEnabledOnly) const { bool checkType = !aType.isEmpty(); foreach (Profile *p, d_ptr->iSubProfiles) { if ((!checkType || aType == p->type()) && (aValue.compare(p->key(aKey), Qt::CaseInsensitive) == 0) && (!aEnabledOnly || p->isEnabled())) { return p; } // no else } return 0; } QList Profile::allSubProfiles() { return d_ptr->iSubProfiles; } QList Profile::allSubProfiles() const { QList constProfiles; foreach (Profile *p, d_ptr->iSubProfiles) { constProfiles.append(p); } return constProfiles; } void Profile::merge(const Profile &aSource) { // Get target sub-profile. Create new if not found. Profile *target = subProfile(aSource.name(), aSource.type()); if (0 == target) { ProfileFactory pf; target = pf.createProfile(aSource.name(), aSource.type()); if (target != 0) { target->d_ptr->iMerged = true; d_ptr->iSubProfiles.append(target); } // no else } // no else if (target != 0) { // Merge keys. Allow multiple keys with the same name. target->d_ptr->iMergedKeys.unite(aSource.d_ptr->iLocalKeys); target->d_ptr->iMergedKeys.unite(aSource.d_ptr->iMergedKeys); // Merge fields. QList sourceFields = aSource.allFields(); foreach (const ProfileField *f, sourceFields) { if (0 == target->field(f->name())) { target->d_ptr->iMergedFields.append(new ProfileField(*f)); } // no else } } // no else // Merge sub-profiles. foreach (Profile *p, aSource.d_ptr->iSubProfiles) { merge(*p); } } bool Profile::isLoaded() const { return d_ptr->iLoaded; } void Profile::setLoaded(bool aLoaded) { d_ptr->iLoaded = aLoaded; } bool Profile::isEnabled() const { return boolKey(KEY_ENABLED, true); } void Profile::setEnabled(bool aEnabled) { setBoolKey(KEY_ENABLED, aEnabled); } bool Profile::isHidden() const { return boolKey(KEY_HIDDEN); } bool Profile::isProtected() const { return boolKey(KEY_PROTECTED); } QString Profile::displayname() const { return key(KEY_DISPLAY_NAME); } QString Profile::generateProfileId(const QStringList &aKeys) { if (aKeys.size() == 0) return QString(); QString aId = QString::number(qHash(aKeys.join(QString()))); return aId; } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/BtHelper.cpp0000644000015600001650000001221512634332753025650 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include "BtHelper.h" const QString BT::BLUEZ_DEST = "org.bluez"; const QString BT::BLUEZ_MANAGER_INTERFACE = "org.bluez.Manager"; const QString BT::BLUEZ_ADAPTER_INTERFACE = "org.bluez.Adapter"; const QString BT::BLUEZ_DEVICE_INTERFACE = "org.bluez.Device"; const QString BT::GET_DEFAULT_ADAPTER = "DefaultAdapter"; const QString BT::FIND_DEVICE = "FindDevice"; const QString BT::DISCOVERSERVICES = "DiscoverServices"; const QString BT::GETPROPERTIES = "GetProperties"; BtHelper::BtHelper(const QString& deviceAddress, QObject* parent) : QObject(parent) { m_deviceAddress = deviceAddress; } BtHelper::~BtHelper() { LOG_DEBUG (""); } bool BtHelper::isServiceSupported (const QList& servicesList, const QString& serviceUUID) { LOG_DEBUG ("isServiceSupported"); foreach (QString service, servicesList) { //LOG_DEBUG ("Record : " << service); if (service.contains(serviceUUID)){ LOG_DEBUG ("Service found " << serviceUUID); return true; } } return false; } QString BtHelper::getDefaultAdapterPath() { LOG_DEBUG ("getDefaultAdapterPath"); QDBusInterface managerInterface( BT::BLUEZ_DEST, "/", BT::BLUEZ_MANAGER_INTERFACE, QDBusConnection::systemBus() ); if( !managerInterface.isValid() ) { LOG_DEBUG ("Manager interface is invalid"); return QString(); } QDBusReply pathReply = managerInterface.call(BT::GET_DEFAULT_ADAPTER); if( !pathReply.isValid() ) { LOG_DEBUG ("Not able to get the adapter path"); return QString(); } return pathReply.value().path(); } QString BtHelper::getDevicePath(QString &defaultAdapterPath) { if (defaultAdapterPath.isEmpty()) { LOG_DEBUG ( "Adapter path is empty"); return QString(); } QDBusInterface adapterInterface(BT::BLUEZ_DEST, defaultAdapterPath, BT::BLUEZ_ADAPTER_INTERFACE, QDBusConnection::systemBus() ); if( !adapterInterface.isValid() ) { LOG_DEBUG ( "Adapter interface is invalid"); return QString(); } QDBusReply pathReply = adapterInterface.call(BT::FIND_DEVICE, m_deviceAddress ); if( !pathReply.isValid() ) { LOG_DEBUG ( "Not able to find the BT device"); return QString(); } return pathReply.value().path(); } bool BtHelper::getServiceRecords(QList& servicesList) { LOG_DEBUG ( "getServiceRecords()"); QString defaultAdapterPath = getDefaultAdapterPath(); LOG_DEBUG ( "Adapter path = " << defaultAdapterPath) ; QString devicePath = getDevicePath(defaultAdapterPath); if (devicePath.isEmpty()) return false; LOG_DEBUG ( "Device path =" << devicePath); QDBusInterface deviceInterface(BT::BLUEZ_DEST, devicePath, BT::BLUEZ_DEVICE_INTERFACE, QDBusConnection::systemBus() ); if( deviceInterface.isValid() == false ) { LOG_DEBUG ("Device interface is not valid"); return false; } QDBusMessage message = deviceInterface.call(BT::DISCOVERSERVICES, QString()); QDBusArgument reply = QDBusReply(message).value(); QMap mapVal; reply >> mapVal; servicesList = mapVal.values(); if (servicesList.size() > 0) return true; return false; } QMap BtHelper::getDeviceProperties() { LOG_DEBUG ( "getDeviceProperties"); QMap mapVal; QString defaultAdapterPath = getDefaultAdapterPath(); LOG_DEBUG ( "Adapter path = " << defaultAdapterPath) ; QString devicePath = getDevicePath(defaultAdapterPath); if (devicePath.isEmpty()) return mapVal; LOG_DEBUG ( "Device path =" << devicePath); QDBusInterface deviceInterface(BT::BLUEZ_DEST, devicePath, BT::BLUEZ_DEVICE_INTERFACE, QDBusConnection::systemBus() ); if( deviceInterface.isValid() == false ) { LOG_DEBUG ("Device interface is not valid"); return mapVal; } QDBusMessage message = deviceInterface.call(BT::GETPROPERTIES); QDBusArgument reply = QDBusReply(message).value(); reply >> mapVal; return mapVal; } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/Profile_p.h0000644000015600001650000000557612634332753025543 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef PROFILE_P_H #define PROFILE_P_H #include #include #include #include "ProfileField.h" namespace Buteo { //! Private implementation class for Profile class. class ProfilePrivate { public: //! \brief Constructor ProfilePrivate(); //! \brief Copy Constructor ProfilePrivate(const ProfilePrivate &aSource); //! \brief Destructor ~ProfilePrivate(); //! Profile name. QString iName; //! Profile type. QString iType; //! Is the profile fully loaded and constructed. bool iLoaded; //! Is the profile merged created by merging from sub-profile. bool iMerged; //! Local keys, that are not merged from sub-profiles. QMap iLocalKeys; //! Keys that are merged from sub-profile files. QMap iMergedKeys; //! Local fields, that are not merged from sub-profiles. QList iLocalFields; //! Fields that are merged from sub-profiles. QList iMergedFields; //! List of sub-profiles. QList iSubProfiles; }; } Buteo::ProfilePrivate::ProfilePrivate() : iLoaded(false), iMerged(false) { } Buteo::ProfilePrivate::ProfilePrivate(const ProfilePrivate &aSource) : iName(aSource.iName), iType(aSource.iType), iLoaded(aSource.iLoaded), iMerged(aSource.iMerged), iLocalKeys(aSource.iLocalKeys), iMergedKeys(aSource.iMergedKeys) { foreach (const ProfileField *localField, aSource.iLocalFields) { iLocalFields.append(new ProfileField(*localField)); } foreach (const ProfileField *mergedField, aSource.iMergedFields) { iMergedFields.append(new ProfileField(*mergedField)); } foreach (Profile *p, aSource.iSubProfiles) { iSubProfiles.append(p->clone()); } } Buteo::ProfilePrivate::~ProfilePrivate() { qDeleteAll(iLocalFields); iLocalFields.clear(); qDeleteAll(iMergedFields); iMergedFields.clear(); qDeleteAll(iSubProfiles); iSubProfiles.clear(); } #endif // PROFILE_P_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/SyncSchedule_p.h0000644000015600001650000000637212634332753026527 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2014-2015 Jolla Ltd * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCSCHEDULE_P_H #define SYNCSCHEDULE_P_H #include #include namespace Buteo { //! Private implementation class for SyncSchedule. class SyncSchedulePrivate { public: /*! \brief Constructor * */ SyncSchedulePrivate(); /*! \brief Copy Constructor * */ SyncSchedulePrivate(const SyncSchedulePrivate &aSource); /*! \brief Parses week day numbers from a string. * * \param aDays String containing the week day numbers. * \return Set of week day numbers. */ DaySet parseDays(const QString &aDays) const; /*! \brief Creates a string from a set of week day numbers. * * \param aDays Set of week day numbers. * \return String of week day numbers. */ QString createDays(const DaySet &aDays) const; /*! \brief Adjusts given date to be in the set of given week days. * * Day is increased until the week day is contained in the given set of * week day numbers. * \param aTime Date/time to adjust. * \param aDays Set of enabled week day numbers. * \return Was day adjusted to a valid day. If the week day was already in * the set of given week days, this function returns false. If the day * set does not contain any valid days, this function sets aTime to null * object and returns false. */ bool adjustDate(QDateTime &aTime, const DaySet &aDays) const; /*! \brief Checks if the given date/time is inside rush hours. * * \param aTime Date/time to check. * \return True if in rush hours. */ bool isRush(const QDateTime &aTime) const; //! Number of Days before the next sync starts DaySet iDays; //! Sync Time QTime iTime; //! sync schedule configure time for intial update QDateTime iScheduleConfiguredTime; //! Time interval unsigned iInterval; bool iEnabled; // ============ RUSH HOUR SETTINGS =========== //! indicates the schedule for rush hour - days DaySet iRushDays; //! indicates the schedule for rush hour start QTime iRushBegin; //! indicates the schedule for rush hour end QTime iRushEnd; //! Rush Hour Time interval unsigned iRushInterval; //! Indicates if Rush Hour is Enabled bool iRushEnabled; //! Indicates if External Rush Hour schedule is Enabled bool iExternalRushEnabled; }; } #endif // SYNCSCHEDULE_P_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/SyncResults.cpp0000644000015600001650000001220512634332753026440 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncResults.h" #include "LogMacros.h" #include #include "ProfileEngineDefs.h" namespace Buteo { //! Private implementation class for SyncResults. class SyncResultsPrivate { public: //! Default Constructors SyncResultsPrivate(); //! Copy Constructor SyncResultsPrivate(const SyncResultsPrivate &aSource); //! List of target results. QList iTargetResults; //! Sync time. QDateTime iTime; //! Sync major code. int iMajorCode; //! Sync minor reason. int iMinorCode; //! Sync target id QString iTargetId; //! Are results for Scheduled Sync bool iScheduled; }; SyncResultsPrivate::SyncResultsPrivate() : iTime(QDateTime::currentDateTime()), iMajorCode(0), iMinorCode(0), iScheduled(false) { } SyncResultsPrivate::SyncResultsPrivate(const SyncResultsPrivate &aSource) : iTargetResults(aSource.iTargetResults), iTime(aSource.iTime), iMajorCode(aSource.iMajorCode), iMinorCode(aSource.iMinorCode), iTargetId(aSource.iTargetId), iScheduled(aSource.iScheduled) { } } using namespace Buteo; SyncResults::SyncResults() : d_ptr(new SyncResultsPrivate()) { } SyncResults::SyncResults(const SyncResults &aSource) : d_ptr(new SyncResultsPrivate(*aSource.d_ptr)) { } SyncResults::SyncResults(QDateTime aTime, int aMajorCode, int aMinorCode) : d_ptr(new SyncResultsPrivate()) { d_ptr->iTime = aTime; d_ptr->iMajorCode = aMajorCode; d_ptr->iMinorCode = aMinorCode; } SyncResults::SyncResults(const QDomElement &aRoot) : d_ptr(new SyncResultsPrivate()) { d_ptr->iTime = QDateTime::fromString(aRoot.attribute(ATTR_TIME), Qt::ISODate); d_ptr->iMajorCode = aRoot.attribute(ATTR_MAJOR_CODE).toInt(); d_ptr->iMinorCode = aRoot.attribute(ATTR_MINOR_CODE).toInt(); d_ptr->iScheduled = (aRoot.attribute(KEY_SYNC_SCHEDULED) == BOOLEAN_TRUE); QDomElement target = aRoot.firstChildElement(TAG_TARGET_RESULTS); for (; !target.isNull(); target = target.nextSiblingElement(TAG_TARGET_RESULTS)) { d_ptr->iTargetResults.append(TargetResults(target)); } } SyncResults::~SyncResults() { delete d_ptr; d_ptr = 0; } SyncResults& SyncResults::operator=(const SyncResults &aRhs) { if (&aRhs != this) { delete d_ptr; d_ptr = new SyncResultsPrivate(*aRhs.d_ptr); } return *this; } QDomElement SyncResults::toXml(QDomDocument &aDoc) const { QDomElement root = aDoc.createElement(TAG_SYNC_RESULTS); root.setAttribute(ATTR_TIME, d_ptr->iTime.toString(Qt::ISODate)); root.setAttribute(ATTR_MAJOR_CODE, QString::number(d_ptr->iMajorCode)); root.setAttribute(ATTR_MINOR_CODE, QString::number(d_ptr->iMinorCode)); root.setAttribute(KEY_SYNC_SCHEDULED, d_ptr->iScheduled ? BOOLEAN_TRUE : BOOLEAN_FALSE); foreach (TargetResults tr, d_ptr->iTargetResults) { root.appendChild(tr.toXml(aDoc)); } return root; } QString SyncResults::toString() const { QDomDocument doc; QDomProcessingInstruction xmlHeading = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""); doc.appendChild(xmlHeading); QDomElement root = toXml(doc); doc.appendChild(root); return doc.toString(PROFILE_INDENT); } QList SyncResults::targetResults() const { return d_ptr->iTargetResults; } void SyncResults::addTargetResults(const TargetResults &aResults) { d_ptr->iTargetResults.append(aResults); } QDateTime SyncResults::syncTime() const { return d_ptr->iTime; } int SyncResults::majorCode() const { return d_ptr->iMajorCode; } void SyncResults::setMajorCode(int aMajorCode) { FUNCTION_CALL_TRACE; d_ptr->iMajorCode = aMajorCode; } int SyncResults::minorCode() const { return d_ptr->iMinorCode; } void SyncResults::setMinorCode(int aMinorCode) { FUNCTION_CALL_TRACE; d_ptr->iMinorCode = aMinorCode; } void SyncResults::setTargetId(const QString& aTargetId) { d_ptr->iTargetId = aTargetId; } QString SyncResults::getTargetId() const { return d_ptr->iTargetId; } bool SyncResults::operator<(const SyncResults &aOther) const { return (d_ptr->iTime < aOther.d_ptr->iTime); } void SyncResults::setScheduled(bool aScheduled) { d_ptr->iScheduled = aScheduled; } bool SyncResults::isScheduled() const { return d_ptr->iScheduled; } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/ProfileManager.h0000644000015600001650000003433512634332753026512 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef PROFILEMANAGER_H #define PROFILEMANAGER_H #include "SyncProfile.h" #include "Profile.h" #include #include namespace Buteo { class ProfileManagerPrivate; /*! \brief * ProfileManager is responsible for storing and retrieving the profiles. * * It also constructs top level profiles by loading and merging all referenced * sub-profiles. The ProfileManager hides the actual storage from the user, so * that it makes no difference if the profiles are stored to simple XML-files * or to a database. Profiles can be queried by name and type. */ class ProfileManager: public QObject { Q_OBJECT public: //! Primary profile path where profiles will be searched. static const QString DEFAULT_PRIMARY_PROFILE_PATH; //! Secondary profile path where profiles will be searched. static const QString DEFAULT_SECONDARY_PROFILE_PATH; //! Search criteria for finding profiles. struct SearchCriteria { //! Enum to identify if a member type exists or not enum Type { //! Sub-profile (and key) exists. EXISTS, //! Sub-profile (or key) does not exist. NOT_EXISTS, //! Key value is equal. EQUAL, //! Key value is not equal. NOT_EQUAL }; //! \brief Constructor. SearchCriteria(); //! \brief Copy constructor. SearchCriteria(const SearchCriteria &aSource); //! Search criteria type. Type iType; //! Sub-profile name. If this is empty but profile type is given, //! matching is tried with each sub-profile of correct type. If both //! profile name and type are empty, mathing is done with keys of the //! main profile. QString iSubProfileName; //! Sub-profile type. If this is empty but profile name is given, //! matching is done with the first sub-profile having the correct name //! regardless of the type. QString iSubProfileType; //! Key name. If this is empty, key comparison is not made. QString iKey; //! Key value. This must be given if criteria type is EQUAL or NOT_EQUAL. QString iValue; }; //! \brief Enum to indicate the change type of the Profile Operation enum ProfileChangeType { //! a New Profile has been added PROFILE_ADDED = 0, //! a Existing Profile has been modified PROFILE_MODIFIED, //! Profile has been Removed PROFILE_REMOVED, //! Profile log file Modified. PROFILE_LOGS_MODIFIED }; /*! \brief Constructor. * * \param aPrimaryPath Path where profiles are searched first. Save * operations will write to this location. * \param aSecondaryPath Path where a profile is searched for if it is * not found from the primary path. Useful for having default read-only * profiles. */ ProfileManager(const QString &aPrimaryPath = DEFAULT_PRIMARY_PROFILE_PATH, const QString &aSecondaryPath = DEFAULT_SECONDARY_PROFILE_PATH); /*! \brief Destructor. */ ~ProfileManager(); /*! \brief Gets the names of all available profiles with the given type. * * \param aType Type of the profiles to get. * \return The list of profile names. */ QStringList profileNames(const QString &aType); /*! \brief Gets a sync profile. * * Loads and merges also all sub-profiles that are referenced from the * main profile. Loads the log of finished synchronization sessions with * this profile. * \param aName Name of the profile to get. * \return The sync profile. NULL if the profile is not found. Caller becomes * the owner of the returned object and is responsible of deleting it after * use. Changes made to the profile are not saved to the persistent profile * storage, unless save function of this class is called. */ SyncProfile *syncProfile(const QString &aName); /*! \brief Gets all sync profiles. * * \return The list of sync profiles. Caller is responsible for deleting * the returned profile objects. */ QList allSyncProfiles(); /*! \brief Gets all visible sync profiles. * * Returns all sync profiles that should be visible in sync ui. A profile * is visible if it has not been explicitly set as hidden. * \return The list of sync profiles. Caller is responsible for deleting * the returned profile objects. */ QList allVisibleSyncProfiles(); /*! \brief Gets profiles with matching data. * * \param aSubProfileName Name of a required sub-profile. If this is given, * the sub-profile must exist and key comparison is made with the keys * of the sub-profile. * \param aSubProfileType Type of a required sub-profile. If this is given but * sub-profile name is empty, the first sub-profile with matching type is * used in comparison. * \param aKey Name of a required key. If this is empty, key comparison is * not made and existance of the sub-profile is enough. * \param aValue Value of the required key. If this is empty, any value * is accepted as long as the key itself exists. * \return List of matching profiles. Caller is responsible for deleting * the returned profile objects. */ QList getSyncProfilesByData(const QString &aSubProfileName, const QString &aSubProfileType, const QString &aKey = "", const QString &aValue = ""); /*! \brief Gets profiles with matching data. * * \param aCriteria List of criteria to use in the search. Each criterion * in the list has to match for a profile to be returned as a result. * \return List of matching profiles. Caller is responsible for deleting * the returned profile objects. */ QList getSyncProfilesByData( const QList &aCriteria); /*! \brief Gets profiles based on supported storages. * * Returns all enabled and visible sync profiles of online destinations * that support the given storage. Device-to-device sync profiles are not * returned. * \param aStorageName Name of the storage that must be supported. * \param aStorageMustBeEnabled True if the supported storage must be * also enabled. Only enabled storages are included in sync session. * \return List of matching profiles. Caller is responsible for deleting * the returned profile objects. */ QList getSyncProfilesByStorage( const QString &aStorageName, bool aStorageMustBeEnabled = false); /*! \brief Gets profiles interested in sync on change for a storage * * Returns all enabled and visible sync profiles of online destinations * Device-to-device sync profiles are not returned. * \param aStorageName Name of the storage * \return List of matching profiles. Caller is responsible for deleting * the returned profile objects. */ QList getSOCProfilesForStorage( const QString &aStorageName); /*! \brief Expands the given profile. * * Loads and merges all sub-profiles that are referenced from the main * profile. * \param aProfile Name of the profile to expand. */ void expand(Profile &aProfile); /*! \brief Saves the given synchronization log. * * \param aLog Log to save. * \return True if saving was successful. */ bool saveLog(const SyncLog &aLog); /*! \brief Saves the results of a sync session to the log. * * This is a convenience function that loads the log associated with the * given profile, appends the given results to the log and then saves the * log. * \param aProfileName Name of the profile used in the sync session. * \param aResults Results. * \return True if saving was successful. */ bool saveSyncResults(QString aProfileName, const SyncResults &aResults); /*! \brief Gets a profile. * * \param aName Name of the profile to get. * \param aType Type of the profile to get. * \return Pointer to the profile. If the profile is not found, NULL is * returned. Caller is responsible for deleting the returned object. * Changes made to the profile are not saved to profile storage, unless * updateProfile function of this class is called */ Profile *profile(const QString &aName, const QString &aType); /*! \brief Gets a profile object from an xml document. * * \param aProfileAsXml Name of the profile to get. * \return Pointer to the profile. If the xml is not valid, NULL is * returned. Caller is responsible for deleting the returned object. * Changes made to the profile are not saved to profile storage, unless * updateProfile function of this class is called */ Profile *profileFromXml(const QString &aProfileAsXml); /*! \brief Gets a temporary profile (saved if sync is sucessfull). * * \param btAddress Address of the remote device bt address/usb . * \param saveNewProfile If to save the profile or not (e.g pc suite profile) * \return Pointer to the profile. * Changes made to the profile are not saved to profile storage, unless * save function of this class is called */ SyncProfile *createTempSyncProfile (const QString &btAddress, bool &saveNewProfile); /*! \brief Updates the existing profile with the profile * given as parameter and emits profileChanged() Signal with appropriate value * depening if profile was newly added (0) or updated (1) * * NOTE: only Sync Profiles can be updated using ProfileManger * * \param aProfile - Profile Object * \return profileId - this will be empty if the update Failed. */ QString updateProfile(const Profile &aProfile); /*! \brief Deletes a profile from the persistent storage. * * This will emit a signalProfileChanged with ChangeType * as Removed if Removal is successful * NOTE: only Sync Profiles can be updated using ProfileManger * \param aProfileId Profile to be remove. * \return Success indicator. */ bool removeProfile(const QString &aProfileId); /*! \brief Renames a profile, and the associated log too * * \param aName The old name of the profile * \param aNewName The new name for the profile * \return Returns true if the rename was successful */ bool rename(const QString &aName, const QString &aNewName); /*! \brief Enables sync'd storages in profile * * \param aProfile Profile of the remote device * \param aStorageMap Map of storage names(hcalendar, hcontacts) and if sync * enabled value true/false * \param aModified Whether the profile was updated as a result of this function call, * and thus requires writing to disk */ void enableStorages (Profile &aProfile, QMap &aStorageMap, bool *aModified = NULL); /*! \brief Sets storage subprofiles hidden status for the given profile * * \param aProfile Profile of the remote device * \param aStorageMap Map of storage names (hcalendar, hcontacts) and visibility status. With value \e true * the storage will be set visible (equals profile attribute hidden=false) * \param aModified Whether the profile was updated as a result of this function call, * and thus requires writing to disk */ void setStoragesVisible(Profile &aProfile, QMap &aStorageMap, bool *aModified = NULL); /*! \brief Sets remote target in profile * * \param aProfile Profile of the remote device * \param aId remote device id * */ void saveRemoteTargetId (Profile &aProfile,const QString& aId); /*! \brief Sets/Overwrites the schedule to a profile * * \param aProfileId Profile Id * \param aScheduleAsXml SyncSchedule Object as an xml string * */ bool setSyncSchedule(QString aProfileId , QString aScheduleAsXml); /*! \brief checks if a profile has retries info and stores the same * * @param aProfile sync profile */ void addRetriesInfo(const SyncProfile* aProfile); /*! \brief gets the next retry after time for a sync profile * * @param aProfile sync profile * @return next retry interval */ QDateTime getNextRetryInterval(const SyncProfile* aProfile); /*! \brief call this to indicate that retries have to stop for a certain * sync for a profile - either the no. of retry attempts exhausted or one of the retries succeeded * * @param aProfileName name of the profile */ void retriesDone(const QString& aProfileName); #ifdef SYNCFW_UNIT_TESTS friend class ProfileManagerTest; #endif signals: /*! \brief Notifies about a change in profile. * * This signal is sent when the profile data is modified or when a profile * is added or deleted in msyncd. * \param aProfileName Name of the changed profile. * \param aChangeType \see ProfileManager::ProfileChangeType * \param aProfileAsXml Updated Profile Object is sent as xml * */ void signalProfileChanged(QString aProfileName, int aChangeType , QString aProfileAsXml); private: ProfileManager& operator=(const ProfileManager &aRhs); ProfileManagerPrivate *d_ptr; QHash > iSyncRetriesInfo; }; } #endif // PROFILEMANAGER_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/SyncLog.cpp0000644000015600001650000000772512634332753025533 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncLog.h" #include "LogMacros.h" #include #include #include "ProfileEngineDefs.h" namespace Buteo { bool syncResultPointerLessThan(const SyncResults *&aLhs, const SyncResults *&aRhs) { if (aLhs && aRhs) { return (*aLhs < *aRhs); } else { return false; } } // Private implementation class for SyncLog. class SyncLogPrivate { public: SyncLogPrivate(); SyncLogPrivate(const SyncLogPrivate &aSource); ~SyncLogPrivate(); // Name of the profile this log belongs to. QString iProfileName; // List of the sync results this log consists of. QList iResults; }; } using namespace Buteo; SyncLogPrivate::SyncLogPrivate() { } SyncLogPrivate::SyncLogPrivate(const SyncLogPrivate &aSource) : iProfileName(aSource.iProfileName) { foreach (const SyncResults *results, aSource.iResults) { iResults.append(new SyncResults(*results)); } } SyncLogPrivate::~SyncLogPrivate() { qDeleteAll(iResults); iResults.clear(); } SyncLog::SyncLog(const QString &aProfileName) : d_ptr(new SyncLogPrivate()) { d_ptr->iProfileName = aProfileName; } SyncLog::SyncLog(const QDomElement &aRoot) : d_ptr(new SyncLogPrivate()) { d_ptr->iProfileName = aRoot.attribute(ATTR_NAME); QDomElement results = aRoot.firstChildElement(TAG_SYNC_RESULTS); for (; !results.isNull(); results = results.nextSiblingElement(TAG_SYNC_RESULTS)) { d_ptr->iResults.append(new SyncResults(results)); } // Sort result entries by sync time. //qSort(d_ptr->iResults.begin(), d_ptr->iResults.end(), syncResultPointerLessThan); } SyncLog::SyncLog(const SyncLog &aSource) : d_ptr(new SyncLogPrivate(*aSource.d_ptr)) { } SyncLog::~SyncLog() { delete d_ptr; d_ptr = 0; } void SyncLog::setProfileName(const QString& aProfileName) { d_ptr->iProfileName = aProfileName; } QString SyncLog::profileName() const { return d_ptr->iProfileName; } QDomElement SyncLog::toXml(QDomDocument &aDoc) const { QDomElement root = aDoc.createElement(TAG_SYNC_LOG); root.setAttribute(ATTR_NAME, d_ptr->iProfileName); foreach (const SyncResults *results, d_ptr->iResults) { root.appendChild(results->toXml(aDoc)); } return root; } const SyncResults* SyncLog::lastResults() const { FUNCTION_CALL_TRACE; if (d_ptr->iResults.isEmpty()) { return 0; } else { return d_ptr->iResults.last(); } } QList SyncLog::allResults() const { return d_ptr->iResults; } void SyncLog::addResults(const SyncResults &aResults) { FUNCTION_CALL_TRACE; // To prevent the log growing too much, the maximum number of entries in //the log is defined const int MAXLOGENTRIES = 5; if (d_ptr->iResults.size() == MAXLOGENTRIES) { // The list is sorted so that the oldest item is in the beginning delete d_ptr->iResults.takeFirst(); } // no else d_ptr->iResults.append(new SyncResults(aResults)); // Sort result entries by sync time. //qSort(d_ptr->iResults.begin(), d_ptr->iResults.end(), syncResultPointerLessThan); } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/Profile.h0000644000015600001650000003076612634332753025223 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef PROFILE_H #define PROFILE_H #include #include #include #include #include "ProfileField.h" class QDomDocument; class QDomElement; namespace Buteo { class ProfileTest; class ProfilePrivate; /*! \brief This class represents a single profile, a collection of settings or * data releated to some entity. * * A profile can contain keys, fields and other profiles as sub-profiles. * Functions for accessing all these in different ways are provided. A profile * object can be created from XML and exported to XML, but otherwise the class * interface does not use XML. New classes can be derived from this class for * different profile types to add helper functions for accessing specific keys * and fields known by the profile type. */ class Profile { public: //! String constants for different profile type names. static const QString TYPE_CLIENT; //! Server type . static const QString TYPE_SERVER; //! Storage type static const QString TYPE_STORAGE; //! Service type //static const QString TYPE_SERVICE; //! Sync type static const QString TYPE_SYNC; /*! \brief Default Constructor * */ Profile(); /*! \brief Constructs a Profile object with given name and type. * * \param aName Profile name. * \param aType Profile type. Prefer using predefined constants like * TYPE_SYNC. */ Profile(const QString &aName, const QString &aType); /*! \brief Constructs a Profile object from XML. * * \param aRoot Root element of the profile XML. */ explicit Profile(const QDomElement &aRoot); /*! \brief Copy constructor. * * \param aSource Copy source. */ Profile(const Profile &aSource); /*! \brief Creates a clone of the profile. * * \return The clone. */ virtual Profile *clone() const; //! \brief Destructor. virtual ~Profile(); /*! \brief Gets the name of the profile. * * \return Profile name. */ QString name() const; /*! \brief Gets the display name of the profile. * * \return Profile display name. */ QString displayname() const; /*! \brief Sets the name of the profile. * * \param aName Name of the profile. */ virtual void setName(const QString &aName); /*! \brief Sets the name of the profile. * * \param aKeys Keys required to generate the profile name. */ virtual void setName(const QStringList &aKeys); /*! \brief Gets the type of the profile. * * \return Profile type. */ QString type() const; /*! \brief Creates a XML representation of the profile. * * \param aDoc Parent document for the created XML elements. The elements * are not inserted to the document by this function, but the document is * required to create the elements. * \param aLocalOnly Should only local profile elements be present in the * generated XML. If this is true, elements merged from sub-profiles are * not included. * \return Generated XML node tree. */ virtual QDomElement toXml(QDomDocument &aDoc, bool aLocalOnly = true) const; /*! \brief Outputs a XML representation of the profile to a string. * * Merged sub-profile data is also included in the output string. * \return Generated XML string. */ QString toString() const; /*! \brief Gets the value of the given key. * * \param aName Name of the key to read. * \param aDefault Default value. * \return Value of the key. If the key was not found, the default is * returned. If there are multiple instances of the key with the given * name, the value of the first instance is returned. */ QString key(const QString &aName, const QString &aDefault = QString()) const; /*! \brief Gets all keys and their values. * * \return Map of key names/values. */ QMap allKeys() const; /*! \brief Gets all keys that are not related to storages. * * \return Map of key names/values. */ QMap allNonStorageKeys() const; /*! \brief Gets the value of the given boolean key. * * Returns true if the key exists and its value equals "true". If * the key does not exist, the default value is returned. * \param aName Name of the key to read. * \param aDefault Value to return if the key does not exist. * \return The boolean value of the key. */ bool boolKey(const QString &aName, bool aDefault = false) const; /*! \brief Gets the values of all keys with the given name. * * If the key does not exist at all, an empty list is returned. * \param aName Name of the key to read. * \return List of values associated with the key. */ QStringList keyValues(const QString &aName) const; /*! \brief Gets the names of all keys. * * \return List of key names. */ QStringList keyNames() const; /*! \brief Sets the value of a key. * * If the key does not exist yet, it is created. * \param aName Name of the key. * \param aValue Value of the key. */ void setKey(const QString &aName, const QString &aValue); /*! \brief Sets multiple values for a key. * * All previous (local) values of the key are removed. A key entry for * each of the provided values is then created. * \param aName Name of the key. * \param aValues Values for the key. */ void setKeyValues(const QString &aName, const QStringList &aValues); /*! \brief Sets the value of a boolean key. * * The key value is set to "true" of "false". If the key does not exist * yet, it is created. * \param aName Name of the key. * \param aValue Value of the key. */ void setBoolKey(const QString &aName, bool aValue); /*! \brief Removes a key from profile. All instances of the key are removed. * * \param aName Name of the key to remove. */ void removeKey(const QString &aName); /*! \brief Gets the field with the given name. * * If the field does not exist, NULL is returned. * To get/set the value associated with the field, use the key handling * functions with the name of the field. * \param aName Name of the field. * \return Pointer to the field. */ const ProfileField *field(const QString &aName) const; /*! \brief Gets all fields. * * \return List of pointers to the fields. */ QList allFields() const; /*! \brief Gets all visible fields of the profile. * * Each field can define its visibility. This functions returns only * fields that are visible. * \return List of pointers to the visible fields. */ QList visibleFields() const; /*! \brief Checks if the profile is valid. * * A profile is valid if: * 1. Name and type are set (not empty). * 2. For each field there is a key with the same name, and the key value * (or all values, if multiple keys with the same name exist) is valid for * the field. * 3. All sub-profiles are valid according to these three rules. * \return Is the profile valid. */ bool isValid() const; /*! \brief Gets the names of all sub-profiles with the given type. * * \param aType Type of sub-profiles to get. If this is empty, all * sub-profile names are returned. * \return Names of the sub-profiles. */ QStringList subProfileNames(const QString &aType = "") const; /*! \brief Gets a sub-profile with the given name and type. * * \param aName Name of the sub-profile to get. * \param aType Type of the sub-profile to get. If the type is empty, * any type is accepted. * \return The first sub-profile that matches the criteria. NULL if no such * sub-profile was found. The returned sub-profile is owned by the main * profile and the user must not delete it. */ Profile *subProfile(const QString &aName, const QString &aType = ""); /*! \brief const method for subProfile \see Profile::subProfile * */ const Profile *subProfile(const QString &aName, const QString &aType = "") const; /*! \brief Gets a sub-profile by key value. * * Returns the first sub-profile that has a key with the given value. * \param aKey Name of the key. * \param aValue Required value of the key. * \param aType Type of the sub-profile. If empty, any type can match. * \param aEnabledOnly Should only enabled sub-profiles be compared. * \return First matching sub-profile, NULL if no match. */ const Profile *subProfileByKeyValue(const QString &aKey, const QString &aValue, const QString &aType, bool aEnabledOnly) const; /*! \brief Gets all sub-profiles. * * \return List of sub-profiles. The returned sub-profiles are owned by the main * profile and the user must not delete them. */ QList allSubProfiles(); /*! \brief Gets all sub-profiles as const * * \return List of sub-profiles. The returned sub-profiles are const and are owned by the main * profile and the user must not delete them. */ QList allSubProfiles() const; /*! \brief Merges a profile to this profile. * * The source profile and all its sub-profiles are merged as direct * sub-profiles of this profile. This function is mainly used by the * ProfileManager, when it constructs a single profile from multiple * sub-profile files. * \param aSource Profile to merge. */ void merge(const Profile &aSource); /*! \brief Checks if the profile is fully constructed by loading all * sub-profiles from separate profile files. * * A profile can have sub-profiles defined directly inside it, but * typically the sub-profiles are made complete by checking if there is * a separate profile file with the same name and type, and then loading * and merging the keys and fields defined in these files to the ones * defined directly in the main profile. * \return Is the profile fully loaded. */ bool isLoaded() const; /*! \brief Sets if the profile is fully loaded. * * This function is used by the ProfileManager. The purpose of this flag * is to avoid loading the same sub-profile multiple times, if there are * more than one references to it in the sub-profile tree. * \param aLoaded Is the profile loaded. */ void setLoaded(bool aLoaded); /*! \brief Returns if the profile is enabled. * * \return Is the profile enabled. */ virtual bool isEnabled() const; /*! \brief Set is the profile is enabled. * * \param aEnabled New enabled status. */ void setEnabled(bool aEnabled); /*! \brief Checks if the profile is hidden. * * A hidden profile should not be visible in sync ui. * \return True if hidden. */ bool isHidden() const; /*! \brief Checks if the profile is protected. * * A protected profile can not be removed using the ProfileManager. * \return True if protected. */ bool isProtected() const; private: Profile& operator=(const Profile &aRhs); ProfilePrivate *d_ptr; /*! \brief Generates a profile id based on keys * * \param aKeys List of keys to generate profile id * \returns Profile name * */ QString generateProfileId(const QStringList &aKeys); #ifdef SYNCFW_UNIT_TESTS friend class ProfileTest; #endif }; } #endif // PROFILE_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/ProfileField.cpp0000644000015600001650000001344312634332753026513 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ProfileField.h" #include "ProfileEngineDefs.h" #include namespace Buteo { //! ProfileField Visbility Const string for always const QString ProfileField::VISIBLE_ALWAYS = "always"; //! ProfileField Visbility Const string for never const QString ProfileField::VISIBLE_NEVER = "never"; //! ProfileField Visbility Const string for user const QString ProfileField::VISIBLE_USER = "user"; //! ProfileField Visbility Const string for boolean const QString ProfileField::TYPE_BOOLEAN = "boolean"; // Private implementation class for ProfileField. class ProfileFieldPrivate { public: //! \brief Constructor ProfileFieldPrivate(); //! \brief Copy Constructor ProfileFieldPrivate(const ProfileFieldPrivate &aSource); //! \brief Name of the ProfileField QString iName; //! \brief Type of the ProfileField QString iType; //! \brief DefaultValue of the ProfileField QString iDefaultValue; //! \brief List of Options of the ProfileField QStringList iOptions; //! \brief Label of the ProfileField QString iLabel; //! \brief Visibility of the ProfileField QString iVisible; //! \brief Write Access Specifier of the ProfileField bool iReadOnly; }; } using namespace Buteo; ProfileFieldPrivate::ProfileFieldPrivate() : iReadOnly(false) { } ProfileFieldPrivate::ProfileFieldPrivate(const ProfileFieldPrivate &aSource) : iName(aSource.iName), iType(aSource.iType), iDefaultValue(aSource.iDefaultValue), iOptions(aSource.iOptions), iLabel(aSource.iLabel), iVisible(aSource.iVisible), iReadOnly(aSource.iReadOnly) { } ProfileField::ProfileField(const QDomElement &aRoot) : d_ptr(new ProfileFieldPrivate()) { d_ptr->iName = aRoot.attribute(ATTR_NAME); d_ptr->iType = aRoot.attribute(ATTR_TYPE); d_ptr->iDefaultValue = aRoot.attribute(ATTR_DEFAULT); d_ptr->iLabel = aRoot.attribute(ATTR_LABEL); d_ptr->iVisible = aRoot.attribute(ATTR_VISIBLE); d_ptr->iReadOnly = (aRoot.attribute(ATTR_READONLY).compare( BOOLEAN_TRUE, Qt::CaseInsensitive) == 0); // Parse options. QDomElement option = aRoot.firstChildElement(TAG_OPTION); for (; !option.isNull(); option = option.nextSiblingElement(TAG_OPTION)) { QString optionStr = option.text(); if (!optionStr.isEmpty()) { d_ptr->iOptions.append(optionStr); } else { // Empty value. } } // Options for boolean type are inserted automatically. if (d_ptr->iOptions.empty()) { if (d_ptr->iType == TYPE_BOOLEAN) { d_ptr->iOptions.append(BOOLEAN_TRUE); d_ptr->iOptions.append(BOOLEAN_FALSE); } // no else } // no else } ProfileField::ProfileField(const ProfileField &aSource) : d_ptr(new ProfileFieldPrivate(*aSource.d_ptr)) { } ProfileField::~ProfileField() { delete d_ptr; d_ptr = 0; } QString ProfileField::name() const { return d_ptr->iName; } QString ProfileField::type() const { return d_ptr->iType; } QString ProfileField::defaultValue() const { return d_ptr->iDefaultValue; } QStringList ProfileField::options() const { return d_ptr->iOptions; } QString ProfileField::label() const { return d_ptr->iLabel; } bool ProfileField::validate(const QString &aValue) const { // Value is valid if it exists in the list of options, // or if options have not been defined. if (!aValue.isEmpty() && (d_ptr->iOptions.contains(aValue) || d_ptr->iOptions.empty())) { return true; } else { return false; } } QDomElement ProfileField::toXml(QDomDocument &aDoc) const { QDomElement root = aDoc.createElement(TAG_FIELD); root.setAttribute(ATTR_NAME, d_ptr->iName); root.setAttribute(ATTR_TYPE, d_ptr->iType); root.setAttribute(ATTR_DEFAULT, d_ptr->iDefaultValue); root.setAttribute(ATTR_LABEL, d_ptr->iLabel); if (!d_ptr->iVisible.isEmpty()) root.setAttribute(ATTR_VISIBLE, d_ptr->iVisible); if (d_ptr->iReadOnly) root.setAttribute(ATTR_READONLY, BOOLEAN_TRUE); if (d_ptr->iType == TYPE_BOOLEAN) { // No need to specify true/false options, field parser will add // them automatically. } else if (!d_ptr->iOptions.isEmpty()) { foreach (QString optionStr, d_ptr->iOptions) { QDomElement e = aDoc.createElement(TAG_OPTION); QDomText t = aDoc.createTextNode(optionStr); e.appendChild(t); root.appendChild(e); } } // no else return root; } QString ProfileField::visible() const { if (d_ptr->iVisible.isEmpty()) { return VISIBLE_USER; } else { return d_ptr->iVisible; } } bool ProfileField::isReadOnly() const { return d_ptr->iReadOnly; } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/TargetResults.h0000644000015600001650000000656412634332753026432 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef TARGETRESULTS_H #define TARGETRESULTS_H #include class QDomDocument; class QDomElement; namespace Buteo { class TargetResultsPrivate; //! \brief Container for number of items added, deleted and modified. struct ItemCounts { //! No. of Items added unsigned added; //! No. of Items deleted unsigned deleted; //! No. of Items modified unsigned modified; //! Default Constructor ItemCounts() : added(0), deleted(0), modified(0) { }; //! Constructor with 3 parameters ItemCounts(unsigned aAdded, unsigned aDeleted, unsigned aModified) : added(aAdded), deleted(aDeleted), modified(aModified) {} }; /*! \brief Sync results for one target. * * TargetResults contains information about how many items were added, deleted * and modified in a specific sync target during the sync session. */ class TargetResults { public: /*! \brief Copy constructor. * * \param aSource Copy source. */ TargetResults(const TargetResults &aSource); /*! \brief Constructor. * * \param aTargetName Name of the target. * \param aLocalItems Counts of local items. * \param aRemoteItems Counts of remote items. */ TargetResults(const QString &aTargetName, ItemCounts aLocalItems, ItemCounts aRemoteItems); /*! \brief Constructs TargetResults from XML. * * \param aRoot Root element of the XML representation. */ explicit TargetResults(const QDomElement &aRoot); /*! \brief Destructor. */ ~TargetResults(); /*! \brief Assignment operator. * * \param aRhs Source. */ TargetResults& operator=(const TargetResults &aRhs); /*! \brief Exports the target results to XML. * * \param aDoc Parent document for the created XML elements. The created * elements are not inserted to the document by this function, but the * document is still required for creating the elements. * \return Root element of the created XML. */ QDomElement toXml(QDomDocument &aDoc) const; /*! \brief Gets the target name. * * \return Target name. */ QString targetName() const; /*! \brief Gets the counts of items added, deleted and modified locally. * * \return Item counts. */ ItemCounts localItems() const; /*! \brief Gets the counts of items added, deleted and modified at remote. * * \return Item counts. */ ItemCounts remoteItems() const; private: TargetResultsPrivate *d_ptr; }; } #endif // TARGETRESULTS_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/SyncSchedule.cpp0000644000015600001650000004410612634332753026540 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2014-2015 Jolla Ltd * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncSchedule.h" #include "SyncSchedule_p.h" #include "ProfileEngineDefs.h" #include "LogMacros.h" #include #include using namespace Buteo; static const QString DAY_SEPARATOR = ","; SyncSchedulePrivate::SyncSchedulePrivate() : iInterval(0), iEnabled(false), iRushInterval(0), iRushEnabled(false), iExternalRushEnabled(false) { } SyncSchedulePrivate::SyncSchedulePrivate(const SyncSchedulePrivate &aSource) : iDays(aSource.iDays), iTime(aSource.iTime), iScheduleConfiguredTime(aSource.iScheduleConfiguredTime), iInterval(aSource.iInterval), iEnabled(aSource.iEnabled), iRushDays(aSource.iRushDays), iRushBegin(aSource.iRushBegin), iRushEnd(aSource.iRushEnd), iRushInterval(aSource.iRushInterval), iRushEnabled(aSource.iRushEnabled), iExternalRushEnabled(aSource.iExternalRushEnabled) { } SyncSchedule::SyncSchedule() : d_ptr(new SyncSchedulePrivate()) { } SyncSchedule::SyncSchedule(const SyncSchedule &aSource) : d_ptr(new SyncSchedulePrivate(*aSource.d_ptr)) { } SyncSchedule::SyncSchedule(const QDomElement &aRoot) : d_ptr(new SyncSchedulePrivate()) { d_ptr->iTime = QTime::fromString(aRoot.attribute(ATTR_TIME), Qt::ISODate); d_ptr->iInterval = aRoot.attribute(ATTR_INTERVAL).toUInt(); d_ptr->iEnabled = (aRoot.attribute(ATTR_ENABLED) == BOOLEAN_TRUE); d_ptr->iDays = d_ptr->parseDays(aRoot.attribute(ATTR_DAYS)); d_ptr->iScheduleConfiguredTime = QDateTime::fromString(aRoot.attribute(ATTR_SYNC_CONFIGURE),Qt::ISODate); QDomElement rush = aRoot.firstChildElement(TAG_RUSH); if (!rush.isNull()) { d_ptr->iRushEnabled = (rush.attribute(ATTR_ENABLED) == BOOLEAN_TRUE); d_ptr->iExternalRushEnabled = (rush.attribute(ATTR_EXTERNAL_SYNC) == BOOLEAN_TRUE); d_ptr->iRushInterval = rush.attribute(ATTR_INTERVAL).toUInt(); d_ptr->iRushBegin = QTime::fromString(rush.attribute(ATTR_BEGIN), Qt::ISODate); d_ptr->iRushEnd = QTime::fromString(rush.attribute(ATTR_END), Qt::ISODate); d_ptr->iRushDays = d_ptr->parseDays(rush.attribute(ATTR_DAYS)); } else { d_ptr->iRushEnabled = false; d_ptr->iExternalRushEnabled = false; d_ptr->iRushInterval = 0; } } SyncSchedule::~SyncSchedule() { delete d_ptr; d_ptr = 0; } SyncSchedule& SyncSchedule::operator=(const SyncSchedule &aRhs) { if (&aRhs != this) { delete d_ptr; d_ptr = new SyncSchedulePrivate(*aRhs.d_ptr); } return *this; } bool SyncSchedule::operator==(const SyncSchedule &aRhs) { if (&aRhs == this) return true; if (d_ptr->iRushDays != aRhs.d_ptr->iRushDays) return false; else if (d_ptr->iRushBegin != aRhs.d_ptr->iRushBegin) return false; else if (d_ptr->iRushEnd != aRhs.d_ptr->iRushEnd) return false; else if (d_ptr->iRushInterval != aRhs.d_ptr->iRushInterval) return false; else if (d_ptr->iInterval != aRhs.d_ptr->iInterval) return false; else if (d_ptr->iEnabled != aRhs.d_ptr->iEnabled) return false; else if (d_ptr->iRushEnabled != aRhs.d_ptr->iRushEnabled) return false; else if (d_ptr->iExternalRushEnabled != aRhs.d_ptr->iExternalRushEnabled) return false; return true; } QDomElement SyncSchedule::toXml(QDomDocument &aDoc) const { QDomElement root = aDoc.createElement(TAG_SCHEDULE); root.setAttribute(ATTR_ENABLED, d_ptr->iEnabled ? BOOLEAN_TRUE : BOOLEAN_FALSE); root.setAttribute(ATTR_TIME, d_ptr->iTime.toString(Qt::ISODate)); root.setAttribute(ATTR_INTERVAL, QString::number(d_ptr->iInterval)); root.setAttribute(ATTR_DAYS, d_ptr->createDays(d_ptr->iDays)); root.setAttribute(ATTR_SYNC_CONFIGURE,d_ptr->iScheduleConfiguredTime.toString(Qt::ISODate)); QDomElement rush = aDoc.createElement(TAG_RUSH); rush.setAttribute(ATTR_ENABLED, d_ptr->iRushEnabled ? BOOLEAN_TRUE : BOOLEAN_FALSE); rush.setAttribute(ATTR_EXTERNAL_SYNC, d_ptr->iExternalRushEnabled ? BOOLEAN_TRUE : BOOLEAN_FALSE); rush.setAttribute(ATTR_INTERVAL, QString::number(d_ptr->iRushInterval)); rush.setAttribute(ATTR_BEGIN, d_ptr->iRushBegin.toString(Qt::ISODate)); rush.setAttribute(ATTR_END, d_ptr->iRushEnd.toString(Qt::ISODate)); rush.setAttribute(ATTR_DAYS, d_ptr->createDays(d_ptr->iRushDays)); root.appendChild(rush); return root; } QString SyncSchedule::toString() const { QDomDocument doc; QDomProcessingInstruction xmlHeading = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""); doc.appendChild(xmlHeading); QDomElement root = toXml(doc); doc.appendChild(root); return doc.toString(PROFILE_INDENT); } DaySet SyncSchedule::days() const { return d_ptr->iDays; } void SyncSchedule::setDays(const DaySet &aDays) { d_ptr->iDays = aDays; } void SyncSchedule::setScheduleConfiguredTime(const QDateTime &aDateTime) { d_ptr->iScheduleConfiguredTime = aDateTime; } QDateTime SyncSchedule::scheduleConfiguredTime() { return d_ptr->iScheduleConfiguredTime; } QTime SyncSchedule::time() const { return d_ptr->iTime; } void SyncSchedule::setTime(const QTime &aTime) { d_ptr->iTime = aTime; } unsigned SyncSchedule::interval() const { return d_ptr->iInterval; } void SyncSchedule::setInterval(unsigned aInterval) { d_ptr->iInterval = aInterval; } bool SyncSchedule::scheduleEnabled() const { return d_ptr->iEnabled; } void SyncSchedule::setScheduleEnabled(bool aEnabled) { d_ptr->iEnabled = aEnabled; } bool SyncSchedule::rushEnabled() const { return d_ptr->iRushEnabled; } void SyncSchedule::setRushEnabled(bool aEnabled) { d_ptr->iRushEnabled = aEnabled; } bool SyncSchedule::syncExternallyDuringRush() const { return d_ptr->iExternalRushEnabled; } void SyncSchedule::setSyncExternallyDuringRush(bool aEnabled) { d_ptr->iExternalRushEnabled = aEnabled; } DaySet SyncSchedule::rushDays() const { return d_ptr->iRushDays; } void SyncSchedule::setRushDays(const DaySet &aDays) { d_ptr->iRushDays = aDays; } QTime SyncSchedule::rushBegin() const { return d_ptr->iRushBegin; } QTime SyncSchedule::rushEnd() const { return d_ptr->iRushEnd; } void SyncSchedule::setRushTime(const QTime &aBegin, const QTime &aEnd) { d_ptr->iRushBegin = aBegin; d_ptr->iRushEnd = aEnd; } unsigned SyncSchedule::rushInterval() const { return d_ptr->iRushInterval; } void SyncSchedule::setRushInterval(unsigned aInterval) { d_ptr->iRushInterval = aInterval; } bool SyncSchedule::inExternalSyncRushPeriod(const QDateTime &aDateTime) const { if (d_ptr->iEnabled && d_ptr->iRushEnabled && d_ptr->iExternalRushEnabled) { if (d_ptr->isRush(aDateTime)) { return true; } } return false; } QDateTime SyncSchedule::nextSyncTime(const QDateTime &aPrevSync) const { QDateTime nextSync; QDateTime scheduleConfiguredTime = d_ptr->iScheduleConfiguredTime; QDateTime now = QDateTime::currentDateTime(); LOG_DEBUG("aPrevSync" << aPrevSync.toString() << "Last Configured Time " << scheduleConfiguredTime.toString() <<"CurrentDateTime"<iTime.isValid() && !d_ptr->iDays.isEmpty()) { // The sync time is defined explicitly (for ex. every Mon, Wed, at // 5:30PM). So choose the next applicable day from now. LOG_DEBUG("Explicit sync time defined."); nextSync.setTime(d_ptr->iTime); nextSync.setDate(now.date()); if (now.time() > d_ptr->iTime) { nextSync = nextSync.addDays(1); } // no else d_ptr->adjustDate(nextSync, d_ptr->iDays); } else if (d_ptr->iInterval > 0) { // Sync time is defined in terms of interval (for ex. every 15 minutes) LOG_DEBUG("Sync interval defined as" << d_ptr->iInterval); // Last sync time is not available/valid (Could happen if the device // is shut down for an extended period before the first sync can be // performed). Hence use the time the // sync was last scheduled as the reference // Figure out the number of intervals passed QDateTime reference; reference = (aPrevSync.isValid()) ? aPrevSync : scheduleConfiguredTime; if(reference > now) { // If the clock was rolled back... LOG_DEBUG("Setting reference to now"); reference = now; } else if (!reference.isValid()) { //It means configuring first time account. Need to sync now only. LOG_DEBUG("Reference is not valid returning current date time"); return QDateTime::currentDateTime(); } int numberOfIntervals = 0; if(0 != d_ptr->iInterval && d_ptr->iEnabled) { int secs = reference.secsTo(now) + 1; numberOfIntervals = secs/(d_ptr->iInterval * 60); if(secs % (d_ptr->iInterval * 60)) { numberOfIntervals++; } LOG_DEBUG("numberOfInterval:"<iInterval); } nextSync = d_ptr->iEnabled ? reference.addSecs(numberOfIntervals * d_ptr->iInterval * 60) : QDateTime(); } LOG_DEBUG("next non rush hour sync is at:: " << nextSync); // Rush is controlled by a external process(e.g always-up-to-date), buteo controls the switch from rush to offRush if (d_ptr->iRushEnabled && d_ptr->iExternalRushEnabled) { LOG_DEBUG("Rush Interval is controlled by a external process."); // Set next sync to rush end QDateTime nextSyncRush; if (d_ptr->isRush(now)) { nextSyncRush.setTime(d_ptr->iRushEnd); nextSyncRush.setDate(now.date()); if (now.time() > d_ptr->iRushEnd) { nextSyncRush = nextSyncRush.addDays(1); } d_ptr->adjustDate(nextSyncRush, d_ptr->iRushDays); LOG_DEBUG("Rush controlled by external process, next scheduled sync at rush end " << nextSyncRush.toString()); } LOG_DEBUG("nextSyncRush" << nextSyncRush.toString()); // Use next sync time calculated with rush settings if necessary. if (nextSyncRush.isValid()) { // check to see if we should use it, or instead use the next non-rush sync time. if (nextSync.isValid() && nextSync > now && nextSync < nextSyncRush && (!d_ptr->isRush(nextSync))) { // the next non-rush sync time occurs after now // but before the next rush sync time, and either it // doesn't fall within the rush period itself or the // next rush sync time is in the next rush period. // we should use the non-rush schedule. LOG_DEBUG("Using non-rush time as the next sync time"); } else { nextSync = nextSyncRush; } } } else if (d_ptr->iRushEnabled && d_ptr->iRushInterval > 0 && !d_ptr->iExternalRushEnabled) { LOG_DEBUG("Calculating next sync time with rush settings. Rush Interval is " << d_ptr->iRushInterval); // Calculate next sync time with rush settings. QDateTime nextSyncRush; bool nextSyncRushInNextRushPeriod = false; if (d_ptr->isRush(now)) { LOG_DEBUG("Current time is in rush"); // We are in rush hour if(aPrevSync.isValid()) { LOG_DEBUG("PrevSync is valid and isRush true.. "); nextSyncRush = aPrevSync.addSecs(d_ptr->iRushInterval * 60); if ((nextSyncRush < now) || (aPrevSync > now)) { // Use current time if the previous sync time is too old, or // the clock has been rolled back nextSyncRush = now.addSecs(d_ptr->iRushInterval * 60); LOG_DEBUG("nextsyncRush based on aPrevSync"<iRushInterval * 60); } if (!d_ptr->isRush(nextSyncRush)) { // If the calculated rush time does not lie in the rush // interval, choose the next available rush time as the begin // time for the rush interval LOG_DEBUG("isRush False"); nextSyncRushInNextRushPeriod = true; nextSyncRush.setTime(d_ptr->iRushBegin); if (nextSyncRush < now) { nextSyncRush = nextSyncRush.addDays(1); } // no else d_ptr->adjustDate(nextSyncRush, d_ptr->iRushDays); } // no else } else { LOG_DEBUG("Current Time is Not Rush"); nextSyncRush.setTime(d_ptr->iRushBegin); nextSyncRush.setDate(now.date()); if (now.time() > d_ptr->iRushBegin) { nextSyncRush = nextSyncRush.addDays(1); } // no else d_ptr->adjustDate(nextSyncRush, d_ptr->iRushDays); } LOG_DEBUG("nextSyncRush" << nextSyncRush.toString()); // Use next sync time calculated with rush settings if necessary. if (nextSyncRush.isValid()) { // check to see if we should use it, or instead use the next non-rush sync time. if (nextSync.isValid() && nextSync > now && nextSync < nextSyncRush && (!d_ptr->isRush(nextSync) || nextSyncRushInNextRushPeriod)) { // the next non-rush sync time occurs after now // but before the next rush sync time, and either it // doesn't fall within the rush period itself or the // next rush sync time is in the next rush period. // we should use the non-rush schedule. LOG_DEBUG("Using non-rush time as the next sync time"); } else { // we should use the rush schedule. LOG_DEBUG("Using rush time as the next sync time"); nextSync = nextSyncRush; } } // no else } // no else //For safer side checking nextSyncTime should not be behind currentDateTime. if ( QDateTime::currentDateTime().secsTo(nextSync) < 0 ) { //If it is the case making it to currentTime. LOG_WARNING("Something went wrong in nextSyncTime calculation resetting to current time"); nextSync = QDateTime::currentDateTime(); } LOG_DEBUG("nextSync" << nextSync.toString()); return nextSync; } QDateTime SyncSchedule::nextRushSwitchTime(const QDateTime &aFromTime) const { if (rushEnabled() && scheduleEnabled()) { if (d_ptr->iRushInterval == d_ptr->iInterval && !d_ptr->iExternalRushEnabled) { LOG_DEBUG("Rush interval is the same as normal interval no need to switch"); return QDateTime(); } if (d_ptr->isRush(aFromTime)) { return QDateTime(aFromTime.date(), d_ptr->iRushEnd); } else { // If rush day and before rush end next switch is at rush begin if (d_ptr->iRushDays.contains(aFromTime.date().dayOfWeek()) && aFromTime.time() < d_ptr->iRushBegin) { return QDateTime(aFromTime.date(), d_ptr->iRushBegin); } else { // Not a rush day or the rush period has ended, attemp switch at next day rush begin, // we can only schedule for 24h return QDateTime(aFromTime.date().addDays(1), d_ptr->iRushBegin); } } } else { return QDateTime(); } } DaySet SyncSchedulePrivate::parseDays(const QString &aDays) const { DaySet daySet; if (!aDays.isNull()) { QStringList dayList = aDays.split(DAY_SEPARATOR, QString::SkipEmptyParts); foreach (QString dayStr, dayList) { bool ok; int dayNum = dayStr.toInt(&ok); if (ok) { daySet.insert(dayNum); } // no else } } // no else return daySet; } QString SyncSchedulePrivate::createDays(const DaySet &aDays) const { QStringList dayList; foreach (int dayNum, aDays) { dayList.append(QString::number(dayNum)); } return dayList.join(DAY_SEPARATOR); } bool SyncSchedulePrivate::adjustDate(QDateTime &aTime, const DaySet &aDays) const { if (aDays.isEmpty()) { aTime = QDateTime(); return false; } // no else bool newValidDay = false; int startDay = aTime.date().dayOfWeek(); while (!aDays.contains(aTime.date().dayOfWeek())) { newValidDay = true; aTime = aTime.addDays(1); // Safety check, avoid infinite loop if date set contains // only invalid values. if (aTime.date().dayOfWeek() == startDay) { // Clear next sync time. newValidDay = false; aTime = QDateTime(); break; } // no else } return newValidDay; } bool SyncSchedulePrivate::isRush(const QDateTime &aTime) const { return (iRushDays.contains(aTime.date().dayOfWeek()) && aTime.time() >= iRushBegin && aTime.time() < iRushEnd); } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/StorageProfile.cpp0000644000015600001650000000373112634332753027073 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "StorageProfile.h" #include "ProfileEngineDefs.h" namespace Buteo { // Private implementation class for StorageProfile. Currently not needed, but // reserved for future usage. class StorageProfilePrivate { public: StorageProfilePrivate(); StorageProfilePrivate(const StorageProfilePrivate &aSource); }; } using namespace Buteo; StorageProfilePrivate::StorageProfilePrivate() { } StorageProfilePrivate::StorageProfilePrivate( const StorageProfilePrivate & /*aSource*/) { } StorageProfile::StorageProfile(const QString &aName) : Profile(aName, Profile::TYPE_STORAGE), d_ptr(new StorageProfilePrivate()) { } StorageProfile::StorageProfile(const QDomElement &aRoot) : Profile(aRoot), d_ptr(new StorageProfilePrivate()) { } StorageProfile::StorageProfile(const StorageProfile &aSource) : Profile(aSource), d_ptr(new StorageProfilePrivate(*aSource.d_ptr)) { } StorageProfile::~StorageProfile() { delete d_ptr; d_ptr = 0; } StorageProfile *StorageProfile::clone() const { return new StorageProfile(*this); } bool StorageProfile::isEnabled() const { return boolKey(KEY_ENABLED, false); } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/SyncSchedule.h0000644000015600001650000002000212634332753026172 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2014-2015 Jolla Ltd * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCSCHEDULE_H #define SYNCSCHEDULE_H #include #include class QDomDocument; class QDomElement; namespace Buteo { class SyncSchedulePrivate; class SyncScheduleTest; typedef QSet DaySet; const QString SYNC_SCHEDULE_ENABLED_KEY_BOOL("scheduler/schedule_enabled"); const QString SYNC_SCHEDULE_PEAK_ENABLED_KEY_BOOL("scheduler/schedule_peak_enabled"); const QString SYNC_SCHEDULE_OFFPEAK_ENABLED_KEY_BOOL("scheduler/schedule_offpeak_enabled"); const QString SYNC_SCHEDULE_PEAK_DAYS_KEY_INT ("scheduler/schedule_peak_days"); const QString SYNC_SCHEDULE_PEAK_START_TIME_KEY_INT ("scheduler/schedule_peak_start_time"); const QString SYNC_SCHEDULE_PEAK_END_TIME_KEY_INT ("scheduler/schedule_peak_end_time"); const QString SYNC_SCHEDULE_PEAK_SCHEDULE_KEY_INT ("scheduler/schedule_peak"); const QString SYNC_SCHEDULE_OFFPEAK_SCHEDULE_KEY_INT ("scheduler/schedule_off_peak"); /*! \brief Class for handling sync schedule settings. * */ class SyncSchedule { public: /*! \brief Constructs an empty schedule. * */ SyncSchedule(); /*! \brief Copy constructor. * * \param aSource Copy source. */ SyncSchedule(const SyncSchedule &aSource); /*! \brief Constructs sync schedule from XML. * * \param aRoot Root element of the XML representation. */ explicit SyncSchedule(const QDomElement &aRoot); /*! \brief Destructor. */ ~SyncSchedule(); /*! \brief Assignment operator. * * \param aRhs Source */ SyncSchedule& operator=(const SyncSchedule &aRhs); /*! \brief Equal to operator. * * \param aRhs Source */ bool operator==(const SyncSchedule &aRhs); /*! \brief Exports the sync schedule to XML. * * \param aDoc Parent document for the created XML elements. * \return Root element of the created XML. */ QDomElement toXml(QDomDocument &aDoc) const; /*! \brief Exports the sync schedule to QString. * * \return return the Schedule as xml formatted string */ QString toString() const; /*! \brief Gets the enabled week days of the sync schedule. * * The returned set contains the numbers of enabled week days. * Day numbers are defined in Qt::DayOfWeek. * \return Set of week day numbers. */ DaySet days() const; /*! \brief Sets the enabled week days. * * \param aDays Set of enabled week days. */ void setDays(const DaySet &aDays); /*! \brief Gets the exact time set in sync schedule. * * \return Sync time. Null object if time has not been defined. */ QTime time() const; /*! \brief Sets the exact time for sync. * * Set to null object QTime() to disable syncing at exact time. * \param aTime Sync time. */ void setTime(const QTime &aTime); /*! \brief Sets scheduled config time. * * \param QDateTime Sync time. */ void setScheduleConfiguredTime(const QDateTime &aDateTime); /*! \brief To get the scheduled config time. * * \return QDateTime Sync time. */ QDateTime scheduleConfiguredTime(); /*! \brief Gets sync interval in minutes. * * \return Interval. */ unsigned interval() const; /*! \brief Sets sync interval in minutes. * * Set to zero to disable syncing with intervals. * \param aInterval Interval. */ void setInterval(unsigned aInterval); /*! \brief Checks if normal schedule is obeyed. * * \return Normal schedule is obeyed. Return value false corresponds to "manual" mode. */ bool scheduleEnabled() const; /*! \brief Sets if normal schedule is to be obeyed. * * \param aEnabled Specify if normal scheduling hours enabled. If set to false, corresponds to "manual" mode. */ void setScheduleEnabled(bool aEnabled); // ============== RUSH HOUR SETTINGS ============================ /*! \brief Checks if rush hour schedule is to be obeyed. * * \return True if rush hour schedule is to be used. False, if rush hour scheduling is off (i.e. manual mode). */ bool rushEnabled() const; /*! \brief Sets rush hour schedule is to be obeyed. * * \param aEnabled If set to false, corresponds to rush hour scheduling off, i.e. "manual" sync. */ void setRushEnabled(bool aEnabled); /*! \brief Checks if rush schedule is controlled by a external process. * * \return True if rush hour schedule is to be used by a external process, The external process will control the sync, buteo will just * controll the schedule outside rush hours and will be responsible to switch from rush to no-rush(and vice-versa) modes. * False, if rush hour scheduling is controlled by this process or if rush hour scheduling is off (i.e. manual mode). */ bool syncExternallyDuringRush() const; /*! \brief Sets if rush schedule is controlled by a external process. * * \param aEnabled If set to true, corresponds to external rush hour scheduling on, i.e. sync controlled by a external process. */ void setSyncExternallyDuringRush(bool aEnabled); /*! \brief Gets days enabled for rush hours. * * \return Set of days enabled for rush hours. */ DaySet rushDays() const; /*! \brief Sets days enabled for rush hours. * * \param aDays Enabled days for rush hours. */ void setRushDays(const DaySet &aDays); /*! \brief Gets begin time of rush hours. * * \return Begin time. */ QTime rushBegin() const; /*! \brief Gets end time of rush hours. * * \return End time. */ QTime rushEnd() const; /*! \brief Sets begin and end times of rush hours. * * \param aBegin Begin time. * \param aEnd End time. */ void setRushTime(const QTime &aBegin, const QTime &aEnd); /*! \brief Gets sync interval for rush hours. * * \return Interval in minutes. */ unsigned rushInterval() const; /*! \brief Sets sync interval for rush hours. * * \param aInterval Interval. */ void setRushInterval(unsigned aInterval); /*! \brief Checks if a given time is inside rush hour and if the sync is controlled by a external process. * * \param aDateTime DateTime to check. */ bool inExternalSyncRushPeriod(const QDateTime &aDateTime) const; /*! \brief Gets next sync time based on the sync schedule settings. * * \param aPrevSync Previous sync time. * \param aPrevious sync time. * \return Next sync time. Null object if schedule is not defined. */ QDateTime nextSyncTime(const QDateTime &aPrevSync) const; /*! \brief Gets next time to switch rush/off-rush schedule intervals. * * \param aFromTime From time to calculate next switch, usually current time. * \return Next time to switch rush/off-rush schedule intervals. Null object if schedule is not defined for rush/off-rush * or if the rush and off-rush intervals are the same. */ QDateTime nextRushSwitchTime(const QDateTime& aFromTime) const; private: SyncSchedulePrivate* d_ptr; #ifdef SYNCFW_UNIT_TESTS friend class SyncScheduleTest; #endif }; } #endif // SYNCSCHEDULE_H buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/ProfileManager.cpp0000644000015600001650000011107112634332753027036 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ProfileManager.h" #include #include #include #include #include "ProfileFactory.h" #include "ProfileEngineDefs.h" #include "SyncCommonDefs.h" #include "LogMacros.h" #include "BtHelper.h" namespace Buteo { static const QString FORMAT_EXT = ".xml"; static const QString BACKUP_EXT = ".bak"; static const QString LOG_EXT = ".log"; static const QString LOG_DIRECTORY = "logs"; static const QString BT_PROFILE_TEMPLATE("bt_template"); const QString ProfileManager::DEFAULT_PRIMARY_PROFILE_PATH = Sync::syncCacheDir(); const QString ProfileManager::DEFAULT_SECONDARY_PROFILE_PATH = "/etc/buteo/profiles"; // Private implementation class for ProfileManager. class ProfileManagerPrivate { public: ProfileManagerPrivate(const QString &aPrimaryPath, const QString &aSecondaryPath); /*! \brief Loads a profile from persistent storage. * * \param aName Name of the profile to load. * \param aType Type of the profile to load. * \return The loaded profile. 0 if the profile was not found. */ Profile *load(const QString &aName, const QString &aType); /*! \brief Loads the synchronization log associated with the given profile. * * \param aProfileName Name of the sync profile whose log shall be loaded. * \return The loaded log. 0 if the log was not found. */ SyncLog *loadLog(const QString &aProfileName); bool parseFile(const QString &aPath, QDomDocument &aDoc); void restoreBackupIfFound(const QString &aProfilePath, const QString &aBackupPath); QDomDocument constructProfileDocument(const Profile &aProfile); bool writeProfileFile(const QString &aProfilePath, const QDomDocument &aDoc); QString findProfileFile(const QString &aName, const QString &aType); bool createBackup(const QString &aProfilePath, const QString &aBackupPath); bool matchProfile(const Profile &aProfile, const ProfileManager::SearchCriteria &aCriteria); bool matchKey(const Profile &aProfile, const ProfileManager::SearchCriteria &aCriteria); bool save(const Profile &aProfile); bool remove(const QString &aName, const QString &aType); bool profileExists(const QString &aProfileId ,const QString &aType); // Primary path for profiles. QString iPrimaryPath; // Secondary path for profiles. QString iSecondaryPath; }; } using namespace Buteo; ProfileManagerPrivate::ProfileManagerPrivate(const QString &aPrimaryPath, const QString &aSecondaryPath) : iPrimaryPath(aPrimaryPath), iSecondaryPath(aSecondaryPath) { if (iPrimaryPath.endsWith(QDir::separator())) { iPrimaryPath.chop(1); } // no else if (iSecondaryPath.endsWith(QDir::separator())) { iSecondaryPath.chop(1); } // no else LOG_DEBUG("Primary profile path set to" << iPrimaryPath); LOG_DEBUG("Secondary profile path set to" << iSecondaryPath); } Profile *ProfileManagerPrivate::load(const QString &aName, const QString &aType) { QString profilePath = findProfileFile(aName, aType); QString backupProfilePath = profilePath + BACKUP_EXT; QDomDocument doc; Profile* profile = 0; restoreBackupIfFound(profilePath, backupProfilePath); if (parseFile(profilePath, doc)) { ProfileFactory pf; profile = pf.createProfile(doc.documentElement()); if (QFile::exists(backupProfilePath)) { QFile::remove(backupProfilePath); } } else { LOG_WARNING("Failed to load profile:" << aName); } return profile; } SyncLog *ProfileManagerPrivate::loadLog(const QString &aProfileName) { QString fileName = iPrimaryPath + QDir::separator() + Profile::TYPE_SYNC + QDir::separator() + LOG_DIRECTORY + QDir::separator() + aProfileName + LOG_EXT + FORMAT_EXT; if (!QFile::exists(fileName)) { LOG_DEBUG("No sync log found for profile:" << aProfileName); return 0; } // no else QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { LOG_WARNING("Failed to open sync log file for reading:" << file.fileName()); return 0; } // no else QDomDocument doc; if (!doc.setContent(&file)) { file.close(); LOG_WARNING("Failed to parse XML from sync log file:" << file.fileName()); return 0; } // no else file.close(); return new SyncLog(doc.documentElement()); } bool ProfileManagerPrivate::matchProfile(const Profile &aProfile, const ProfileManager::SearchCriteria &aCriteria) { //FUNCTION_CALL_TRACE; bool matched = false; const Profile *testProfile = &aProfile; if (!aCriteria.iSubProfileName.isEmpty()) { // Sub-profile name was given, request a sub-profile with a // matching name and type. testProfile = aProfile.subProfile(aCriteria.iSubProfileName, aCriteria.iSubProfileType); if (testProfile != 0) { matched = matchKey(*testProfile, aCriteria); } else { if (aCriteria.iType == ProfileManager::SearchCriteria::NOT_EXISTS) { matched = true; } else { matched = false; } } } else if (!aCriteria.iSubProfileType.isEmpty()) { // Sub-profile name was empty, but type was given. Get all // sub-profiles with the matching type. QStringList subProfileNames = aProfile.subProfileNames(aCriteria.iSubProfileType); if (!subProfileNames.isEmpty()) { matched = false; foreach (const QString &subProfileName, subProfileNames) { testProfile = aProfile.subProfile(subProfileName, aCriteria.iSubProfileType); if (testProfile != 0 && matchKey(*testProfile, aCriteria)) { matched = true; break; } } } else { if (aCriteria.iType == ProfileManager::SearchCriteria::NOT_EXISTS) { matched = true; } else { matched = false; } } } else { matched = matchKey(aProfile, aCriteria); } return matched; } bool ProfileManagerPrivate::matchKey(const Profile &aProfile, const ProfileManager::SearchCriteria &aCriteria) { bool matched = false; if (!aCriteria.iKey.isEmpty()) { // Key name was given, get a key with matching name. QString value = aProfile.key(aCriteria.iKey); if (value.isNull()) { if (aCriteria.iType == ProfileManager::SearchCriteria::NOT_EXISTS || aCriteria.iType == ProfileManager::SearchCriteria::NOT_EQUAL) { matched = true; } else { matched = false; } } else { switch (aCriteria.iType) { case ProfileManager::SearchCriteria::EXISTS: matched = true; break; case ProfileManager::SearchCriteria::NOT_EXISTS: matched = false; break; case ProfileManager::SearchCriteria::EQUAL: matched = (value == aCriteria.iValue); break; case ProfileManager::SearchCriteria::NOT_EQUAL: matched = (value != aCriteria.iValue); break; default: matched = false; break; } } } else { if (aCriteria.iType == ProfileManager::SearchCriteria::NOT_EXISTS) { matched = false; } else { matched = true; } } return matched; } ProfileManager::SearchCriteria::SearchCriteria() : iType(ProfileManager::SearchCriteria::EQUAL) { } ProfileManager::SearchCriteria::SearchCriteria(const SearchCriteria &aSource) : iType(aSource.iType), iSubProfileName(aSource.iSubProfileName), iSubProfileType(aSource.iSubProfileType), iKey(aSource.iKey), iValue(aSource.iValue) { } ProfileManager::ProfileManager(const QString &aPrimaryPath, const QString &aSecondaryPath) : d_ptr(new ProfileManagerPrivate(aPrimaryPath, aSecondaryPath)) { FUNCTION_CALL_TRACE; } ProfileManager::~ProfileManager() { FUNCTION_CALL_TRACE; delete d_ptr; d_ptr = 0; } Profile *ProfileManager::profile(const QString &aName, const QString &aType) { return d_ptr->load(aName, aType); } SyncProfile *ProfileManager::syncProfile(const QString &aName) { Profile *p = profile(aName, Profile::TYPE_SYNC); SyncProfile *syncProfile = 0; if (p != 0 && p->type() == Profile::TYPE_SYNC) { // RTTI is not allowed, use static_cast. Should be safe, because // type is verified. syncProfile = static_cast(p); // Load and merge all sub-profiles. expand(*syncProfile); // Load sync log. If not found, create an empty log. if (syncProfile->log() == 0) { SyncLog *log = d_ptr->loadLog(aName); if (0 == log) { log = new SyncLog(aName); } // no else syncProfile->setLog(log); } // no else } else { if (p != 0) { delete p; } } return syncProfile; } QStringList ProfileManager::profileNames(const QString &aType) { // Search for all profile files from the primary directory QStringList names; QString nameFilter = QString("*") + FORMAT_EXT; { QDir dir(d_ptr->iPrimaryPath + QDir::separator() + aType); QFileInfoList fileInfoList = dir.entryInfoList(QStringList(nameFilter), QDir::Files | QDir::NoSymLinks); foreach (const QFileInfo &fileInfo, fileInfoList) { names.append(fileInfo.completeBaseName()); } } // Search for all profile files from the secondary directory { QDir dir(d_ptr->iSecondaryPath + QDir::separator() + aType); QFileInfoList fileInfoList = dir.entryInfoList(QStringList(nameFilter), QDir::Files | QDir::NoSymLinks); foreach (const QFileInfo &fileInfo, fileInfoList) { // Add only if the list does not yet contain the name. QString profileName = fileInfo.completeBaseName(); if (!names.contains(profileName)) { names.append(profileName); } } } return names; } QList ProfileManager::allSyncProfiles() { FUNCTION_CALL_TRACE; QList profiles; QStringList names = profileNames(Profile::TYPE_SYNC); foreach (const QString &name, names) { SyncProfile *p = syncProfile(name); if (p != 0) { profiles.append(p); } // no else } return profiles; } QList ProfileManager::allVisibleSyncProfiles() { FUNCTION_CALL_TRACE; QList profiles = allSyncProfiles(); QList visibleProfiles; foreach (SyncProfile *p, profiles) { if (!p->isHidden()) { visibleProfiles.append(p); } else { delete p; } } return visibleProfiles; } QList ProfileManager::getSyncProfilesByData( const QString &aSubProfileName, const QString &aSubProfileType, const QString &aKey, const QString &aValue) { FUNCTION_CALL_TRACE; QList allProfiles = allSyncProfiles(); QList matchingProfiles; foreach (SyncProfile *profile, allProfiles) { Profile *testProfile = profile; if (!aSubProfileName.isEmpty()) { // Sub-profile name was given, request a sub-profile with a // matching name and type. testProfile = profile->subProfile(aSubProfileName, aSubProfileType); } else if (!aSubProfileType.isEmpty()) { // Sub-profile name was empty, but type was given. Get the first // sub-profile with the matching type. QStringList subProfileNames = profile->subProfileNames(aSubProfileType); if (!subProfileNames.isEmpty()) { testProfile = profile->subProfile(subProfileNames.first(), aSubProfileType); } else { testProfile = 0; } } if (0 == testProfile) // Sub-profile was not found. { delete profile; profile = 0; continue; // Not a match, continue with next profile. } if (!aKey.isEmpty()) { // Key name was given, get a key with matching name. QString value = testProfile->key(aKey); if (value.isNull() || // Key was not found. (!aValue.isEmpty() && (value != aValue))) // Value didn't match { delete profile; profile = 0; continue; // Not a match, continue with next profile. } // no else } // no else // Match, add profile to the list to be returned. matchingProfiles.append(profile); } return matchingProfiles; } QList ProfileManager::getSyncProfilesByData( const QList &aCriteria) { FUNCTION_CALL_TRACE; QList allProfiles = allSyncProfiles(); QList matchingProfiles; foreach (SyncProfile *profile, allProfiles) { bool matched = true; if (profile == 0) continue; foreach (const SearchCriteria &criteria, aCriteria) { if (!d_ptr->matchProfile(*profile, criteria)) { matched = false; break; } } if (matched) { matchingProfiles.append(profile); } else { delete profile; profile = 0; } } return matchingProfiles; } QList ProfileManager::getSOCProfilesForStorage( const QString &aStorageName) { FUNCTION_CALL_TRACE; QList criteriaList; // Require that the profile is not disabled. // Profile is enabled by default. Comparing with enabled = true would // not work, because the key may not exist at all, even if the profile // is enabled. SearchCriteria profileEnabled; profileEnabled.iType = SearchCriteria::NOT_EQUAL; profileEnabled.iKey = KEY_ENABLED; profileEnabled.iValue = BOOLEAN_FALSE; criteriaList.append(profileEnabled); // Profile must not be hidden. SearchCriteria profileVisible; profileVisible.iType = SearchCriteria::NOT_EQUAL; profileVisible.iKey = KEY_HIDDEN; profileVisible.iValue = BOOLEAN_TRUE; criteriaList.append(profileVisible); // Online service. SearchCriteria onlineService; onlineService.iType = SearchCriteria::EQUAL; //onlineService.iSubProfileType = Profile::TYPE_SERVICE; // Service profile name is left empty. Key value is matched with all // found service sub-profiles, though there should be only one. onlineService.iKey = KEY_DESTINATION_TYPE; onlineService.iValue = VALUE_ONLINE; criteriaList.append(onlineService); // The profile should be interested // in SOC SearchCriteria socSupported; //socSupported.iSubProfileType = Profile::TYPE_SERVICE; socSupported.iType = SearchCriteria::EQUAL; socSupported.iKey = KEY_SOC; socSupported.iValue = BOOLEAN_TRUE; criteriaList.append(socSupported); SearchCriteria storageSupported; storageSupported.iSubProfileType = Profile::TYPE_STORAGE; storageSupported.iType = SearchCriteria::EQUAL; storageSupported.iKey = KEY_LOCAL_URI; storageSupported.iValue = aStorageName; criteriaList.append(storageSupported); return getSyncProfilesByData(criteriaList); } QList ProfileManager::getSyncProfilesByStorage( const QString &aStorageName, bool aStorageMustBeEnabled) { FUNCTION_CALL_TRACE; QList criteriaList; // Require that the profile is not disabled. // Profile is enabled by default. Comparing with enabled = true would // not work, because the key may not exist at all, even if the profile // is enabled. SearchCriteria profileEnabled; profileEnabled.iType = SearchCriteria::NOT_EQUAL; profileEnabled.iKey = KEY_ENABLED; profileEnabled.iValue = BOOLEAN_FALSE; criteriaList.append(profileEnabled); // Profile must not be hidden. SearchCriteria profileVisible; profileVisible.iType = SearchCriteria::NOT_EQUAL; profileVisible.iKey = KEY_HIDDEN; profileVisible.iValue = BOOLEAN_TRUE; criteriaList.append(profileVisible); // Online service. SearchCriteria onlineService; onlineService.iType = SearchCriteria::EQUAL; //onlineService.iSubProfileType = Profile::TYPE_SERVICE; // Service profile name is left empty. Key value is matched with all // found service sub-profiles, though there should be only one. onlineService.iKey = KEY_DESTINATION_TYPE; onlineService.iValue = VALUE_ONLINE; criteriaList.append(onlineService); // Storage must be supported. SearchCriteria storageSupported; storageSupported.iSubProfileName = aStorageName; storageSupported.iSubProfileType = Profile::TYPE_STORAGE; if (aStorageMustBeEnabled) { // Storage must be enabled also. Storages are disabled by default, // so we can compare with enabled = true. storageSupported.iType = SearchCriteria::EQUAL; storageSupported.iKey = KEY_ENABLED; storageSupported.iValue = BOOLEAN_TRUE; } else { // Existence of the storage sub-profile is sufficient. storageSupported.iType = SearchCriteria::EXISTS; } criteriaList.append(storageSupported); return getSyncProfilesByData(criteriaList); } bool ProfileManagerPrivate::save(const Profile &aProfile) { FUNCTION_CALL_TRACE; QDomDocument doc = constructProfileDocument(aProfile); if (doc.isNull()) { LOG_WARNING("No profile data to write"); return false; } // Create path for the new profile file. QDir dir; dir.mkpath(iPrimaryPath + QDir::separator() + aProfile.type()); QString profilePath(iPrimaryPath + QDir::separator() + aProfile.type() + QDir::separator() + aProfile.name() + FORMAT_EXT); // Create a backup of the existing profile file. QString oldProfilePath = findProfileFile(aProfile.type(), aProfile.name()); QString backupPath = profilePath + BACKUP_EXT; if (QFile::exists(oldProfilePath) && !createBackup(oldProfilePath, backupPath)) { LOG_WARNING("Failed to create profile backup"); } bool profileWritten = false; if (writeProfileFile(profilePath, doc)) { QFile::remove(backupPath); profileWritten = true; } else { LOG_WARNING("Failed to save profile:" << aProfile.name()); profileWritten = false; } return profileWritten; } Profile* ProfileManager::profileFromXml(const QString &aProfileAsXml) { FUNCTION_CALL_TRACE; Profile *profile = NULL; if(!aProfileAsXml.isEmpty()) { QDomDocument doc; if(doc.setContent(aProfileAsXml,true)) { ProfileFactory pf; profile = pf.createProfile(doc.documentElement()); } } return profile; } QString ProfileManager::updateProfile(const Profile &aProfile) { FUNCTION_CALL_TRACE; // We must have a profile exiting before updating it... bool exists = d_ptr->profileExists(aProfile.name(),aProfile.type()); QString profileId(""); // We need to save before emit the signalProfileChanged, if this is the first // update the profile will only exists on disk after the save and any operation // using this profile triggered by the signal will fail. if(d_ptr->save(aProfile)) { profileId = aProfile.name(); } // Profile did not exist, it was a new one. Add it and emit signal with "added" value: if (!exists) { emit signalProfileChanged(aProfile.name(),ProfileManager::PROFILE_ADDED,aProfile.toString()); } else { emit signalProfileChanged(aProfile.name(),ProfileManager::PROFILE_MODIFIED,aProfile.toString()); } return profileId; } SyncProfile *ProfileManager::createTempSyncProfile (const QString &destAddress, bool &saveNewProfile) { FUNCTION_CALL_TRACE; if (destAddress.contains("USB")) { //USB - PCSUite no requirement to save profile LOG_INFO("USB connect - pc"); SyncProfile *profile = new SyncProfile(PC_SYNC); profile->setBoolKey(KEY_HIDDEN, true); profile->setKey(KEY_DISPLAY_NAME, PC_SYNC); return profile; } BtHelper btHelp(destAddress); QMap mapVal = btHelp.getDeviceProperties(); uint classType = mapVal.value("Class").toInt(); uint pcsuiteClass = 0x100; //Major Device Class - Computer! if (classType & pcsuiteClass) { LOG_INFO("Device major class is Computer"); // not required to save profile SyncProfile *profile = new SyncProfile(PC_SYNC); profile->setBoolKey(KEY_HIDDEN, true); profile->setKey(KEY_DISPLAY_NAME, PC_SYNC); return profile; } saveNewProfile = true; QString profileDisplayName = mapVal.value("Name").toString(); if (profileDisplayName.isEmpty()) { //Todo : What to show if name is empty !! //Fixes 171340 profileDisplayName = QString ("qtn_sync_dest_name_device_default"); } LOG_INFO("Profile Name :" << profileDisplayName); SyncProfile *tProfile = syncProfile(BT_PROFILE_TEMPLATE); //Profile *service = tProfile->serviceProfile(); //if (service != 0) { tProfile->setKey(KEY_DISPLAY_NAME, profileDisplayName); QStringList keys ; keys << destAddress << tProfile->name(); tProfile->setName(keys); tProfile->setEnabled(true); tProfile->setBoolKey("hidden", false); tProfile->setKey(KEY_BT_ADDRESS, destAddress); tProfile->setKey(KEY_BT_NAME, profileDisplayName); //} else { // LOG_WARNING("No service profile, unable to update properties"); //} return tProfile; } void ProfileManager::enableStorages(Profile &aProfile, QMap &aStorageMap, bool *aModified) { FUNCTION_CALL_TRACE; QMapIterator i(aStorageMap); LOG_INFO("ProfileManager::enableStorages"); while (i.hasNext()) { i.next(); Profile *profile = aProfile.subProfile(i.key(), Profile::TYPE_STORAGE); if (profile) { if (profile->isEnabled() != i.value()) { profile->setEnabled(i.value()); if (aModified) { *aModified = true; } } } else { LOG_WARNING("No storage profile by key :" << i.key()); } } return ; } void ProfileManager::setStoragesVisible(Profile &aProfile, QMap &aStorageMap, bool *aModified) { FUNCTION_CALL_TRACE; QMapIterator i(aStorageMap); LOG_INFO("ProfileManager::enableStorages"); while (i.hasNext()) { i.next(); Profile *profile = aProfile.subProfile(i.key(), Profile::TYPE_STORAGE); if (profile) { // For setting the "hidden" value to correspond visiblity, invert the value from map. if (profile->boolKey(Buteo::KEY_HIDDEN) == i.value()) { profile->setBoolKey(Buteo::KEY_HIDDEN, !i.value()); if (aModified) { *aModified = true; } } } else { LOG_WARNING("No storage profile by key :" << i.key()); } } return ; } bool ProfileManager::removeProfile(const QString &aProfileId) { FUNCTION_CALL_TRACE; bool success = false; SyncProfile *profile = syncProfile(aProfileId); if(profile){ success = d_ptr->remove(aProfileId,profile->type()); if(success) { emit signalProfileChanged(aProfileId,ProfileManager::PROFILE_REMOVED, QString("")); } delete profile; profile = NULL; } return success; } bool ProfileManagerPrivate::remove(const QString &aName, const QString &aType) { FUNCTION_CALL_TRACE; bool success = false; QString filePath = iPrimaryPath + QDir::separator() + aType + QDir::separator() + aName + FORMAT_EXT; // Try to load profile without expanding it. We need to check from the // profile data if the profile is protected before removing it. Profile *p = load(aName, aType); if (p) { if (!p->isProtected()) { success = QFile::remove(filePath); if (success){ QString logFilePath = iPrimaryPath + QDir::separator() + aType + QDir::separator() + LOG_DIRECTORY + QDir::separator() + aName + LOG_EXT + FORMAT_EXT; //Initial the will be no log this will fail. QFile::remove(logFilePath); } } else { LOG_DEBUG( "Cannot remove protected profile:" << aName ); } delete p; p = 0; } else { LOG_DEBUG( "Profile not found from the primary path, cannot remove:" << aName ); } return success; } void ProfileManager::expand(Profile &aProfile) { if (aProfile.isLoaded()) return; // Already expanded. // Load and merge sub-profiles. int prevSubCount = 0; QList subProfiles = aProfile.allSubProfiles(); int subCount = subProfiles.size(); while (subCount > prevSubCount) { foreach (Profile *sub, subProfiles) { if (!sub->isLoaded()) { Profile *loadedProfile = profile(sub->name(), sub->type()); if (loadedProfile != 0) { aProfile.merge(*loadedProfile); delete loadedProfile; loadedProfile = 0; } else { // No separate profile file for the sub-profile. LOG_DEBUG( "Referenced sub-profile not found:" << sub->name() ); LOG_DEBUG( "Referenced from:" << aProfile.name() << aProfile.type() ); } sub->setLoaded(true); } // no else } // Load/merge may have created new sub-profile entries. Those need // to be loaded also. Loop if sub-profile count has changed. prevSubCount = subCount; subProfiles = aProfile.allSubProfiles(); subCount = subProfiles.size(); } aProfile.setLoaded(true); } bool ProfileManager::saveLog(const SyncLog &aLog) { FUNCTION_CALL_TRACE; QDir dir; QString fullPath = d_ptr->iPrimaryPath + QDir::separator() + Profile::TYPE_SYNC + QDir::separator() + LOG_DIRECTORY; dir.mkpath(fullPath); QFile file(fullPath + QDir::separator() + aLog.profileName() + LOG_EXT + FORMAT_EXT); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { LOG_WARNING("Failed to open sync log file for writing:" << file.fileName()); return false; } // no else QDomDocument doc; QDomProcessingInstruction xmlHeading = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""); doc.appendChild(xmlHeading); QDomElement root = aLog.toXml(doc); if (root.isNull()) { LOG_WARNING("Failed to convert sync log to XML"); return false; } // no else doc.appendChild(root); QTextStream outputStream(&file); outputStream << doc.toString(PROFILE_INDENT); file.close(); return true; } void ProfileManager::saveRemoteTargetId(Profile &aProfile,const QString& aTargetId ) { FUNCTION_CALL_TRACE; LOG_DEBUG("saveRemoteTargetId :" << aTargetId); aProfile.setKey (KEY_REMOTE_ID, aTargetId); updateProfile(aProfile); //addProfile(aProfile); } bool ProfileManager::rename(const QString &aName, const QString &aNewName) { FUNCTION_CALL_TRACE; bool ret = false; // Rename the sync profile QString source = d_ptr->iPrimaryPath + QDir::separator() + Profile::TYPE_SYNC + QDir::separator() + aName + FORMAT_EXT; QString destination = d_ptr->iPrimaryPath + QDir::separator() + Profile::TYPE_SYNC + QDir::separator() + aNewName + FORMAT_EXT; ret = QFile::rename(source, destination); if(true == ret) { // Rename the sync log QString sourceLog = d_ptr->iPrimaryPath + QDir::separator() + Profile::TYPE_SYNC + QDir::separator() + LOG_DIRECTORY + QDir::separator() + aName + LOG_EXT + FORMAT_EXT; QString destinationLog = d_ptr->iPrimaryPath + QDir::separator() + Profile::TYPE_SYNC + QDir::separator() + LOG_DIRECTORY + QDir::separator() + aNewName + LOG_EXT + FORMAT_EXT; ret = QFile::rename(sourceLog, destinationLog); if(false == ret) { // Roll back the earlier rename QFile::rename(destination, source); } } if(false == ret) { LOG_WARNING("Failed to rename profile" << aName); } return ret; } bool ProfileManager::saveSyncResults(QString aProfileName, const SyncResults &aResults) { FUNCTION_CALL_TRACE; bool success = false; SyncProfile *profile = syncProfile(aProfileName); if (profile) { SyncLog *log = profile->log(); if (log) { log->addResults(aResults); success = saveLog(*log); //Emitting signal emit signalProfileChanged(aProfileName,ProfileManager::PROFILE_LOGS_MODIFIED,profile->toString()); } delete profile; profile = 0; } return success; } bool ProfileManager::setSyncSchedule(QString aProfileId , QString aScheduleAsXml) { FUNCTION_CALL_TRACE; bool status = false; SyncProfile *profile = syncProfile(aProfileId); if (profile) { profile->setSyncType(SyncProfile::SYNC_SCHEDULED); QDomDocument doc; if(doc.setContent(aScheduleAsXml,true)) { SyncSchedule schedule(doc.documentElement()); profile->setSyncSchedule(schedule); updateProfile(*profile); status = true; } delete profile; profile = NULL; } else { LOG_WARNING("Invalid Profile Supplied"); } return status; } bool ProfileManagerPrivate::parseFile(const QString &aPath, QDomDocument &aDoc) { //FUNCTION_CALL_TRACE; bool parsingOk = false; if (QFile::exists(aPath)) { QFile file(aPath); if (file.open(QIODevice::ReadOnly)) { parsingOk = aDoc.setContent(&file); file.close(); if (!parsingOk) { LOG_WARNING("Failed to parse profile XML: " << aPath); } } else { LOG_WARNING("Failed to open profile file for reading:" << aPath); } } else { LOG_WARNING("Profile file not found:" << aPath); } return parsingOk; } QDomDocument ProfileManagerPrivate::constructProfileDocument(const Profile &aProfile) { //FUNCTION_CALL_TRACE; QDomDocument doc; QDomElement root = aProfile.toXml(doc); if (root.isNull()) { LOG_WARNING("Failed to convert profile to XML"); } else { QDomProcessingInstruction xmlHeading = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""); doc.appendChild(xmlHeading); doc.appendChild(root); } return doc; } bool ProfileManagerPrivate::writeProfileFile(const QString &aProfilePath, const QDomDocument &aDoc) { FUNCTION_CALL_TRACE; LOG_WARNING("writeProfileFile() called, forcing disk write:" << aProfilePath); QFile file(aProfilePath); bool profileWritten = false; if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { QTextStream outputStream(&file); outputStream << aDoc.toString(PROFILE_INDENT); file.close(); profileWritten = true; } else { LOG_WARNING("Failed to open profile file for writing:" << aProfilePath); profileWritten = false; } return profileWritten; } void ProfileManagerPrivate::restoreBackupIfFound(const QString &aProfilePath, const QString &aBackupPath) { //FUNCTION_CALL_TRACE; if (QFile::exists(aBackupPath)) { LOG_WARNING("Profile backup file found. The actual profile may be corrupted."); QDomDocument doc; if (parseFile(aBackupPath, doc)) { LOG_DEBUG("Restoring profile from backup"); QFile::remove(aProfilePath); QFile::copy(aBackupPath, aProfilePath); } else { LOG_WARNING("Failed to parse backup file"); LOG_DEBUG("Removing backup file"); QFile::remove(aBackupPath); } } } bool ProfileManagerPrivate::createBackup(const QString &aProfilePath, const QString &aBackupPath) { FUNCTION_CALL_TRACE; return QFile::copy(aProfilePath, aBackupPath); } QString ProfileManagerPrivate::findProfileFile(const QString &aName, const QString &aType) { QString fileName = aType + QDir::separator() + aName + FORMAT_EXT; QString primaryPath = iPrimaryPath + QDir::separator() + fileName; QString secondaryPath = iSecondaryPath + QDir::separator() + fileName; if (QFile::exists(primaryPath)) { return primaryPath; } else if (!QFile::exists(secondaryPath)) { return primaryPath; } else { return secondaryPath; } } // this function checks to see if its a new profile or an // existing profile being modified under $Sync::syncCacheDir/profiles directory. bool ProfileManagerPrivate::profileExists(const QString &aProfileId ,const QString &aType) { QString profileFile = iPrimaryPath + QDir::separator() + aType + QDir::separator() + aProfileId + FORMAT_EXT; LOG_DEBUG("profileFile:" << profileFile); return QFile::exists(profileFile); } void ProfileManager::addRetriesInfo(const SyncProfile* profile) { FUNCTION_CALL_TRACE; if(profile) { if(profile->hasRetries() && !iSyncRetriesInfo.contains(profile->name())) { LOG_DEBUG("syncretries : retries info present for profile" << profile->name()); iSyncRetriesInfo[profile->name()] = profile->retryIntervals(); } } } QDateTime ProfileManager::getNextRetryInterval(const SyncProfile* aProfile) { FUNCTION_CALL_TRACE; QDateTime nextRetryInterval; if(aProfile && iSyncRetriesInfo.contains(aProfile->name()) && !iSyncRetriesInfo[aProfile->name()].isEmpty()) { quint32 mins = iSyncRetriesInfo[aProfile->name()].takeFirst(); nextRetryInterval = QDateTime::currentDateTime().addSecs(mins * 60); LOG_DEBUG("syncretries : retry for profile" << aProfile->name() << "in" << mins <<"minutes"); LOG_DEBUG("syncretries :" << iSyncRetriesInfo[aProfile->name()].count() <<"attempts remain"); } return nextRetryInterval; } void ProfileManager::retriesDone(const QString& aProfileName) { FUNCTION_CALL_TRACE; if(iSyncRetriesInfo.contains(aProfileName)) { iSyncRetriesInfo.remove(aProfileName); LOG_DEBUG("syncretries : retry success for" << aProfileName); } } buteo-syncfw-0.7.21+16.04.20151216.1/libbuteosyncfw/profile/TargetResults.cpp0000644000015600001650000000770512634332753026763 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "TargetResults.h" #include "ProfileEngineDefs.h" #include namespace Buteo { // Private implementation class for TargetResults class TargetResultsPrivate { public: TargetResultsPrivate(); TargetResultsPrivate(const TargetResultsPrivate &aSource); // Target name. QString iTargetName; ItemCounts iLocalItems; ItemCounts iRemoteItems; }; } using namespace Buteo; TargetResultsPrivate::TargetResultsPrivate() { } TargetResultsPrivate::TargetResultsPrivate(const TargetResultsPrivate &aSource) : iTargetName(aSource.iTargetName), iLocalItems(aSource.iLocalItems), iRemoteItems(aSource.iRemoteItems) { } TargetResults::TargetResults(const TargetResults &aSource) : d_ptr(new TargetResultsPrivate(*aSource.d_ptr)) { } TargetResults::TargetResults(const QString &aTargetName, ItemCounts aLocalItems, ItemCounts aRemoteItems) : d_ptr(new TargetResultsPrivate()) { d_ptr->iTargetName = aTargetName; d_ptr->iLocalItems = aLocalItems; d_ptr->iRemoteItems = aRemoteItems; } TargetResults::TargetResults(const QDomElement &aRoot) : d_ptr(new TargetResultsPrivate()) { d_ptr->iTargetName = aRoot.attribute(ATTR_NAME); QDomElement local = aRoot.firstChildElement(TAG_LOCAL); if (!local.isNull()) { d_ptr->iLocalItems.added = local.attribute(ATTR_ADDED).toUInt(); d_ptr->iLocalItems.deleted = local.attribute(ATTR_DELETED).toUInt(); d_ptr->iLocalItems.modified = local.attribute(ATTR_MODIFIED).toUInt(); } // no else QDomElement remote = aRoot.firstChildElement(TAG_REMOTE); if (!remote.isNull()) { d_ptr->iRemoteItems.added = remote.attribute(ATTR_ADDED).toUInt(); d_ptr->iRemoteItems.deleted = remote.attribute(ATTR_DELETED).toUInt(); d_ptr->iRemoteItems.modified = remote.attribute(ATTR_MODIFIED).toUInt(); } // no else } TargetResults::~TargetResults() { delete d_ptr; d_ptr = 0; } TargetResults& TargetResults::operator=(const TargetResults &aRhs) { if (&aRhs != this) { delete d_ptr; d_ptr = new TargetResultsPrivate(*aRhs.d_ptr); } return *this; } QDomElement TargetResults::toXml(QDomDocument &aDoc) const { QDomElement root = aDoc.createElement(TAG_TARGET_RESULTS); root.setAttribute(ATTR_NAME, d_ptr->iTargetName); QDomElement local = aDoc.createElement(TAG_LOCAL); local.setAttribute(ATTR_ADDED, d_ptr->iLocalItems.added); local.setAttribute(ATTR_DELETED, d_ptr->iLocalItems.deleted); local.setAttribute(ATTR_MODIFIED, d_ptr->iLocalItems.modified); root.appendChild(local); QDomElement remote = aDoc.createElement(TAG_REMOTE); remote.setAttribute(ATTR_ADDED, d_ptr->iRemoteItems.added); remote.setAttribute(ATTR_DELETED, d_ptr->iRemoteItems.deleted); remote.setAttribute(ATTR_MODIFIED, d_ptr->iRemoteItems.modified); root.appendChild(remote); return root; } QString TargetResults::targetName() const { return d_ptr->iTargetName; } ItemCounts TargetResults::localItems() const { return d_ptr->iLocalItems; } ItemCounts TargetResults::remoteItems() const { return d_ptr->iRemoteItems; } buteo-syncfw-0.7.21+16.04.20151216.1/bin/0000755000015600001650000000000012634333235017503 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/bin/test_info.pl0000755000015600001650000000515112634332753022043 0ustar pbuserpbgroup00000000000000#!/usr/bin/perl #/* # * This file is part of buteo-syncfw package # * # * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). # * # * Contact: Sateesh Kavuri # * # * This library is free software; you can redistribute it and/or # * modify it under the terms of the GNU Lesser General Public License # * version 2.1 as published by the Free Software Foundation. # * # * This library is distributed in the hope that it will be useful, but # * WITHOUT ANY WARRANTY; without even the implied warranty of # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # * Lesser General Public License for more details. # * # * You should have received a copy of the GNU Lesser General Public # * License along with this library; if not, write to the Free Software # * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # * 02110-1301 USA # * # */ # Author : Srikanth Kavoori # Not Tested fully yet. There could be some issues. # If you see any issues Let me know and i shall fix it :) open(INPUTFILE, "<$ARGV[0]"); # Totals: 0 passed, 2 failed, 0 skipped $tests_string = "Totals:"; $pass_count = 0; $fail_count = 0; $skip_count = 0; $total_count = 0; while() { my($line) = $_; if(/$tests_string/) { #print $line; #print "Found usr_include or header file on line $. \n" ; $pass_start_index = rindex($line,':') + 1; $passed_index = rindex($line,"passed") - 1; $length = $passed_index - $start_index; $pass_count += substr($line,$pass_start_index,$length); #print "pass_count $pass_count "; $fail_start_index = rindex($line,'passed,') + 8; $failed_index = rindex($line,"failed") - 1; $length = $failed_index - $fail_start_index; $fail_count += substr($line,$fail_start_index,$length); #print "fail_count $fail_count "; $skip_start_index = rindex($line,'failed,') + 8; $skipped_index = rindex($line,"skipped") - 1; $length = $failed_index - $skip_start_index; $skip_count += substr($line,$skip_start_index,$length); #print "skip_count $skip_count \n"; } } #if($filecount != 0 ) { # $coverage_percent=sprintf("%.2f",$coverage_percent/$filecount); #} $total_count += $pass_count + $fail_count + $skip_count; print "Total No. TESTS: $total_count \n"; print "Passed: $pass_count \n"; print "Failed: $fail_count \n"; print "Skipped: $skip_count \n"; open(SUMMARY_FILE,">$ARGV[1]"); print SUMMARY_FILE "" ; print SUMMARY_FILE "Total TESTS: $total_count \n"; print SUMMARY_FILE "Passed: $pass_count \n"; print SUMMARY_FILE "Failed: $fail_count \n"; print SUMMARY_FILE "Skipped: $skip_count \n"; close(SUMMARY_FILE); close(INPUTFILE); buteo-syncfw-0.7.21+16.04.20151216.1/bin/gcov_info.pl0000644000015600001650000000444012634332753022017 0ustar pbuserpbgroup00000000000000#!/usr/bin/perl #/* # * This file is part of buteo-syncfw package # * # * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). # * # * Contact: Sateesh Kavuri # * # * This library is free software; you can redistribute it and/or # * modify it under the terms of the GNU Lesser General Public License # * version 2.1 as published by the Free Software Foundation. # * # * This library is distributed in the hope that it will be useful, but # * WITHOUT ANY WARRANTY; without even the implied warranty of # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # * Lesser General Public License for more details. # * # * You should have received a copy of the GNU Lesser General Public # * License along with this library; if not, write to the Free Software # * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # * 02110-1301 USA # * # */ # # Author : Srikanth Kavoori # Not Tested fully yet. There could be some issues. # If you see any issues Let me know and i shall fix it :) open(INPUTFILE, "<$ARGV[0]"); open(EXTRACTED_INFO,">$ARGV[2]"); $exclude_string = "/usr/include/"; $exclude_string2 = "\\.h"; $exclude_string3 = "compilers"; $coverage_count_string ="Lines executed:"; $count = 0; $filecount = 0; $coverage_percent = 0; while() { my($line) = $_; if ($count == 3) { $count = 0; } if(/$exclude_string/ || /$exclude_string2/ || /$exclude_string3/) { #print "Found usr_include or header file on line $. \n" ; $count ++; } if ($count == 0) { # Print the line to the extracted_info and add a newline print EXTRACTED_INFO "$line\n"; if(/$coverage_count_string/) { $filecount ++; $index = rindex($line,':') + 1; $percent = substr($line,$index); $perc_index = rindex($percent,'%'); $percent = substr($percent,0,$perc_index); $lines = substr($line, rindex($line, "of") + 3); $total_lines += $lines; $covered_lines += $percent * $lines; } } else { #count should be 1 or 2 omit the line $count ++; } } if($filecount != 0 ) { $coverage_percent=sprintf("%.2f",$covered_lines/$total_lines); } print "Total No. of files : $filecount Coverage Percent : $coverage_percent "; open(PERCENTFILE,">$ARGV[1]"); print PERCENTFILE "$coverage_percent\n" ; close(PERCENTFILE); close(INPUTFILE); close(EXTRACTED_INFO); buteo-syncfw-0.7.21+16.04.20151216.1/bin/run-tests-sync-fw.sh0000755000015600001650000001120012634332753023370 0ustar pbuserpbgroup00000000000000#!/bin/bash #/* # * This file is part of buteo-syncfw package # * # * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). # * # * Contact: Sateesh Kavuri # * # * This library is free software; you can redistribute it and/or # * modify it under the terms of the GNU Lesser General Public License # * version 2.1 as published by the Free Software Foundation. # * # * This library is distributed in the hope that it will be useful, but # * WITHOUT ANY WARRANTY; without even the implied warranty of # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # * Lesser General Public License for more details. # * # * You should have received a copy of the GNU Lesser General Public # * License along with this library; if not, write to the Free Software # * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # * 02110-1301 USA # * # */ # #Author - Srikanth Kavoori # This file should run unittests for sync-fw # and create the result file with unittest rate # and coverage to this folder with name # sync-fw-results # # The release number should be in the file # this script generates the results automatically # for the latest weekXxX directory under sync-fw #this script updation should be rarely needed #Script Specific Variables TARGET=sync-fw TARGET_WEEK=week$(date +%V)$(date +%G) RESULTS_DIR=$PWD # adding environment varaible to get coverage rate export SBOX_USE_CCACHE=no ccache -c if [ $# == 0 ]; then WD=$PWD ROOTDIR=$WD/.. else WD=$1 TARGET_WEEK=$(ls -c $WD/../$TARGET | head -1) echo "TARGET WEEK is $TARGET_WEEK" ROOTDIR=$WD/../$TARGET/$TARGET_WEEK echo "ROOTDIR is $ROOTDIR" echo $1 RESULTS_DIR=$2 echo "Results Dir is $RESULTS_DIR" fi TARGETS=(libsyncpluginmgr libsyncprofile msyncd) RESULTS_FILE=$RESULTS_DIR/$TARGET-results_$TARGET_WEEK TEMPFILE1=$WD/.temp_results if [ -f $TEMPFILE1 ] then rm -f $TEMPFILE1 fi TEMPFILE2=$WD/.gcov_info.txt if [ -f $TEMPFILE2 ] then rm -f $TEMPFILE2 fi TEMPFILE3=$WD/.percent if [ -f $TEMPFILE3 ] then rm -f $TEMPFILE3 fi export LD_LIBRARY_PATH=$ROOT_DIR/libsyncprofile:$ROOT_DIR/libsyncpluginmgr:$LD_LIBRARY_PATH echo "Running the unit tests for $TARGET..." echo "Results will be stored in: $RESULTS_FILE ...." cd $ROOTDIR echo ${TARGETS[@]} echo "Building sync-fw so as to run unit tests on the libraries" qmake make clean make coverage if [ $? -ne 0 ]; then echo "sync-fw building failed...exiting" exit 1 else echo "sync-fw build successful" echo "Building unit tests " cd unittests echo "running unit tests " cd tests export LD_LIBRARY_PATH=../../libsyncprofile:../../libsyncpluginmgr timed -d meego-run ./sync-fw-tests >> $TEMPFILE1 2>&1 rm ../../msyncd/*Test.* rm ../../msyncd/tests.* cd .. fi if [ $? -ne 0 ]; then echo "Running unit tests failed...exiting " exit 1 else echo "Getting Code Coverage Results " cd $ROOTDIR for target in ${TARGETS[@]} do if [ -d $target ]; then echo "Getting Coverage Results from $PWD" cd $target rm -f moc_* file_list=$(ls *.gcno) #echo "Printing File List :$file_list " for file in $file_list do echo "Running gcov on $file" gcov $file >> $TEMPFILE2 done cd .. fi done fi cd $ROOTDIR # get coverage information for the files using perl echo "executing perl $ROOTDIR/bin/gcov_info.pl $TEMPFILE2 $TEMPFILE3" GCOV_REPORT=$WD/gcov_report.txt perl $WD/gcov_info.pl $TEMPFILE2 $TEMPFILE3 $GCOV_REPORT if [ ! $? -eq 0 ]; then echo "Perl Script Failed to execute ... Exiting ... " exit 0 fi SUMMARY_FILE=$WD/.summary_file if [ -f $SUMMARY_FILE ] then rm -f $SUMMARY_FILE fi perl $WD/test_info.pl $TEMPFILE1 $SUMMARY_FILE echo "Writing the file $RESULTS_FILE" echo "#Results for $TARGET_WEEK " > $RESULTS_FILE echo "Results Summary STARTED " >> $RESULTS_FILE echo "#Current gcov reported coverage (line rate) is" >> $RESULTS_FILE cat $TEMPFILE3 >> $RESULTS_FILE echo "Unit test Results Summary " >> $RESULTS_FILE cat $SUMMARY_FILE >> $RESULTS_FILE echo "Results Summary ENDED " >> $RESULTS_FILE echo "****************UNIT_TEST Results START**************" >> $RESULTS_FILE cat $TEMPFILE1 >> $RESULTS_FILE echo "****************UNIT_TEST Results END**************" >> $RESULTS_FILE rm -f $TEMPFILE1 $TEMPFILE2 $TEMPFILE3 cd $ROOTDIR echo $PWD qmake make distclean cd $WD echo "$RESULTS_FILE created" buteo-syncfw-0.7.21+16.04.20151216.1/unittests/0000755000015600001650000000000012634333235020775 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/0000755000015600001650000000000012634333235022137 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/testapplication.pri0000644000015600001650000000102612634332753026061 0ustar pbuserpbgroup00000000000000include(tests_common.pri) pro_file_basename = $$basename(_PRO_FILE_) pro_file_basename ~= s/\\.pro$// TEMPLATE = app TARGET = $${pro_file_basename} HEADERS = $${pro_file_basename}.h SOURCES = $${pro_file_basename}.cpp target.path = $${INSTALL_TESTDIR}/$${tests_subdir} INSTALLS += target #check.depends = all #check.commands = '\ # cd "$${PWD}" \ # && export LD_LIBRARY_PATH="$${OUT_PWD}/$${tests_subdir_r}/../src:\$\${LD_LIBRARY_PATH}" \ # && $${OUT_PWD}/$${TARGET}' #check.CONFIG = phony #QMAKE_EXTRA_TARGETS += check buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/tests.pro0000644000015600001650000000056512634332753024035 0ustar pbuserpbgroup00000000000000include(tests_common.pri) TEMPLATE = subdirs SUBDIRS = \ msyncdtests \ pluginmanagertests \ syncfwclienttests \ syncprofiletests \ # install testwrapper.files = runstarget.sh testwrapper.path = $${INSTALL_TESTDIR} INSTALLS += testwrapper testdefinition.files = tests.xml testdefinition.path = $${INSTALL_TESTDIR} INSTALLS += testdefinition buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/0000755000015600001650000000000012634333235025557 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/ProfileTest.pro0000644000015600001650000000004012634332753030537 0ustar pbuserpbgroup00000000000000include(../testapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/SyncProfileTest.h0000644000015600001650000000226712634332753031040 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCPROFILETEST_H #define SYNCPROFILETEST_H #include namespace Buteo { class SyncProfileTest: public QObject { Q_OBJECT private slots: void testConstruction(); void testProperties(); void testResults(); void testNextSyncTime(); void testSubProfiles(); }; } #endif // SYNCPROFILETEST_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/StorageProfileTest.pro0000644000015600001650000000004012634332753032064 0ustar pbuserpbgroup00000000000000include(../testapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/SyncLogTest.cpp0000644000015600001650000001022412634332753030504 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncLogTest.h" #include #include "SyncLog.h" using namespace Buteo; static const QString NAME = "ovi-calendar"; static const QString LOG_XML = "" "" "" "" "" "" "" ""; void SyncLogTest::testLog() { // Create from scratch. SyncLog log1(NAME); QCOMPARE(log1.profileName(), NAME); QVERIFY(log1.lastResults() == 0); QCOMPARE(log1.allResults().size(), 0); // Create from XML. QDomDocument doc; QVERIFY(doc.setContent(LOG_XML, false)); SyncLog log2(doc.documentElement()); QCOMPARE(log2.profileName(), NAME); QVERIFY(log2.lastResults() != 0); QCOMPARE(log2.allResults().size(), 1); TargetResults tr2 = log2.lastResults()->targetResults().at(0); QCOMPARE(tr2.targetName(), QString("hcalendar")); QCOMPARE(tr2.localItems().added, (unsigned)2); QCOMPARE(tr2.localItems().deleted, (unsigned)3); QCOMPARE(tr2.localItems().modified, (unsigned)4); QCOMPARE(tr2.remoteItems().added, (unsigned)5); QCOMPARE(tr2.remoteItems().deleted, (unsigned)6); QCOMPARE(tr2.remoteItems().modified, (unsigned)7); QCOMPARE(log2.lastResults()->isScheduled(), true); // Copy constructor. SyncLog log3(log2); QDomDocument doc2; doc2.appendChild(log2.toXml(doc2)); QDomDocument doc3; doc3.appendChild(log3.toXml(doc3)); QVERIFY(doc2.toString().size() >= LOG_XML.size()); QCOMPARE(doc2.toString(), doc3.toString()); // Add new results. SyncResults newResults; newResults.setMajorCode(Buteo::SyncResults::SYNC_RESULT_CANCELLED); QCOMPARE(newResults.majorCode(), 2); QCOMPARE(newResults < *log2.lastResults(), false); newResults.addTargetResults(TargetResults("hcontacts", ItemCounts(2, 3, 4), ItemCounts(5, 6, 7))); log2.addResults(newResults); QVERIFY(log2.lastResults() != 0); QCOMPARE(log2.lastResults()->majorCode(), 2); QCOMPARE(log2.allResults().size(), 2); QCOMPARE(log2.allResults().at(0)->majorCode(), 1); // Verify target results contents. QCOMPARE(log2.lastResults()->targetResults().size(), 1); TargetResults tr = log2.lastResults()->targetResults().at(0); QCOMPARE(tr.targetName(), QString("hcontacts")); QCOMPARE(tr.localItems().added, (unsigned)2); QCOMPARE(tr.localItems().deleted, (unsigned)3); QCOMPARE(tr.localItems().modified, (unsigned)4); QCOMPARE(tr.remoteItems().added, (unsigned)5); QCOMPARE(tr.remoteItems().deleted, (unsigned)6); QCOMPARE(tr.remoteItems().modified, (unsigned)7); } void SyncLogTest::testAddResults() { const int MAXLOGENTRIES = 5; SyncLog log(NAME); SyncResults newResults; newResults.setMajorCode(Buteo::SyncResults::SYNC_RESULT_CANCELLED); newResults.addTargetResults(TargetResults("hcontacts", ItemCounts(2, 3, 4), ItemCounts(5, 6, 7))); for (int i = 0; i < 7; ++i) { log.addResults(newResults); QVERIFY(log.allResults().size() <= MAXLOGENTRIES); } } QTEST_MAIN(Buteo::SyncLogTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/ProfileFieldTest.cpp0000644000015600001650000000450712634332753031501 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ProfileFieldTest.h" #include #include "ProfileField.h" using namespace Buteo; static const QString FIELD_XML = "" "" "" ""; void ProfileFieldTest::testField() { QDomDocument doc; QVERIFY(doc.setContent(FIELD_XML, false)); // Create from XML. ProfileField pf_original(doc.documentElement()); // Copy construction. ProfileField pf(pf_original); // Verify properties. QCOMPARE(pf.name(), QString("Notebook Name")); QCOMPARE(pf.type(), QString("combo")); QCOMPARE(pf.defaultValue(), QString("myNotebook")); QCOMPARE(pf.label(), QString("Notebook")); QCOMPARE(pf.visible(), ProfileField::VISIBLE_USER); QCOMPARE(pf.isReadOnly(), true); QStringList options = pf.options(); QCOMPARE(options.size(), 2); QCOMPARE(options[0], QString("myNotebook")); QCOMPARE(options[1], QString("otherNotebook")); // Test value validation. QCOMPARE(pf.validate("myNotebook"), true); QCOMPARE(pf.validate("otherNotebook"), true); QCOMPARE(pf.validate("something else"), false); // XML output. QDomDocument doc2; QDomElement root = pf.toXml(doc2); QVERIFY(!root.isNull()); doc2.appendChild(root); QCOMPARE(doc.toString(), doc2.toString()); } QTEST_MAIN(Buteo::ProfileFieldTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/ProfileFactoryTest.cpp0000644000015600001650000000610412634332753032060 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ProfileFactoryTest.h" #include #include #include "ProfileFactory.h" #include "SyncProfile.h" #include "StorageProfile.h" using namespace Buteo; void ProfileFactoryTest::testCreateDirect() { const QString NAME = "name"; ProfileFactory pf; { QString type = Profile::TYPE_CLIENT; QScopedPointer p(pf.createProfile(NAME, type)); QVERIFY(p != 0); QCOMPARE(p->name(), NAME); QCOMPARE(p->type(), type); } { QString type = Profile::TYPE_SYNC; QScopedPointer p(pf.createProfile(NAME, type)); QVERIFY(p != 0); QCOMPARE(p->name(), NAME); QCOMPARE(p->type(), type); QVERIFY(dynamic_cast(p.data()) != 0); } { QString type = Profile::TYPE_STORAGE; QScopedPointer p(pf.createProfile(NAME, type)); QVERIFY(p != 0); QCOMPARE(p->name(), NAME); QCOMPARE(p->type(), type); QVERIFY(dynamic_cast(p.data()) != 0); } } void ProfileFactoryTest::testCreateFromXml() { const QString NAME = "name"; ProfileFactory pf; QDomDocument doc; { QString type = Profile::TYPE_CLIENT; Profile clientProfile(NAME, type); QDomElement root = clientProfile.toXml(doc); QScopedPointer p(pf.createProfile(root)); QVERIFY(p != 0); QCOMPARE(p->name(), NAME); QCOMPARE(p->type(), type); } { QString type = Profile::TYPE_SYNC; SyncProfile syncProfile(NAME); QDomElement root = syncProfile.toXml(doc); QScopedPointer p(pf.createProfile(root)); QVERIFY(p != 0); QCOMPARE(p->name(), NAME); QCOMPARE(p->type(), type); QVERIFY(dynamic_cast(p.data()) != 0); } { QString type = Profile::TYPE_STORAGE; StorageProfile storageProfile(NAME); QDomElement root = storageProfile.toXml(doc); QScopedPointer p(pf.createProfile(root)); QVERIFY(p != 0); QCOMPARE(p->name(), NAME); QCOMPARE(p->type(), type); QVERIFY(dynamic_cast(p.data()) != 0); } } QTEST_MAIN(Buteo::ProfileFactoryTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/StorageProfileTest.h0000644000015600001650000000212612634332753031522 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef STORAGEPROFILETEST_H #define STORAGEPROFILETEST_H #include namespace Buteo { class StorageProfileTest: public QObject { Q_OBJECT private slots: void testStorageProfile(); }; } #endif // STORAGEPROFILETEST_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/ProfileTest.h0000644000015600001650000000333612634332753030201 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef PROFILETEST_H #define PROFILETEST_H #include "Profile.h" #include namespace Buteo { class ProfileTest: public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void testConstruction(); void testProperties(); void testKeys(); void testFields(); void testSubProfiles(); void testValidate(); void testMerge(); void testXmlConversion(); private: Profile *loadFromXmlFile(const QString &aName, const QString &aType, const QString &aProfileDir = QString()); bool saveToXmlFile(const Profile &aProfile, const QString &aName, bool aLocalOnly = true, const QString &aProfileDir = QString()); QString profileFileToString(const QString &aName, const QString &aType, const QString &aProfileDir = QString()); }; } #endif // PROFILETEST_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/ProfileTest.cpp0000644000015600001650000003414312634332753030534 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ProfileTest.h" #include "Profile_p.h" #include #include #include "ProfileEngineDefs.h" using namespace Buteo; static const QString PROFILE_DIR = "syncprofiletests/testprofiles/user"; static const QString TMP_PROFILE_DIR = "syncprofiletests/testprofiles/tmp"; static const QString EXPECTED_PROFILE_DIR = "syncprofiletests/testprofiles/expected"; void ProfileTest::initTestCase() { } void ProfileTest::cleanupTestCase() { } void ProfileTest::testConstruction() { QString name = "ovi-calendar"; QString type = Profile::TYPE_SYNC; // Construction from scratch. { Profile p(name, type); QCOMPARE(p.name(), name); QCOMPARE(p.type(), type); } // Construction from XML. { QScopedPointer p(loadFromXmlFile(name, Profile::TYPE_SYNC)); QVERIFY(p != 0); QCOMPARE(p->name(), name); QCOMPARE(p->type(), type); QCOMPARE(p->isEnabled(), true); QStringList subProfileNames = p->subProfileNames(); QCOMPARE(subProfileNames.size(), 3); Profile *sp = p->subProfile("hcalendar", Profile::TYPE_STORAGE); QVERIFY(sp != 0); QCOMPARE(sp->key("Notebook Name"), QString("myNotebook")); } // Copy constructor. { QScopedPointer p(loadFromXmlFile("hcalendar", Profile::TYPE_STORAGE)); QVERIFY(p != 0); QScopedPointer p2(loadFromXmlFile("testsync-ovi", Profile::TYPE_SYNC)); QVERIFY(p2 != 0); p2->merge(*p); Profile p3(*p2); QCOMPARE(p3.toString(), p2->toString()); } } void ProfileTest::testProperties() { QString name = "ovi-calendar"; QString newName = "ovi-new"; QString type = Profile::TYPE_SYNC; Profile p(name, type); // Change name. QCOMPARE(p.name(), name); p.setName(newName); QCOMPARE(p.name(), newName); // Change enabled status. QCOMPARE(p.isEnabled(), true); p.setEnabled(false); QCOMPARE(p.isEnabled(), false); // Change loaded status. QCOMPARE(p.isLoaded(), false); p.setLoaded(true); QCOMPARE(p.isLoaded(), true); } void ProfileTest::testKeys() { const QString NAME = "ovi-calendar"; const QString TYPE = Profile::TYPE_SYNC; const QString KEY1 = "key1"; const QString VALUE1 = "value1"; const QString KEY2 = "key2"; const QString VALUE2 = "value2"; const QString VALUE3 = "value3"; const QString BOOLKEY = "boolkey"; const QString DEFAULT = "default"; Profile p(NAME, TYPE); // No keys. QVERIFY(p.key(KEY1).isNull()); QCOMPARE(p.key(KEY1, DEFAULT), DEFAULT); QVERIFY(p.allKeys().isEmpty()); QCOMPARE(p.boolKey(BOOLKEY), false); // Default = false QCOMPARE(p.boolKey(BOOLKEY, true), true); // Default = true QVERIFY(p.keyValues(KEY1).isEmpty()); QVERIFY(p.keyNames().isEmpty()); // Add keys. p.setKey(KEY1, VALUE1); QStringList values; values << VALUE2 << VALUE3; QCOMPARE(values.size(), 2); p.setKeyValues(KEY2, values); p.setBoolKey(BOOLKEY, true); QCOMPARE(p.key(KEY1), VALUE1); QCOMPARE(p.key(KEY2), VALUE2); QCOMPARE(p.boolKey(BOOLKEY), true); QMap allKeys = p.allKeys(); QCOMPARE(allKeys.size(), 4); // Note: two values for KEY2. QCOMPARE(allKeys.value(KEY1), VALUE1); QCOMPARE(allKeys.value(KEY2), VALUE2); QCOMPARE(allKeys.value(BOOLKEY), QString("true")); QStringList key2Values = p.keyValues(KEY2); QCOMPARE(key2Values.size(), 2); QCOMPARE(key2Values[0], VALUE2); QCOMPARE(key2Values[1], VALUE3); QStringList keyNames = p.keyNames(); QCOMPARE(keyNames.size(), 3); QCOMPARE(keyNames[0], BOOLKEY); QCOMPARE(keyNames[1], KEY1); QCOMPARE(keyNames[2], KEY2); // Modify keys. p.setKey(KEY1, VALUE2); p.setKey(KEY2, VALUE1); p.setKey(BOOLKEY, QString("false")); QCOMPARE(p.key(KEY1), VALUE2); QCOMPARE(p.keyValues(KEY1).size(), 1); QCOMPARE(p.key(KEY2), VALUE1); QCOMPARE(p.boolKey(BOOLKEY), false); key2Values = p.keyValues(KEY2); QCOMPARE(key2Values.size(), 2); QCOMPARE(key2Values[0], VALUE1); QCOMPARE(key2Values[1], VALUE3); values.clear(); p.setKeyValues(KEY2, values); QCOMPARE(p.keyValues(KEY2).size(), 0); values << VALUE2 << VALUE1; p.setKeyValues(KEY2, values); key2Values = p.keyValues(KEY2); QCOMPARE(key2Values.size(), 2); QCOMPARE(key2Values[0], VALUE2); QCOMPARE(key2Values[1], VALUE1); // Remove key. p.removeKey(KEY2); key2Values = p.keyValues(KEY2); QCOMPARE(key2Values.size(), 0); } void ProfileTest::testFields() { // Load a profile that has fields: calendar storage profile. const QString NAME = "hcalendar"; const QString TYPE = Profile::TYPE_STORAGE; QScopedPointer p(loadFromXmlFile(NAME, TYPE)); QVERIFY(p != 0); QCOMPARE(p->name(), NAME); QCOMPARE(p->type(), TYPE); // There should be 3 fields. QList allFields = p->allFields(); QCOMPARE(allFields.size(), 3); // The last field should be about notebook name. const ProfileField *field = p->field("Notebook Name"); QCOMPARE(field, allFields.last()); // Field that does not exist. QVERIFY(p->field("unknown") == 0); // Compare field properties. QCOMPARE(field->name(), QString("Notebook Name")); QCOMPARE(field->type(), QString("combo")); QCOMPARE(field->defaultValue(), QString("myNotebook")); QStringList options = field->options(); QCOMPARE(options.size(), 2); QCOMPARE(options[0], QString("myNotebook")); QCOMPARE(options[1], QString("otherNotebook")); QCOMPARE(field->validate("myNotebook"), true); QCOMPARE(field->validate("otherNotebook"), true); QCOMPARE(field->validate("invalidNotebook"), false); // All fields are visible. QCOMPARE(p->visibleFields().size(), 3); } void ProfileTest::testSubProfiles() { // Load a profile that has sub-profiles. const QString NAME = "ovi-calendar"; const QString TYPE = Profile::TYPE_SYNC; QScopedPointer p(loadFromXmlFile(NAME, TYPE)); QVERIFY(p != 0); QCOMPARE(p->name(), NAME); QCOMPARE(p->type(), TYPE); QStringList subProfileNames = p->subProfileNames(); QCOMPARE(subProfileNames[0], QString("syncml")); QCOMPARE(subProfileNames[1], QString("hcalendar")); QCOMPARE(subProfileNames[2], QString("hcontacts")); QCOMPARE(p->subProfileNames(Profile::TYPE_CLIENT).size(), 1); QCOMPARE(p->subProfileNames(Profile::TYPE_SYNC).size(), 0); QCOMPARE(p->subProfileNames(Profile::TYPE_STORAGE).size(), 2); // Sub-profile that does not exist. QVERIFY(p->subProfile("unknown", Profile::TYPE_CLIENT) == 0); const Profile *const_p = p.data(); const Profile *sub = const_p->subProfile("hcalendar", Profile::TYPE_STORAGE); QVERIFY(sub != 0); QCOMPARE(sub, p->subProfile("hcalendar")); QCOMPARE(sub->isEnabled(), true); QCOMPARE(sub->key("Notebook Name"), QString("myNotebook")); QList allSubProfiles = p->allSubProfiles(); QCOMPARE(allSubProfiles.size(), 3); // Sub-profile by key value. sub = p->subProfileByKeyValue("Notebook Name", "myNotebook", Profile::TYPE_STORAGE, true); QVERIFY(sub != 0); QCOMPARE(sub, p->subProfile("hcalendar")); } void ProfileTest::testMerge() { QScopedPointer p(loadFromXmlFile("testsync-ovi", Profile::TYPE_SYNC)); QScopedPointer p2(loadFromXmlFile("hcalendar", Profile::TYPE_STORAGE)); QVERIFY(p != 0); QVERIFY(p2 != 0); Profile *sub = p->subProfile("hcalendar"); QVERIFY(sub != 0); QVERIFY(sub->key("Local URI").isNull()); QVERIFY(sub->allFields().isEmpty()); p->merge(*p2); QCOMPARE(sub->key("Local URI"), QString("./Calendar")); QCOMPARE(sub->allFields().size(), 3); QCOMPARE(sub->d_ptr->iLocalKeys.size(), 4); QCOMPARE(sub->d_ptr->iMergedKeys.size(), 1); QCOMPARE(sub->d_ptr->iLocalFields.size(), 0); QCOMPARE(sub->d_ptr->iMergedFields.size(), 3); // Merge service to sync profile. QScopedPointer p3(loadFromXmlFile("ovi-calendar", Profile::TYPE_SYNC)); QVERIFY(p3 != 0); p3->merge(*p); QVERIFY(p3->subProfile("syncml", Profile::TYPE_CLIENT) != 0); } void ProfileTest::testValidate() { QScopedPointer p(loadFromXmlFile("hcalendar", Profile::TYPE_STORAGE)); QVERIFY(p != 0); // Invalid, because field values are not set. QCOMPARE(p->isValid(), false); // Valid after setting field values. p->setKey("Target URI", "cal"); p->setKey("Calendar Format", "vcalendar"); p->setKey("Notebook Name", "myNotebook"); QCOMPARE(p->isValid(), true); // Invalid when name or type is empty. p->setName(""); QCOMPARE(p->isValid(), false); p->setName("hcalendar"); p->d_ptr->iType = ""; QCOMPARE(p->isValid(), false); p->d_ptr->iType = Profile::TYPE_STORAGE; QCOMPARE(p->isValid(), true); // Invalid when field value is invalid. p->setKey("Notebook Name", "invalid"); QCOMPARE(p->isValid(), false); p->setKey("Notebook Name", "myNotebook"); // Validate profile that has sub-profiles. QScopedPointer p2(loadFromXmlFile("testsync-ovi", Profile::TYPE_SYNC)); QVERIFY(p2 != 0); p->setEnabled(true); p2->merge(*p); QCOMPARE(p2->isValid(), true); // Removing required key makes the profile invalid. Profile *sub = p2->subProfile("hcalendar"); QVERIFY(sub != 0); sub->setKey("Target URI", QString::null); QEXPECT_FAIL("", "It seems all keys are dispensable with the current implementation", Continue); QCOMPARE(p2->isValid(), false); // Disabling the profile that requires the removed key makes the profile // valid again. sub->setEnabled(false); QCOMPARE(p2->isValid(), true); } void ProfileTest::testXmlConversion() { // TODO: Compare XML documents in a clever way, then fix the *-expected.xml // files which need to be updated after "service" profile-type removal // (left empty now) QEXPECT_FAIL("", "The order in which QDomDocument::toString() writes out XML attributes is undefined - cannot simply compare XML strings", Abort); QVERIFY(false); QScopedPointer p(loadFromXmlFile("hcalendar", Profile::TYPE_STORAGE)); QVERIFY(p != 0); QVERIFY(saveToXmlFile(*p, "hcalendar-output", true, TMP_PROFILE_DIR)); QCOMPARE(profileFileToString("hcalendar-output", Profile::TYPE_STORAGE, TMP_PROFILE_DIR), profileFileToString("hcalendar-expected", Profile::TYPE_STORAGE, EXPECTED_PROFILE_DIR)); // Merge storage profile to service profile. QScopedPointer p2(loadFromXmlFile("ovi-calendar", Profile::TYPE_SYNC)); QVERIFY(p2 != 0); p2->merge(*p); // Output local profile data only, no merged sub-profile data. QVERIFY(saveToXmlFile(*p2, "ovi-calendar-output", true, TMP_PROFILE_DIR)); QCOMPARE(profileFileToString("ovi-calendar-output", Profile::TYPE_SYNC, TMP_PROFILE_DIR), profileFileToString("ovi-calendar-expected", Profile::TYPE_SYNC, EXPECTED_PROFILE_DIR)); // Output merged sub-profiles also. QVERIFY(saveToXmlFile(*p2, "ovi-calendar-output", false, TMP_PROFILE_DIR)); QCOMPARE(profileFileToString("ovi-calendar-output", Profile::TYPE_SYNC, TMP_PROFILE_DIR), profileFileToString("ovi-calendar-merged-expected", Profile::TYPE_SYNC, EXPECTED_PROFILE_DIR)); } Profile *ProfileTest::loadFromXmlFile(const QString &aName, const QString &aType, const QString &aProfileDir) { QString profileDir = aProfileDir.isEmpty() ? PROFILE_DIR : aProfileDir; QFile file(profileDir + "/" + aType + "/" + aName + ".xml"); if (!file.open(QIODevice::ReadOnly)) { return 0; } // no else QDomDocument doc; if (!doc.setContent(&file)) { file.close(); return 0; } // no else file.close(); return new Profile(doc.documentElement()); } bool ProfileTest::saveToXmlFile(const Profile &aProfile, const QString &aName, bool aLocalOnly, const QString &aProfileDir) { QString profileDir = aProfileDir.isEmpty() ? PROFILE_DIR : aProfileDir; QDir dir; dir.mkpath(profileDir + "/" + aProfile.type()); QFile file(profileDir + "/" + aProfile.type() + "/" + aName + ".xml"); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { return false; } // no else QDomDocument doc; QDomProcessingInstruction xmlHeading = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""); doc.appendChild(xmlHeading); QDomElement root = aProfile.toXml(doc, aLocalOnly); if (root.isNull()) { return false; } // no else doc.appendChild(root); QTextStream outputStream(&file); outputStream << doc.toString(PROFILE_INDENT); file.close(); return true; } QString ProfileTest::profileFileToString(const QString &aName, const QString &aType, const QString &aProfileDir) { QString profileDir = aProfileDir.isEmpty() ? PROFILE_DIR : aProfileDir; QString output; QFile file(profileDir + "/" + aType + "/" + aName + ".xml"); if (file.open(QIODevice::ReadOnly)) { QTextStream outputStream(&file); output = outputStream.readAll(); } // no else return output; } QTEST_MAIN(Buteo::ProfileTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/SyncLogTest.h0000644000015600001650000000211212634332753030146 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCLOGTEST_H #define SYNCLOGTEST_H #include namespace Buteo { class SyncLogTest: public QObject { Q_OBJECT private slots: void testLog(); void testAddResults(); }; } #endif // SYNCLOGTEST_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/SyncScheduleTest.h0000644000015600001650000000220512634332753031164 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCSCHEDULETEST_H #define SYNCSCHEDULETEST_H #include namespace Buteo { class SyncScheduleTest: public QObject { Q_OBJECT private slots: void testConstruction(); void testProperties(); void testNextSyncTime(); }; } #endif // SYNCSCHEDULETEST_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/SyncProfileTest.cpp0000644000015600001650000002011412634332753031362 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncProfileTest.h" #include #include #include "SyncProfile.h" #include "ProfileEngineDefs.h" using namespace Buteo; static const QString NAME = "ovi-calendar"; static const QString TYPE = Profile::TYPE_SYNC; static const QString PROFILE_XML = "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""; void SyncProfileTest::testConstruction() { // Create from scratch. SyncProfile p1(NAME); QCOMPARE(p1.name(), NAME); QCOMPARE(p1.type(), TYPE); // Create from XML. QDomDocument doc; QVERIFY(doc.setContent(PROFILE_XML, false)); SyncProfile p2(doc.documentElement()); QCOMPARE(p2.name(), NAME); QCOMPARE(p2.type(), TYPE); QCOMPARE(p2.syncSchedule().interval(), (unsigned)30); // Copy constructor. QScopedPointer p3(p2.clone()); QVERIFY(p3 != 0); QCOMPARE(p3->toString(), p2.toString()); } void SyncProfileTest::testProperties() { // Create from XML. QDomDocument doc; QVERIFY(doc.setContent(PROFILE_XML, false)); SyncProfile p(doc.documentElement()); // Get non-storage keys. QMap keys = p.allNonStorageKeys(); QCOMPARE(keys.size(), 2); QCOMPARE(keys.value("username"), QString("me")); QCOMPARE(keys.value("enabled"), QString("true")); // Get storage backend names. QStringList storages = p.storageBackendNames(); QCOMPARE(storages.size(), 1); QCOMPARE(storages[0], QString("cal-backend")); // Change sync type. QEXPECT_FAIL("", "Broken since d6d974e (Added functions to enable/disable normal scheduling). " "Not sure how to fix this.test Maybe setSyncType should be just removed from the API", Continue); QCOMPARE(p.syncType(), SyncProfile::SYNC_MANUAL); p.setSyncType(SyncProfile::SYNC_SCHEDULED); QCOMPARE(p.syncType(), SyncProfile::SYNC_SCHEDULED); // Destination type. QCOMPARE(p.destinationType(), SyncProfile::DESTINATION_TYPE_UNDEFINED); //Profile *service = p.serviceProfile(); //QVERIFY(service != 0); p.setKey(KEY_DESTINATION_TYPE, VALUE_ONLINE); QCOMPARE(p.destinationType(), SyncProfile::DESTINATION_TYPE_ONLINE); p.setKey(KEY_DESTINATION_TYPE, VALUE_DEVICE); QCOMPARE(p.destinationType(), SyncProfile::DESTINATION_TYPE_DEVICE); // Sync direction. QCOMPARE(p.syncDirection(), SyncProfile::SYNC_DIRECTION_UNDEFINED); Profile *client = p.clientProfile(); QVERIFY(client != 0); client->setKey(KEY_SYNC_DIRECTION, VALUE_TWO_WAY); QCOMPARE(p.syncDirection(), SyncProfile::SYNC_DIRECTION_TWO_WAY); client->setKey(KEY_SYNC_DIRECTION, VALUE_FROM_REMOTE); QCOMPARE(p.syncDirection(), SyncProfile::SYNC_DIRECTION_FROM_REMOTE); client->setKey(KEY_SYNC_DIRECTION, VALUE_TO_REMOTE); QCOMPARE(p.syncDirection(), SyncProfile::SYNC_DIRECTION_TO_REMOTE); p.setSyncDirection(SyncProfile::SYNC_DIRECTION_TWO_WAY); QCOMPARE(client->key(KEY_SYNC_DIRECTION), VALUE_TWO_WAY); p.setSyncDirection(SyncProfile::SYNC_DIRECTION_FROM_REMOTE); QCOMPARE(client->key(KEY_SYNC_DIRECTION), VALUE_FROM_REMOTE); p.setSyncDirection(SyncProfile::SYNC_DIRECTION_TO_REMOTE); QCOMPARE(client->key(KEY_SYNC_DIRECTION), VALUE_TO_REMOTE); SyncProfile emptyProfile("empty"); emptyProfile.setSyncDirection(SyncProfile::SYNC_DIRECTION_TWO_WAY); // Conflict resolution policy. QCOMPARE(p.conflictResolutionPolicy(), SyncProfile::CR_POLICY_UNDEFINED); QVERIFY(client != 0); client->setKey(KEY_CONFLICT_RESOLUTION_POLICY, VALUE_PREFER_REMOTE); QCOMPARE(p.conflictResolutionPolicy(), SyncProfile::CR_POLICY_PREFER_REMOTE_CHANGES); client->setKey(KEY_CONFLICT_RESOLUTION_POLICY, VALUE_PREFER_LOCAL); QCOMPARE(p.conflictResolutionPolicy(), SyncProfile::CR_POLICY_PREFER_LOCAL_CHANGES); p.setConflictResolutionPolicy(SyncProfile::CR_POLICY_PREFER_REMOTE_CHANGES); QCOMPARE(client->key(KEY_CONFLICT_RESOLUTION_POLICY), VALUE_PREFER_REMOTE); p.setConflictResolutionPolicy(SyncProfile::CR_POLICY_PREFER_LOCAL_CHANGES); QCOMPARE(client->key(KEY_CONFLICT_RESOLUTION_POLICY), VALUE_PREFER_LOCAL); emptyProfile.setConflictResolutionPolicy(SyncProfile::CR_POLICY_PREFER_REMOTE_CHANGES); } void SyncProfileTest::testResults() { SyncProfile p(NAME); // Create log with some content. p.setLog(new SyncLog(NAME)); QVERIFY(p.lastSyncTime().isNull()); QDateTime now = QDateTime::currentDateTime(); SyncResults syncResults(now, 1, 0); syncResults.addTargetResults(TargetResults("hcalendar", ItemCounts(1, 2, 3), ItemCounts())); p.addResults(syncResults); QCOMPARE(p.lastSyncTime(), now); const SyncResults *sr = p.lastResults(); QVERIFY(sr != 0); QCOMPARE(sr->syncTime(), now); QCOMPARE(sr->majorCode(), 1); QCOMPARE(sr->targetResults().size(), 1); // 0 log. p.setLog(0); QVERIFY(p.lastResults() == 0); QVERIFY(p.lastSyncTime().isNull()); p.addResults(syncResults); QCOMPARE(p.lastSyncTime(), now); } void SyncProfileTest::testNextSyncTime() { // No next sync time in manual mode. SyncProfile p(NAME); QVERIFY(p.nextSyncTime(QDateTime::currentDateTime()).isNull()); // Scheduled sync. p.setSyncType(SyncProfile::SYNC_SCHEDULED); QDateTime lastSync(QDateTime::currentDateTime()); SyncResults r(lastSync, Buteo::SyncResults::SYNC_RESULT_SUCCESS, Buteo::SyncResults::NO_ERROR); p.addResults(r); SyncSchedule s; const unsigned INTERVAL = 15; s.setInterval(INTERVAL); DaySet days; days << Qt::Monday << Qt::Tuesday << Qt::Wednesday << Qt::Thursday << Qt::Friday << Qt::Saturday << Qt::Sunday; s.setDays(days); s.setScheduleEnabled(true); p.setSyncSchedule(s); QDateTime nextSync = p.nextSyncTime(p.lastSyncTime()); QCOMPARE(nextSync, lastSync.addSecs(INTERVAL * 60)); } void SyncProfileTest::testSubProfiles() { // Create from XML. QDomDocument doc; QVERIFY(doc.setContent(PROFILE_XML, false)); SyncProfile p(doc.documentElement()); const Profile *client = p.clientProfile(); QVERIFY(client != 0); QVERIFY(client->name() == "syncml"); QList storages = p.storageProfiles(); QCOMPARE(storages.size(), 2); QList storages2 = p.storageProfilesNonConst(); QCOMPARE(storages2.size(), 2); const Profile *server = p.serverProfile(); QVERIFY(server != 0); QVERIFY(server->name() == "syncmlserver"); } QTEST_MAIN(Buteo::SyncProfileTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/ProfileFactoryTest.h0000644000015600001650000000216112634332753031524 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef PROFILEFACTORYTEST_H #define PROFILEFACTORYTEST_H #include namespace Buteo { class ProfileFactoryTest: public QObject { Q_OBJECT private slots: void testCreateDirect(); void testCreateFromXml(); }; } #endif // PROFILEFACTORYTEST_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/SyncLogTest.pro0000644000015600001650000000004012634332753030515 0ustar pbuserpbgroup00000000000000include(../testapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/0000755000015600001650000000000012634333235030302 5ustar pbuserpbgroup00000000000000././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/testsync-ovi.servicebuteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/testsync-ovi.serv0000644000015600001650000000073412634332753033662 0ustar pbuserpbgroup00000000000000 Sync ovi OviSync icon_syncl buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/system/0000755000015600001650000000000012634333235031626 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/system/sync/0000755000015600001650000000000012634333235032602 5ustar pbuserpbgroup00000000000000././@LongLink0000000000000000000000000000015500000000000011216 Lustar 00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/system/sync/corrupted.xmlbuteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/system/sync/corru0000644000015600001650000000077712634332753033676 0ustar pbuserpbgroup00000000000000 buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/expected/0000755000015600001650000000000012634333235032103 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/expected/sync/0000755000015600001650000000000012634333235033057 5ustar pbuserpbgroup00000000000000././@LongLink0000000000000000000000000000017300000000000011216 Lustar 00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/expected/sync/ovi-calendar-expected.xmlbuteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/expected/sync/ovi0000644000015600001650000000042412634332753033603 0ustar pbuserpbgroup00000000000000 ././@LongLink0000000000000000000000000000020200000000000011207 Lustar 00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/expected/sync/ovi-calendar-merged-expected.xmlbuteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/expected/sync/ovi0000644000015600001650000000042412634332753033603 0ustar pbuserpbgroup00000000000000 buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/expected/storage/0000755000015600001650000000000012634333235033547 5ustar pbuserpbgroup00000000000000././@LongLink0000000000000000000000000000017300000000000011216 Lustar 00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/expected/storage/hcalendar-expected.xmlbuteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/expected/storage/0000644000015600001650000000066212634332753033561 0ustar pbuserpbgroup00000000000000 buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/user/0000755000015600001650000000000012634333235031260 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/user/sync/0000755000015600001650000000000012634333235032234 5ustar pbuserpbgroup00000000000000././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/user/sync/testsync-ovi.xmlbuteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/user/sync/testsyn0000644000015600001650000000201612634332753033673 0ustar pbuserpbgroup00000000000000 ././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/user/sync/ovi-calendar.xmlbuteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/user/sync/ovi-cal0000644000015600001650000000213012634332753033511 0ustar pbuserpbgroup00000000000000 buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/user/storage/0000755000015600001650000000000012634333235032724 5ustar pbuserpbgroup00000000000000././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/user/storage/hcalendar.xmlbuteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/user/storage/hcal0000644000015600001650000000057312634332753033567 0ustar pbuserpbgroup00000000000000 buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/user/client/0000755000015600001650000000000012634333235032536 5ustar pbuserpbgroup00000000000000././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/user/client/syncml.xmlbuteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/testprofiles/user/client/syncm0000644000015600001650000000032712634332753033620 0ustar pbuserpbgroup00000000000000 buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/ProfileFactoryTest.pro0000644000015600001650000000004012634332753032067 0ustar pbuserpbgroup00000000000000include(../testapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/ProfileManagerTest.pro0000644000015600001650000000004012634332753032032 0ustar pbuserpbgroup00000000000000include(../testapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/StorageProfileTest.cpp0000644000015600001650000000404512634332753032057 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "StorageProfileTest.h" #include #include "StorageProfile.h" using namespace Buteo; static const QString NAME = "hcalendar"; static const QString TYPE = Profile::TYPE_STORAGE; static const QString PROFILE_XML = "" "" "" "" "" "" ""; void StorageProfileTest::testStorageProfile() { // Create from scratch. StorageProfile p1(NAME); QCOMPARE(p1.name(), NAME); QCOMPARE(p1.type(), TYPE); // Create from XML. QDomDocument doc; QVERIFY(doc.setContent(PROFILE_XML, false)); StorageProfile p2(doc.documentElement()); QCOMPARE(p2.name(), NAME); QCOMPARE(p2.type(), TYPE); // Copy constructor. StorageProfile p3(p2); QCOMPARE(p3.toString(), p2.toString()); // Storage profile is disabled by default. Profile *p = &p1; QCOMPARE(p->isEnabled(), false); p->setEnabled(true); QCOMPARE(p->isEnabled(), true); } QTEST_MAIN(Buteo::StorageProfileTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/SyncScheduleTest.cpp0000644000015600001650000001614312634332753031525 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncScheduleTest.h" #include "SyncSchedule.h" #include "SyncSchedule_p.h" #include using namespace Buteo; static const QString SCHEDULE_XML = "" "" ""; void SyncScheduleTest::testConstruction() { // Create from scratch. SyncSchedule s; QCOMPARE(s.interval(), (unsigned)0); QCOMPARE(s.time().isNull(), true); QCOMPARE(s.days().isEmpty(), true); QCOMPARE(s.rushEnabled(), false); QCOMPARE(s.rushInterval(), (unsigned)0); QCOMPARE(s.rushDays().isEmpty(), true); // Create from XML. QDomDocument doc; QVERIFY(doc.setContent(SCHEDULE_XML, false)); SyncSchedule s2(doc.documentElement()); QCOMPARE(s2.interval(), (unsigned)30); QVERIFY(s2.time() == QTime(12, 34, 56, 0)); DaySet days = s2.days(); QCOMPARE(days.contains(Qt::Monday), true); QCOMPARE(days.contains(Qt::Tuesday), true); QCOMPARE(days.contains(Qt::Wednesday), true); QCOMPARE(days.contains(Qt::Thursday), true); QCOMPARE(days.contains(Qt::Friday), true); QCOMPARE(days.contains(Qt::Saturday), true); QCOMPARE(days.contains(Qt::Sunday), false); QCOMPARE(s2.rushEnabled(), true); QCOMPARE(s2.rushInterval(), (unsigned)15); QVERIFY(s2.rushBegin() == QTime(8, 0, 0, 0)); QVERIFY(s2.rushEnd() == QTime(16, 0, 0, 0)); DaySet rushDays = s2.rushDays(); QCOMPARE(rushDays.contains(Qt::Monday), true); QCOMPARE(rushDays.contains(Qt::Tuesday), false); QCOMPARE(rushDays.contains(Qt::Wednesday), false); QCOMPARE(rushDays.contains(Qt::Thursday), true); QCOMPARE(rushDays.contains(Qt::Friday), true); QCOMPARE(rushDays.contains(Qt::Saturday), false); QCOMPARE(rushDays.contains(Qt::Sunday), false); // Copy constructor. SyncSchedule s3(s2); QDomDocument doc2; QDomDocument doc3; doc2.appendChild(s2.toXml(doc2)); doc3.appendChild(s3.toXml(doc3)); QVERIFY(doc2.toString().size() >= SCHEDULE_XML.size()); QCOMPARE(doc2.toString(), doc3.toString()); } void SyncScheduleTest::testProperties() { SyncSchedule s; DaySet d; d.insert(Qt::Tuesday); d.insert(Qt::Saturday); s.setDays(d); QVERIFY(s.days() == d); QTime t(1, 2, 3, 0); s.setTime(t); QVERIFY(s.time() == t); unsigned interval = 20; s.setInterval(interval); QVERIFY(s.interval() == interval); s.setRushEnabled(true); QVERIFY(s.rushEnabled() == true); d.insert(Qt::Wednesday); s.setRushDays(d); QVERIFY(s.rushDays() == d); QTime rushBegin(8, 0, 0, 0); QTime rushEnd(16, 0, 0, 0); s.setRushTime(rushBegin, rushEnd); QVERIFY(s.rushBegin() == rushBegin); QVERIFY(s.rushEnd() == rushEnd); unsigned rushInterval = 5; s.setRushInterval(rushInterval); QVERIFY(s.rushInterval() == rushInterval); } void SyncScheduleTest::testNextSyncTime() { QEXPECT_FAIL("", "Implementation of SyncSchedule has changed so that it _currently_ does not " "allow to use the approach originally used by this test case", Abort); QVERIFY(false); const unsigned INTERVAL = 30; const unsigned RUSH_INTERVAL = 10; SyncSchedule s; QDateTime previous(QDate(2009, 10, 7), QTime(11, 0, 0, 0)); QDateTime now(QDate(2009, 10, 7), QTime(12, 0, 0, 0)); // No schedule settings. QVERIFY(s.nextSyncTime(previous).isNull()); // Exact time. QTime exact(15, 0, 0, 0); s.setTime(exact); QDateTime next = s.nextSyncTime(previous); QVERIFY(next.isNull()); DaySet days; days.insert(Qt::Wednesday); days.insert(Qt::Monday); s.setDays(days); next = s.nextSyncTime(previous); QVERIFY(!next.isNull()); QVERIFY(next.date() == now.date()); QVERIFY(next.time() == exact); now.setTime(QTime(15, 0, 1, 0)); next = s.nextSyncTime(previous); QVERIFY(next.date() == now.date().addDays(5)); QVERIFY(next.time() == exact); // Interval. s.setTime(QTime()); s.setInterval(INTERVAL); next = s.nextSyncTime(previous); QVERIFY(next == previous.addSecs(INTERVAL * 60)); // Interval, no previous sync. next = s.nextSyncTime(QDateTime()); QVERIFY(next == now); // Interval, sync missed. next = s.nextSyncTime(previous); // now = previous + 1h QVERIFY(next == now); // Interval, across day boundary, disabled days in the middle. previous.setTime(QTime(23, 50, 0, 0)); next = s.nextSyncTime(previous); QVERIFY(next.date() == previous.addDays(5).date()); // Disabled days are skipped. QVERIFY(next.time() == QTime(0, 0, 0, 0)); // Sync as soon as day starts. // Rush enabled, no effect. DaySet rushDays; rushDays.insert(Qt::Monday); rushDays.insert(Qt::Friday); s.setRushDays(rushDays); s.setRushEnabled(true); s.setRushTime(QTime(8, 0, 0, 0), QTime(16, 0, 0, 0)); s.setRushInterval(RUSH_INTERVAL); previous.setTime(QTime(11, 50, 0, 0)); next = s.nextSyncTime(previous); QVERIFY(next == previous.addSecs(INTERVAL * 60)); // No previous sync. s.setInterval(0); next = s.nextSyncTime(QDateTime()); QVERIFY(next == now); s.setInterval(INTERVAL); // Currently in rush, sync missed. now.setDate(QDate(2009, 10, 9)); next = s.nextSyncTime(previous); QVERIFY(next == now); // Currently in rush, previous in rush. now.setTime(QTime(12, 0, 0, 0)); QVERIFY(s.d_ptr->isRush(now)); next = s.nextSyncTime(now); QCOMPARE(next, now.addSecs(RUSH_INTERVAL * 60)); // Currently in rush, next out of rush. now.setTime(QTime(15, 55, 0, 0)); next = s.nextSyncTime(now); QCOMPARE(next.date().dayOfWeek(), (int)Qt::Monday); QCOMPARE(next.time(), QTime(0, 0, 0, 0)); s.setInterval(0); next = s.nextSyncTime(now); QCOMPARE(next.date().dayOfWeek(), (int)Qt::Monday); QCOMPARE(next.time(), s.rushBegin()); // Not currently in rush. now.setTime(QTime(6, 0, 0, 0)); next = s.nextSyncTime(now); QCOMPARE(next.date(), now.date()); QCOMPARE(next.time(), s.rushBegin()); now.setTime(QTime(17, 0, 0, 0)); next = s.nextSyncTime(now); QCOMPARE(next.date().dayOfWeek(), (int)Qt::Monday); QCOMPARE(next.time(), s.rushBegin()); } QTEST_MAIN(Buteo::SyncScheduleTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/ProfileManagerTest.h0000644000015600001650000000271712634332753031476 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef PROFILEMANAGERTEST_H #define PROFILEMANAGERTEST_H #include namespace Buteo { class ProfileManagerTest: public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void testGetProfile(); void testGetSyncProfile(); void testGetByData(); void testGetBySingleCriteria(); void testGetByMultipleCriteria(); void testGetByStorage(); void testLog(); void testSave(); void testHiddenProfiles(); void testRemovingProfiles(); void testOverrideKey(); void testBackup(); }; } #endif // PROFILEMANAGERTEST_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/SyncScheduleTest.pro0000644000015600001650000000004012634332753031530 0ustar pbuserpbgroup00000000000000include(../testapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/ProfileFieldTest.pro0000644000015600001650000000004012634332753031503 0ustar pbuserpbgroup00000000000000include(../testapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/ProfileManagerTest.cpp0000644000015600001650000005560412634332753032034 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ProfileManagerTest.h" #include "ProfileManager.h" #include "Profile_p.h" #include "ProfileEngineDefs.h" #include "StorageProfile.h" #include "SyncResults.h" #include #include using namespace Buteo; static const QString HCALENDAR = "hcalendar"; static const QString OVI_CALENDAR = "ovi-calendar"; static const QString CORRUPTED = "corrupted"; static const QString SYNCML = "syncml"; static const QString USERPROFILE_DIR = "syncprofiletests/testprofiles/user"; static const QString SYSTEMPROFILE_DIR = "syncprofiletests/testprofiles/system"; void ProfileManagerTest::initTestCase() { } void ProfileManagerTest::cleanupTestCase() { } void ProfileManagerTest::testGetProfile() { ProfileManager pm(USERPROFILE_DIR, USERPROFILE_DIR); // Storage profiles exist. QStringList names = pm.profileNames(Profile::TYPE_STORAGE); QVERIFY(!names.isEmpty()); QVERIFY(names.contains(HCALENDAR)); // No profiles for unknown type. QVERIFY(pm.profileNames("unknown").isEmpty()); // Getting unknown profile returns 0. QVERIFY(pm.profile("unknown", Profile::TYPE_SYNC) == 0); // Get a storage profile. QScopedPointer p(pm.profile(HCALENDAR, Profile::TYPE_STORAGE)); QVERIFY(p != 0); QCOMPARE(p->name(), HCALENDAR); QCOMPARE(p->type(), Profile::TYPE_STORAGE); QVERIFY(dynamic_cast(p.data()) != 0); // Getting unknown sync profile returns 0. QVERIFY(pm.syncProfile("unknown") == 0); // Get a sync profile. QScopedPointer sp(pm.profile(OVI_CALENDAR, Profile::TYPE_SYNC)); QVERIFY(sp != 0); QCOMPARE(sp->name(), OVI_CALENDAR); QCOMPARE(sp->type(), Profile::TYPE_SYNC); // No merged keys & fields before expanding. QCOMPARE(sp->isLoaded(), false); Profile *sub = sp->subProfile(HCALENDAR, Profile::TYPE_STORAGE); QVERIFY(sub != 0); QCOMPARE(sub->isLoaded(), false); QCOMPARE(sub->name(), HCALENDAR); QCOMPARE(sub->type(), Profile::TYPE_STORAGE); QCOMPARE(sub->key("Local URI"), QString()); // Merged keys & fields available after expanding. pm.expand(*sp); QCOMPARE(sp->isLoaded(), true); QCOMPARE(sub->isLoaded(), true); QCOMPARE(sub->key("Local URI"), QString("./Calendar")); } void ProfileManagerTest::testGetSyncProfile() { ProfileManager pm(USERPROFILE_DIR + '/', SYSTEMPROFILE_DIR + '/'); // Getting unknown sync profile returns 0. QVERIFY(pm.syncProfile("unknown") == 0); // Get a sync profile. QScopedPointer sp(pm.syncProfile(OVI_CALENDAR)); QVERIFY(sp != 0); QCOMPARE(sp->name(), OVI_CALENDAR); QCOMPARE(sp->type(), Profile::TYPE_SYNC); // Sync profile is expanded automatically. QCOMPARE(sp->isLoaded(), true); Profile *sub = sp->subProfile(SYNCML, Profile::TYPE_CLIENT); QVERIFY(sub != 0); sub = sp->subProfile(HCALENDAR, Profile::TYPE_STORAGE); QVERIFY(sub != 0); QCOMPARE(sub->key("Local URI"), QString("./Calendar")); // Get all sync profiles. QList allProfiles = pm.allSyncProfiles(); QVERIFY(!allProfiles.isEmpty()); QCOMPARE(allProfiles.first()->name(), OVI_CALENDAR); QCOMPARE(allProfiles.first()->type(), Profile::TYPE_SYNC); foreach (SyncProfile *p, allProfiles) { delete p; } allProfiles.clear(); } void ProfileManagerTest::testGetByData() { ProfileManager pm(USERPROFILE_DIR, USERPROFILE_DIR); QList profiles; // Get profiles with non-existent key. profiles = pm.getSyncProfilesByData("", "", "unknown"); QVERIFY(profiles.isEmpty()); // Get profiles with existing key, defined value, no match. profiles = pm.getSyncProfilesByData("", "", "enabled", "false"); QVERIFY(profiles.isEmpty()); // Get profiles with existing key, undefined value. profiles = pm.getSyncProfilesByData("", "", "enabled"); QVERIFY(!profiles.isEmpty()); QVERIFY(profiles[0] != 0); foreach (SyncProfile *p, profiles) { delete p; } profiles.clear(); // Get profiles with existing key, defined value, match. profiles = pm.getSyncProfilesByData("", "", "enabled", "true"); QVERIFY(!profiles.isEmpty()); QVERIFY(profiles[0] != 0); QCOMPARE(profiles[0]->key("enabled"), QString("true")); foreach (SyncProfile *p, profiles) { delete p; } profiles.clear(); // Get profiles by sub-profile information, no match. profiles = pm.getSyncProfilesByData("unknown", Profile::TYPE_STORAGE); QVERIFY(profiles.isEmpty()); // Get profiles by sub-profile information, key and value defined, no match. profiles = pm.getSyncProfilesByData(HCALENDAR, Profile::TYPE_STORAGE, "Target URI", "unknown"); QVERIFY(profiles.isEmpty()); // Get profiles by sub-profile information, sub-profile name not defined, // key and value defined, no match. profiles = pm.getSyncProfilesByData("", Profile::TYPE_STORAGE, "Target URI", "unknown"); QVERIFY(profiles.isEmpty()); // Get profiles by sub-profile information, no key defined. profiles = pm.getSyncProfilesByData(HCALENDAR, Profile::TYPE_STORAGE); QVERIFY(!profiles.isEmpty()); QVERIFY(profiles[0] != 0); QCOMPARE(profiles[0]->name(), OVI_CALENDAR); foreach (SyncProfile *p, profiles) { delete p; } profiles.clear(); // Get profiles by sub-profile information, key and value defined. profiles = pm.getSyncProfilesByData(HCALENDAR, Profile::TYPE_STORAGE, "Target URI", "./EventTask/Tasks"); QVERIFY(!profiles.isEmpty()); QVERIFY(profiles[0] != 0); QCOMPARE(profiles[0]->name(), OVI_CALENDAR); foreach (SyncProfile *p, profiles) { delete p; } profiles.clear(); // Get profiles by sub-profile information, sub-profile name not defined, // key and value defined. profiles = pm.getSyncProfilesByData("", Profile::TYPE_STORAGE, "Target URI", "./EventTask/Tasks"); QVERIFY(!profiles.isEmpty()); QVERIFY(profiles[0] != 0); QCOMPARE(profiles[0]->name(), OVI_CALENDAR); foreach (SyncProfile *p, profiles) { delete p; } profiles.clear(); } void ProfileManagerTest::testGetBySingleCriteria() { ProfileManager pm(USERPROFILE_DIR, USERPROFILE_DIR); QList profiles; // Get profiles with non-existent key. ProfileManager::SearchCriteria criteria; QList criteriaList; criteria.iType = ProfileManager::SearchCriteria::EXISTS; criteria.iKey = "unknown"; criteriaList.append(criteria); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(profiles.isEmpty()); // Get profiles with existing key, defined value, no match. criteria.iType = ProfileManager::SearchCriteria::EQUAL; criteria.iKey = "enabled"; criteria.iValue = "false"; criteriaList.clear(); criteriaList.append(criteria); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(profiles.isEmpty()); // Get profiles with existing key, undefined value. criteria.iType = ProfileManager::SearchCriteria::EXISTS; criteria.iKey = "enabled"; criteria.iValue = QString::null; criteriaList.clear(); criteriaList.append(criteria); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(!profiles.isEmpty()); QVERIFY(profiles[0] != 0); foreach (SyncProfile *p, profiles) { delete p; } profiles.clear(); // Get profiles with existing key, defined value, match. criteria.iType = ProfileManager::SearchCriteria::EQUAL; criteria.iKey = "enabled"; criteria.iValue = "true"; criteriaList.clear(); criteriaList.append(criteria); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(!profiles.isEmpty()); QVERIFY(profiles[0] != 0); QCOMPARE(profiles[0]->key("enabled"), QString("true")); foreach (SyncProfile *p, profiles) { delete p; } profiles.clear(); // Get profiles by sub-profile information, no match. criteria.iType = ProfileManager::SearchCriteria::EXISTS; criteria.iSubProfileName = "unknown"; criteria.iSubProfileType = Profile::TYPE_STORAGE; criteria.iKey = QString::null; criteria.iValue = QString::null; criteriaList.clear(); criteriaList.append(criteria); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(profiles.isEmpty()); // Get profiles by sub-profile. criteria.iType = ProfileManager::SearchCriteria::EXISTS; criteria.iSubProfileName = HCALENDAR; criteria.iSubProfileType = Profile::TYPE_STORAGE; criteria.iKey = QString::null; criteria.iValue = QString::null; criteriaList.clear(); criteriaList.append(criteria); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(!profiles.isEmpty()); QVERIFY(profiles[0] != 0); QCOMPARE(profiles[0]->name(), OVI_CALENDAR); foreach (SyncProfile *p, profiles) { delete p; } profiles.clear(); // Get profiles by sub-profile, no match. criteria.iType = ProfileManager::SearchCriteria::NOT_EXISTS; criteria.iSubProfileName = HCALENDAR; criteria.iSubProfileType = Profile::TYPE_STORAGE; criteria.iKey = QString::null; criteria.iValue = QString::null; criteriaList.clear(); criteriaList.append(criteria); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(profiles.isEmpty()); // Get profiles by sub-profile. criteria.iType = ProfileManager::SearchCriteria::NOT_EXISTS; criteria.iSubProfileName = "unknown"; criteria.iSubProfileType = Profile::TYPE_STORAGE; criteria.iKey = QString::null; criteria.iValue = QString::null; criteriaList.clear(); criteriaList.append(criteria); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(!profiles.isEmpty()); QVERIFY(profiles[0] != 0); foreach (SyncProfile *p, profiles) { delete p; } profiles.clear(); // Get profiles by sub-profile information, key and value defined, no match. criteria.iType = ProfileManager::SearchCriteria::EQUAL; criteria.iSubProfileName = HCALENDAR; criteria.iSubProfileType = Profile::TYPE_STORAGE; criteria.iKey = "Target URI"; criteria.iValue = "unknown"; criteriaList.clear(); criteriaList.append(criteria); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(profiles.isEmpty()); // Get profiles by sub-profile information, sub-profile name not defined, // key and value defined, no match. criteria.iType = ProfileManager::SearchCriteria::EQUAL; criteria.iSubProfileName = QString::null; criteria.iSubProfileType = Profile::TYPE_STORAGE; criteria.iKey = "Target URI"; criteria.iValue = "unknown"; criteriaList.clear(); criteriaList.append(criteria); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(profiles.isEmpty()); // Get profiles by sub-profile information, no key defined. criteria.iType = ProfileManager::SearchCriteria::EXISTS; criteria.iSubProfileName = HCALENDAR; criteria.iSubProfileType = Profile::TYPE_STORAGE; criteria.iKey = QString::null; criteria.iValue = QString::null; criteriaList.clear(); criteriaList.append(criteria); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(!profiles.isEmpty()); QVERIFY(profiles[0] != 0); QCOMPARE(profiles[0]->name(), OVI_CALENDAR); foreach (SyncProfile *p, profiles) { delete p; } profiles.clear(); // Get profiles by sub-profile information, key and value defined. criteria.iType = ProfileManager::SearchCriteria::EQUAL; criteria.iSubProfileName = HCALENDAR; criteria.iSubProfileType = Profile::TYPE_STORAGE; criteria.iKey = "Target URI"; criteria.iValue = "./EventTask/Tasks"; criteriaList.clear(); criteriaList.append(criteria); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(!profiles.isEmpty()); QVERIFY(profiles[0] != 0); QCOMPARE(profiles[0]->name(), OVI_CALENDAR); foreach (SyncProfile *p, profiles) { delete p; } profiles.clear(); // Get profiles by sub-profile information, key and value defined, no match. criteria.iType = ProfileManager::SearchCriteria::NOT_EQUAL; criteria.iSubProfileName = HCALENDAR; criteria.iSubProfileType = Profile::TYPE_STORAGE; criteria.iKey = "Target URI"; criteria.iValue = "./EventTask/Tasks"; criteriaList.clear(); criteriaList.append(criteria); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(profiles.isEmpty()); // Get profiles by sub-profile information, key and value defined. criteria.iType = ProfileManager::SearchCriteria::NOT_EQUAL; criteria.iSubProfileName = HCALENDAR; criteria.iSubProfileType = Profile::TYPE_STORAGE; criteria.iKey = "Target URI"; criteria.iValue = "foobar"; criteriaList.clear(); criteriaList.append(criteria); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(!profiles.isEmpty()); QVERIFY(profiles[0] != 0); foreach (SyncProfile *p, profiles) { delete p; } profiles.clear(); // Get profiles by sub-profile information, sub-profile name not defined, // key and value defined. criteria.iType = ProfileManager::SearchCriteria::EQUAL; criteria.iSubProfileName = QString::null; criteria.iSubProfileType = Profile::TYPE_STORAGE; criteria.iKey = "Target URI"; criteria.iValue = "./EventTask/Tasks"; criteriaList.clear(); criteriaList.append(criteria); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(!profiles.isEmpty()); QVERIFY(profiles[0] != 0); QCOMPARE(profiles[0]->name(), OVI_CALENDAR); foreach (SyncProfile *p, profiles) { delete p; } profiles.clear(); } void ProfileManagerTest::testGetByMultipleCriteria() { ProfileManager pm(USERPROFILE_DIR, USERPROFILE_DIR); QList profiles; QList criteriaList; // Profile must be enabled. ProfileManager::SearchCriteria criteria1; criteria1.iType = ProfileManager::SearchCriteria::EQUAL; criteria1.iKey = KEY_ENABLED; criteria1.iValue = BOOLEAN_TRUE; criteriaList.append(criteria1); // Profile must not be hidden. ProfileManager::SearchCriteria criteria2; criteria2.iType = ProfileManager::SearchCriteria::NOT_EQUAL; criteria2.iKey = KEY_HIDDEN; criteria2.iValue = BOOLEAN_TRUE; criteriaList.append(criteria2); // Profile must have a hcalendar storage. ProfileManager::SearchCriteria criteria3; criteria3.iType = ProfileManager::SearchCriteria::EXISTS; criteria3.iSubProfileName = "hcalendar"; criteria3.iSubProfileType = Profile::TYPE_STORAGE; criteriaList.append(criteria3); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(!profiles.isEmpty()); QVERIFY(profiles[0] != 0); QCOMPARE(profiles[0]->name(), OVI_CALENDAR); foreach (SyncProfile *p, profiles) { delete p; } profiles.clear(); // Add a fourth criteria, that doesn't match to any profile. ProfileManager::SearchCriteria criteria4; criteria4.iType = ProfileManager::SearchCriteria::EQUAL; criteria4.iSubProfileName = "hcalendar"; criteria4.iSubProfileType = Profile::TYPE_STORAGE; criteria4.iKey = "Notebook Name"; criteria4.iValue = "Personal"; criteriaList.append(criteria4); profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(profiles.isEmpty()); // Replace the fourth criteria with a criteria that matches. criteria4.iValue = "myNotebook"; criteriaList[3] = criteria4; profiles = pm.getSyncProfilesByData(criteriaList); QVERIFY(!profiles.isEmpty()); QVERIFY(profiles[0] != 0); QCOMPARE(profiles[0]->name(), OVI_CALENDAR); foreach (SyncProfile *p, profiles) { delete p; } profiles.clear(); } void ProfileManagerTest::testGetByStorage() { ProfileManager pm(USERPROFILE_DIR, USERPROFILE_DIR); QList profiles; // Get profiles by storage. profiles = pm.getSyncProfilesByStorage("hcalendar", true); QVERIFY(!profiles.isEmpty()); QVERIFY(profiles[0] != 0); QCOMPARE(profiles[0]->name(), OVI_CALENDAR); foreach (SyncProfile *p, profiles) { delete p; } profiles.clear(); } void ProfileManagerTest::testLog() { ProfileManager pm(USERPROFILE_DIR, USERPROFILE_DIR); { QScopedPointer p(pm.syncProfile(OVI_CALENDAR)); QVERIFY(p != 0); // Create log with some content. p->setLog(new SyncLog(OVI_CALENDAR)); QDateTime now = QDateTime::currentDateTime(); SyncResults syncResults(now, Buteo::SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::INTERNAL_ERROR); syncResults.addTargetResults(TargetResults("hcalendar", ItemCounts(1, 2, 3), ItemCounts(4, 5, 6))); p->addResults(syncResults); // Save log. pm.saveLog(*p->log()); } // Load profile. Log is loaded also. { QScopedPointer p(pm.syncProfile(OVI_CALENDAR)); QVERIFY(p != 0); const SyncLog *loadedLog = p->log(); QVERIFY(loadedLog != 0); QCOMPARE(loadedLog->profileName(), OVI_CALENDAR); QVERIFY(p->lastResults() != 0); QCOMPARE(p->lastResults()->majorCode(), 1); QList targetResults = p->lastResults()->targetResults(); QCOMPARE(targetResults.size(), 1); QCOMPARE(targetResults[0].targetName(), QString("hcalendar")); QCOMPARE(targetResults[0].localItems().added, (unsigned)1); QCOMPARE(targetResults[0].localItems().deleted, (unsigned)2); QCOMPARE(targetResults[0].localItems().modified, (unsigned)3); QCOMPARE(targetResults[0].remoteItems().added, (unsigned)4); QCOMPARE(targetResults[0].remoteItems().deleted, (unsigned)5); QCOMPARE(targetResults[0].remoteItems().modified, (unsigned)6); } // Save results through ProfileManager. { QCOMPARE(QFile::remove( USERPROFILE_DIR + "/sync/logs/" + OVI_CALENDAR + ".log.xml"), true); SyncResults syncResults(QDateTime::currentDateTime(), Buteo::SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::INTERNAL_ERROR); syncResults.setMajorCode(Buteo::SyncResults::SYNC_RESULT_SUCCESS); pm.saveSyncResults(OVI_CALENDAR, syncResults); QScopedPointer p(pm.syncProfile(OVI_CALENDAR)); QVERIFY(p != 0); const SyncLog *log = p->log(); QVERIFY(log != 0); QVERIFY(log->lastResults() != 0); QVERIFY(log->lastResults()->majorCode() == Buteo::SyncResults::SYNC_RESULT_SUCCESS); } } void ProfileManagerTest::testSave() { ProfileManager pm(USERPROFILE_DIR + "/primary", USERPROFILE_DIR); { QScopedPointer p(pm.syncProfile(OVI_CALENDAR)); QVERIFY(p != 0); QCOMPARE(p->isEnabled(), true); p->setEnabled(false); pm.updateProfile(*p); } { QScopedPointer p(pm.syncProfile(OVI_CALENDAR)); QVERIFY(p != 0); QCOMPARE(p->isEnabled(), false); // Profile file in secondary directory is not affected. { ProfileManager pm2(USERPROFILE_DIR, USERPROFILE_DIR); QScopedPointer p2(pm2.syncProfile(OVI_CALENDAR)); QVERIFY(p2 != 0); QCOMPARE(p2->isEnabled(), true); } p->setEnabled(true); pm.updateProfile(*p); } } void ProfileManagerTest::testHiddenProfiles() { ProfileManager pm(USERPROFILE_DIR, USERPROFILE_DIR); // Get number of visible sync profiles. QList profiles = pm.allVisibleSyncProfiles(); int num_profiles = profiles.size(); qDeleteAll(profiles); profiles.clear(); // Make one of the profiles hidden. QScopedPointer p(pm.syncProfile(OVI_CALENDAR)); QVERIFY(p != 0); p->setBoolKey(KEY_HIDDEN, true); pm.updateProfile(*p); // Verify that number of visible profiles is reduced. profiles = pm.allVisibleSyncProfiles(); QCOMPARE(profiles.size(), num_profiles - 1); qDeleteAll(profiles); profiles.clear(); // Make profile visible again. p->removeKey(KEY_HIDDEN); pm.updateProfile(*p); } void ProfileManagerTest::testRemovingProfiles() { ProfileManager pm(USERPROFILE_DIR, USERPROFILE_DIR); // Create a temporary profile that can be deleted. const QString TEMP_NAME = "TempProfile"; QScopedPointer p(pm.syncProfile(OVI_CALENDAR)); QVERIFY(p != 0); p->setName(TEMP_NAME); pm.updateProfile(*p); // Try removing protected profile. p->setBoolKey(KEY_PROTECTED, true); pm.updateProfile(*p); QCOMPARE(pm.removeProfile(TEMP_NAME), false); // Disable protectiong and remove profile. p->removeKey(KEY_PROTECTED); pm.updateProfile(*p); QCOMPARE(pm.removeProfile(TEMP_NAME), true); } void ProfileManagerTest::testOverrideKey() { ProfileManager pm(USERPROFILE_DIR, USERPROFILE_DIR); QScopedPointer p(static_cast( pm.profile(OVI_CALENDAR, Profile::TYPE_SYNC))); QVERIFY(p != 0); Profile *storage = p->subProfile(HCALENDAR, Profile::TYPE_STORAGE); QVERIFY(storage != 0); // Set target URI to the main profile (storage section). const QString URI_KEY = "Target URI"; const QString URI = "./new/uri"; storage->setKey(URI_KEY, URI); // Service sub-profile file contains a target uri also, but the key defined // in the main profile overrides it. pm.expand(*p); QCOMPARE(storage->key(URI_KEY), URI); } void ProfileManagerTest::testBackup() { ProfileManager pm(USERPROFILE_DIR + '/', SYSTEMPROFILE_DIR + '/'); // Copy to backup. QString fileName = USERPROFILE_DIR + '/' + Profile::TYPE_SYNC + '/' + OVI_CALENDAR + ".xml"; QFile file(fileName); QVERIFY(file.copy(fileName + ".bak")); // Get a sync profile. Profile is restored from a backup. QScopedPointer sp(pm.syncProfile(OVI_CALENDAR)); QVERIFY(sp != 0); QCOMPARE(sp->name(), OVI_CALENDAR); QCOMPARE(sp->type(), Profile::TYPE_SYNC); // Backup is removed after successful load. QVERIFY(!QFile::exists(fileName + ".bak")); } QTEST_MAIN(Buteo::ProfileManagerTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/ProfileFieldTest.h0000644000015600001650000000210512634332753031136 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef PROFILEFIELDTEST_H #define PROFILEFIELDTEST_H #include namespace Buteo { class ProfileFieldTest: public QObject { Q_OBJECT private slots: void testField(); }; } #endif // PROFILEFIELDTEST_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/syncprofiletests.pro0000644000015600001650000000276312634332753031735 0ustar pbuserpbgroup00000000000000include(../tests_common.pri) TEMPLATE = subdirs SUBDIRS = \ ProfileFactoryTest.pro \ ProfileFieldTest.pro \ ProfileManagerTest.pro \ ProfileTest.pro \ StorageProfileTest.pro \ SyncLogTest.pro \ SyncProfileTest.pro \ SyncScheduleTest.pro \ testprofiles_client.files = testprofiles/user/client/* testprofiles_client.path = $${INSTALL_TESTDIR}/syncprofiletests/testprofiles/user/client/ testprofiles_storage.files = testprofiles/user/storage/* testprofiles_storage.path = $${INSTALL_TESTDIR}/syncprofiletests/testprofiles/user/storage/ testprofiles_sync.files = testprofiles/user/sync/* testprofiles_sync.path = $${INSTALL_TESTDIR}/syncprofiletests/testprofiles/user/sync/ testprofiles_syssync.files = testprofiles/system/sync/* testprofiles_syssync.path = $${INSTALL_TESTDIR}/syncprofiletests/testprofiles/system/sync/ testprofiles_expstorage.files = testprofiles/expected/storage/* testprofiles_expstorage.path = $${INSTALL_TESTDIR}/syncprofiletests/testprofiles/expected/storage/ testprofiles_expsync.files = testprofiles/expected/sync/* testprofiles_expsync.path = $${INSTALL_TESTDIR}/syncprofiletests/testprofiles/expected/sync/ testaccount_service.files = testprofiles/testsync-ovi.service testaccount_service.path = /usr/share/accounts/services/ INSTALLS += \ testaccount_service \ testprofiles_client \ testprofiles_storage \ testprofiles_sync \ testprofiles_syssync \ testprofiles_expstorage \ testprofiles_expsync \ buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncprofiletests/SyncProfileTest.pro0000644000015600001650000000004012634332753031374 0ustar pbuserpbgroup00000000000000include(../testapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/pluginmanagertests/0000755000015600001650000000000012634333235026053 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/pluginmanagertests/ClientPluginTest.cpp0000644000015600001650000000400512634332753032017 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ClientPluginTest.h" #include "PluginManager.h" #include "SyncProfile.h" using namespace Buteo; void ClientPluginTest::testCreateDestroy() { QDir dir = QDir(QCoreApplication::applicationDirPath() + "/.."); QString path = dir.absolutePath(); if (dir.cd("../dummyplugins/dummyclient")) { path = dir.absolutePath(); } // no else PluginManager pluginManager( path ); SyncProfile profile( "dummyprofile" ); ClientPlugin* client1 = pluginManager.createClient( "hdummy", profile, this ); QVERIFY( client1 ); QVERIFY( pluginManager.iLoadedDlls.count() == 1 ); ClientPlugin* client2 = pluginManager.createClient( "hdummy", profile, this ); QVERIFY( client2 ); QVERIFY( pluginManager.iLoadedDlls.count() == 1 ); pluginManager.destroyClient( client1 ); QVERIFY( pluginManager.iLoadedDlls.count() == 1 ); pluginManager.destroyClient( client2 ); QEXPECT_FAIL("", "According to a comment in PluginManager.cpp: KLUDGE: Due to NB #169065, crashes are seen in QMetaType if we unload DLLs. Hence commenting", Continue); QVERIFY( pluginManager.iLoadedDlls.count() == 0 ); } QTEST_MAIN(Buteo::ClientPluginTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/pluginmanagertests/StoragePluginTest.cpp0000644000015600001650000000366212634332753032215 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "StoragePluginTest.h" #include "PluginManager.h" using namespace Buteo; void StoragePluginTest::testCreateDestroy() { QDir dir = QDir(QCoreApplication::applicationDirPath() + "/.."); QString path = dir.absolutePath(); if (dir.cd("../dummyplugins/dummystorage/")) { path = dir.absolutePath(); } // no else PluginManager pluginManager( path ); StoragePlugin* storage1 = pluginManager.createStorage( "hdummy" ); QVERIFY( storage1 ); QVERIFY( pluginManager.iLoadedDlls.count() == 1 ); StoragePlugin* storage2 = pluginManager.createStorage( "hdummy" ); QVERIFY( storage2 ); QVERIFY( pluginManager.iLoadedDlls.count() == 1 ); pluginManager.destroyStorage( storage1 ); QVERIFY( pluginManager.iLoadedDlls.count() == 1 ); pluginManager.destroyStorage(storage2 ); QEXPECT_FAIL("", "According to a comment in PluginManager.cpp: KLUDGE: Due to NB #169065, crashes are seen in QMetaType if we unload DLLs. Hence commenting", Continue); QVERIFY( pluginManager.iLoadedDlls.count() == 0 ); } QTEST_MAIN(Buteo::StoragePluginTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/pluginmanagertests/ServerPluginTest.h0000644000015600001650000000406312634332753031520 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SERVERPLUGINTEST_H #define SERVERPLUGINTEST_H #include #include "PluginCbInterface.h" namespace Buteo { class ServerPluginTest : public QObject, public PluginCbInterface { Q_OBJECT; public: virtual bool requestStorage(const QString &/*aStorageName*/, const SyncPluginBase */*aCaller*/) { return false; } virtual void releaseStorage(const QString &/*aStorageName*/, const SyncPluginBase */*aCaller*/) { } virtual StoragePlugin* createStorage(const QString &/*aPluginName*/) { return NULL; } virtual void destroyStorage(StoragePlugin */*aStorage*/) { } virtual QString getDeviceIMEI() { return QString( "000000000000000" ); } virtual bool isConnectivityAvailable( Sync::ConnectivityType /*aType*/ ) { return false; } virtual Profile* getSyncProfileByRemoteAddress(const QString& aAddress) { Q_UNUSED(aAddress); return 0; } virtual QString getValue(const QString& aAddress, const QString& aKey) { Q_UNUSED(aAddress); Q_UNUSED(aKey); return ""; } private slots: void testCreateDestroy(); private: }; } #endif buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/pluginmanagertests/ClientPluginTest.pro0000644000015600001650000000004012634332753032030 0ustar pbuserpbgroup00000000000000include(../testapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/pluginmanagertests/ServerPluginTest.pro0000644000015600001650000000004012634332753032060 0ustar pbuserpbgroup00000000000000include(../testapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/pluginmanagertests/StoragePluginTest.h0000644000015600001650000000210012634332753031644 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef STORAGEPLUGINTEST_H #define STORAGEPLUGINTEST_H #include namespace Buteo { class StoragePluginTest : public QObject { Q_OBJECT private slots: void testCreateDestroy(); private: }; } #endif buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/pluginmanagertests/StoragePluginTest.pro0000644000015600001650000000004012634332753032216 0ustar pbuserpbgroup00000000000000include(../testapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/pluginmanagertests/ServerPluginTest.cpp0000644000015600001650000000400412634332753032046 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ServerPluginTest.h" #include "PluginManager.h" #include "SyncProfile.h" using namespace Buteo; void ServerPluginTest::testCreateDestroy() { QDir dir = QDir(QCoreApplication::applicationDirPath() + "/.."); QString path = dir.absolutePath(); if (dir.cd("../dummyplugins/dummyserver")) { path = dir.absolutePath(); } // no else PluginManager pluginManager( path ); SyncProfile profile( "dummyprofile" ); ServerPlugin* server1 = pluginManager.createServer( "hdummy", profile, this ); QVERIFY( server1 ); QVERIFY( pluginManager.iLoadedDlls.count() == 1 ); ServerPlugin* server2 = pluginManager.createServer( "hdummy", profile, this ); QVERIFY( server2 ); QVERIFY( pluginManager.iLoadedDlls.count() == 1 ); pluginManager.destroyServer( server1 ); QVERIFY( pluginManager.iLoadedDlls.count() == 1 ); pluginManager.destroyServer( server2 ); QEXPECT_FAIL("", "According to a comment in PluginManager.cpp: KLUDGE: Due to NB #169065, crashes are seen in QMetaType if we unload DLLs. Hence commenting", Continue); QVERIFY( pluginManager.iLoadedDlls.count() == 0 ); } QTEST_MAIN(Buteo::ServerPluginTest) ././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/pluginmanagertests/DeletedItemsIdStorageTest.probuteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/pluginmanagertests/DeletedItemsIdStorageTest.pr0000644000015600001650000000004012634332753033426 0ustar pbuserpbgroup00000000000000include(../testapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/pluginmanagertests/DeletedItemsIdStorageTest.h0000644000015600001650000000241312634332753033242 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef DELETEDITEMSIDSTORAGETEST_H #define DELETEDITEMSIDSTORAGETEST_H #include #include namespace Buteo { class DeletedItemsIdStorage; class DeletedItemsIdStorageTest : public QObject { Q_OBJECT private slots: void init(); void cleanup(); void testInit(); void testItemIdStoring(); void testSnapshot(); private: DeletedItemsIdStorage* iDeletedItems; }; } #endif ././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/pluginmanagertests/DeletedItemsIdStorageTest.cppbuteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/pluginmanagertests/DeletedItemsIdStorageTest.cp0000644000015600001650000000621112634332753033415 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "DeletedItemsIdStorageTest.h" #include "DeletedItemsIdStorage.h" #include #include using namespace Buteo; const QString DBFILE("/tmp/deleteditemsidstoragetest.db"); /*! \fn DeletedItemsIdStorageTest::init() */ void DeletedItemsIdStorageTest::init() { iDeletedItems = new DeletedItemsIdStorage; } /*! \fn DeletedItemsIdStorageTest::cleanup() */ void DeletedItemsIdStorageTest::cleanup() { delete iDeletedItems; iDeletedItems = NULL; } /*! \fn DeletedItemsIdStorageTest::testInit() */ void DeletedItemsIdStorageTest::testInit() { bool initResult; bool uninitResult; initResult = iDeletedItems->init(DBFILE); QVERIFY(initResult == true); uninitResult = iDeletedItems->uninit(); QVERIFY(uninitResult == true); } /*! \fn DeletedItemsIdStorageTest::testItemIdStoring() */ void DeletedItemsIdStorageTest::testItemIdStoring() { iDeletedItems->init(DBFILE); QDateTime creationTime = QDateTime::fromTime_t(100000); QDateTime deletionTime = QDateTime::fromTime_t(20000000); QString itemId = "foo1"; iDeletedItems->addDeletedItem(itemId, creationTime, deletionTime); QDateTime fetchTime = QDateTime::fromTime_t(5000000); QList itemIdList; bool success = iDeletedItems->getDeletedItems(itemIdList, fetchTime); QVERIFY(success); QVERIFY(itemIdList.size() > 0); QVERIFY(itemIdList.first() == itemId); iDeletedItems->uninit(); } /*! \fn DeletedItemsIdStorageTest::testSnapshot() */ void DeletedItemsIdStorageTest::testSnapshot() { iDeletedItems->init(DBFILE); QDateTime now = QDateTime::currentDateTime(); // Zero milliseconds. now.setTime(now.time().addMSecs(-now.time().msec())); QList setItemIdList; setItemIdList << "foo" << "bar" << "zed"; QList setCreationTimes; setCreationTimes << now << now << now; iDeletedItems->setSnapshot(setItemIdList, setCreationTimes); QList receivedItemIdList; QList receivedCreationTimes; iDeletedItems->getSnapshot(receivedItemIdList, receivedCreationTimes); QVERIFY(setItemIdList == receivedItemIdList); QVERIFY(setCreationTimes == receivedCreationTimes); iDeletedItems->uninit(); } QTEST_MAIN(Buteo::DeletedItemsIdStorageTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/pluginmanagertests/ClientPluginTest.h0000644000015600001650000000406412634332753031471 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef CLIENTPLUGINTEST_H #define CLIENTPLUGINTEST_H #include #include "PluginCbInterface.h" namespace Buteo { class ClientPluginTest : public QObject, public PluginCbInterface { Q_OBJECT; public: virtual bool requestStorage(const QString &/*aStorageName*/, const SyncPluginBase */*aCaller*/) { return false; } virtual void releaseStorage(const QString &/*aStorageName*/, const SyncPluginBase */*aCaller*/) { } virtual StoragePlugin* createStorage(const QString &/*aPluginName*/) { return NULL; } virtual void destroyStorage(StoragePlugin */*aStorage*/) { } virtual QString getDeviceIMEI() { return QString( "000000000000000" ); } virtual bool isConnectivityAvailable( Sync::ConnectivityType /*aType*/ ) { return false; } virtual Profile* getSyncProfileByRemoteAddress(const QString& aAddress) { Q_UNUSED(aAddress); return 0; } virtual QString getValue(const QString& aAddress, const QString& aKey) { Q_UNUSED(aAddress); Q_UNUSED(aKey); return ""; } private slots: void testCreateDestroy(); private: }; } #endif buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/pluginmanagertests/pluginmanagertests.pro0000644000015600001650000000034312634332753032515 0ustar pbuserpbgroup00000000000000TEMPLATE = subdirs SUBDIRS = \ ClientPluginTest.pro \ DeletedItemsIdStorageTest.pro \ ServerPluginTest.pro \ StoragePluginTest.pro \ coverage.CONFIG += recursive QMAKE_EXTRA_TARGETS += coverage buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/sync-fw-tests.ref0000644000015600001650000000114212634332753025365 0ustar pbuserpbgroup00000000000000Totals: 3 passed, 0 failed, 0 skipped Totals: 8 passed, 0 failed, 0 skipped Totals: 3 passed, 0 failed, 0 skipped Totals: 3 passed, 0 failed, 0 skipped Totals: 3 passed, 0 failed, 0 skipped Totals: 5 passed, 0 failed, 0 skipped Totals: 7 passed, 0 failed, 0 skipped Totals: 4 passed, 0 failed, 0 skipped Totals: 3 passed, 0 failed, 0 skipped Totals: 10 passed, 0 failed, 0 skipped Totals: 3 passed, 0 failed, 0 skipped Totals: 4 passed, 0 failed, 0 skipped Totals: 10 passed, 0 failed, 0 skipped Totals: 3 passed, 0 failed, 0 skipped Totals: 3 passed, 0 failed, 0 skipped Totals: 3 passed, 0 failed, 0 skipped buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/0000755000015600001650000000000012634333235024517 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/ClientThreadTest.cpp0000644000015600001650000000745512634332753030450 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ClientThreadTest.h" #include "SyncResults.h" using namespace Buteo; const QString PROFILE = "profile"; const QString PLUGIN = "plugin"; //QString TYPE = Profile::TYPE_CLIENT; //Defining pure virtual functions of base classes bool ClientPluginDerived::startSync() { return true; } bool ClientPluginDerived::init() { if(iTestClSignal == true) { return false; } return true; } bool ClientPluginDerived::uninit() { return true; } void ClientPluginDerived::connectivityStateChanged(Sync::ConnectivityType ,bool) { } bool ClientPluginDerived::cleanUp() { return true; } //Constructor of the derived class ClientPluginDerived::ClientPluginDerived(const QString& aPluginName, const SyncProfile& aProfile, PluginCbInterface* aCbInterface) :ClientPlugin(aPluginName, aProfile, aCbInterface), iTestClSignal(false) { } void ClientThreadTest::initTestCase() { iClientThread = new ClientThread(); iSyncProfile = new SyncProfile(PROFILE); iPluginDerived = new ClientPluginDerived(PLUGIN, *iSyncProfile, NULL); iPlugin = iPluginDerived; iClientThreadRet = false; } void ClientThreadTest::cleanupTestCase() { QVERIFY(iSyncProfile != 0); delete iSyncProfile; QVERIFY(iPluginDerived != 0); delete iPluginDerived; QVERIFY(iClientThread != 0); delete iClientThread; iSyncProfile = 0; iPluginDerived = 0; iClientThread = 0; iPlugin = 0; } void ClientThreadTest::testClientThreadConstructor() { QVERIFY(iClientThread->getPlugin() == NULL); QCOMPARE(iClientThread->iRunning, false); } void ClientThreadTest::testGetPlugin() { iClientThreadRet = iClientThread->startThread(iPlugin); QTest::qWait(20); QCOMPARE(iClientThreadRet, true); QCOMPARE(iClientThread->iRunning, true); QCOMPARE(iClientThread->getPlugin(), iPlugin); } void ClientThreadTest::testGetProfileName() { QCOMPARE(iClientThreadRet, true); QCOMPARE(iClientThread->getProfileName(), PROFILE); } void ClientThreadTest::testClientThread() { //The thread is already running QCOMPARE(iClientThread->startThread(iPlugin), false); QCOMPARE(iClientThread->iRunning, true); } void ClientThreadTest::testGetSyncResults() { SyncResults syncRes; bool dateTime; QDateTime current; QVERIFY(iClientThread != 0); current = QDateTime::currentDateTime(); iClientThread->stopThread(); iClientThread->wait(9000); QCOMPARE(iClientThread->iRunning, false); syncRes = iClientThread->getSyncResults(); QCOMPARE(syncRes.majorCode(), 0); QCOMPARE(syncRes.isScheduled(), false); dateTime = syncRes.syncTime() >= current; QVERIFY(dateTime); //QCOMPARE(syncRes.syncTime().date(), QDateTime::currentDateTime().date()); } void ClientThreadTest::testInitError() { iPluginDerived->iTestClSignal = true; //Test for the signal QSignalSpy spy(iClientThread, SIGNAL(initError(const QString &, const QString &, int ))); QCOMPARE(iClientThread->startThread(iPlugin), true); QTest::qWait(20); QCOMPARE(spy.count(), 1); } QTEST_MAIN(Buteo::ClientThreadTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SyncBackupTest.pro0000644000015600001650000000004312634332753030144 0ustar pbuserpbgroup00000000000000include(msyncdtestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/IPHeartBeatTest.pro0000644000015600001650000000004312634332753030172 0ustar pbuserpbgroup00000000000000include(msyncdtestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/ServerActivatorTest.cpp0000644000015600001650000001575012634332753031222 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ServerActivatorTest.h" #include #include #include #include "Profile.h" #include #include using namespace Buteo; void ServerActivatorTest::initTestCase() { iTransportTracker = new TransportTracker(this); iProfileManager = new ProfileManager("profile1", "profile2"); // failure in next step would result in crash upon cleanupTestCase() iServerActivator = 0; // add server profiles through Profile class Profile myProfile("sampleServerProfile", Profile::TYPE_SERVER); QVERIFY(!iProfileManager->updateProfile(myProfile).isEmpty()); Profile myProfile2("sampleServerProfile2", Profile::TYPE_SERVER); iProfileManager->updateProfile(myProfile2); iServerActivator = new ServerActivator(*iProfileManager, *iTransportTracker, this); // iServers.keys() does the same QVERIFY(iServerActivator->iServers.count()); } void ServerActivatorTest::cleanupTestCase() { // deallocate the memory delete iServerActivator; delete iProfileManager; delete iTransportTracker; } void ServerActivatorTest :: testRef() { // dummy server name which fails const QString SERVERNAME = "dummy"; // saved server profile name const QString ANOTHERSERVER = "sampleServerProfile"; QSignalSpy enabledSpy(iServerActivator, SIGNAL(serverEnabled(QString))); QSignalSpy disabledSpy(iServerActivator, SIGNAL(serverDisabled(QString))); int returnedVal = iServerActivator->addRef(SERVERNAME); // this call should fail as there is no "dummy" profile saved. so compare with expected value '0' QCOMPARE(returnedVal, 0); QCOMPARE(enabledSpy.count(), 0); returnedVal = iServerActivator->addRef(ANOTHERSERVER); // when the server name is found, ref count will be incremented and returned to the calling function QCOMPARE(returnedVal, 1); QCOMPARE(enabledSpy.count(), 1); returnedVal = iServerActivator->removeRef(SERVERNAME); QCOMPARE(returnedVal, 0); returnedVal = iServerActivator->removeRef(ANOTHERSERVER); QCOMPARE(returnedVal, 0); QCOMPARE(disabledSpy.count(), 1); // signals will be emitted only if reference count is 1 for addRef and 0 for removeRef } void ServerActivatorTest :: testEnabledServers() { // test enableServers() without adding server name. expected result is empty stringlist QStringList serverData = iServerActivator->enabledServers(); QVERIFY(serverData.empty()); // add a servername and test the function again(). expected result is added // server name as stringlist returned iServerActivator->addRef("sampleServerProfile2"); serverData = iServerActivator->enabledServers(); QVERIFY(serverData.count() && serverData.contains("sampleServerProfile2")); } void ServerActivatorTest :: testConnectivityStateChanged() { // adding a server profile and use USB transport medium const QString SERVER_XML= " " " " " "; QDomDocument doc; QVERIFY(doc.setContent(SERVER_XML, false)); Profile sampleServerProfile(doc.documentElement()); sampleServerProfile.setName("sampleProfile"); const QString PROFILE_PATH("syncprofiletests/testprofiles/user"); ProfileManager myProfileManager(PROFILE_PATH, PROFILE_PATH); myProfileManager.updateProfile(sampleServerProfile); TransportTracker myTrasportTracker(this); ServerActivator sampleServerActivator(myProfileManager, myTrasportTracker, this); QSignalSpy enabledSpy(&sampleServerActivator, SIGNAL(serverEnabled(QString))); QSignalSpy disabledSpy(&sampleServerActivator, SIGNAL(serverDisabled(QString))); // make sure that the present count is '0' QCOMPARE(enabledSpy.count(), 0); sampleServerActivator.onConnectivityStateChanged(Sync::CONNECTIVITY_USB, true); // after calling onConnectivityStateChanged(), total signal emissions will be total number // of server profiles available. QCOMPARE(enabledSpy.count(), 1); // make sure that the present count is '0' QCOMPARE(disabledSpy.count(), 0); sampleServerActivator.onConnectivityStateChanged(Sync::CONNECTIVITY_USB, false); QCOMPARE(disabledSpy.count(), 1); } void ServerActivatorTest :: testTransportsFromProfile() { Profile profileToSetKey("profile", Profile::TYPE_SERVER); profileToSetKey.setKey("profilekey", "keyval"); QList typeReceived = iServerActivator->transportsFromProfile(&profileToSetKey); QVERIFY(typeReceived.empty() != 0); // XML profiles creation const QString USB_XML= " " " " " "; const QString BT_XML= " " " " " "; const QString INTERNET_XML= " " " " " "; // test for USB transport QDomDocument doc; QVERIFY(doc.setContent(USB_XML, false)); Profile usbServerProfile(doc.documentElement()); typeReceived = iServerActivator->transportsFromProfile(&usbServerProfile); QCOMPARE(typeReceived.count(), 1); QCOMPARE(typeReceived.takeFirst(), Sync::CONNECTIVITY_USB); // test for BT transport QVERIFY(doc.setContent(BT_XML, false)); Profile btServerProfile(doc.documentElement()); typeReceived = iServerActivator->transportsFromProfile(&btServerProfile); QCOMPARE(typeReceived.count(), 1); QCOMPARE(typeReceived.takeFirst(), Sync::CONNECTIVITY_BT); // test for internet transport QVERIFY(doc.setContent(INTERNET_XML, false)); Profile internetServerProfile(doc.documentElement()); typeReceived = iServerActivator->transportsFromProfile(&internetServerProfile); QCOMPARE(typeReceived.count(), 1); QCOMPARE(typeReceived.takeFirst(), Sync::CONNECTIVITY_INTERNET); } QTEST_MAIN(Buteo::ServerActivatorTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/ServerThreadTest.pro0000644000015600001650000000004312634332753030500 0ustar pbuserpbgroup00000000000000include(msyncdtestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/msyncdtestapplication.pri0000644000015600001650000000010612634332753031655 0ustar pbuserpbgroup00000000000000include(../testapplication.pri) include(../../../msyncd/unittest.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SyncSchedulerTest.pro0000644000015600001650000000004312634332753030655 0ustar pbuserpbgroup00000000000000include(msyncdtestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SynchronizerTest.cpp0000644000015600001650000002125012634332753030564 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SynchronizerTest.h" #include "TransportTracker.h" #include "ServerActivator.h" #include "ServerPluginRunner.h" using namespace Buteo; void SynchronizerTest::initTestCase() { iSync = new Synchronizer(NULL); iProfile = new SyncProfile("Profile"); iProfileQ = new SyncProfile("Profile1"); iSyncSession = new SyncSession(iProfile, NULL); iSyncSessionQ = new SyncSession(iProfileQ, NULL); } void SynchronizerTest::cleanupTestCase() { iSync->close(); QVERIFY(iSyncSession != 0); delete iSyncSession; QVERIFY(iSyncSessionQ != 0); delete iSyncSessionQ; QVERIFY(iSync != 0); iSync->close(); delete iSync; iSync = 0; iSyncSession = 0; iSyncSessionQ = 0; } void SynchronizerTest::testSyncConstructor() { QVERIFY(iSync->iSyncScheduler == 0); QVERIFY(iSync->iTransportTracker == 0); QVERIFY(iSync->iServerActivator == 0); QVERIFY(iSync->iAccounts == 0); QVERIFY(iSync->iSyncBackup == 0); QCOMPARE(iSync->iClosing, false); } void SynchronizerTest::testInitialize() { QCOMPARE(iSync->isConnectivityAvailable(Sync::CONNECTIVITY_USB), false); QCOMPARE(iSync->initialize(), true); QVERIFY(iSync->iSyncScheduler != 0); QVERIFY(iSync->iTransportTracker != 0); QVERIFY(iSync->iServerActivator != 0); QVERIFY(iSync->iAccounts != 0); QVERIFY(iSync->iSyncBackup != 0); } void SynchronizerTest::testSync() { QEXPECT_FAIL("", "This test case is so broken so that I do not find it worth to fix", Abort); // For some notes see comments starting with XXX QVERIFY(false); QString name; QStringList alist; alist << "storage"; SyncSession *aSync = 0; QVERIFY(aSync == 0); QVERIFY(iSync->iSyncBackup != 0); QCOMPARE(iSync->startSyncNow(aSync), false); QVERIFY(iSyncSession != 0); QCOMPARE(iSync->startSyncNow(iSyncSession), false); QCOMPARE(iSync->startSync("Profile"), false); // XXX broken since 7678242 (Check network connectivity before attempting online sync) QCOMPARE(iSync->startScheduledSync("Profile"), false); iSync->iActiveSessions.insert(iSyncSession->profileName(), iSyncSession); QCOMPARE(iSync->startSync("Profile"), true); QCOMPARE(iSync->startScheduledSync("Profile"), true); QCOMPARE(iSync->runningSyncs().takeFirst(), iSyncSession->profileName()); QCOMPARE(iSync->startSync("Profile1"), false); QCOMPARE(iSync->startScheduledSync("Profile1"), false); iSync->iSyncQueue.enqueue(iSyncSessionQ); QSignalSpy sigStatus(iSync, SIGNAL(syncStatus(QString, int, QString, int))); QCOMPARE(iSync->startSync("Profile1"), true); QCOMPARE(sigStatus.count(), 1); QSignalSpy sigStatus1(iSync, SIGNAL(syncStatus(QString, int, QString, int))); QCOMPARE(iSync->startScheduledSync("Profile1"), true); QTRY_COMPARE(sigStatus1.count(), 1); QCOMPARE(iSync->startSync("S40"), false); QCOMPARE(iSync->requestStorages(alist), true); //test startNextSync() QCOMPARE(iSync->iSyncQueue.isEmpty(), false); QVERIFY(iSync->iSyncQueue.head() != 0); // XXX this->iSyncSession->deleteLater() is be called upon this but iSyncSession is still accessed // later and expected to be valid at cleanupTestCase() QCOMPARE(iSync->startNextSync(), true); QCOMPARE(iSync->iSyncQueue.isEmpty(), true); QCOMPARE(iSync->startNextSync(), false); //test abortSync() iSync->iActiveSessions.insert(iSyncSession->profileName(), iSyncSession); QCOMPARE(iSync->iActiveSessions.contains("Profile"), true); iSync->abortSync("Profile"); //test createStorage(const QString) QCOMPARE(QString("Plugin").isEmpty(), false); QVERIFY(iSync->createStorage("Plugin") == NULL); // XXX This leads to memory corruption QCOMPARE(iSync->requestStorage("Storage", reinterpret_cast(this)), true); //Test startServer(const QString &aProfileName) QVERIFY(iSync->iServerActivator != 0); // XXX Try to use QTRY_COMPARE() here to see the effects mentioned in the above XXX comments QCOMPARE(iSync->iServers.isEmpty(), true); QVERIFY(iSync->iProfileManager.profile("profile", Profile::TYPE_SERVER) == 0); iSync->startServer("profile"); QCOMPARE(iSync->iServers.isEmpty(), true); QVERIFY(iSync->iProfileManager.profile("syncml", Profile::TYPE_SERVER) != 0); iSync->startServer("syncml"); QCOMPARE(iSync->iServers.isEmpty(), false); QTest::qSleep( 100 ); // Fake that synchronizer is closing, so that server plug-in is deleted // immediately. Otherwise plug-in will get deleted only after synchronizer // destructor when plug-in manager is already destroyed. This will cause a // a crash because plug-in runner uses the plug-in manager to destroy the // plug-in. In the real application this situation does not happen, because // event loop is running and deleteLater works normally. iSync->iClosing = true; iSync->stopServer("syncml"); iSync->iClosing = false; QCOMPARE(iSync->iServers.isEmpty(), true); QCOMPARE(iSync->iServerActivator->enabledServers().isEmpty(), true); QVERIFY(iSync->iTransportTracker != 0); iSync->iTransportTracker->updateState(Sync::CONNECTIVITY_USB, true); //test startServers() QVERIFY(iSync->iServerActivator != 0); QCOMPARE(iSync->iServerActivator->enabledServers().isEmpty(), false); iSync->startServers(); QTest::qSleep( 100 ); // Fake that synchronizer is closing, so that server plug-in is deleted // immediately. iSync->iClosing = true; iSync->stopServers(); iSync->iClosing = false; //test onServerDone() //This is not called in a slot activated by the signal. sender() returns 0 QVERIFY(iSync->iProfileManager.profile("syncml", Profile::TYPE_SERVER) != 0); iSync->startServer("syncml"); QTest::qSleep( 100 ); QCOMPARE(iSync->iServers.isEmpty(), false); QCOMPARE(iSync->iServers.size(), 1); iSync->onServerDone(); QCOMPARE(iSync->iServers.size(), 1); //test onNewSession(const QString &aDestination). //This is not called in a slot activated by the signal. sender() returns 0 QCOMPARE(iSync->iActiveSessions.isEmpty(), false); QCOMPARE(iSync->iActiveSessions.size(), 1); iSync->onNewSession("USB"); QCOMPARE(iSync->iActiveSessions.size(), 1); /* ServerPluginRunner *plugin = new ServerPluginRunner("Plugin", NULL, NULL, NULL, NULL); QSignalSpy spy(plugin, SIGNAL(newSession(const QString ))); plugin->onNewSession("USB"); QCOMPARE(spy.count(), 1); QTest::qWait(20); QCOMPARE(iSync->iActiveSessions.size(), 1);*/ QVERIFY(iSync->iSyncScheduler != 0); iSync->reschedule("Profile"); //test is TransportAvailable(const SyncSession *aSession) QVERIFY(iSyncSession != 0); QVERIFY(iSync->iTransportTracker != 0); QVERIFY(iSyncSession->profile() != 0); } void SynchronizerTest::testSignals() { QStringList alist; alist << "storage"; QSignalSpy sigStorage(iSync, SIGNAL(storageReleased())); iSync->releaseStorages(alist); QCOMPARE(sigStorage.count(), 1); qRegisterMetaType("Sync::TransferDatabase"); qRegisterMetaType("Sync::TransferType"); QSignalSpy sigTransfer(iSync, SIGNAL(transferProgress(QString, int, int, QString, int))); iSync->onTransferProgress("Profile", Sync::LOCAL_DATABASE, Sync::ITEM_ADDED, "Mime", 1); QCOMPARE(sigTransfer.count(), 1); QSignalSpy sigStorage1(iSync, SIGNAL(storageReleased())); iSync->releaseStorage("Storage", NULL); QCOMPARE(sigStorage1.count(), 1); QSignalSpy sigbackupRestoreStarts(iSync, SIGNAL(backupInProgress())); iSync->backupStarts(); QCOMPARE(sigbackupRestoreStarts.count(), 1); QSignalSpy sigbackupDone(iSync, SIGNAL(backupDone())); iSync->backupFinished(); QCOMPARE(sigbackupDone.count(), 1); QSignalSpy sigrestoreStarts(iSync, SIGNAL(restoreInProgress())); iSync->restoreStarts(); QCOMPARE(sigrestoreStarts.count(), 1); QSignalSpy sigrestoreFinished(iSync, SIGNAL(restoreDone())); iSync->restoreFinished(); QCOMPARE(sigrestoreFinished.count(), 1); QSignalSpy sessionStatus(iSync, SIGNAL(syncStatus(QString, int, QString, int))); iSync->onSessionFinished("Profile", Sync::SYNC_DONE, "Msg", 0); QCOMPARE(sessionStatus.count(), 1); } QTEST_MAIN(Buteo::SynchronizerTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/ClientPluginRunnerTest.cpp0000644000015600001650000001027512634332753031663 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ClientPluginRunnerTest.h" #include "PluginManager.h" #include "synchronizer.h" #include "ClientThread.h" using namespace Buteo; const QString PROFILE = "Profile"; const QString PLUGIN = "plugin"; void ClientPluginRunnerTest::initTestCase() { iSprofile = new SyncProfile(PROFILE); iPluginMgr = new PluginManager(); iPluginCbIf = new Synchronizer(NULL); iClientPrunner = new ClientPluginRunner(PLUGIN,iSprofile, iPluginMgr, iPluginCbIf, NULL); } void ClientPluginRunnerTest::cleanupTestCase() { QVERIFY(iSprofile); delete iSprofile; QVERIFY(iClientPrunner); delete iClientPrunner; QVERIFY(iPluginMgr); delete iPluginMgr; QVERIFY(iPluginCbIf); delete iPluginCbIf; iSprofile = 0; iPluginMgr = 0; iPluginCbIf = 0; iClientPrunner = 0; } void ClientPluginRunnerTest::testCpluginRunnerConstructor() { SyncResults syncRes; bool dateTime; QDateTime current; QVERIFY(iClientPrunner->plugin() == 0); QVERIFY(iClientPrunner->iThread == 0); QCOMPARE(iClientPrunner->iProfile, iSprofile); QCOMPARE(iClientPrunner->pluginType(), PluginRunner::PLUGIN_CLIENT); QCOMPARE(iClientPrunner->pluginName(), PLUGIN); QCOMPARE(iClientPrunner->iInitialized, false); QVERIFY(iClientPrunner->iPluginMgr != 0); QVERIFY(iClientPrunner->iPluginCbIf != 0); //test syncResults() current = QDateTime::currentDateTime(); syncRes = iClientPrunner->syncResults(); QCOMPARE(syncRes.majorCode(), 0); QCOMPARE(syncRes.isScheduled(), false); dateTime = syncRes.syncTime() >= current; QVERIFY(dateTime); } void ClientPluginRunnerTest::testInit() { SyncResults syncRes; iClientPrunner->iInitialized = true; QCOMPARE(iClientPrunner->init(), true); iClientPrunner->iInitialized = false; QVERIFY(iClientPrunner->iPluginMgr != 0); QVERIFY(iClientPrunner->iPluginCbIf != 0); QVERIFY(iClientPrunner->iProfile != 0); QCOMPARE(iClientPrunner->init(), false); QVERIFY(iClientPrunner->plugin() == 0); QVERIFY(iClientPrunner->iThread == 0); iClientPrunner->iInitialized = true; QCOMPARE(iClientPrunner->iInitialized, true); } void ClientPluginRunnerTest::testStart() { //test start() QCOMPARE(iClientPrunner->iInitialized, true); QVERIFY(iClientPrunner->iThread == 0); QCOMPARE(iClientPrunner->start(), false); iClientPrunner->iThread = new ClientThread(); QVERIFY(iClientPrunner->iThread != 0); QCOMPARE(iClientPrunner->start(), false); } void ClientPluginRunnerTest::testSignals() { QSignalSpy doneSignal(iClientPrunner, SIGNAL(done())); iClientPrunner->onThreadExit(); QCOMPARE(doneSignal.count(), 1); QSignalSpy successSignal(iClientPrunner, SIGNAL(success(const QString &, const QString &))); iClientPrunner->onSuccess(PROFILE, "Message"); QCOMPARE(successSignal.count(), 1); QSignalSpy errorSignal(iClientPrunner, SIGNAL(error(const QString &, const QString &, int))); iClientPrunner->onError(PROFILE, "Message", 0); QCOMPARE(errorSignal.count(), 1); QSignalSpy transferSignal(iClientPrunner, SIGNAL(transferProgress(const QString &, Sync::TransferDatabase, Sync::TransferType, const QString &, int))); qRegisterMetaType("Sync::TransferDatabase"); qRegisterMetaType("Sync::TransferType"); iClientPrunner->onTransferProgress(PROFILE, Sync::LOCAL_DATABASE, Sync::ITEM_ADDED, "Mime",1); QCOMPARE(transferSignal.count(), 1); } QTEST_MAIN(Buteo::ClientPluginRunnerTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SyncSigHandlerTest.cpp0000644000015600001650000000265212634332753030751 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncSigHandlerTest.h" #include #include using namespace Buteo; void SyncSigHandlerTest :: init() { FUNCTION_CALL_TRACE; // initiate sighandler iSigHandler = new SyncSigHandler(); } void SyncSigHandlerTest :: cleanup() { FUNCTION_CALL_TRACE; delete iSigHandler ; iSigHandler = NULL; } void SyncSigHandlerTest :: testSigTerm() { FUNCTION_CALL_TRACE; //kill("TERM", 'pidof msyncd'); LOG_DEBUG("Check there should not be any core dump in /home/user/Mydocs/core-dumps"); } QTEST_MAIN(Buteo::SyncSigHandlerTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SyncSessionTest.h0000644000015600001650000000444412634332753030022 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCSESSIONTEST_H #define SYNCSESSIONTEST_H #include #include "SyncResults.h" #include "StorageBooker.h" #include "SyncProfile.h" #include "PluginRunner.h" namespace Buteo { class SyncSession; class SyncSessionPluginRunnerTest; class SyncSessionTest : public QObject { Q_OBJECT private slots: void init(); void cleanup(); void testPluginRunner(); void testStartAbortStop(); void testProfile(); void testScheduled(); void testResults(); void testStorages(); void testOnSuccess(); void testOnError(); void testOnTransferProgress(); void testOnDone(); private: SyncSession *iSyncSession; SyncProfile *iSyncProfile; PluginRunner *iNullPluginRunner; QPointer iSyncSessionPluginRunnerTest; public: static bool isValuePassedTrue; }; class SyncSessionPluginRunnerTest : public PluginRunner { Q_OBJECT public slots: bool init(); bool start(); void stop(); void abort(Sync::SyncStatus aStatus = Sync::SYNC_ABORTED); bool cleanUp(); SyncResults syncResults(); SyncPluginBase *plugin(); public: SyncSessionPluginRunnerTest(const QString &aPluginName, PluginManager *aPluginMgr, PluginCbInterface *aPluginCbIf); private: PluginRunner *iPluginRunner; public: static int testValue; // to cross-check the value while calling stop() / abort() }; } #endif // SYNCSESSIONTEST_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/ClientThreadTest.pro0000644000015600001650000000004312634332753030450 0ustar pbuserpbgroup00000000000000include(msyncdtestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/ClientPluginRunnerTest.h0000644000015600001650000000265212634332753031330 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef CLIENTPLUGINRUNNERTEST_H_ #define CLIENTPLUGINRUNNERTEST_H_ #include #include #include "ClientPluginRunner.h" #include "SyncProfile.h" namespace Buteo { class ClientPluginRunnerTest: public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void testCpluginRunnerConstructor(); void testInit(); void testStart(); void testSignals(); private: ClientPluginRunner *iClientPrunner; SyncProfile *iSprofile; PluginManager *iPluginMgr; PluginCbInterface *iPluginCbIf; }; } #endif /*CLIENTPLUGINRUNNERTEST_H_*/ buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SyncSchedulerTest.h0000644000015600001650000000341412634332753030311 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCSCHEDULERTEST_H_ #define SYNCSCHEDULERTEST_H_ #include #include #include "SyncProfile.h" namespace Buteo { class SyncScheduler; class SyncSchedulerTest: public QObject { Q_OBJECT private slots: void init(); void cleanup(); void syncTriggered(QString aProfileName); void testAddRemoveProfile(); void testSetNextAlarm(); private: SyncScheduler* iSyncScheduler; QString iSyncProfileName; }; class SyncProfileStub : public SyncProfile { public: SyncProfileStub(const QString& aName) : SyncProfile(aName) {} ~SyncProfileStub() {} QDateTime nextSyncTime() const { const int HOUR = 3600; int syncTime_t = QDateTime::currentDateTime().toTime_t() + HOUR; return QDateTime::fromTime_t(syncTime_t); } }; } #endif // SYNCSCHEDULERTEST_H_ buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/AccountsHelperTest.h0000644000015600001650000000262712634332753030462 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef ACCOUNTSHELPERTEST_H #define ACCOUNTSHELPERTEST_H #include #include "AccountsHelper.h" #include namespace Buteo { class AccountsHelperTest: public QObject { Q_OBJECT public: AccountsHelperTest(); private slots: void initTestCase(); void cleanupTestCase(); void testProfileAdded(); void testAddAccountData(); private: Accounts::Manager iManager; Accounts::Account *iAccount; ProfileManager iProfileManager; AccountsHelper *iAccountsHelper; }; } #endif // ACCOUNTSHELPERTEST_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/ServerPluginRunnerTest.cpp0000644000015600001650000001072612634332753031714 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ServerPluginRunnerTest.h" #include #include #include using namespace Buteo; void ServerPluginRunnerTest::initTestCase() { iTransportTracker = new TransportTracker(this); iProfileManager = new ProfileManager("profile1", "profile2"); iProfile = new Profile("dummyprofile", Profile::TYPE_SERVER); iProfileManager->updateProfile(*iProfile); iServerActivator = new ServerActivator(*iProfileManager, *iTransportTracker); QDir dir = QDir(QCoreApplication::applicationDirPath() + "/.."); QString path = dir.absolutePath(); if (dir.cd("../dummyplugins/dummyserver")) { path = dir.absolutePath(); } // no else iPluginManager = new PluginManager(path) ; // TODO: need to update with valid PluginCbInterface pointer iServerPluginRunner = new ServerPluginRunner("hdummy", iProfile, iPluginManager, reinterpret_cast(this), iServerActivator); /* test init() */ bool initResult = iServerPluginRunner->init(); // after returning from init(), iPlugin should have the pointer to server thread and // iThread memory allocation should be success QVERIFY(iServerPluginRunner->plugin()); QVERIFY(iServerPluginRunner->iThread); QVERIFY(initResult); } void ServerPluginRunnerTest::cleanupTestCase() { // test ThreadStop() QVERIFY(iServerPluginRunner->iThread); iServerPluginRunner->stop(); // deallocate allocated memory delete iServerPluginRunner; delete iPluginManager; delete iServerActivator; delete iProfileManager; delete iTransportTracker; /* don't deallocate memory for iProfile, as this will be deallocated by * ServerPluginRunner class. */ } void ServerPluginRunnerTest::testStartAbort() { /* testing start() */ QVERIFY(iServerPluginRunner->iInitialized); bool isStarted = iServerPluginRunner->start(); QVERIFY(isStarted); /* test abort() */ // test plugin pointer for NULL QVERIFY(iServerPluginRunner->plugin()); iServerPluginRunner->abort(); } void ServerPluginRunnerTest::testSyncResults() { // test plugin pointer for NULL QVERIFY(iServerPluginRunner->plugin()); // we can't set values from ServerPluginRunner. Hence, comparing the default return value QCOMPARE(iServerPluginRunner->syncResults().majorCode(), 0); } void ServerPluginRunnerTest::testSignals() { QSignalSpy sessionSpy(iServerPluginRunner, SIGNAL(newSession(QString))); QSignalSpy errorSpy(iServerPluginRunner, SIGNAL(error(QString, QString, int))); QSignalSpy successSpy(iServerPluginRunner, SIGNAL(success(QString, QString))); QSignalSpy doneSpy(iServerPluginRunner, SIGNAL(done())); // registering metatypes that are not known qRegisterMetaType("Sync::TransferDatabase"); qRegisterMetaType("Sync::TransferType"); QSignalSpy transferSpy(iServerPluginRunner, SIGNAL(transferProgress(QString, Sync::TransferDatabase, Sync::TransferType, QString, int))); QVERIFY(iServerPluginRunner->iServerActivator); iServerPluginRunner->onNewSession("toDevice"); QCOMPARE(sessionSpy.count(), 1); iServerPluginRunner->onTransferProgress("profile", Sync::LOCAL_DATABASE, Sync::ITEM_ADDED, "text", 1); QCOMPARE(transferSpy.count(), 1); iServerPluginRunner->onError("profile", "message", 2); QCOMPARE(errorSpy.count(), 1); iServerPluginRunner->onSuccess("profile", "message"); QCOMPARE(successSpy.count(), 1); iServerPluginRunner->onThreadExit(); QCOMPARE(doneSpy.count(), 1); } QTEST_MAIN(Buteo::ServerPluginRunnerTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SyncSigHandlerTest.h0000644000015600001650000000227312634332753030415 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCSIGHANDLERTEST_H #define SYNCSIGHANDLERTEST_H #include "SyncSigHandler.h" #include "LogMacros.h" namespace Buteo { class SyncSigHandlerTest : public QObject { Q_OBJECT private slots: void init(); void cleanup(); void testSigTerm(); private: SyncSigHandler *iSigHandler; }; } #endif // SYNCSESSIONTEST_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/ServerPluginRunnerTest.h0000644000015600001650000000310112634332753031346 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SERVERPLUGINRUNNERTEST_H #define SERVERPLUGINRUNNERTEST_H #include "ServerPluginRunner.h" #include "TransportTracker.h" #include "Profile.h" #include "ProfileManager.h" #include "PluginManager.h" #include "ServerActivator.h" namespace Buteo { class ServerPluginRunnerTest : public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void testStartAbort(); void testSyncResults(); void testSignals(); private: ServerPluginRunner *iServerPluginRunner; TransportTracker *iTransportTracker; ProfileManager *iProfileManager; ServerActivator *iServerActivator; PluginManager *iPluginManager; Profile *iProfile; }; } #endif // SERVERPLUGINRUNNERTEST_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SyncSessionTest.pro0000644000015600001650000000004312634332753030362 0ustar pbuserpbgroup00000000000000include(msyncdtestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/IPHeartBeatTest.cpp0000644000015600001650000000427312634332753030165 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "IPHeartBeatTest.h" #include "IPHeartBeat.h" using namespace Buteo; void IPHeartBeatTest::initTestCase() { iHbeat = new IPHeartBeat(this); connect(iHbeat,SIGNAL(onHeartBeat(QString)),this,SLOT(onBeatTriggered(QString))); } void IPHeartBeatTest::cleanupTestCase() { delete iHbeat; } void IPHeartBeatTest::testSetHeartBeat() { iBeatReceived = false; if(iHbeat->setHeartBeat("someprofile",0,2) == true) { QTest::qWait(1000*3); QVERIFY(iBeatReceived == true); } else { // If iphbd service is not running setHeartBeat() will fail // Not letting that to make the test to fail qDebug() << "******* Start the iphbd service and run this test *******"; } } void IPHeartBeatTest::testRemoveHeartBeat() { iHbeat->setHeartBeat("someprofile",0,20); iHbeat->removeWait("someprofile"); QVERIFY(iHbeat->iBeatsWaiting.contains("someprofile") == false); } void IPHeartBeatTest::testRemoveAllHeartBeats() { iHbeat->setHeartBeat("someprofile",0,20); iHbeat->setHeartBeat("anotherprofile",50,100); iHbeat->removeAllWaits(); QVERIFY(iHbeat->iBeatsWaiting.size() == 0); } void IPHeartBeatTest::testInternalBeat() { iHbeat->internalBeatTriggered(-1); } void IPHeartBeatTest::onBeatTriggered(QString /*aProfName*/) { iBeatReceived = true; } QTEST_MAIN(Buteo::IPHeartBeatTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/ServerThreadTest.h0000644000015600001650000000374412634332753030142 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SERVERTHREADTEST_H_ #define SERVERTHREADTEST_H_ #include #include #include #include #include "ServerThread.h" #include "ServerPlugin.h" #include "SyncCommonDefs.h" #include "Profile.h" namespace Buteo { class ServerPluginDerived: public ServerPlugin { Q_OBJECT public: ServerPluginDerived(const QString& aPluginName, const Profile& aProfile, PluginCbInterface* aCbInterface); bool startListen(); void stopListen(); bool init(); bool uninit(); void suspend(); void resume(); virtual bool cleanUp(); bool iTestSignal; public slots: void connectivityStateChanged( Sync::ConnectivityType , bool); }; class ServerThreadTest: public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void testServerThreadConstructor(); void testGetPlugin(); void testGetProfileName(); void testThread(); void testStopThErrorSignal(); private: ServerThread *iServerThread; ServerPluginDerived *iPluginDerived; ServerPlugin *iThreadTestSp; const Profile *iProfile; bool iThreadreturn; }; } #endif /*SERVERTHREADTEST_H_*/ buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/IPHeartBeatTest.h0000644000015600001650000000252612634332753027631 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef IPHEARTBEAT_TEST_H #define IPHEARTBEAT_TEST_H #include #include namespace Buteo { class IPHeartBeat; class IPHeartBeatTest: public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void testSetHeartBeat(); void testRemoveHeartBeat(); void testRemoveAllHeartBeats(); void testInternalBeat(); void onBeatTriggered(QString aProfName); private: IPHeartBeat* iHbeat; bool iBeatReceived; }; } #endif buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SynchronizerTest.h0000644000015600001650000000264212634332753030235 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCHRONIZERTEST_H_ #define SYNCHRONIZERTEST_H_ #include #include #include #include "synchronizer.h" #include "SyncSession.h" namespace Buteo { class SynchronizerTest: public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void testSyncConstructor(); void testInitialize(); void testSync(); void testSignals(); private: Synchronizer *iSync; SyncSession *iSyncSession; SyncProfile *iProfile; SyncSession *iSyncSessionQ; SyncProfile *iProfileQ; }; } #endif /*SYNCHRONIZERTEST_H_*/ buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SyncSigHandlerTest.pro0000644000015600001650000000004312634332753030757 0ustar pbuserpbgroup00000000000000include(msyncdtestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/PluginRunnerTest.h0000644000015600001650000000260312634332753030165 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef PLUGINRUNNERTEST_H_ #define PLUGINRUNNERTEST_H_ #include #include #include "PluginRunner.h" #include "PluginManager.h" #include "SyncProfile.h" #include "ClientPluginRunner.h" namespace Buteo { class PluginRunnerTest : public QObject { Q_OBJECT private slots: void initTestCase(); void testPluginRunnerConstructor(); void cleanupTestCase(); private: PluginRunner *iPRunner; PluginManager *iPManager; SyncProfile *iSProfile; ClientPluginRunner *iClientPRunner; }; } #endif /*PLUGINRUNNERTEST_H_*/ buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SyncQueueTest.cpp0000644000015600001650000000362712634332753030020 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncQueueTest.h" #include "SyncQueue.h" #include "SyncSession.h" #include using namespace Buteo; void SyncQueueTest::testQueue() { const QString NAME1 = "Name1"; const QString NAME2 = "Name2"; SyncSession s1(new SyncProfile(NAME1)); SyncSession s2(new SyncProfile(NAME2)); SyncQueue q; // Empty queue. QCOMPARE(q.isEmpty(), true); QVERIFY(q.head() == NULL); QVERIFY(q.dequeue() == NULL); QCOMPARE(q.contains(NAME1), false); // Add items. q.enqueue(&s1); q.enqueue(&s2); QCOMPARE(q.isEmpty(), false); QCOMPARE(q.head(), &s1); QCOMPARE(q.contains(NAME1), true); QCOMPARE(q.contains(NAME2), true); QCOMPARE(q.dequeue(), &s1); QCOMPARE(q.contains(NAME1), false); QCOMPARE(q.contains(NAME2), true); QCOMPARE(q.isEmpty(), false); q.enqueue(&s1); QCOMPARE(q.head(), &s2); QCOMPARE(q.dequeue(), &s2); QCOMPARE(q.head(), &s1); QCOMPARE(q.dequeue(), &s1); QCOMPARE(q.isEmpty(), true); QVERIFY(q.dequeue() == NULL); } QTEST_MAIN(Buteo::SyncQueueTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/ServerPluginRunnerTest.pro0000644000015600001650000000004312634332753031721 0ustar pbuserpbgroup00000000000000include(msyncdtestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/StorageBookerTest.pro0000644000015600001650000000004312634332753030650 0ustar pbuserpbgroup00000000000000include(msyncdtestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/PluginRunnerTest.cpp0000644000015600001650000000350212634332753030517 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "PluginRunnerTest.h" using namespace Buteo; const QString PLUGIN = "plugin"; const QString PROFILE = "profile"; void PluginRunnerTest::initTestCase() { iSProfile = new SyncProfile(PROFILE); iPManager = new PluginManager(); iClientPRunner = new ClientPluginRunner(PLUGIN, iSProfile, iPManager, NULL, NULL); iPRunner = iClientPRunner; } void PluginRunnerTest::testPluginRunnerConstructor() { QCOMPARE(iPRunner->iInitialized, false); QCOMPARE(iPRunner->iPluginMgr, iPManager); QVERIFY(iPRunner->iPluginCbIf == NULL); QCOMPARE(iPRunner->iType, PluginRunner::PLUGIN_CLIENT); QCOMPARE(iPRunner->pluginType(), PluginRunner::PLUGIN_CLIENT); QCOMPARE(iPRunner->pluginName(), PLUGIN); } void PluginRunnerTest::cleanupTestCase() { QVERIFY(iPManager != 0); delete iPManager; QVERIFY(iSProfile != 0); delete iSProfile; QVERIFY(iClientPRunner != 0); delete iClientPRunner; iPManager = 0; iSProfile = 0; iClientPRunner = 0; iPRunner = 0; } QTEST_MAIN(Buteo::PluginRunnerTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/AccountsHelperTest.cpp0000644000015600001650000000532612634332753031014 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "AccountsHelperTest.h" #include #include using namespace Buteo; static const QString PROFILE_XML = "" "" "" "" ""; static const QString OVI_PROVIDER = "ovi"; static const QString DUMMY_USER = "dummy"; static const QString USERPROFILE_DIR = "syncprofiletests/testprofiles/user"; static const QString SYSTEMPROFILE_DIR = "syncprofiletests/testprofiles/system"; static const QString SERVICE_SYNC = "Sync"; static const QString SERVICE_NAME = "testsync-ovi"; AccountsHelperTest::AccountsHelperTest() : QObject(NULL), iManager(SERVICE_SYNC, this), iProfileManager(USERPROFILE_DIR, SYSTEMPROFILE_DIR) { iAccountsHelper = new AccountsHelper(iProfileManager, 0); } void AccountsHelperTest::initTestCase() { iAccount = iManager.createAccount(OVI_PROVIDER); iAccount->setDisplayName(DUMMY_USER); iAccount->setEnabled(true); Accounts::Service service = iManager.service(SERVICE_NAME); iAccount->selectService(service); iAccount->setEnabled(true); iAccount->selectService(); QVERIFY(iAccount != NULL); iAccount->sync(); } void AccountsHelperTest::cleanupTestCase() { if (iAccount != NULL) { iAccount->remove(); iAccount->sync(); delete iAccount; iAccount = NULL; } // no else } void AccountsHelperTest::testProfileAdded() { // Ensure that the profile with the username was added correctly SyncProfile *syncProfile = iProfileManager.syncProfile(SERVICE_NAME + "-" + iAccount->displayName()); //QVERIFY(syncProfile != 0); Q_UNUSED(syncProfile); } void AccountsHelperTest::testAddAccountData() { } QTEST_MAIN(Buteo::AccountsHelperTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/msyncdtests.pro0000644000015600001650000000122312634332753027623 0ustar pbuserpbgroup00000000000000TEMPLATE = subdirs SUBDIRS = \ AccountsHelperTest.pro \ ClientPluginRunnerTest.pro \ ClientThreadTest.pro \ PluginRunnerTest.pro \ ServerActivatorTest.pro \ ServerPluginRunnerTest.pro \ ServerThreadTest.pro \ StorageBookerTest.pro \ SyncBackupTest.pro \ SyncQueueTest.pro \ SyncSessionTest.pro \ SyncSigHandlerTest.pro \ SynchronizerTest.pro \ TransportTrackerTest.pro \ !contains(DEFINES, USE_KEEPALIVE) { SUBDIRS += \ IPHeartBeatTest.pro \ SyncSchedulerTest.pro \ } coverage.CONFIG += recursive QMAKE_EXTRA_TARGETS += coverage buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SyncSchedulerTest.cpp0000644000015600001650000000457112634332753030651 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncSchedulerTest.h" #include "SyncScheduler.h" #include "SyncProfile.h" #include using namespace Buteo; void SyncSchedulerTest::init() { iSyncScheduler = new SyncScheduler(); iSyncProfileName.clear(); } void SyncSchedulerTest::cleanup() { if (iSyncScheduler) { delete iSyncScheduler; iSyncScheduler = NULL; } } void SyncSchedulerTest::syncTriggered(QString aProfileName) { iSyncProfileName = aProfileName; } void SyncSchedulerTest::testAddRemoveProfile() { SyncProfileStub profile("foo"); profile.setEnabled(true); profile.setSyncType(SyncProfile::SYNC_SCHEDULED); bool profileAdded = iSyncScheduler->addProfile(&profile); QVERIFY(profileAdded); QVERIFY(iSyncScheduler->iSyncScheduleProfiles.contains(profile.name())); SyncProfileStub invalidProfile("bar"); profile.setEnabled(false); profileAdded = iSyncScheduler->addProfile(&invalidProfile); QVERIFY(!profileAdded); iSyncScheduler->removeProfile("foo"); QVERIFY(iSyncScheduler->iSyncScheduleProfiles.isEmpty()); } void SyncSchedulerTest::testSetNextAlarm() { QString profileName = "foo"; SyncProfileStub profile(profileName); profile.setEnabled(true); profile.setSyncType(SyncProfile::SYNC_SCHEDULED); //int sanity = alarm_event_is_sane(iSyncScheduler->iAlarmEventParameters); //QVERIFY(sanity == 1); int alarmId = iSyncScheduler->setNextAlarm(&profile); QVERIFY(alarmId > 0); iSyncScheduler->removeAlarmEvent(alarmId); } QTEST_MAIN(Buteo::SyncSchedulerTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/ClientPluginRunnerTest.pro0000644000015600001650000000004312634332753031671 0ustar pbuserpbgroup00000000000000include(msyncdtestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SyncBackupTest.h0000644000015600001650000000234512634332753027602 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCBACKUPTEST_H_ #define SYNCBACKUPTEST_H_ #include #include #include #include "SyncBackup.h" namespace Buteo { class SyncBackupTest: public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void testInitialize(); void testBackup(); private: SyncBackup *iBackup; }; } #endif /*SYNCBACKUPTEST_H_*/ buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/ServerActivatorTest.h0000644000015600001650000000264512634332753030666 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SERVERACTIVATOR_H #define SERVERACTIVATOR_H #include "ServerActivator.h" #include "TransportTracker.h" #include namespace Buteo { class ServerActivatorTest : public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void testRef(); void testEnabledServers(); void testConnectivityStateChanged(); void testTransportsFromProfile(); private: ServerActivator *iServerActivator; TransportTracker *iTransportTracker; ProfileManager *iProfileManager; }; } #endif // SERVERACTIVATOR_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/TransportTrackerTest.h0000644000015600001650000000236012634332753031045 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef TRANSPORTTRACKERTEST_H #define TRANSPORTTRACKERTEST_H #include "TransportTracker.h" namespace Buteo { class TransportTrackerTest: public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void testConnectivityAvailable(); void testStateChanged(); private: TransportTracker *iTransportTracker; }; } #endif // TRANSPORTTRACKERTEST_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SyncBackupTest.cpp0000644000015600001650000000416512634332753030137 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncBackupTest.h" #include using namespace Buteo; void SyncBackupTest::initTestCase() { iBackup = new SyncBackup(); } void SyncBackupTest::cleanupTestCase() { QVERIFY(iBackup != 0); delete iBackup; iBackup = 0; } void SyncBackupTest::testInitialize() { QVERIFY(iBackup->iReply == 0); QVERIFY(iBackup->iBackupRestore == 0); QVERIFY(iBackup->iWatchService != 0); QVERIFY(iBackup->iAdaptor != 0); } void SyncBackupTest::testBackup() { QDBusMessage msg; QSignalSpy sigStatus1(iBackup, SIGNAL(startBackup())); QCOMPARE(iBackup->backupStarts(msg), uchar(0)); QCOMPARE(sigStatus1.count(), 1); QCOMPARE(iBackup->getBackUpRestoreState(), 1); QSignalSpy sigStatus2(iBackup, SIGNAL(backupDone())); QCOMPARE(iBackup->backupFinished(msg), uchar(0)); QCOMPARE(sigStatus2.count(), 1); QCOMPARE(iBackup->getBackUpRestoreState(), 0); QSignalSpy sigStatus3(iBackup, SIGNAL(startRestore())); QCOMPARE(iBackup->restoreStarts(msg), uchar(0)); QCOMPARE(sigStatus3.count(), 1); QCOMPARE(iBackup->getBackUpRestoreState(), 1); QSignalSpy sigStatus4(iBackup, SIGNAL(restoreDone())); QCOMPARE(iBackup->restoreFinished(msg), uchar(0)); QCOMPARE(sigStatus4.count(), 1); QCOMPARE(iBackup->getBackUpRestoreState(), 0); } QTEST_MAIN(Buteo::SyncBackupTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SyncQueueTest.h0000644000015600001650000000207012634332753027454 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCQUEUETEST_H #define SYNCQUEUETEST_H #include namespace Buteo { class SyncQueueTest: public QObject { Q_OBJECT private slots: void testQueue(); }; } #endif // SYNCQUEUETEST_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/ClientThreadTest.h0000644000015600001650000000367512634332753030115 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef CLIENTTHREADTEST_H_ #define CLIENTTHREADTEST_H_ #include #include #include #include "ClientThread.h" #include "ClientPlugin.h" #include "SyncProfile.h" namespace Buteo { class ClientPluginDerived: public ClientPlugin { Q_OBJECT public: ClientPluginDerived(const QString& aPluginName, const SyncProfile& aProfile, PluginCbInterface* aCbInterface); bool startSync(); bool init(); bool uninit(); virtual bool cleanUp(); bool iTestClSignal; public slots: void connectivityStateChanged(Sync::ConnectivityType aType, bool aState); }; class ClientThreadTest: public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void testClientThreadConstructor(); void testGetPlugin(); void testGetProfileName(); void testClientThread(); void testGetSyncResults(); void testInitError(); private: ClientThread *iClientThread; ClientPluginDerived *iPluginDerived; ClientPlugin *iPlugin; SyncProfile *iSyncProfile; bool iClientThreadRet; }; } #endif /*CLIENTTHREADTEST_H_*/ buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/PluginRunnerTest.pro0000644000015600001650000000004312634332753030532 0ustar pbuserpbgroup00000000000000include(msyncdtestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SynchronizerTest.pro0000644000015600001650000000004312634332753030577 0ustar pbuserpbgroup00000000000000include(msyncdtestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/ServerActivatorTest.pro0000644000015600001650000000004312634332753031225 0ustar pbuserpbgroup00000000000000include(msyncdtestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/TransportTrackerTest.cpp0000644000015600001650000001306112634332753031400 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "TransportTrackerTest.h" #include #include #include #include using namespace Buteo; void TransportTrackerTest :: initTestCase() { // instantiating TransportTrackerTest iTransportTracker = new TransportTracker(this); /* TODO : For the following test cases, conditional compilation flag need to be added, as these * fail under scractch box */ /* // check memory allocation for HalProxy QVERIFY(iTransportTracker->iHalProxy); // check memory allocation for USB // check for valid pointer of iInternet QVERIFY(iTransportTracker->iInternet); */ } void TransportTrackerTest :: cleanupTestCase() { // deallocate the memory delete iTransportTracker; } void TransportTrackerTest :: testConnectivityAvailable() { // set the connectivity status for each type and compare it with the value returned // first set value as false iTransportTracker->updateState(Sync::CONNECTIVITY_USB, false); iTransportTracker->updateState(Sync::CONNECTIVITY_BT, false); iTransportTracker->updateState(Sync::CONNECTIVITY_INTERNET, false); bool usbTransportStatus = iTransportTracker->isConnectivityAvailable(Sync::CONNECTIVITY_USB); QCOMPARE(usbTransportStatus, false); bool btTransportStatus = iTransportTracker->isConnectivityAvailable(Sync::CONNECTIVITY_BT); QCOMPARE(btTransportStatus, false); bool internetTransportStatus = iTransportTracker->isConnectivityAvailable(Sync::CONNECTIVITY_INTERNET); QCOMPARE(internetTransportStatus, false); // next set the value true iTransportTracker->updateState(Sync::CONNECTIVITY_USB, true); iTransportTracker->updateState(Sync::CONNECTIVITY_BT, true); iTransportTracker->updateState(Sync::CONNECTIVITY_INTERNET, true); usbTransportStatus = iTransportTracker->isConnectivityAvailable(Sync::CONNECTIVITY_USB); QCOMPARE(usbTransportStatus, true); btTransportStatus = iTransportTracker->isConnectivityAvailable(Sync::CONNECTIVITY_BT); QCOMPARE(btTransportStatus, true); internetTransportStatus = iTransportTracker->isConnectivityAvailable(Sync::CONNECTIVITY_INTERNET); QCOMPARE(internetTransportStatus, true); } void TransportTrackerTest :: testStateChanged() { qRegisterMetaType("Sync::ConnectivityType"); QSignalSpy connectivityStateSpy(iTransportTracker, SIGNAL(connectivityStateChanged(Sync::ConnectivityType, bool))); QSignalSpy networkStateSpy(iTransportTracker, SIGNAL(networkStateChanged(bool,Sync::InternetConnectionType))); // change USB state and verify bool usbCurrentState = iTransportTracker->isConnectivityAvailable(Sync::CONNECTIVITY_USB); iTransportTracker->onUsbStateChanged(!usbCurrentState); QCOMPARE(iTransportTracker->isConnectivityAvailable(Sync::CONNECTIVITY_USB), !usbCurrentState); QCOMPARE(connectivityStateSpy.count(), 1); QCOMPARE(connectivityStateSpy.first().at(0).value(), Sync::CONNECTIVITY_USB); QCOMPARE(connectivityStateSpy.first().at(1).value(), !usbCurrentState); connectivityStateSpy.clear(); // change BT state and verify bool btCurrentState = iTransportTracker->isConnectivityAvailable(Sync::CONNECTIVITY_BT); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) iTransportTracker->onBtStateChanged("Powered", QDBusVariant(QVariant(!btCurrentState))); #else iTransportTracker->onBtStateChanged(!btCurrentState); #endif QCOMPARE(iTransportTracker->isConnectivityAvailable(Sync::CONNECTIVITY_BT), !btCurrentState); QCOMPARE(connectivityStateSpy.count(), 1); QCOMPARE(connectivityStateSpy.first().at(0).value(), Sync::CONNECTIVITY_BT); QCOMPARE(connectivityStateSpy.first().at(1).value(), !btCurrentState); connectivityStateSpy.clear(); // change internet state and verify bool internetCurrentState = iTransportTracker->isConnectivityAvailable(Sync::CONNECTIVITY_INTERNET); iTransportTracker->onInternetStateChanged(!internetCurrentState, Sync::INTERNET_CONNECTION_UNKNOWN); QCOMPARE(iTransportTracker->isConnectivityAvailable(Sync::CONNECTIVITY_INTERNET), !internetCurrentState); QEXPECT_FAIL("", "IMO connectivityStateChanged() should be emitted also for CONNECTIVITY_INTERNET", Continue); QCOMPARE(connectivityStateSpy.count(), 1); //QCOMPARE(connectivityStateSpy.first().at(0).value(), Sync::CONNECTIVITY_INTERNET); //QCOMPARE(connectivityStateSpy.first().at(1).value(), !internetCurrentState); //connectivityStateSpy.clear(); QCOMPARE(networkStateSpy.count(), 1); QCOMPARE(networkStateSpy.first().at(0).value(), !internetCurrentState); networkStateSpy.clear(); } QTEST_MAIN(Buteo::TransportTrackerTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/StorageBookerTest.h0000644000015600001650000000211212634332753030276 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef STORAGEBOOKERTEST_H #define STORAGEBOOKERTEST_H #include namespace Buteo { class StorageBookerTest: public QObject { Q_OBJECT private slots: void testBooking(); }; } #endif // STORAGEBOOKERTEST_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SyncQueueTest.pro0000644000015600001650000000004312634332753030023 0ustar pbuserpbgroup00000000000000include(msyncdtestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/SyncSessionTest.cpp0000644000015600001650000002715012634332753030354 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncSessionTest.h" #include #include #include #include "SyncSession.h" #include "PluginManager.h" #include "PluginCbInterface.h" #include "SyncResults.h" #include using namespace Buteo; bool SyncSessionTest :: isValuePassedTrue; int SyncSessionPluginRunnerTest :: testValue; void SyncSessionTest :: init() { iNullPluginRunner = 0; // initialize SyncSession iSyncProfile = new SyncProfile("foo"); iSyncProfile->setEnabled(true); iSyncProfile->setSyncType(SyncProfile::SYNC_SCHEDULED); // TODO: need to include profile creation using xml // SyncSession pointer without iPluginRunner initialization iSyncSession = new SyncSession(iSyncProfile); // check whether the given profile is assigned to SyncSession->iProfile or not QCOMPARE(iSyncSession->profile(), iSyncProfile); // initialize iPluginRunner PluginManager samplePluginManager; iSyncSessionPluginRunnerTest = new SyncSessionPluginRunnerTest("testPlugin", &samplePluginManager, 0 ); } void SyncSessionTest :: cleanup() { // release the allocated memory // profile name will be deallocated in SyncSession. So, not deallocating delete iSyncSession ; iSyncSession = NULL; delete iSyncSessionPluginRunnerTest; } void SyncSessionTest :: testPluginRunner() { /* testing setPluginRunner() & pluginRunner() */ // set iPluginRunner to a value and check whether the same exists in iPluginRunner /* by passing a valid PluginRunner pointer to SyncSession, the class should be able to * register with the signals successfully. If it not a valid PluginRunner pointer, * if should throw warnings */ iSyncSession->setPluginRunner(iSyncSessionPluginRunnerTest, true); QCOMPARE(iSyncSession->iPluginRunnerOwned, true); QCOMPARE(iSyncSession->pluginRunner(), iSyncSessionPluginRunnerTest.data()); // Check whether the same value is set to iPluginRunner or not QCOMPARE(iSyncSession->iPluginRunner, iSyncSession->pluginRunner()); iSyncSession->onDestroyed(iSyncSessionPluginRunnerTest); QCOMPARE(iSyncSession->pluginRunner(), iNullPluginRunner); /* set NULL to iPluginRunner and test */ iSyncSession->setPluginRunner(iNullPluginRunner, false); QCOMPARE(iSyncSession->iPluginRunnerOwned, false); QCOMPARE(iSyncSession->iPluginRunner, iNullPluginRunner); // Check whether the same value is set to iPluginRunner or not QCOMPARE(iSyncSession->iPluginRunner, iSyncSession->pluginRunner()); iSyncSession->onDestroyed(iSyncSessionPluginRunnerTest); QCOMPARE(iSyncSession->pluginRunner(), iNullPluginRunner); } void SyncSessionTest :: testProfile() { /* testing profile() and profileName() */ const QString PROFILENAME ="Profile"; iSyncSession->iProfile->setName(PROFILENAME); SyncProfile *sampleProfile = iSyncSession->profile(); QCOMPARE(PROFILENAME, sampleProfile->name()); QString profileName = iSyncSession->profileName(); QVERIFY(profileName != 0); QCOMPARE(iSyncSession->profileName(), iSyncSession->iProfile->name()); } void SyncSessionTest :: testStartAbortStop() { bool isStarted; // testing with iNullPluginRunner iSyncSession->setPluginRunner(iNullPluginRunner, true); QCOMPARE(iNullPluginRunner, iSyncSession->pluginRunner()); /* testing start() */ isStarted = iSyncSession->start(); QCOMPARE(isStarted, false); /* testing abort() */ iSyncSession->abort(); QVERIFY(!iSyncSession->iAborted); // stop() neither updates nor calls other functions for iPluginRunner NULL value. so, omitting this function for NULL value // testing with iSyncSessionPluginRunnerTest iSyncSession->setPluginRunner(iSyncSessionPluginRunnerTest, true); QCOMPARE(iSyncSessionPluginRunnerTest.data(), iSyncSession->iPluginRunner); /* testing start() */ // test start() with both possible values isValuePassedTrue = false; isStarted = iSyncSession->start(); QCOMPARE(isStarted, isValuePassedTrue); isValuePassedTrue = true; isStarted = iSyncSession->start(); QCOMPARE(isStarted, isValuePassedTrue); /* testing abort() */ iSyncSession->abort(); QCOMPARE(SyncSessionPluginRunnerTest::testValue, 2); iSyncSession->stop(); QCOMPARE(SyncSessionPluginRunnerTest::testValue, 3); } void SyncSessionTest :: testResults() { SyncResults results1; results1.setMajorCode(Buteo::SyncResults::SYNC_RESULT_CANCELLED); iSyncSession->updateResults(results1); SyncResults results2 = iSyncSession->results(); QCOMPARE(results1.majorCode(), results2.majorCode()); // testing setFailureResult() iSyncSession->setFailureResult(Buteo::SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::INTERNAL_ERROR); SyncResults givenResult = iSyncSession->results(); SyncResults receivedtResult = iSyncSession->results(); QCOMPARE(givenResult.majorCode(), receivedtResult.majorCode()); } void SyncSessionTest :: testScheduled() { /* testing both setScheduled() & isScheduled() */ bool givenScheduledData = false; iSyncSession->setScheduled(givenScheduledData); QCOMPARE(iSyncSession->isScheduled(), givenScheduledData); givenScheduledData = true; iSyncSession->setScheduled(givenScheduledData); QCOMPARE(iSyncSession->isScheduled(), givenScheduledData); } void SyncSessionTest :: testStorages() { // TODO: need to check before and after calling reserveStorage() // testing reserveStorages() StorageBooker *myTestStorageBooker = new StorageBooker ; QString oneStorage; foreach (QString storage, iSyncProfile->storageBackendNames()) { // take one name of storage to check the storage oneStorage = storage; break; } QVERIFY(myTestStorageBooker->isStorageAvailable(oneStorage, iSyncProfile->name())); bool isSuccess = iSyncSession->reserveStorages(myTestStorageBooker); QCOMPARE(iSyncSession->iStorageBooker, myTestStorageBooker); QVERIFY(isSuccess); QVERIFY(myTestStorageBooker->isStorageAvailable(oneStorage, iSyncProfile->name())); //testing releaseStorages() // releaseStorages() can be verified iSyncSession->releaseStorages(); QVERIFY(myTestStorageBooker->isStorageAvailable(oneStorage, iSyncProfile->name())); if(myTestStorageBooker) { delete myTestStorageBooker; myTestStorageBooker = NULL; } } void SyncSessionTest :: testOnSuccess() { const QString PROFILE = "sampleProfile"; const QString MESSAGE = "testMessage"; iSyncSession->setPluginRunner(iSyncSessionPluginRunnerTest, true); iSyncSession->onSuccess(PROFILE, MESSAGE); QCOMPARE(iSyncSession->iMessage, MESSAGE); QVERIFY(iSyncSession->iFinished); // testing iStatus when onSuccess() is called iSyncSession->iAborted = false; iSyncSession->onSuccess(PROFILE, MESSAGE); QCOMPARE(iSyncSession->iStatus, Sync::SYNC_DONE); iSyncSession->iAborted = true; iSyncSession->onSuccess(PROFILE, MESSAGE); QCOMPARE(iSyncSession->iStatus, Sync::SYNC_ABORTED); isValuePassedTrue = iSyncSession->isScheduled(); QCOMPARE(iSyncSession->iPluginRunner->syncResults().majorCode(), iSyncSession->iResults.majorCode()); QCOMPARE(iSyncSession->isScheduled(), iSyncSession->iResults.isScheduled()); // finished() signal is checked in testOnDone() } void SyncSessionTest :: testOnError() { const QString MESSAGE = "testMessage"; int errorCode = 4; iSyncSession->setPluginRunner(iSyncSessionPluginRunnerTest, true); iSyncSession->onError("sampleProfile", MESSAGE, errorCode); QCOMPARE(iSyncSession->iMessage, MESSAGE); QCOMPARE(iSyncSession->iErrorCode, errorCode); QCOMPARE(iSyncSession->iStatus, Sync::SYNC_ERROR); QVERIFY(iSyncSession->iFinished); isValuePassedTrue = iSyncSession->iScheduled; QCOMPARE(iSyncSession->iPluginRunner->syncResults().majorCode(), iSyncSession->iResults.majorCode()); QCOMPARE(iSyncSession->results().isScheduled(), iSyncSession->iResults.isScheduled()); } void SyncSessionTest :: testOnTransferProgress() { // registering metatypes that are not known qRegisterMetaType("Sync::TransferDatabase"); qRegisterMetaType("Sync::TransferType"); QSignalSpy sampleSpy(iSyncSession, SIGNAL(transferProgress(QString, Sync::TransferDatabase, Sync::TransferType,QString,int))); QVERIFY(iSyncSessionPluginRunnerTest); // call functions that emit transferProgress() signal iSyncSession->onTransferProgress("profile", Sync::LOCAL_DATABASE, Sync::ITEM_ADDED, "text", 1); QCOMPARE(sampleSpy.count(), 1); } void SyncSessionTest :: testOnDone() { // registering unknown metatype qRegisterMetaType("Sync::SyncStatus"); QSignalSpy sampleSpy(iSyncSession, SIGNAL(finished(QString, Sync::SyncStatus,QString, int))); // call functions that emit finished() signal iSyncSession->onDone(); QCOMPARE(sampleSpy.count(), 1); int errorCode = 1; iSyncSession->onSuccess("testProfile", "testMessage"); QCOMPARE(sampleSpy.count(), 2); iSyncSession->onError("testProfile", "testMessage", errorCode); QCOMPARE(sampleSpy.count(), 3); } // ############################################ /* * Starting SyncSessionPluginRunnerTest class */ // ############################################ SyncSessionPluginRunnerTest :: SyncSessionPluginRunnerTest(const QString &aPluginName, PluginManager *aPluginMgr, PluginCbInterface *aPluginCbIf) : PluginRunner(PLUGIN_CLIENT, aPluginName, aPluginMgr, aPluginCbIf) { } bool SyncSessionPluginRunnerTest :: init() { /* Checking whether control is coming to PluginRuneer derived class or not */ if(SyncSessionTest::isValuePassedTrue ) return true; else return false; } bool SyncSessionPluginRunnerTest :: start() { /* Checking whether control is coming to PluginRuneer derived class or not */ if(SyncSessionTest::isValuePassedTrue ) return true; else return false; } void SyncSessionPluginRunnerTest ::stop () { // check the value after returning to the calling function testValue = 3; } void SyncSessionPluginRunnerTest :: abort(Sync::SyncStatus /*aStatus*/) { // check the value after returning to the calling function testValue = 2; } SyncResults SyncSessionPluginRunnerTest :: syncResults() { SyncResults results; results.setScheduled(SyncSessionTest::isValuePassedTrue); return results; } SyncPluginBase* SyncSessionPluginRunnerTest :: plugin() { // This is not being used by SyncSession. returning NULL to supress compile warning return (SyncPluginBase*)0; } bool SyncSessionPluginRunnerTest :: cleanUp() { // check the value after returning to the calling function return true; } QTEST_MAIN(Buteo::SyncSessionTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/AccountsHelperTest.pro0000644000015600001650000000004312634332753031021 0ustar pbuserpbgroup00000000000000include(msyncdtestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/ServerThreadTest.cpp0000644000015600001650000000705412634332753030473 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ServerThreadTest.h" #include "PluginManager.h" using namespace Buteo; const QString PGNAME = "Plugin"; const QString PFNAME = "Profile"; QString TYPE = Profile::TYPE_SERVER; /* Defining the pure virtual functions of the base classes */ bool ServerPluginDerived::startListen() { return true; } void ServerPluginDerived::stopListen() { } void ServerPluginDerived::suspend() { } void ServerPluginDerived::resume() { } bool ServerPluginDerived::cleanUp() { return true; } bool ServerPluginDerived::init() { if (iTestSignal == true) { return false; } return true; } bool ServerPluginDerived::uninit() { return true; } void ServerPluginDerived::connectivityStateChanged(Sync::ConnectivityType, bool) { } /* Constructor of the serverPluginDerived Class */ ServerPluginDerived::ServerPluginDerived(const QString& aPluginName, const Profile& aProfile, PluginCbInterface* aCbInterface) : ServerPlugin(aPluginName, aProfile, aCbInterface), iTestSignal(false) { } void ServerThreadTest::initTestCase() { iThreadreturn = false; iProfile = new Profile(PFNAME, TYPE); iServerThread = new ServerThread(); iPluginDerived = new ServerPluginDerived(PGNAME, *iProfile, NULL); iThreadTestSp = iPluginDerived; } void ServerThreadTest::cleanupTestCase() { QVERIFY(iProfile != 0); delete iProfile; QVERIFY(iPluginDerived != 0); delete iPluginDerived; QVERIFY(iServerThread != 0); delete iServerThread; iProfile = 0; iPluginDerived = 0; iServerThread = 0; iThreadTestSp = 0; } void ServerThreadTest::testServerThreadConstructor() { QVERIFY(iServerThread->getPlugin() == NULL); QCOMPARE(iServerThread->iRunning, false); } void ServerThreadTest::testGetPlugin() { iThreadreturn =iServerThread->startThread(iThreadTestSp); QTest::qWait(20); QCOMPARE(iThreadreturn, true); QCOMPARE(iServerThread->getPlugin(), iThreadTestSp); } void ServerThreadTest::testGetProfileName() { QCOMPARE(iThreadreturn, true); QCOMPARE(iServerThread->getProfileName(), PFNAME); } void ServerThreadTest::testThread() { //The Thread is already started in testGetPlugin() QCOMPARE(iServerThread->startThread(iThreadTestSp), false); QCOMPARE(iServerThread->iRunning, true); QCOMPARE(iServerThread->getPlugin(), iThreadTestSp); } void ServerThreadTest::testStopThErrorSignal() { QVERIFY(iServerThread != 0); iServerThread->stopThread(); iServerThread->wait(9000); iPluginDerived->iTestSignal = true; //Test for the signal QSignalSpy spy(iServerThread, SIGNAL(initError(const QString &, const QString &, int))); QCOMPARE(iServerThread->startThread(iThreadTestSp), true); QTest::qWait(20); QCOMPARE(spy.count(), 1); } QTEST_MAIN(Buteo::ServerThreadTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/TransportTrackerTest.pro0000644000015600001650000000004312634332753031412 0ustar pbuserpbgroup00000000000000include(msyncdtestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/msyncdtests/StorageBookerTest.cpp0000644000015600001650000001027212634332753030637 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "StorageBookerTest.h" #include "StorageBooker.h" using namespace Buteo; void StorageBookerTest::testBooking() { const QString STORAGE1 = "Storage1"; const QString STORAGE2 = "Storage2"; const QString CLIENT1 = "Client1"; const QString CLIENT2 = "Client2"; const QString EMPTY_CLIENT = ""; StorageBooker booker; QStringList allStorages; allStorages << STORAGE1 << STORAGE2; // No reservations yet, storages are available. QCOMPARE(booker.isStorageAvailable(STORAGE1, CLIENT1), true); QCOMPARE(booker.isStorageAvailable(STORAGE1, EMPTY_CLIENT), true); QCOMPARE(booker.storagesAvailable(allStorages, CLIENT2), true); // Reserve storage1 for client1. Other clients are not able to reserve. QCOMPARE(booker.reserveStorage(STORAGE1, CLIENT1), true); QCOMPARE(booker.reserveStorage(STORAGE1, CLIENT2), false); QCOMPARE(booker.reserveStorage(STORAGE1, EMPTY_CLIENT), false); QCOMPARE(booker.isStorageAvailable(STORAGE1, CLIENT1), true); QCOMPARE(booker.isStorageAvailable(STORAGE1, CLIENT2), false); QCOMPARE(booker.storagesAvailable(allStorages, CLIENT1), true); QCOMPARE(booker.storagesAvailable(allStorages, CLIENT2), false); // Reserve more again, release gives correct remaingin ref count. QCOMPARE(booker.reserveStorage(STORAGE1, CLIENT1), true); QCOMPARE(booker.releaseStorage(STORAGE1), (unsigned)1); QCOMPARE(booker.reserveStorage(STORAGE1, CLIENT2), false); QCOMPARE(booker.releaseStorage(STORAGE1), (unsigned)0); // Another client is able to reserve after all references are released. QCOMPARE(booker.reserveStorage(STORAGE1, CLIENT2), true); QCOMPARE(booker.releaseStorage(STORAGE1), (unsigned)0); // Reserve multiple storages at a time. QCOMPARE(booker.reserveStorages(allStorages, CLIENT2), true); QCOMPARE(booker.reserveStorages(allStorages, CLIENT1), false); QCOMPARE(booker.storagesAvailable(allStorages, CLIENT2), true); QCOMPARE(booker.storagesAvailable(allStorages, CLIENT1), false); booker.releaseStorages(allStorages); QCOMPARE(booker.storagesAvailable(allStorages, CLIENT1), true); QCOMPARE(booker.reserveStorages(allStorages, EMPTY_CLIENT), true); QCOMPARE(booker.storagesAvailable(allStorages, CLIENT1), false); QCOMPARE(booker.storagesAvailable(allStorages, CLIENT2), false); QCOMPARE(booker.storagesAvailable(allStorages, EMPTY_CLIENT), false); QCOMPARE(booker.releaseStorage(STORAGE1), (unsigned)0); QCOMPARE(booker.storagesAvailable(allStorages, CLIENT1), false); QCOMPARE(booker.releaseStorage(STORAGE2), (unsigned)0); QCOMPARE(booker.storagesAvailable(allStorages, CLIENT1), true); // Reserve all, reserve individual, release individual, release all. QCOMPARE(booker.reserveStorages(allStorages, CLIENT1), true); QCOMPARE(booker.reserveStorage(STORAGE1, CLIENT1), true); QCOMPARE(booker.reserveStorage(STORAGE2, CLIENT1), true); QCOMPARE(booker.reserveStorage(STORAGE1, CLIENT2), false); QCOMPARE(booker.reserveStorage(STORAGE2, CLIENT2), false); QCOMPARE(booker.releaseStorage(STORAGE1), (unsigned)1); QCOMPARE(booker.releaseStorage(STORAGE2), (unsigned)1); QCOMPARE(booker.storagesAvailable(allStorages, CLIENT2), false); booker.releaseStorages(allStorages); QCOMPARE(booker.storagesAvailable(allStorages, CLIENT2), true); } QTEST_MAIN(Buteo::StorageBookerTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/tests.xml0000644000015600001650000001271312634332753024033 0ustar pbuserpbgroup00000000000000 systemctl --user stop msyncd systemctl --user start msyncd /opt/tests/buteo-syncfw/runstarget.sh msyncdtests/AccountsHelperTest /opt/tests/buteo-syncfw/runstarget.sh msyncdtests/ClientPluginRunnerTest /opt/tests/buteo-syncfw/runstarget.sh msyncdtests/ClientThreadTest /opt/tests/buteo-syncfw/runstarget.sh msyncdtests/PluginRunnerTest /opt/tests/buteo-syncfw/runstarget.sh msyncdtests/ServerActivatorTest /opt/tests/buteo-syncfw/runstarget.sh msyncdtests/ServerPluginRunnerTest /opt/tests/buteo-syncfw/runstarget.sh msyncdtests/ServerThreadTest /opt/tests/buteo-syncfw/runstarget.sh msyncdtests/StorageBookerTest /opt/tests/buteo-syncfw/runstarget.sh msyncdtests/SyncBackupTest /opt/tests/buteo-syncfw/runstarget.sh msyncdtests/SyncQueueTest /opt/tests/buteo-syncfw/runstarget.sh msyncdtests/SyncSessionTest /opt/tests/buteo-syncfw/runstarget.sh msyncdtests/SyncSigHandlerTest /opt/tests/buteo-syncfw/runstarget.sh msyncdtests/SynchronizerTest /opt/tests/buteo-syncfw/runstarget.sh msyncdtests/TransportTrackerTest /opt/tests/buteo-syncfw/runstarget.sh pluginmanagertests/ClientPluginTest /opt/tests/buteo-syncfw/runstarget.sh pluginmanagertests/DeletedItemsIdStorageTest /opt/tests/buteo-syncfw/runstarget.sh pluginmanagertests/ServerPluginTest /opt/tests/buteo-syncfw/runstarget.sh pluginmanagertests/StoragePluginTest /opt/tests/buteo-syncfw/runstarget.sh syncprofiletests/ProfileFactoryTest /opt/tests/buteo-syncfw/runstarget.sh syncprofiletests/ProfileFieldTest /opt/tests/buteo-syncfw/runstarget.sh syncprofiletests/ProfileManagerTest /opt/tests/buteo-syncfw/runstarget.sh syncprofiletests/ProfileTest /opt/tests/buteo-syncfw/runstarget.sh syncprofiletests/StorageProfileTest /opt/tests/buteo-syncfw/runstarget.sh syncprofiletests/SyncLogTest /opt/tests/buteo-syncfw/runstarget.sh syncprofiletests/SyncProfileTest /opt/tests/buteo-syncfw/runstarget.sh syncprofiletests/SyncScheduleTest buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/tests_common.pri0000644000015600001650000000513112634332753025371 0ustar pbuserpbgroup00000000000000isEmpty(TESTS_COMMON_PRI_INCLUDED) { TESTS_COMMON_PRI_INCLUDED = 1 equals(QT_MAJOR_VERSION, 4): DASH_QT_VERSION = "" equals(QT_MAJOR_VERSION, 5): DASH_QT_VERSION = "-qt5" equals(QT_MAJOR_VERSION, 4): NODASH_QT_VERSION = "" equals(QT_MAJOR_VERSION, 5): NODASH_QT_VERSION = "5" tests_subdir = $$relative_path($$dirname(_PRO_FILE_), $${PWD}) tests_subdir_r = $$relative_path($${PWD}, $$dirname(_PRO_FILE_)) QT += testlib \ core \ dbus \ xml \ dbus \ network \ sql QT -= gui CONFIG += link_pkgconfig link_prl PKGCONFIG += dbus-1 equals(QT_MAJOR_VERSION, 5): PKGCONFIG += Qt5SystemInfo LIBS += -lgcov LIBS += -L$${OUT_PWD}/$${tests_subdir_r}/../../libbuteosyncfw PKGCONFIG += libsignon-qt$${NODASH_QT_VERSION} accounts-qt$${NODASH_QT_VERSION} LIBS += -lbuteosyncfw$${NODASH_QT_VERSION} # This is needed to avoid adding the /usr/lib link directory before the # newer version in the present directories QMAKE_LIBDIR_QT = $${OUT_PWD}/$${tests_subdir_r}/../../libbuteosyncfw DEFINES += SYNCFW_UNIT_TESTS QMAKE_CXXFLAGS += -Wall \ -fprofile-arcs \ -ftest-coverage \ -g -O0 INCLUDEPATH = \ $${PWD} \ $${PWD}/../.. \ $${PWD}/../../libbuteosyncfw/clientfw \ $${PWD}/../../libbuteosyncfw/common \ $${PWD}/../../libbuteosyncfw/pluginmgr \ $${PWD}/../../libbuteosyncfw/profile \ $${PWD}/../../msyncd \ # This way time to run qmake is reduced by ~30% equals(QT_MAJOR_VERSION, 5): CONFIG -= depend_includepath DEPENDPATH = \ $${PWD}/../../libbuteosyncfw/clientfw \ $${PWD}/../../libbuteosyncfw/common \ $${PWD}/../../libbuteosyncfw/pluginmgr \ $${PWD}/../../libbuteosyncfw/profile \ $${PWD}/../../msyncd \ # TODO: append $${DASH_QT_VERSION} INSTALL_TESTDIR = /opt/tests/buteo-syncfw INSTALL_TESTDATADIR = $${INSTALL_TESTDIR}/data # ##################################################################### # make coverage (debug) # ##################################################################### coverage.CONFIG += recursive QMAKE_EXTRA_TARGETS += coverage CONFIG(debug,debug|release) { QMAKE_EXTRA_TARGETS += cov_cxxflags \ cov_lflags cov_cxxflags.target = coverage cov_cxxflags.depends = CXXFLAGS \ += \ -fprofile-arcs \ -ftest-coverage cov_lflags.target = coverage cov_lflags.depends = LFLAGS \ += \ -fprofile-arcs \ -ftest-coverage coverage.commands = @echo \ "Built with coverage support..." build_pass|!debug_and_release:coverage.depends = all QMAKE_CLEAN += $(OBJECTS_DIR)/*.gcda \ $(OBJECTS_DIR)/*.gcno \ $(OBJECTS_DIR)/*.gcov } } buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncfwclienttests/0000755000015600001650000000000012634333235025732 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncfwclienttests/SyncClientInterfaceTest.pro0000644000015600001650000000005112634332753033210 0ustar pbuserpbgroup00000000000000include(syncfwclienttestapplication.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncfwclienttests/syncfwclienttests.pro0000755000015600001650000000020312634332753032251 0ustar pbuserpbgroup00000000000000TEMPLATE = subdirs SUBDIRS = \ SyncClientInterfaceTest.pro \ coverage.CONFIG += recursive QMAKE_EXTRA_TARGETS += coverage buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncfwclienttests/SyncClientInterfaceTest.cpp0000755000015600001650000000652512634332753033211 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include "SyncClientInterfaceTest.h" #include "synchronizer.h" #include "Profile.h" #include "SyncSchedule.h" #include "SyncProfile.h" #include "SyncClientInterfacePrivate.h" #include using namespace Buteo; static const QString USERPROFILE_DIR = "syncprofiletests/testprofiles/user"; static const QString SYSTEMPROFILE_DIR = "syncprofiletests/testprofiles/system"; // FIXME: This test case tries to execute real sync scenario but with invalid // profiles - this simply cannot work. void SyncClientInterfaceTest::initTestCase() { iSync = new Synchronizer(NULL); iInterface = new SyncClientInterface(); qRegisterMetaType("Buteo::Profile"); qRegisterMetaType("Buteo::SyncResults"); } void SyncClientInterfaceTest::cleanupTestCase() { QVERIFY(iInterface != 0); delete iInterface; iInterface = 0; QVERIFY(iSync != 0); delete iSync; iSync = 0; } void SyncClientInterfaceTest::testIsValid() { QCOMPARE(iInterface->isValid(),false); } void SyncClientInterfaceTest::testStartSync() { QString empty(""); QCOMPARE(iInterface->startSync(empty),false); QSignalSpy sigStatus(iInterface, SIGNAL(resultsAvailable(QString,Buteo::SyncResults))); QString profile("testsync-ovi"); QCOMPARE(iInterface->startSync(profile),true); /* QCOMPARE(sigStatus.count(),1);*/ } void SyncClientInterfaceTest::testAbortSync() { QString profile("testsync-ovi"); iInterface->abortSync(profile); QStringList list = iInterface->getRunningSyncList(); sleep(1); QCOMPARE(list.size(),1); } void SyncClientInterfaceTest::testGetRunningSyncList() { QString profile("testsync-ovi"); iInterface->startSync(profile); QStringList list = iInterface->getRunningSyncList(); QCOMPARE(list.size(),1); } void SyncClientInterfaceTest::testSetSyncSchedule() { QString profile("testsync-ovi"); Buteo::SyncSchedule schedule; QSignalSpy sigProfile(iInterface, SIGNAL(profileChanged(QString,int,QString))); QCOMPARE(iInterface->setSyncSchedule(profile,schedule),true); } void SyncClientInterfaceTest::testAddProfile() { Buteo::SyncProfile profileToAdd("testsync-ovi"); QVERIFY(iInterface->updateProfile(profileToAdd)); } void SyncClientInterfaceTest::testUpdateProfile() { Buteo::SyncProfile profileToChange("testsync-ovi"); QVERIFY(iInterface->updateProfile(profileToChange)); } void SyncClientInterfaceTest::testRemoveProfile() { QString profileToChange("testsync-ovi"); QVERIFY(iInterface->removeProfile(profileToChange)); } QTEST_MAIN(Buteo::SyncClientInterfaceTest) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncfwclienttests/SyncClientInterfaceTest.h0000755000015600001650000000276012634332753032653 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SyncClientInterfaceTEST_H_ #define SyncClientInterfaceTEST_H_ #include #include "SyncClientInterface.h" namespace Buteo { class Synchronizer; class SyncProfile; class SyncClientInterfaceTest: public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void testIsValid(); void testAddProfile(); void testStartSync(); void testAbortSync(); void testGetRunningSyncList(); void testSetSyncSchedule(); void testUpdateProfile(); void testRemoveProfile(); private: Buteo::SyncClientInterface *iInterface; Synchronizer *iSync; SyncProfile *iProfile; }; } #endif /*SyncClientInterfaceTEST_H_*/ ././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncfwclienttests/syncfwclienttestapplication.pributeo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/syncfwclienttests/syncfwclienttestapplication.p0000644000015600001650000000010612634332753033750 0ustar pbuserpbgroup00000000000000include(../testapplication.pri) include(../../../msyncd/unittest.pri) buteo-syncfw-0.7.21+16.04.20151216.1/unittests/tests/runstarget.sh0000755000015600001650000000126012634332753024677 0ustar pbuserpbgroup00000000000000#!/bin/sh -e if [ $# -lt 1 ]; then echo "You need to pass test executable as an argument!" exit 1 fi TESTS_DIR="$(dirname "${0}")" TMP_DIR="/tmp/$(basename "${TESTS_DIR}")" rm -rf "${TMP_DIR}" mkdir -p "${TMP_DIR}" # Copy test data into tmp dir (read/write enabled location) mkdir -p "${TMP_DIR}/syncprofiletests" cp -a "${TESTS_DIR}/syncprofiletests/testprofiles" "${TMP_DIR}/syncprofiletests/" # Test data are searched with paths relative to CWD cd "${TMP_DIR}" export LD_LIBRARY_PATH="${TESTS_DIR}:${LD_LIBRARY_PATH}" # Accept both absolute and relative path to test executable TEST="$(cd "${TESTS_DIR}"; readlink -f "${1}")" shift exec "${TEST}" "${@}" -maxwarnings 0 buteo-syncfw-0.7.21+16.04.20151216.1/unittests/unittests.pro0000644000015600001650000000017512634332753023570 0ustar pbuserpbgroup00000000000000TEMPLATE = subdirs SUBDIRS += dummyplugins \ tests coverage.CONFIG += recursive QMAKE_EXTRA_TARGETS += coverage buteo-syncfw-0.7.21+16.04.20151216.1/unittests/dummyplugins/0000755000015600001650000000000012634333235023532 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/dummyplugins/dummyplugins.pro0000644000015600001650000000161712634332753027022 0ustar pbuserpbgroup00000000000000TEMPLATE = subdirs SUBDIRS += dummyclient \ dummyserver \ dummystorage # ##################################################################### # make coverage (debug) # ##################################################################### coverage.CONFIG += recursive QMAKE_EXTRA_TARGETS += coverage CONFIG(debug,debug|release){ QMAKE_EXTRA_TARGETS += cov_cxxflags \ cov_lflags cov_cxxflags.target = coverage cov_cxxflags.depends = CXXFLAGS \ += \ -fprofile-arcs \ -ftest-coverage cov_lflags.target = coverage cov_lflags.depends = LFLAGS \ += \ -fprofile-arcs \ -ftest-coverage coverage.commands = @echo \ "Built with coverage support..." build_pass|!debug_and_release : coverage.depends = all QMAKE_CLEAN += $(OBJECTS_DIR)/*.gcda \ $(OBJECTS_DIR)/*.gcno \ $(OBJECTS_DIR)/*.gcov } buteo-syncfw-0.7.21+16.04.20151216.1/unittests/dummyplugins/dummyserver/0000755000015600001650000000000012634333235026114 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/dummyplugins/dummyserver/DummyServer.h0000644000015600001650000000343112634332753030554 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef DUMMYSERVER_H #define DUMMYSERVER_H #include "ServerPlugin.h" namespace Buteo { class DummyServer : public ServerPlugin { public: DummyServer( const QString& aPluginName, const Profile& aProfile, PluginCbInterface *aCbInterface ); virtual ~DummyServer(); virtual bool init(); virtual bool uninit(); virtual bool startListen(); virtual void stopListen(); virtual void suspend(); virtual void resume(); virtual bool cleanUp(); public slots: virtual void connectivityStateChanged( Sync::ConnectivityType aType, bool aState ); }; extern "C" DummyServer* createPlugin( const QString& aPluginName, const Profile& aProfile, PluginCbInterface *aCbInterface ); extern "C" void destroyPlugin( DummyServer *aServer ); } #endif // DUMMYSERVER_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/dummyplugins/dummyserver/dummyserver.pro0000644000015600001650000000252212634332753031225 0ustar pbuserpbgroup00000000000000TEMPLATE = lib TARGET = hdummy-server DEPENDPATH += . INCLUDEPATH += . \ ../../.. \ ../../../libbuteosyncfw/common \ ../../../libbuteosyncfw/pluginmgr \ ../../../libbuteosyncfw/profile CONFIG += plugin QT -= gui #input HEADERS += DummyServer.h SOURCES += DummyServer.cpp #clean QMAKE_CLEAN += $(TARGET) $(TARGET0) $(TARGET1) $(TARGET2) QMAKE_CLEAN += $(OBJECTS_DIR)/*.gcda $(OBJECTS_DIR)/*.gcno $(OBJECTS_DIR)/*.gcov $(OBJECTS_DIR)/moc_* target.path = /opt/tests/buteo-syncfw/ INSTALLS += target # ##################################################################### # make coverage (debug) # ##################################################################### coverage.CONFIG += recursive QMAKE_EXTRA_TARGETS += coverage CONFIG(debug,debug|release){ QMAKE_EXTRA_TARGETS += cov_cxxflags \ cov_lflags cov_cxxflags.target = coverage cov_cxxflags.depends = CXXFLAGS \ += \ -fprofile-arcs \ -ftest-coverage cov_lflags.target = coverage cov_lflags.depends = LFLAGS \ += \ -fprofile-arcs \ -ftest-coverage coverage.commands = @echo \ "Built with coverage support..." build_pass|!debug_and_release : coverage.depends = all QMAKE_CLEAN += $(OBJECTS_DIR)/*.gcda \ $(OBJECTS_DIR)/*.gcno \ $(OBJECTS_DIR)/*.gcov } buteo-syncfw-0.7.21+16.04.20151216.1/unittests/dummyplugins/dummyserver/DummyServer.cpp0000644000015600001650000000365712634332753031121 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "DummyServer.h" using namespace Buteo; extern "C" DummyServer* createPlugin( const QString& aPluginName, const Profile& aProfile, PluginCbInterface *aCbInterface ) { return new DummyServer( aPluginName, aProfile, aCbInterface ); } extern "C" void destroyPlugin( DummyServer *aServer ) { delete aServer; } DummyServer::DummyServer( const QString& aPluginName, const Profile& aProfile, PluginCbInterface *aCbInterface ) : ServerPlugin( aPluginName, aProfile, aCbInterface ) { } DummyServer::~DummyServer() { } bool DummyServer::init() { return true; } bool DummyServer::uninit() { return true; } bool DummyServer::startListen() { return true; } bool DummyServer::cleanUp() { return true; } void DummyServer::stopListen() { } void DummyServer::suspend() { } void DummyServer::resume() { } void DummyServer::connectivityStateChanged( Sync::ConnectivityType /*aType*/, bool /*aState*/ ) { } buteo-syncfw-0.7.21+16.04.20151216.1/unittests/dummyplugins/dummystorage/0000755000015600001650000000000012634333235026252 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/dummyplugins/dummystorage/DummyStorage.h0000644000015600001650000000513112634332753031047 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef DUMMYSTORAGE_H #define DUMMYSTORAGE_H #include "StoragePlugin.h" namespace Buteo { class DummyStorage : public StoragePlugin { public: DummyStorage( const QString& aPluginName ); virtual ~DummyStorage(); virtual bool init( const QMap& aProperties ); virtual bool uninit(); virtual bool getAllItems( QList& aItems ); virtual bool getNewItems( QList& aNewItems, const QDateTime& aTime ); virtual bool getModifiedItems( QList& aModifiedItems, const QDateTime& aTime ); virtual bool getDeletedItems( QList& aDeletedItems, const QDateTime& aTime ); virtual bool getAllItemIds( QList& aItems ); virtual bool getNewItemIds( QList& aNewItems, const QDateTime& aTime ); virtual bool getModifiedItemIds( QList& aModifiedItems, const QDateTime& aTime ); virtual bool getDeletedItemIds( QList& aDeletedItems, const QDateTime& aTime ); virtual StorageItem* newItem(); virtual StorageItem* getItem( const QString& aItemId ); virtual QList getItems(const QStringList& aItemIdList ); virtual OperationStatus addItem( StorageItem& aItem ); virtual QList addItems( const QList& aItems ); virtual OperationStatus modifyItem( StorageItem& aItem ); virtual QList modifyItems( const QList& aItems ); virtual OperationStatus deleteItem( const QString& aItemId ); virtual QList deleteItems( const QList& aItemIds ); }; extern "C" StoragePlugin* createPlugin( const QString& aPluginName ); extern "C" void destroyPlugin( StoragePlugin* aStorage ); } #endif // DUMMYSTORAGE_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/dummyplugins/dummystorage/dummystorage.pro0000644000015600001650000000252612634332753031525 0ustar pbuserpbgroup00000000000000TEMPLATE = lib TARGET = hdummy-storage DEPENDPATH += . INCLUDEPATH += . \ ../../.. \ ../../../libbuteosyncfw/common \ ../../../libbuteosyncfw/pluginmgr \ ../../../libbuteosyncfw/profile CONFIG += plugin QT -= gui #input HEADERS += DummyStorage.h SOURCES += DummyStorage.cpp #clean QMAKE_CLEAN += $(TARGET) $(TARGET0) $(TARGET1) $(TARGET2) QMAKE_CLEAN += $(OBJECTS_DIR)/*.gcda $(OBJECTS_DIR)/*.gcno $(OBJECTS_DIR)/*.gcov $(OBJECTS_DIR)/moc_* target.path = /opt/tests/buteo-syncfw/ INSTALLS += target # ##################################################################### # make coverage (debug) # ##################################################################### coverage.CONFIG += recursive QMAKE_EXTRA_TARGETS += coverage CONFIG(debug,debug|release){ QMAKE_EXTRA_TARGETS += cov_cxxflags \ cov_lflags cov_cxxflags.target = coverage cov_cxxflags.depends = CXXFLAGS \ += \ -fprofile-arcs \ -ftest-coverage cov_lflags.target = coverage cov_lflags.depends = LFLAGS \ += \ -fprofile-arcs \ -ftest-coverage coverage.commands = @echo \ "Built with coverage support..." build_pass|!debug_and_release : coverage.depends = all QMAKE_CLEAN += $(OBJECTS_DIR)/*.gcda \ $(OBJECTS_DIR)/*.gcno \ $(OBJECTS_DIR)/*.gcov } buteo-syncfw-0.7.21+16.04.20151216.1/unittests/dummyplugins/dummystorage/DummyStorage.cpp0000644000015600001650000000713312634332753031406 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "DummyStorage.h" using namespace Buteo; extern "C" StoragePlugin* createPlugin( const QString& aPluginName ) { return new DummyStorage( aPluginName ); } extern "C" void destroyPlugin( StoragePlugin* aStorage ) { delete aStorage; } DummyStorage::DummyStorage( const QString& aPluginName ) : StoragePlugin( aPluginName ) { } DummyStorage::~DummyStorage() { } bool DummyStorage::init( const QMap& /*aProperties*/ ) { return true; } bool DummyStorage::uninit() { return true; } bool DummyStorage::getAllItems( QList& /*aItems*/ ) { return true; } bool DummyStorage::getNewItems( QList& /*aNewItems*/, const QDateTime& /*aTime*/ ) { return true; } bool DummyStorage::getModifiedItems( QList& /*aModifiedItems*/, const QDateTime& /*aTime*/ ) { return true; } bool DummyStorage::getDeletedItems( QList& /*aDeletedItems*/, const QDateTime& /*aTime*/ ) { return true; } bool DummyStorage::getAllItemIds( QList& /*aItems*/ ) { return true; } bool DummyStorage::getNewItemIds( QList& /*aNewItems*/, const QDateTime& /*aTime*/ ) { return true; } bool DummyStorage::getModifiedItemIds( QList& /*aModifiedItems*/, const QDateTime& /*aTime*/ ) { return true; } bool DummyStorage::getDeletedItemIds( QList& /*aDeletedItems*/, const QDateTime& /*aTime*/ ) { return true; } StorageItem* DummyStorage::newItem() { return NULL; } StorageItem* DummyStorage::getItem( const QString& /*aItemId*/ ) { return NULL; } QList DummyStorage::getItems(const QStringList& aItemIdList ) { QList items; return items; } StoragePlugin::OperationStatus DummyStorage::addItem( StorageItem& /*aItem*/ ) { return STATUS_OK; } QList DummyStorage::addItems( const QList& aItems ) { QList statuses; for( int i = 0; i < aItems.count(); ++i ) { statuses.append( STATUS_OK ); } return statuses; } StoragePlugin::OperationStatus DummyStorage::modifyItem( StorageItem& /*aItem*/ ) { return STATUS_OK; } QList DummyStorage::modifyItems( const QList& aItems ) { QList statuses; for( int i = 0; i < aItems.count(); ++i ) { statuses.append( STATUS_OK ); } return statuses; } StoragePlugin::OperationStatus DummyStorage::deleteItem( const QString& /*aItemId*/ ) { return STATUS_OK; } QList DummyStorage::deleteItems( const QList& aItemIds ) { QList statuses; for( int i = 0; i < aItemIds.count(); ++i ) { statuses.append( STATUS_OK ); } return statuses; } buteo-syncfw-0.7.21+16.04.20151216.1/unittests/dummyplugins/dummyclient/0000755000015600001650000000000012634333235026064 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/unittests/dummyplugins/dummyclient/DummyClient.h0000644000015600001650000000340712634332753030477 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef DUMMYCLIENT_H #define DUMMYCLIENT_H #include "ClientPlugin.h" namespace Buteo { class DummyClient : public ClientPlugin { public: DummyClient( const QString& aPluginName, const SyncProfile& aProfile, PluginCbInterface* aCbInterface ); ~DummyClient(); virtual bool init(); virtual bool uninit(); virtual bool startSync(); virtual void abortSync(); virtual bool cleanUp(); virtual SyncResults getSyncResults(); public slots: virtual void connectivityStateChanged( Sync::ConnectivityType aType, bool aState ); }; extern "C" DummyClient* createPlugin( const QString& aPluginName, const SyncProfile& aProfile, PluginCbInterface* aCbInterface ); extern "C" void destroyPlugin( DummyClient* aClient ); } #endif // DUMMYCLIENT_H buteo-syncfw-0.7.21+16.04.20151216.1/unittests/dummyplugins/dummyclient/DummyClient.cpp0000644000015600001650000000372012634332753031030 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "DummyClient.h" using namespace Buteo; extern "C" DummyClient* createPlugin( const QString& aPluginName, const SyncProfile& aProfile, PluginCbInterface *aCbInterface ) { return new DummyClient( aPluginName, aProfile, aCbInterface ); } extern "C" void destroyPlugin( DummyClient* aClient ) { delete aClient; } DummyClient::DummyClient( const QString& aPluginName, const SyncProfile& aProfile, PluginCbInterface* aCbInterface ) : ClientPlugin( aPluginName, aProfile, aCbInterface ) { } DummyClient::~DummyClient() { } bool DummyClient::init() { return true; } bool DummyClient::uninit() { return true; } bool DummyClient::startSync() { return true; } void DummyClient::abortSync() { } bool DummyClient::cleanUp() { return false; } SyncResults DummyClient::getSyncResults() { SyncResults results; return results; } void DummyClient::connectivityStateChanged( Sync::ConnectivityType /*aType*/, bool /*aState*/ ) { } buteo-syncfw-0.7.21+16.04.20151216.1/unittests/dummyplugins/dummyclient/dummyclient.pro0000644000015600001650000000252212634332753031145 0ustar pbuserpbgroup00000000000000TEMPLATE = lib TARGET = hdummy-client DEPENDPATH += . INCLUDEPATH += . \ ../../.. \ ../../../libbuteosyncfw/common \ ../../../libbuteosyncfw/pluginmgr \ ../../../libbuteosyncfw/profile QT -= gui CONFIG += plugin #input HEADERS += DummyClient.h SOURCES += DummyClient.cpp #clean QMAKE_CLEAN += $(TARGET) $(TARGET0) $(TARGET1) $(TARGET2) QMAKE_CLEAN += $(OBJECTS_DIR)/*.gcda $(OBJECTS_DIR)/*.gcno $(OBJECTS_DIR)/*.gcov $(OBJECTS_DIR)/moc_* target.path = /opt/tests/buteo-syncfw INSTALLS += target # ##################################################################### # make coverage (debug) # ##################################################################### coverage.CONFIG += recursive QMAKE_EXTRA_TARGETS += coverage CONFIG(debug,debug|release){ QMAKE_EXTRA_TARGETS += cov_cxxflags \ cov_lflags cov_cxxflags.target = coverage cov_cxxflags.depends = CXXFLAGS \ += \ -fprofile-arcs \ -ftest-coverage cov_lflags.target = coverage cov_lflags.depends = LFLAGS \ += \ -fprofile-arcs \ -ftest-coverage coverage.commands = @echo \ "Built with coverage support..." build_pass|!debug_and_release : coverage.depends = all QMAKE_CLEAN += $(OBJECTS_DIR)/*.gcda \ $(OBJECTS_DIR)/*.gcno \ $(OBJECTS_DIR)/*.gcov } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/0000755000015600001650000000000012634333235020230 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/com.meego.msyncd.service0000644000015600001650000000007312634332753024763 0ustar pbuserpbgroup00000000000000[D-BUS Service] Name=com.meego.msyncd Exec=/usr/bin/msyncd buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncSigHandler.cpp0000644000015600001650000000551012634332753023616 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include #include #include "SyncSigHandler.h" #include "LogMacros.h" int SyncSigHandler::iSigHupFd[2]; int SyncSigHandler::iSigTermFd[2]; SyncSigHandler::SyncSigHandler(QObject *aParent, const char */*aName*/) : QObject(aParent) { FUNCTION_CALL_TRACE; //Adding signal hanlder for unix Signals signal(SIGTERM, termSignalHandler); signal(SIGINT, termSignalHandler); signal(SIGHUP, hupSignalHandler); //Adding socketpair to monitor those fd's. if (::socketpair(AF_UNIX, SOCK_STREAM, 0, iSigHupFd)) { LOG_CRITICAL("Couldn't create HUP socketpair"); } if (::socketpair(AF_UNIX, SOCK_STREAM, 0, iSigTermFd)) { LOG_CRITICAL("Couldn't create TERM socketpair"); } //SocketNotifier for read those fd's. iSigHup = new QSocketNotifier(iSigHupFd[1], QSocketNotifier::Read, this); connect(iSigHup, SIGNAL(activated(int)), this, SLOT(handleSigHup())); iSigTerm = new QSocketNotifier(iSigTermFd[1], QSocketNotifier::Read, this); connect(iSigTerm, SIGNAL(activated(int)), this, SLOT(handleSigTerm())); } SyncSigHandler::~SyncSigHandler() { FUNCTION_CALL_TRACE; if (iSigHup) { delete iSigHup; iSigHup = 0; } if (iSigTerm) { delete iSigTerm; iSigTerm = 0; } } // Linux signal handler. //This application is shutdown by sending a SIGTERM to it. void SyncSigHandler::termSignalHandler(int /*signal*/) { char a = 1; ::write(iSigTermFd[0], &a, sizeof(a)); } void SyncSigHandler::hupSignalHandler(int /*signal*/) { // Do nothing } //Qt Slot will eventually get called corresponding to Unix signal. void SyncSigHandler::handleSigTerm() { FUNCTION_CALL_TRACE; iSigTerm->setEnabled(false); char tmp; ::read(iSigTermFd[1], &tmp, sizeof(tmp)); // Doing Qt stuff.Exiting application QCoreApplication::exit(0); } void SyncSigHandler::handleSigHup() { FUNCTION_CALL_TRACE; } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/gschemas/0000755000015600001650000000000012634333235022022 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/gschemas/com.meego.msyncd.gschema.xml0000644000015600001650000000057612634332753027333 0ustar pbuserpbgroup00000000000000

    Scheduled Sync over cellular Allow scheduled syncs to run over cellular connections. true buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncSigHandler.h0000644000015600001650000000611612634332753023266 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCSIGHANDLER_H #define SYNCSIGHANDLER_H #include /*! \brief About this class. * We can't call Qt functions from Unix signal handlers.We can only call async-signal-safe functions from signal handlers. * So this provides a way to use Unix signal handlers with Qt. The strategy is to have our Unix signal handler will eventually * cause a Qt signal to be emitted, and then we simply return from our Unix signal handler. * Back in our Qt program, that Qt signal gets emitted and then received by our Qt slot function, where we are safely doing Qt stuff * which weren't allowed to do in the Unix signal handler. * One simple way to make this happen is declares a socket pair in our class for each Unix signal we want to handle. * The socket pairs are declared as static data members.We also created a QSocketNotifier to monitor the read end of each socket pair, * declare your Unix signal handlers to be static class methods, and declare a slot function corresponding to each of our * Unix signal handlers. In this class, we intend to handle both the SIGHUP and SIGTERM signals. */ class SyncSigHandler : public QObject { Q_OBJECT public: /*! \brief Constructor * * @param aParent object * @param aName const char */ SyncSigHandler(QObject *aParent = 0, const char *aName = 0); /*! \brief Destructor * */ ~SyncSigHandler(); // Unix signal handlers. static void hupSignalHandler(int unused); static void termSignalHandler(int unused); public slots: /*! \brief QT signal handler to handle SIG_HUP * * @return None */ void handleSigHup(); /*! \brief QT signal handler to handle SIG_TERM * * @return None */ void handleSigTerm(); private: //socket pair for each Unix signal to handle static int iSigHupFd[2]; static int iSigTermFd[2]; //QSocketNotifier to monitor the read end of each socket pair, // declare your Unix signal handlers to be static class methods QSocketNotifier *iSigHup; QSocketNotifier *iSigTerm; #ifdef SYNCFW_UNIT_TESTS friend class SyncSigHandlerTest; #endif }; #endif // SYNCSIGHANDLER_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/UnitTest.cpp0000644000015600001650000000171112634332753022517 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2014 Jolla Ltd * * Contact: Valerio Valerio * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "UnitTest.h" #ifdef SYNCFW_UNIT_TESTS bool __SYNCFW_UNIT_TESTS_RUNTIME = true; #else bool __SYNCFW_UNIT_TESTS_RUNTIME = false; #endif buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncOnChange.cpp0000644000015600001650000001015512634332753023261 0ustar pbuserpbgroup00000000000000#include "SyncOnChange.h" #include "SyncOnChangeScheduler.h" #include "SyncProfile.h" #include "StorageChangeNotifier.h" #include "LogMacros.h" using namespace Buteo; SyncOnChange::SyncOnChange() : iStorageChangeNotifier(new StorageChangeNotifier()), iSOCScheduler(0) { FUNCTION_CALL_TRACE; } SyncOnChange::~SyncOnChange() { FUNCTION_CALL_TRACE; QStringList storages; disable(); storages = getSOCStorageNames(); for(QStringList::const_iterator storageItr = storages.constBegin(); storageItr != storages.constEnd(); ++storageItr) { cleanup(*storageItr); } delete iStorageChangeNotifier; } bool SyncOnChange::enable(const QHash >& aSOCStorageMap, SyncOnChangeScheduler* aSOCScheduler, PluginManager* aPluginManager, QStringList& aFailedStorages) { FUNCTION_CALL_TRACE; bool enabled = false; QStringList storages; iSOCStorageMap = aSOCStorageMap; iSOCScheduler = aSOCScheduler; storages = getSOCStorageNames(); iStorageChangeNotifier->loadNotifiers(aPluginManager, storages); enabled = iStorageChangeNotifier->startListen(aFailedStorages); for(QStringList::const_iterator failedStorageItr = aFailedStorages.constBegin(); failedStorageItr != aFailedStorages.constEnd(); ++failedStorageItr) { cleanup(*failedStorageItr); } if(storages.count() > aFailedStorages.count()) { QObject::connect(iStorageChangeNotifier, SIGNAL(storageChange(QString)), this, SLOT(sync(QString))); } return enabled; } void SyncOnChange::enable() { FUNCTION_CALL_TRACE; if(iStorageChangeNotifier) { QStringList aFailedStorages; bool enabled = iStorageChangeNotifier->startListen(aFailedStorages); Q_UNUSED(enabled); for(QStringList::const_iterator failedStorageItr = aFailedStorages.constBegin(); failedStorageItr != aFailedStorages.constEnd(); ++failedStorageItr) { cleanup(*failedStorageItr); } iStorageChangeNotifier->checkForChanges(); } } void SyncOnChange::disable() { FUNCTION_CALL_TRACE; iStorageChangeNotifier->stopListen(); } void SyncOnChange::disableNext() { FUNCTION_CALL_TRACE; iStorageChangeNotifier->stopListen(true); } void SyncOnChange::cleanup(const QString& aStorageName) { FUNCTION_CALL_TRACE; QList profilesList; if(iSOCStorageMap.contains(aStorageName)) { profilesList = iSOCStorageMap.value(aStorageName); } for(QList::iterator profileItr = profilesList.begin(); profileItr != profilesList.end(); ++profileItr) { delete (*profileItr); } iSOCStorageMap.remove(aStorageName); } QStringList SyncOnChange::getSOCStorageNames() { FUNCTION_CALL_TRACE; QStringList storages; for(QHash >::const_iterator storageNameItr = iSOCStorageMap.constBegin(); storageNameItr != iSOCStorageMap.constEnd(); ++storageNameItr) { storages << storageNameItr.key(); } return storages; } void SyncOnChange::sync(QString aStorageName) { FUNCTION_CALL_TRACE; QList profilesList; if(iSOCStorageMap.contains(aStorageName)) { profilesList = iSOCStorageMap.value(aStorageName); } for(QList::iterator profileItr = profilesList.begin(); profileItr != profilesList.end(); ++profileItr) { iSOCScheduler->addProfile(*profileItr); } } void SyncOnChange::addProfile(const QString& aStorageName, SyncProfile* aProfile) { FUNCTION_CALL_TRACE; bool found = false; QList profilesList = iSOCStorageMap.value(aStorageName); for(QList::iterator profileItr = profilesList.begin(); profileItr != profilesList.end(); ++profileItr) { if(*profileItr && aProfile && (*profileItr)->name() == aProfile->name()) { found = true; break; } } if(!found) { iSOCStorageMap[aStorageName].append(aProfile); } } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/ClientThread.h0000644000015600001650000000715412634332753022762 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef CLIENTTHREAD_H #define CLIENTTHREAD_H #include #include #include #include "SignOn/AuthService" #include "SignOn/Identity" namespace Buteo { class ClientPlugin; /*! \brief Thread for client plugins * */ class ClientThread : public QThread { Q_OBJECT; public: /*! \brief Constructor * */ ClientThread(); /*! \brief Destructor * */ virtual ~ClientThread(); /*! \brief Returns profile that this thread is running * * @return Profile name */ QString getProfileName() const; /*! \brief Returns plugin that this thread is running * * @return Plugin */ ClientPlugin* getPlugin() const; /*! \brief Starts client thread * * @param aClientPlugin Client plug-in to run. Plug-in is owned by the caller, * and must not be deleted while the thread is running. * @return True on success, otherwise false */ bool startThread( ClientPlugin* aClientPlugin ); /*! \brief Stops client thread * */ void stopThread(); /*! \brief Returns the results for this particular thread * */ SyncResults getSyncResults(); signals: /*! \brief Emitted when synchronization cannot be started due to an * error in plugin initialization * * @param aProfileName Name of the profile being synchronized * @param aMessage Message data related to error event * @param aErrorCode Error code */ void initError( const QString &aProfileName, const QString &aMessage, int aErrorCode); protected: /*! \brief overriding method for QThread::run */ virtual void run(); private: ClientPlugin* iClientPlugin; SyncResults iSyncResults; SignOn::Identity *iIdentity; SignOn::AuthService *iService; SignOn::AuthSession *iSession; QString iProvider; bool iRunning; mutable QMutex iMutex; #ifdef SYNCFW_UNIT_TESTS friend class ClientThreadTest; #endif /*! * \brief invokes iClientPlugin->startSync() * * It should be called when profile is ready for use, with * credentials set in the Username/Password keys. It is called * either in run() or, if the Username key starts with the * "sso-provider=" prefix, after retrieving the credentials from * SSO (queryIdentities() -> identities() -> session -> * identityResponse() -> startSync()). * * @return true for success (run thread), else failure (running * thread is no longer necessary) */ bool startSync(); private slots: void identities(const QList &identityList); void identityResponse(const SignOn::SessionData &session); void identityError(SignOn::Error error); }; } #endif // CLIENTTHREAD_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/com.meego.msyncd.xml0000644000015600001650000001331212634332753024123 0ustar pbuserpbgroup00000000000000 buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncQueue.h0000644000015600001650000000523112634332753022327 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCQUEUE_H #define SYNCQUEUE_H #include namespace Buteo { class SyncSession; /*! \brief Class for queuing sync sessions. * * The queue is sorted every time when new items are added to it, so that * the sync sessions with highest priority will be at the front of the queue. */ class SyncQueue { public: /*! \brief Adds a new profile to the queue. Queue is sorted automatically. * * \param aSession Session to add to queue */ void enqueue(SyncSession *aSession); /*! \brief Removes the sync session corresponding to the profile name and returns it. * * \return The removed item. NULL if the queue was empty. */ SyncSession *dequeue(const QString &aProfileName); /*! \brief Removes the first item from the queue and returns it. * * \return The removed item. NULL if the queue was empty. */ SyncSession *dequeue(); /*! \brief Returns the first item in the queue but does not remove it. * * \return First item of the queue. NULL if the queue is empty. */ SyncSession *head(); /*! \brief Checks if the queue is empty. * * \return Is the queue empty. */ bool isEmpty() const; /*! \brief Current size of the sync queue. * * \return Number of elements in the sync queue. */ int size() const; /*! \brief Checks if a profile with the given name is in the queue. * * \return Is the profile in the queue. */ bool contains(const QString &aProfileName) const; /*! \brief Returns as a const reference, the list of all SyncSessions * currently queued. * * \return Is the profile in the queue. */ const QList& getQueuedSyncSessions() const; private: void sort(); QQueue iItems; }; } #endif // SYNCQUEUE_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncBackupAdaptor.cpp0000644000015600001650000000604412634332753024321 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ /* * This file was generated by qdbusxml2cpp version 0.7 * Command line was: qdbusxml2cpp -a SyncBackupAdaptor -c SyncBackupAdaptor com.nokia.syncbackup.xml * * qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * This is an auto-generated file. * Do not edit! All changes made to it will be lost. */ #include "SyncBackupAdaptor.h" #include #include #include #include #include #include #include using namespace Buteo; /* * Implementation of adaptor class SyncBackupAdaptor */ SyncBackupAdaptor::SyncBackupAdaptor(QObject *parent) : QDBusAbstractAdaptor(parent) { // constructor setAutoRelaySignals(true); } SyncBackupAdaptor::~SyncBackupAdaptor() { // destructor } uchar SyncBackupAdaptor::backupFinished(const QDBusMessage &message) { // handle method call com.nokia.backupclient.backupFinished uchar out0; QMetaObject::invokeMethod(parent(), "backupFinished", Q_RETURN_ARG(uchar, out0), Q_ARG (QDBusMessage ,message)); return out0; } uchar SyncBackupAdaptor::backupStarts(const QDBusMessage &message) { // handle method call com.nokia.backupclient.backupStarts uchar out0; QMetaObject::invokeMethod(parent(), "backupStarts", Q_RETURN_ARG(uchar, out0), Q_ARG (QDBusMessage ,message)); return out0; } bool SyncBackupAdaptor::getBackUpRestoreState() { // handle method call com.nokia.backupclient.getBackUpRestoreState bool out0; QMetaObject::invokeMethod(parent(), "getBackUpRestoreState", Q_RETURN_ARG(bool, out0)); return out0; } uchar SyncBackupAdaptor::restoreFinished(const QDBusMessage &message) { // handle method call com.nokia.backupclient.restoreFinished uchar out0; QMetaObject::invokeMethod(parent(), "restoreFinished", Q_RETURN_ARG(uchar, out0), Q_ARG (QDBusMessage ,message)); return out0; } uchar SyncBackupAdaptor::restoreStarts(const QDBusMessage &message) { // handle method call com.nokia.backupclient.restoreStarts uchar out0; QMetaObject::invokeMethod(parent(), "restoreStarts", Q_RETURN_ARG(uchar, out0), Q_ARG (QDBusMessage ,message)); return out0; } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncOnChange.h0000644000015600001650000000511612634332753022727 0ustar pbuserpbgroup00000000000000#ifndef SYNCONCHANGE_H #define SYNCONCHANGE_H #include #include #include namespace Buteo { class SyncProfile; class StorageChangeNotifier; class PluginManager; class SyncOnChangeScheduler; /*! \brief this class initiates a sync if there are changes * in storage(s) it's asked to monitor */ class SyncOnChange : public QObject { Q_OBJECT public: /*! \brief constructor */ SyncOnChange(); /*! \brief destructor */ ~SyncOnChange(); /*! \brief enable sync on change for a list of storages * for the interested profiles * * Destroys the profile objects when they are no longer needed * * @param aPluginManager Used to load SOC storage plug-ins * @param aSOCScheduler used to schedule SOC * @param aSOCStorageMap map of well-known storage name * to list of sync profiles insterested in SOC for that * storage * @param list of storage names for which SOC couldn't be enabled * @return false if SOC can't be enabled for one or more * storages */ bool enable(const QHash >& aSOCStorageMap, SyncOnChangeScheduler* aSOCScheduler, PluginManager* aPluginManager, QStringList& aFailedStorages); /*! If the storage change notifier plug-in's have already been loaded, * call this to re-enable sync on change. Handy to call after a disable. * * This also checks if there were changes when SOC was disabled, and notifies * if there were any */ void enable(); /*! \brief disable sync on change immediately, i.e stop listening * to change notifiers */ void disable(); /*! \brief Note the next change, and disable SOC if that happens */ void disableNext(); /*! \brief adds a profile to the list of profiles interested in soc for a specific storage * * @param aStorageName storage name * @param aProfile sync profile */ void addProfile(const QString& aStorageName, SyncProfile* aProfile); public Q_SLOTS: /*! initiate sync for this storage */ void sync(QString aStorageName); private: /*! \brief destroys profile objects interested in SOC for this * storage */ void cleanup(const QString& aStorageName); /*! \brief Get the names for storages for which SOC is desired * * @return list of storage names */ QStringList getSOCStorageNames(); StorageChangeNotifier* iStorageChangeNotifier; QHash > iSOCStorageMap; SyncOnChangeScheduler* iSOCScheduler; }; } #endif buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/main.cpp0000644000015600001650000000467712634332753021702 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include #include "LogMacros.h" #include "Logger.h" #include "synchronizer.h" #include "SyncSigHandler.h" Q_DECL_EXPORT int main( int argc, char* argv[] ) { // remove this later on if not needed in harmattan, // this IS needed for fremantle dbus_threads_init_default(); // magical line making program not crash QCoreApplication app(argc, argv); // The below two lines are added as a workaround for QT bug 11413 // http://bugreports.qt.nokia.com/browse/QTBUG-11413 QDBusConnection::sessionBus(); QDBusConnection::systemBus(); // Initialize the logger Buteo::Logger::instance(); LOG_DEBUG("Starting Log At :" << QDateTime::currentDateTime() ); Buteo::Synchronizer *synchronizer = new Buteo::Synchronizer(&app); if (synchronizer == 0) { LOG_FATAL("Failed to create synchronizer"); } if(!synchronizer->initialize() ) { delete synchronizer; synchronizer = 0; return -1; } //Note:- Since we can't call Qt functions from Unix signal handlers. // This class provide hanlding unix signal. SyncSigHandler *sigHandler = new SyncSigHandler(); LOG_DEBUG("Entering event loop"); int returnValue = app.exec(); LOG_DEBUG("Exiting event loop"); synchronizer->close(); delete synchronizer; synchronizer = 0; if (sigHandler) { delete sigHandler; sigHandler = 0; } LOG_DEBUG("Stopping logger"); Buteo::Logger::deleteInstance(); qDebug() << "Exiting program"; return returnValue; } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/BackgroundSync.h0000644000015600001650000001205712634332753023326 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2014 Jolla Ltd * * Contact: Valerio Valerio * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef BACKGROUNDSYNC_H #define BACKGROUNDSYNC_H #include #include #include #include class BackgroundActivity; /// \brief BackgroundSync implementation. /// /// This class manages background syncs for different profiles. class BackgroundSync : public QObject { Q_OBJECT /// \brief Internal structure to hold profile-background activity stucture-notifier map struct BActivityStruct { QString id; BackgroundActivity* backgroundActivity; BackgroundActivity::Frequency frequency; }; /// \brief Internal structure to hold profile-background activity switch stucture-notifier map struct BActivitySwitchStruct { QString id; BackgroundActivity* backgroundActivity; QDateTime nextSwitch; }; public: /*! \brief Constructor. * \param aParent Parent object. */ BackgroundSync(QObject *aParent); /** * \brief Destructor */ virtual ~BackgroundSync(); /*! \brief Schedules a background sync for this profile. * * The beat will be generated between minWaitTime and maxWaitTime seconds * \param aProfName Name of the profile. * \param seconds Sync frequency in seconds * \return Success indicator. */ bool set(const QString& aProfName, int seconds); /*! \brief Removes background sync for a profile. * * \param aProfName Name of the profile. */ bool remove(const QString& aProfName); /*! \brief Removes all background syncs for all profiles. */ void removeAll(); // Sync switch /*! \brief Schedules a switch(rush/off-rush) for a background sync running for this profile, the switch should be * added after the background activity. * * \param aProfName Name of the profile. * \param aSwitchTime when the switch will occurs * \return Success indicator. */ bool setSwitch(const QString& aProfName, const QDateTime& aSwitchTime); /*! \brief Removes a switch(rush/off-rush) for a profile. * * \param aProfName Name of the profile. */ bool removeSwitch(const QString& aProfName); signals: /*! \brief This signal will be emitted when a background sync timer for particular profile is triggered. * * \param aProfName Name of the profile for which background sync timer is triggered. */ void onBackgroundSyncRunning(QString aProfName); /*! \brief This signal will be emitted when a switch timer for particular profile is triggered. * * \param aProfName Name of the profile for which switch timer is triggered. */ void onBackgroundSwitchRunning(const QString& aProfName); public slots: /*! \brief Called when background sync is completed * * \param aProfName Name of the profile for which background sync is completed. */ void onBackgroundSyncCompleted(QString aProfName); private slots: /*! \brief Called when background sync timer starts running */ void onBackgroundSyncStarted(); /*! \brief Called when a switch timer starts running */ void onBackgroundSwitchStarted(); private: /*! \brief Finds the name of the profile which uses particular background activity * * \param activityId Id of the background activity * \return name of the profile. */ QString getProfNameFromId(const QString activityId) const; /*! \brief Returns a valid BackgroundActivity frequency * * \param seconds Amounth of time for the frequency * \return BackgroundActivity frequency */ BackgroundActivity::Frequency frequencyFromSeconds(int seconds); // Sync switch /*! \brief Removes all scheduled switches(rush/off-rush) for all profiles. */ void removeAllSwitches(); /*! \brief Finds the name of the profile which uses particular switch activity id * * \param activityId Id of the switch activity * \return name of the profile. */ QString getProfNameFromSwitchId(const QString activityId) const; private: ///Map of structures waiting for background sync QMap iScheduledSyncs; ///Map of switch timer structures QMap iScheduledSwitch; }; #endif // BACKGROUNDSYNC_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncScheduler.cpp0000644000015600001650000001743612634332753023526 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2014-2015 Jolla Ltd * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifdef USE_KEEPALIVE #include "BackgroundSync.h" #else #include "IPHeartBeat.h" #endif #include "SyncScheduler.h" #include "SyncProfile.h" #include "LogMacros.h" #include using namespace Buteo; SyncScheduler::SyncScheduler(QObject *aParent) : QObject(aParent) { FUNCTION_CALL_TRACE; #ifdef USE_KEEPALIVE iBackgroundActivity = new BackgroundSync(this); connect(iBackgroundActivity,SIGNAL(onBackgroundSyncRunning(QString)),this,SLOT(doIPHeartbeatActions(QString))); connect(iBackgroundActivity,SIGNAL(onBackgroundSwitchRunning(QString)),this,SLOT(rescheduleBackgroundActivity(QString))); #else iIPHeartBeatMan = new IPHeartBeat(this); connect(iIPHeartBeatMan,SIGNAL(onHeartBeat(QString)),this,SLOT(doIPHeartbeatActions(QString))); // Create the alarm inventory object iAlarmInventory = new SyncAlarmInventory(); if(iAlarmInventory) { connect ( iAlarmInventory, SIGNAL(triggerAlarm(int)), this, SLOT(doAlarmActions(int)) ); if(!iAlarmInventory->init()) { LOG_WARNING("AlarmInventory Init Failed"); } } #endif } SyncScheduler::~SyncScheduler() { FUNCTION_CALL_TRACE; #ifdef USE_KEEPALIVE iBackgroundActivity->removeAll(); #else removeAllAlarms(); if (iAlarmInventory) { delete iAlarmInventory; iAlarmInventory = 0; } #endif } void SyncScheduler::addProfileForSyncRetry(const SyncProfile* aProfile, QDateTime aNextSyncTime) { FUNCTION_CALL_TRACE; if(aProfile && aProfile->isEnabled()) { #ifdef USE_KEEPALIVE setNextAlarm(aProfile, aNextSyncTime); #else //remove alarm removeProfile(aProfile->name()); int alarmId = setNextAlarm(aProfile, aNextSyncTime); if (alarmId > 0) { iSyncScheduleProfiles.insert(aProfile->name(), alarmId); LOG_DEBUG("syncretries : retry scheduled for profile" << aProfile->name()); } #endif } } bool SyncScheduler::addProfile(const SyncProfile* aProfile) { FUNCTION_CALL_TRACE; if (!aProfile) return false; // In case Keepalive is used no need to remove // existent profile will be updated #ifdef USE_KEEPALIVE if (aProfile->isEnabled() && aProfile->syncType() == SyncProfile::SYNC_SCHEDULED) { setNextAlarm(aProfile); return true; } else { removeProfile(aProfile->name()); return false; } #else bool profileAdded = false; // Remove possible old alarm first. removeProfile(aProfile->name()); if (aProfile->isEnabled() && aProfile->syncType() == SyncProfile::SYNC_SCHEDULED) { int alarmId = setNextAlarm(aProfile); if (alarmId > 0) { iSyncScheduleProfiles.insert(aProfile->name(), alarmId); profileAdded = true; LOG_DEBUG("Sync scheduled: profile =" << aProfile->name() << "time =" << aProfile->nextSyncTime()); } // no else } // no else return profileAdded; #endif } void SyncScheduler::removeProfile(const QString &aProfileName) { FUNCTION_CALL_TRACE; #ifdef USE_KEEPALIVE if(iBackgroundActivity->remove(aProfileName)) { LOG_DEBUG("Scheduled sync removed: profile =" << aProfileName); } #else if (iSyncScheduleProfiles.contains(aProfileName)) { int alarmEventID = iSyncScheduleProfiles.value(aProfileName); removeAlarmEvent(alarmEventID); iSyncScheduleProfiles.remove(aProfileName); LOG_DEBUG("Scheduled sync removed: profile =" << aProfileName); } #endif } void SyncScheduler::doIPHeartbeatActions(QString aProfileName) { FUNCTION_CALL_TRACE; emit syncNow(aProfileName); } #ifdef USE_KEEPALIVE void SyncScheduler::rescheduleBackgroundActivity(const QString& aProfileName) { FUNCTION_CALL_TRACE; SyncProfile* profile = iProfileManager.syncProfile(aProfileName); if (profile) { if (profile->syncExternallyEnabled() || profile->syncExternallyDuringRush()) { emit externalSyncChanged(profile, false); } setNextAlarm(profile); } else { LOG_WARNING("Invalid profile, can't reschedule switch timer for " << aProfileName); } } #endif int SyncScheduler::setNextAlarm(const SyncProfile* aProfile, QDateTime aNextSyncTime) { FUNCTION_CALL_TRACE; int alarmEventID = -1; if(aProfile == 0) { return alarmEventID; } QDateTime nextSyncTime; if(!aNextSyncTime.isValid()) { nextSyncTime = aProfile->nextSyncTime(aProfile->lastSyncTime()); } else { nextSyncTime = aNextSyncTime; } if (nextSyncTime.isValid()) { // The existing event object can be used by just updating the alarm time // and enqueuing it again. #ifdef USE_KEEPALIVE alarmEventID = 1; iBackgroundActivity->set(aProfile->name(), QDateTime::currentDateTime().secsTo(nextSyncTime) + 1); if (aProfile->rushEnabled()) { QDateTime nextSyncSwitch = aProfile->nextRushSwitchTime(QDateTime::currentDateTime()); if (nextSyncSwitch.isValid()) { iBackgroundActivity->setSwitch(aProfile->name(), nextSyncSwitch); } else { iBackgroundActivity->removeSwitch(aProfile->name()); LOG_DEBUG("Removing switch timer for" << aProfile->name() << " invalid switch timer"); } } else { iBackgroundActivity->removeSwitch(aProfile->name()); } #else iAlarmInventory->addAlarm(nextSyncTime); #endif if (alarmEventID == 0) { LOG_WARNING("Failed to add alarm for scheduled sync of profile" << aProfile->name()); } } else { LOG_WARNING("Next sync time is not valid, sync not scheduled for profile" << aProfile->name()); } return alarmEventID; } #ifndef USE_KEEPALIVE void SyncScheduler::doAlarmActions(int aAlarmEventID) { FUNCTION_CALL_TRACE; const QString syncProfileName = iSyncScheduleProfiles.key(aAlarmEventID); if (!syncProfileName.isEmpty()) { iSyncScheduleProfiles.remove(syncProfileName); // Use global slots (min time == max time) for scheduling heart beats. if(iIPHeartBeatMan->setHeartBeat(syncProfileName, IPHB_GS_WAIT_2_5_MINS, IPHB_GS_WAIT_2_5_MINS)) { //Do nothing, sync will be triggered on getting heart beat } else { emit syncNow(syncProfileName); } } // no else, in error cases simply ignore } void SyncScheduler::removeAlarmEvent(int aAlarmEventID) { FUNCTION_CALL_TRACE; bool err = iAlarmInventory->removeAlarm( aAlarmEventID ); if (err < false) { LOG_WARNING("No alarm found for ID " << aAlarmEventID); } else { LOG_DEBUG("Removed alarm, ID =" << aAlarmEventID); } } void SyncScheduler::removeAllAlarms() { FUNCTION_CALL_TRACE; iAlarmInventory->removeAllAlarms(); } #endif buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/ServerActivator.cpp0000644000015600001650000001317712634332753024074 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ServerActivator.h" #include "ProfileEngineDefs.h" #include "LogMacros.h" using namespace Buteo; ServerActivator::ServerActivator(ProfileManager &aProfileManager, TransportTracker &aTransportTracker, QObject *aParent) : QObject(aParent), iProfileManager(aProfileManager), iTransportTracker(aTransportTracker) { FUNCTION_CALL_TRACE; // Get server profiles and transports used by them. QStringList serverProfileNames = iProfileManager.profileNames( Profile::TYPE_SERVER); foreach (QString serverProfileName, serverProfileNames) { Profile *serverProfile = iProfileManager.profile(serverProfileName, Profile::TYPE_SERVER); if (serverProfile != 0) { if (serverProfile->isEnabled()) { ServerData data; data.iTransports = transportsFromProfile(serverProfile); // Normally server plug-in is loaded only when a transport // used by it is available. A server profile can force the // the plug-in to be always loaded be defining the following // key. if (serverProfile->boolKey(KEY_LOAD_WITHOUT_TRANSPORT)) { data.iRefCount++; } iServers.insert(serverProfileName, data); } delete serverProfile; serverProfile = 0; } else { LOG_WARNING("Failed to load server profile:" << serverProfileName); } } // Add one reference per matching and enabled transport. QList transports; transports.append(Sync::CONNECTIVITY_BT); transports.append(Sync::CONNECTIVITY_USB); transports.append(Sync::CONNECTIVITY_INTERNET); foreach (Sync::ConnectivityType transport, transports) { bool transportEnabled = iTransportTracker.isConnectivityAvailable(transport); foreach (QString serverName, iServers.keys()) { if (transportEnabled && iServers[serverName].iTransports.contains(transport)) { iServers[serverName].iRefCount++; } } } connect(&aTransportTracker, SIGNAL(connectivityStateChanged(Sync::ConnectivityType, bool)), this, SLOT(onConnectivityStateChanged(Sync::ConnectivityType, bool))); } ServerActivator::~ServerActivator() { FUNCTION_CALL_TRACE; } int ServerActivator::addRef(const QString &aServerName, bool emitSignal /*= true*/) { FUNCTION_CALL_TRACE; int refCount = 0; if (iServers.contains(aServerName)) { refCount = ++iServers[aServerName].iRefCount; if (emitSignal && (refCount == 1)) { emit serverEnabled(aServerName); } // no else } else { LOG_WARNING("Unknown server:" << aServerName); } return refCount; } int ServerActivator::removeRef(const QString &aServerName, bool emitSignal /*= true*/) { FUNCTION_CALL_TRACE; int refCount = 0; if (iServers.contains(aServerName)) { ServerData &data = iServers[aServerName]; if (data.iRefCount > 0) { data.iRefCount--; if (emitSignal && data.iRefCount == 0) { emit serverDisabled(aServerName); } } // no else refCount = data.iRefCount; } else { LOG_WARNING("Unknown server:" << aServerName); } return refCount; } QStringList ServerActivator::enabledServers() const { FUNCTION_CALL_TRACE; QStringList enabledServers; foreach (QString serverName, iServers.keys()) { if (iServers[serverName].iRefCount > 0) { enabledServers.append(serverName); } } return enabledServers; } void ServerActivator::onConnectivityStateChanged(Sync::ConnectivityType aType, bool aState) { FUNCTION_CALL_TRACE; foreach (QString serverName, iServers.keys()) { if (iServers[serverName].iTransports.contains(aType)) { if (aState) { addRef(serverName); } else { removeRef(serverName); } } } } QList ServerActivator::transportsFromProfile( const Profile *aProfile) { FUNCTION_CALL_TRACE; QList transports; if (aProfile != 0) { if (aProfile->boolKey(KEY_BT_TRANSPORT)) { transports.append(Sync::CONNECTIVITY_BT); } if (aProfile->boolKey(KEY_USB_TRANSPORT)) { transports.append(Sync::CONNECTIVITY_USB); } if (aProfile->boolKey(KEY_INTERNET_TRANSPORT)) { transports.append(Sync::CONNECTIVITY_INTERNET); } } return transports; } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/ServerThread.cpp0000644000015600001650000000552112634332753023341 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ServerThread.h" #include "ServerPlugin.h" #include "LogMacros.h" #include #include using namespace Buteo; ServerThread::ServerThread() : iServerPlugin( 0 ), iRunning(false) { FUNCTION_CALL_TRACE; } ServerThread::~ServerThread() { FUNCTION_CALL_TRACE; } QString ServerThread::getProfileName() const { FUNCTION_CALL_TRACE; QString profileName; if (iServerPlugin != 0) { profileName = iServerPlugin->getProfileName(); } return profileName; } ServerPlugin* ServerThread::getPlugin() const { FUNCTION_CALL_TRACE; return iServerPlugin; } bool ServerThread::startThread( ServerPlugin* aServerPlugin ) { FUNCTION_CALL_TRACE; if (aServerPlugin == 0) return false; { QMutexLocker locker(&iMutex); if( iRunning ) { return false; } else { iRunning = true; } } iServerPlugin = aServerPlugin; // Move to server thread iServerPlugin->moveToThread( this ); start(); return true; } void ServerThread::stopThread() { FUNCTION_CALL_TRACE; exit(); } void ServerThread::run() { FUNCTION_CALL_TRACE; if (iServerPlugin == 0) { LOG_CRITICAL("Server plug-in is NULL"); return; } if( !iServerPlugin->init() ) { LOG_DEBUG( "Could not initialize server plugin:" << iServerPlugin->getPluginName() ); emit initError( iServerPlugin->getProfileName(), "", 0 ); return; } if( !iServerPlugin->startListen() ) { LOG_DEBUG( "Could not start server plugin:" << iServerPlugin->getPluginName() ); emit initError( iServerPlugin->getProfileName(), "", 0 ); return; } exec(); iServerPlugin->stopListen(); iServerPlugin->uninit(); // Move back to application thread iServerPlugin->moveToThread( QCoreApplication::instance()->thread() ); { QMutexLocker locker(&iMutex); iRunning = false; } } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncQueue.cpp0000644000015600001650000000642112634332753022664 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncQueue.h" #include "SyncSession.h" #include "SyncProfile.h" #include "LogMacros.h" using namespace Buteo; void SyncQueue::enqueue(SyncSession *aSession) { FUNCTION_CALL_TRACE; iItems.enqueue(aSession); sort(); } SyncSession *SyncQueue::dequeue() { FUNCTION_CALL_TRACE; SyncSession *p = NULL; if (!iItems.isEmpty()) { p = iItems.dequeue(); } // no else return p; } SyncSession* SyncQueue::dequeue(const QString &aProfileName) { FUNCTION_CALL_TRACE; SyncSession *ret = 0; QQueue::iterator i; for (i = iItems.begin(); i != iItems.end(); ++i) { if ((*i)->profileName() == aProfileName) { ret = *i; iItems.erase(i); break; } } return ret; } SyncSession *SyncQueue::head() { FUNCTION_CALL_TRACE; SyncSession *p = NULL; if (!iItems.isEmpty()) { p = iItems.head(); } // no else return p; } bool SyncQueue::isEmpty() const { FUNCTION_CALL_TRACE; return iItems.isEmpty(); } int SyncQueue::size() const { FUNCTION_CALL_TRACE; return iItems.size(); } bool SyncQueue::contains(const QString &aProfileName) const { FUNCTION_CALL_TRACE; QQueue::const_iterator i; for (i = iItems.begin(); i != iItems.end(); ++i) { if ((*i)->profileName() == aProfileName) return true; } return false; } bool syncSessionPointerLessThan(SyncSession *&aLhs, SyncSession *&aRhs) { if (aLhs && aRhs) { // Manual sync has higher priority than scheduled sync. if (aLhs->isScheduled() != aRhs->isScheduled()) return !aLhs->isScheduled(); SyncProfile *lhsProfile = aLhs->profile(); SyncProfile *rhsProfile = aRhs->profile(); if (lhsProfile == 0 || rhsProfile == 0) return false; // Device sync has higher priority than online sync. SyncProfile::DestinationType lhsDestType = lhsProfile->destinationType(); SyncProfile::DestinationType rhsDestType = rhsProfile->destinationType(); if (lhsDestType != rhsDestType) return (lhsDestType == SyncProfile::DESTINATION_TYPE_DEVICE); } return false; } void SyncQueue::sort() { FUNCTION_CALL_TRACE; // @todo: Sort queued profiles using some criteria. } const QList& SyncQueue::getQueuedSyncSessions() const { FUNCTION_CALL_TRACE; return iItems; } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/msyncd-lib.pro0000644000015600001650000000622212634332753023021 0ustar pbuserpbgroup00000000000000TEMPLATE = lib CONFIG += staticlib TARGET = msyncd QT += xml \ dbus \ sql \ network QT -= gui CONFIG += \ link_pkgconfig \ create_prl DEPENDPATH += . INCLUDEPATH += . \ ../ \ ../libbuteosyncfw/pluginmgr \ ../libbuteosyncfw/common \ ../libbuteosyncfw/profile PKGCONFIG += dbus-1 gio-2.0 equals(QT_MAJOR_VERSION, 4): { PKGCONFIG += libsignon-qt accounts-qt CONFIG += mobility MOBILITY += systeminfo LIBS += -lbuteosyncfw } equals(QT_MAJOR_VERSION, 5): { PKGCONFIG += libsignon-qt5 accounts-qt5 Qt5SystemInfo LIBS += -lbuteosyncfw5 packagesExist(qt5-boostable) { DEFINES += HAS_BOOSTER PKGCONFIG += qt5-boostable } else { warning("qt5-boostable not available; startup times will be slower") } } QMAKE_LIBDIR_QT += ../libsyncprofile/ LIBS += -L../libbuteosyncfw # Input HEADERS += ServerActivator.h \ synchronizer.h \ SyncDBusInterface.h \ SyncBackupProxy.h \ SyncDBusAdaptor.h \ SyncBackupAdaptor.h \ ClientThread.h \ ServerThread.h \ StorageBooker.h \ SyncQueue.h \ SyncScheduler.h \ SyncBackup.h \ AccountsHelper.h \ SyncSession.h \ PluginRunner.h \ ClientPluginRunner.h \ ServerPluginRunner.h \ SyncAlarmInventory.h \ SyncSigHandler.h \ StorageChangeNotifier.h \ SyncOnChange.h \ SyncOnChangeScheduler.h SOURCES += ServerActivator.cpp \ synchronizer.cpp \ SyncDBusAdaptor.cpp \ SyncBackupAdaptor.cpp \ ClientThread.cpp \ ServerThread.cpp \ StorageBooker.cpp \ SyncQueue.cpp \ SyncScheduler.cpp \ SyncBackup.cpp \ AccountsHelper.cpp \ SyncSession.cpp \ PluginRunner.cpp \ ClientPluginRunner.cpp \ ServerPluginRunner.cpp \ SyncAlarmInventory.cpp \ SyncSigHandler.cpp \ StorageChangeNotifier.cpp \ SyncOnChange.cpp \ SyncOnChangeScheduler.cpp contains(DEFINES, USE_KEEPALIVE) { PKGCONFIG += keepalive HEADERS += \ BackgroundSync.h SOURCES += \ BackgroundSync.cpp } else { PKGCONFIG += libiphb HEADERS += \ IPHeartBeat.h SOURCES += \ IPHeartBeat.cpp } QMAKE_CXXFLAGS = -Wall \ -g \ -Wno-cast-align \ -O2 \ -finline-functions # ##################################################################### # make coverage (debug) # ##################################################################### coverage.CONFIG += recursive QMAKE_EXTRA_TARGETS += coverage CONFIG(debug,debug|release) { QMAKE_EXTRA_TARGETS += cov_cxxflags \ cov_lflags cov_cxxflags.target = coverage cov_cxxflags.depends = CXXFLAGS \ += \ -fprofile-arcs \ -ftest-coverage cov_lflags.target = coverage cov_lflags.depends = LFLAGS \ += \ -fprofile-arcs \ -ftest-coverage # QMAKE_CXXFLAGS += -fprofile-arcs -ftest-coverage # QMAKE_LFLAGS += -fprofile-arcs -ftest-coverage # -ftest-coverage coverage.commands = @echo \ "Built with coverage support..." build_pass|!debug_and_release:coverage.depends = all QMAKE_CLEAN += $(OBJECTS_DIR)/*.gcda \ $(OBJECTS_DIR)/*.gcno \ $(OBJECTS_DIR)/*.gcov } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/ServerPluginRunner.h0000644000015600001650000000651512634332753024233 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SERVERPLUGINRUNNER_H #define SERVERPLUGINRUNNER_H #include "PluginRunner.h" namespace Buteo { class ServerActivator; class ServerPlugin; class ServerThread; class Profile; /*! \brief Class for running server sync plug-ins */ class ServerPluginRunner : public PluginRunner { Q_OBJECT public: /*! \brief Constructor * * @param aPluginName Name of the plug-in to run * @param aProfile Profile for the server plug-in. Ownership is transferred. * @param aPluginMgr PluginManager instance for creating and destroying * plug-ins by name * @param aPluginCbIf Callback interface that the created plug-in can use * @param aServerActivator Server activator, controls enabled/disabled state * of the server plug-in * @param aParent Parent object */ ServerPluginRunner(const QString &aPluginName, Profile *aProfile, PluginManager *aPluginMgr, PluginCbInterface *aPluginCbIf, ServerActivator *aServerActivator, QObject *aParent = 0); //! \brief Destructor virtual ~ServerPluginRunner(); //! @see PluginRunner::init virtual bool init(); //! @see PluginRunner::start virtual bool start(); //! @see PluginRunner::stop virtual void stop(); //! @see PluginRunner::abort virtual void abort(Sync::SyncStatus aStatus = Sync::SYNC_ABORTED); //! @see PluginRunner::syncResults virtual SyncResults syncResults(); //! @see PluginRunner::plugin virtual SyncPluginBase *plugin(); //! @see PluginRunner::plugin virtual bool cleanUp(); // Suspend a server plug-in void suspend(); // Resume a suspended server plug-in void resume(); private slots: // Slots for catching plug-in signals. void onNewSession(const QString &aDestination); void onTransferProgress(const QString &aProfileName, Sync::TransferDatabase aDatabase, Sync::TransferType aType, const QString &aMimeType, int aCommittedItems); void onStorageAccquired(const QString &aMimeType ); void onError(const QString &aProfileName, const QString &aMessage, int aErrorCode); void onSuccess(const QString &aProfileName, const QString &aMessage); // Slot for observing thread exit void onThreadExit(); private: void onSessionDone(); Profile *iProfile; ServerPlugin *iPlugin; ServerThread *iThread; ServerActivator *iServerActivator; #ifdef SYNCFW_UNIT_TESTS friend class ServerPluginRunnerTest; #endif }; } #endif // SERVERPLUGINRUNNER_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncOnChangeScheduler.cpp0000644000015600001650000000452212634332753025121 0ustar pbuserpbgroup00000000000000#include #include "SyncOnChangeScheduler.h" #include "SyncProfile.h" #include "LogMacros.h" using namespace Buteo; SyncOnChangeScheduler::SyncOnChangeScheduler() { FUNCTION_CALL_TRACE; } SyncOnChangeScheduler::~SyncOnChangeScheduler() { FUNCTION_CALL_TRACE; foreach(QObject *o, iSOCTimers.values()) { delete o; } iSOCTimers.clear(); iSOCProfileNames.clear(); } bool SyncOnChangeScheduler::addProfile(const SyncProfile* aProfile) { FUNCTION_CALL_TRACE; bool scheduled = false; if(aProfile && !iSOCProfileNames.contains(aProfile->name())) { qint32 time = aProfile->syncOnChangeAfter(); iSOCProfileNames << aProfile->name(); SyncOnChangeTimer *SOCtimer = new SyncOnChangeTimer(aProfile, time); QObject::connect(SOCtimer, SIGNAL(timeout(const SyncProfile*)), this, SLOT(sync(const SyncProfile*)), Qt::QueuedConnection); SOCtimer->fire(); scheduled = true; iSOCTimers.insert(aProfile->name(), SOCtimer); LOG_DEBUG("Sync on change scheduled for profile"<< aProfile->name()); } else if(aProfile) { LOG_DEBUG("Sync on change already scheduled for profile" << aProfile->name()); } return scheduled; } void SyncOnChangeScheduler::removeProfile(const QString &aProfileName) { FUNCTION_CALL_TRACE; iSOCProfileNames.removeAll(aProfileName); // cancel timer delete iSOCTimers.take(aProfileName); } void SyncOnChangeScheduler::sync(const SyncProfile* aProfile) { FUNCTION_CALL_TRACE; iSOCProfileNames.removeAll(aProfile->name()); iSOCTimers.remove(aProfile->name()); SyncOnChangeTimer *SOCtimer = qobject_cast(sender()); if(SOCtimer) { LOG_DEBUG("Sync on change for profile" << aProfile->name()); delete SOCtimer; emit syncNow(aProfile->name()); } } SyncOnChangeTimer::SyncOnChangeTimer(const SyncProfile* profile, const quint32& aTimeout) : iSyncProfile(profile), iTimeout(aTimeout) { FUNCTION_CALL_TRACE; } SyncOnChangeTimer::~SyncOnChangeTimer() { FUNCTION_CALL_TRACE; } void SyncOnChangeTimer::fire() { FUNCTION_CALL_TRACE; QTimer::singleShot(iTimeout*1000, this, SLOT(onTimeout())); } void SyncOnChangeTimer::onTimeout() { FUNCTION_CALL_TRACE; emit timeout(iSyncProfile); } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncBackup.h0000644000015600001650000000564112634332753022455 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCBACKUP_H #define SYNCBACKUP_H #include "SyncBackupProxy.h" #include "SyncBackupAdaptor.h" namespace Buteo { /*! \brief Handles Sync requirements towards Backup * * This class communicates with the Backup daemon for Backup state before * starting a sync. */ class SyncBackup : public SyncBackupProxy // Derived from QObject { Q_OBJECT public: /*! * \brief Default Constructor */ SyncBackup (); /*! * \brief Destructor */ ~SyncBackup (); //! \brief path to the backup dbus object static const char* DBUS_BACKUP_OBJECT; /*! * \brief Requests the current state og backup/restore operation. */ bool getBackUpRestoreState(); /*! \brief Reply to backup framework, result of o/p * @param aResult - result */ void sendReply (uchar aResult); signals : public slots: // From backup framework ... /*! \brief Called by backup framework when backup starts * @param message - result * @return uchar */ uchar backupStarts(const QDBusMessage &message); /*! Called by backup framework when backup is completed * @param message - result * @return uchar */ uchar backupFinished(const QDBusMessage &message); /*! Called by backup framework when it starts to restore a backup. * @param message - result * @return uchar */ uchar restoreStarts(const QDBusMessage &message); /*! Called by backup framework when backup ie restored * @param message - result * @return uchar */ uchar restoreFinished(const QDBusMessage &message); /*! Called if backup service exits/aborts... * @param serviceName - name of the service */ void backupServiceUnregistered(const QString &serviceName); private : bool iBackupRestore; QDBusMessage *iReply; QDBusServiceWatcher *iWatchService; SyncBackupAdaptor *iAdaptor; // Reply to backup dbus framework that response will // be delayed uchar sendDelayReply (const QDBusMessage &message); #ifdef SYNCFW_UNIT_TESTS friend class SyncBackupTest; #endif }; } #endif // SYNCBACKUP_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/synchronizer.h0000644000015600001650000003453712634332753023156 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2014-2015 Jolla Ltd * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCHRONIZER_H #define SYNCHRONIZER_H #include "SyncDBusInterface.h" #include "SyncQueue.h" #include "StorageBooker.h" #include "SyncScheduler.h" #include "SyncBackup.h" #include "SyncOnChange.h" #include "SyncOnChangeScheduler.h" #include "SyncCommonDefs.h" #include "ProfileManager.h" #include "PluginManager.h" #include "PluginCbInterface.h" #include "ClientPlugin.h" #include #include #include #include #include #include #include struct _GSettings; namespace Buteo { class PluginManager; class ServerPluginRunner; class NetworkManager; class TransportTracker; class ServerActivator; class AccountsHelper; /// \brief The main entry point to the synchronization framework. /// /// This class manages other components and connects them to provide /// the fully functioning synchronization framework. class Synchronizer : public SyncDBusInterface, // Derived from QObject public PluginCbInterface { Q_OBJECT public: /// \brief The contructor. Synchronizer(QCoreApplication *aApplication); /// \brief Destructor virtual ~Synchronizer(); /// \brief registers the dbus service and creates handlers for various /// tasks of the synchronizer bool initialize(); /// \brief stops the daemon and unregisters the dbus object void close(); // From PluginCbInterface // --------------------------------------------------------------------------- /// \see PluginCbInterface::requestStorage virtual bool requestStorage(const QString &aStorageName, const SyncPluginBase *aCaller); /// \see PluginCbInterface::releaseStorage virtual void releaseStorage(const QString &aStorageName, const SyncPluginBase *aCaller); /// \see PluginCbInterface::createStorage virtual StoragePlugin* createStorage(const QString &aPluginName); /// \see PluginCbInterface::destroyStorage virtual void destroyStorage(StoragePlugin *aStorage); /// \see PluginCbInterface::isConnectivityAvailable virtual bool isConnectivityAvailable( Sync::ConnectivityType aType ); /// \see PluginCbInterface::getSyncProfileByRemoteAddress virtual Profile* getSyncProfileByRemoteAddress(const QString& aAddress); /// \see PluginCbInterface::getValue virtual QString getValue(const QString& aAddress, const QString& aKey); // From SyncDBusInterface // -------------------------------------------------------------------------- public slots: //! \see SyncDBusInterface::startSync virtual bool startSync(QString aProfileName); //! \see SyncDBusInterface::abortSync virtual void abortSync(QString aProfileName); //! \see SyncDBusInterface::removeProfile virtual bool removeProfile(QString aProfileAsXml); //! \see SyncDBusInterface::updateProfile virtual bool updateProfile(QString aProfileAsXml); //! \see SyncDBusInterface::requestStorages virtual bool requestStorages(QStringList aStorageNames); //! \see SyncDBusInterface::releaseStorages virtual void releaseStorages(QStringList aStorageNames); //! \see SyncDBusInterface::runningSyncs virtual QStringList runningSyncs(); //! \see SyncDBusInterface::setSyncSchedule virtual bool setSyncSchedule(QString aProfileId , QString aScheduleAsXml); //! \see SyncDBusInterface::saveSyncResults virtual bool saveSyncResults(QString aProfileId,QString aSyncResults); //! \see SyncDBusInterface::createSyncProfileForAccount virtual QString createSyncProfileForAccount(uint aAccountId); /*! \brief To get lastSyncResult. * \param aProfileId * \return QString of syncResult. */ virtual QString getLastSyncResult(const QString &aProfileId); /*! \brief Gets all visible sync profiles. * * Returns all sync profiles that should be visible in sync ui. A profile * is visible if it has not been explicitly set as hidden. * \return The list of sync profiles. */ virtual QStringList allVisibleSyncProfiles(); /*! \brief Gets a sync profile. * * Loads and merges also all sub-profiles that are referenced from the * main profile. Loads the log of finished synchronization sessions with * this profile. * \param aProfileId Name of the profile to get. * \return The sync profile as Xml string. */ virtual QString syncProfile(const QString &aProfileId); virtual QStringList syncProfilesByKey(const QString &aKey, const QString &aValue); virtual QStringList syncProfilesByType(const QString &aType); // -------------------------------------------------------------------------- //! Called starts a schedule sync. bool startScheduledSync(QString aProfileName); //! Called when backup starts void backupStarts(); //! Called when backup is completed void backupFinished(); //! Called when starting to restore a backup. void restoreStarts(); //! Called when backup is restored void restoreFinished(); //! Called to get the current backup/restore state virtual bool getBackUpRestoreState(); void start(unsigned int aAccountId); /*! \brief Stops sync for all profiles matching the given account ID. * * \param aAccountId The account ID. */ void stop(unsigned int aAccountId); /*! \brief Returns the list of account IDs for which sync is ongoing * * \return The list of account IDs currectly syncing. */ QList syncingAccounts(); /*! \brief Returns the status of the sync for the given account Id * * \param aAccountId The account ID. * \param aFailedReason This is an out parameter. In case the last sync has * failed, this will contain the code indicating the failure reason (TODO: * Define error codes). In case the last sync has not failed, this must be * ignored * \param aPrevSyncTime This is an out parameter. The previous sync time. * Invalid time is returned if there was no last sync. * \param aNextSyncTime This is an out parameter. The next sync time. * \return The status of sync: 0 = Sync is running, * 1 = Last sync succeeded, 2 = last sync failed */ int status(unsigned int aAccountId, int &aFailedReason, qlonglong &aPrevSyncTime, qlonglong &aNextSyncTime); /*! \brief Queries the sync externally status of a given account, * 'syncedExternallyStatus' signal is emitted with the reply is ready, clients should listen * to the later. * * \param aAccountId The account ID. * \param aClientProfileName The name of the client profile resposible for the sync, this is used to distinguish accounts * having several services enabled. */ void isSyncedExternally(unsigned int aAccountId, const QString aClientProfileName); signals: //! emitted by releaseStorages call void storageReleased(); /*! \brief emit this signal when the sync session is completed, * this is useful when the session status is not important. * * @param aProfileName */ void syncDone(const QString &aProfileName); private slots: /*! \brief Handler for storage released signal. * * Tries to start the next sync in queue, which may have been blocked * earlier by storage reservations. */ void onStorageReleased(); void onTransferProgress( const QString &aProfileName, Sync::TransferDatabase aDatabase, Sync::TransferType aType, const QString &aMimeType, int aCommittedItems ); void onSessionFinished( const QString &aProfileName, Sync::SyncStatus aStatus, const QString &aMessage, int aErrorCode ); void onStorageAccquired(const QString &aProfileName, const QString &aMimeType); void onSyncProgressDetail(const QString &aProfileName,int aProgressDetail); void onServerDone(); void onNewSession(const QString &aDestination); void slotProfileChanged(QString aProfileName, int aChangeType , QString aProfileAsXml); /*! \brief Starts a server plug-in * * @param aProfileName Server profile name */ void startServer(const QString &aProfileName); /*! \brief Stops a server plug-in * * @param aProfileName Server profile name */ void stopServer(const QString &aProfileName); void onNetworkStateChanged(bool aState, Sync::InternetConnectionType type); /*! \brief call this to request the sync daemon to enable soc * for a profile. The sync daemon decides as of now for which storages * soc should be enabled * * @param aProfileName profile name */ void enableSOCSlot(const QString& aProfileName); /*! \brief Adds a profile to sync scheduler * * @param aProfileName Name of the profile to schedule. */ void reschedule(const QString &aProfileName); /*! \brief Handles the sync status signal * * @param aProfileName Name of the profile * @param aStatus Status of the sync * @param aMessage Status message as a string * @param aMoreDetails In case of failure, contains detailed reason */ void slotSyncStatus(QString aProfileName, int aStatus, QString aMessage, int aMoreDetails); /*! \brief Handles the removed scheduled sync signal * * @param aProfileName Name of the profile */ void removeScheduledSync(const QString &aProfileName); /*! \brief Checks the status of external sync for a given profile, when the status * changes(or aQuery param is set to true) or the profile is added for the first time 'syncedExternallyStatus' dbus signal * will be emitted to notify possible clients. * * @param aProfile the profile that the state will be checked * @param aQuery When true 'syncedExternallyStatus' dbus signal will be emitted even if the state did not change. */ void externalSyncStatus(const SyncProfile *aProfile, bool aQuery=false); private: bool startSync(const QString &aProfileName, bool aScheduled); /*! \brief Starts a sync with the given profile. * * \param aProfile Profile to use in sync. Ownership is transferred. * The profile is automatically deleted when the sync finishes. */ bool startSyncNow(SyncSession *aSession); /*! \brief Tries to starts next sync request from the sync queue. * * \return Is it possible to try starting more syncs by calling this * function again. Will be true if the first sync request in the queue * is not blocked by already reserved storages. */ bool startNextSync(); /*! \brief To clean up session * \param aSession * \param aStatus of sync * \return None */ void cleanupSession(SyncSession *aSession, Sync::SyncStatus aStatus); /*! \brief Start all server plug-ins * * @param resume, if true resume servers instead of starting them */ void startServers( bool resume = false ); /*! \brief Stop all server plug-ins * * @param suspend, if true suspend servers instead of stopping them */ void stopServers( bool suspend = false ); /*! \brief Helper function when backup/restore starts. * */ void backupRestoreStarts (); /*! \brief Helper function when backup/restore is done. * */ void backupRestoreFinished(); /*! \brief Initializes sync scheduler * */ void initializeScheduler(); bool isBackupRestoreInProgress (); /*! \brief Requests for a cleanup from the plugin for the given profileId * * @param aProfileId Name/Id of the profile * @return True or False to indicate success or failure */ bool cleanupProfile(const QString &profileId); bool clientProfileActive(const QString &clientProfileName); /*! \brief Removes the external sync status for a given profile, if status changes * 'syncedExternallyStatus' dbus signal will be emitted to notify possible clients. * * @param aProfile the profile that the status should be removed. */ void removeExternalSyncStatus(const SyncProfile *aProfile); /*! \brief Check if sheduled sync is allowed for this type of connection. * * @param aType the connection type; */ bool acceptScheduledSync(bool aConnected, Sync::InternetConnectionType aType) const; QMap iActiveSessions; QMap iExternalSyncProfileStatus; QList iProfilesToRemove; QMap iServers; QList iWaitingOnlineSyncs; NetworkManager *iNetworkManager; QMap iCountersStorage; PluginManager iPluginManager; ProfileManager iProfileManager; SyncQueue iSyncQueue; StorageBooker iStorageBooker; SyncScheduler *iSyncScheduler; SyncBackup *iSyncBackup; TransportTracker *iTransportTracker; ServerActivator *iServerActivator; AccountsHelper *iAccounts; bool iClosing; SyncOnChange iSyncOnChange; SyncOnChangeScheduler iSyncOnChangeScheduler; /*! \brief Save the counter for given profile * * @param aProfile profile to save counter */ void saveProfileCounter(const SyncProfile* aProfile); /*! \brief Restore the counter for given profile * * @param aProfile profile to restore counter */ void restoreProfileCounter(SyncProfile* aProfile); bool iSOCEnabled; QString iUUID; QString iRemoteName; #ifdef SYNCFW_UNIT_TESTS friend class SynchronizerTest; #endif QDBusInterface *iSyncUIInterface; _GSettings *iSettings; }; } #endif // SYNCHRONIZER_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/ServerPluginRunner.cpp0000644000015600001650000001570212634332753024564 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ServerPluginRunner.h" #include "ServerThread.h" #include "ServerActivator.h" #include "ServerPlugin.h" #include "LogMacros.h" #include "PluginManager.h" using namespace Buteo; ServerPluginRunner::ServerPluginRunner(const QString &aPluginName, Profile *aProfile, PluginManager *aPluginMgr, PluginCbInterface *aPluginCbIf, ServerActivator *aServerActivator, QObject *aParent) : PluginRunner(PLUGIN_SERVER, aPluginName, aPluginMgr, aPluginCbIf, aParent), iProfile(aProfile), iPlugin(0), iThread(0), iServerActivator(aServerActivator) { FUNCTION_CALL_TRACE; } ServerPluginRunner::~ServerPluginRunner() { FUNCTION_CALL_TRACE; disconnect(); if (iPlugin != 0 && iPluginMgr != 0) { iPluginMgr->destroyServer(iPlugin); iPlugin = 0; } delete iThread; iThread = 0; delete iProfile; iProfile = 0; } bool ServerPluginRunner::init() { FUNCTION_CALL_TRACE; if (iInitialized) return true; if (iPluginMgr == 0 || iPluginCbIf == 0 || iProfile == 0) { LOG_WARNING("Invalid members, failed to initialize"); return false; } iPlugin = iPluginMgr->createServer(iPluginName, *iProfile, iPluginCbIf); if (iPlugin == 0) { LOG_WARNING("Failed to create server plug-in:" << iPluginName); return false; } iThread = new ServerThread(); if (iThread == 0) { LOG_WARNING("Failed to create server thread"); return false; } // Pass connectivity state change signal to the plug-in. connect(this, SIGNAL(connectivityStateChanged(Sync::ConnectivityType, bool)), iPlugin, SLOT(connectivityStateChanged(Sync::ConnectivityType, bool))); connect(iPlugin, SIGNAL(newSession(const QString &)), this, SLOT(onNewSession(const QString &))); // Connect signals from the plug-in. connect(iPlugin, SIGNAL(transferProgress(const QString &, Sync::TransferDatabase, Sync::TransferType, const QString &,int)), this, SLOT(onTransferProgress(const QString &, Sync::TransferDatabase, Sync::TransferType, const QString &,int))); connect(iPlugin, SIGNAL(error(const QString &, const QString &, int)), this, SLOT(onError(const QString &, const QString &, int))); connect(iPlugin, SIGNAL(success(const QString &, const QString &)), this, SLOT(onSuccess(const QString &, const QString &))); connect(iPlugin, SIGNAL(accquiredStorage(const QString &)), this, SLOT(onStorageAccquired(const QString &))); connect(iPlugin,SIGNAL(syncProgressDetail(const QString &,int)), this ,SIGNAL(syncProgressDetail(const QString &,int))); // Connect signals from the thread. connect(iThread, SIGNAL(initError(const QString &, const QString &, int)), this, SLOT(onError(const QString &, const QString &, int))); #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) connect(iThread, SIGNAL(terminated()), this, SLOT(onThreadExit())); #endif connect(iThread, SIGNAL(finished()), this, SLOT(onThreadExit())); iInitialized = true; return true; } bool ServerPluginRunner::start() { FUNCTION_CALL_TRACE; bool rv = false; if (iInitialized && iThread != 0) { rv = iThread->startThread(iPlugin); } return rv; } void ServerPluginRunner::stop() { FUNCTION_CALL_TRACE; // Disconnect all signals from this object to the plug-in. disconnect(this, 0, iPlugin, 0); if (iThread != 0) { iThread->stopThread(); iThread->wait(); } } void ServerPluginRunner::abort(Sync::SyncStatus aStatus) { FUNCTION_CALL_TRACE; if (iPlugin != 0) { iPlugin->abortSync(aStatus); } } void ServerPluginRunner::suspend() { FUNCTION_CALL_TRACE; if (iPlugin != 0) { iPlugin->suspend(); } } void ServerPluginRunner::resume() { FUNCTION_CALL_TRACE; if (iPlugin != 0) { iPlugin->resume(); } } SyncPluginBase *ServerPluginRunner::plugin() { FUNCTION_CALL_TRACE; return iPlugin; } SyncResults ServerPluginRunner::syncResults() { FUNCTION_CALL_TRACE; if (iPlugin != 0) { return iPlugin->getSyncResults(); } else { return SyncResults(); } } bool ServerPluginRunner::cleanUp() { FUNCTION_CALL_TRACE; bool retval = false ; if (iPlugin != 0) { retval = iPlugin->cleanUp(); } return retval; } void ServerPluginRunner::onNewSession(const QString &aDestination) { // Add reference to the server plug-in, so that the plug-in // will not be stopped when there is a session running. if (iServerActivator != 0) { iServerActivator->addRef(plugin()->getProfileName()); } emit newSession(aDestination); } void ServerPluginRunner::onTransferProgress(const QString &aProfileName, Sync::TransferDatabase aDatabase, Sync::TransferType aType, const QString &aMimeType, int aCommittedItems) { FUNCTION_CALL_TRACE; emit transferProgress(aProfileName, aDatabase, aType, aMimeType, aCommittedItems); } void ServerPluginRunner::onError(const QString &aProfileName, const QString &aMessage, int aErrorCode) { FUNCTION_CALL_TRACE; emit error(aProfileName, aMessage, aErrorCode); onSessionDone(); } void ServerPluginRunner::onSuccess(const QString &aProfileName, const QString &aMessage) { FUNCTION_CALL_TRACE; emit success(aProfileName, aMessage); onSessionDone(); } void ServerPluginRunner::onStorageAccquired(const QString &aMimeType ) { FUNCTION_CALL_TRACE; emit storageAccquired(aMimeType); } void ServerPluginRunner::onThreadExit() { FUNCTION_CALL_TRACE; emit done(); } void ServerPluginRunner::onSessionDone() { FUNCTION_CALL_TRACE; // Remove reference to the server plug-in. This may result in stopping // the server plug-in, if it doesn't need to be active any more. #if 0 // SyncML Server plugin should not die after one single session // Don't think removing the server reference is useful if (iServerActivator != 0) { iServerActivator->removeRef(plugin()->getProfileName()); } #endif } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/StorageChangeNotifier.h0000644000015600001650000000432112634332753024617 0ustar pbuserpbgroup00000000000000#ifndef STORAGECHANGENOTIFIER_H #define STORAGECHANGENOTIFIER_H #include #include namespace Buteo { class StorageChangeNotifierPlugin; class PluginManager; /*! \brief Notifies about changes in storages * that it's asked to monitor */ class StorageChangeNotifier : public QObject { Q_OBJECT public: /*! \brief constructor */ StorageChangeNotifier(); /*! \brief destructor */ ~StorageChangeNotifier(); /*! \brief load all implemented storage change notifier plug-in's * * @param aPluginManager used to load SOC storage plugins * @param aStorageNames list of storages we wan't to monitor */ void loadNotifiers(PluginManager* aPluginManager, const QStringList& aStorageNames); /*! Call this to start monitoring changes in storages * * @param list of storage names which can't be monitored * @return true if we can monitor all storages requested for * false otherwise */ bool startListen(QStringList& aFailedStorages); /*! \brief call this to ignore taking action on * storage changes. Whether there was a change can * be determined by calling hasChanges() on the notifier plug-in * and startListen() can be called again * * @param disableAfterNextChange if set to true, we stop listening * to change notifiers after they've notified about the next change. * This is useful for eg to note that a change did occur during a d2d * sync (during which we disable SOC), but we don't want to get notified * for each batch change, the one notification lets itself be known to us * when we call checkForChanges() */ void stopListen(bool disableAfterNextChange = false); /*! Manually check and notify changes in storage */ void checkForChanges(); private Q_SLOTS: /*! \brief process a storage change notification */ void storageChanged(); Q_SIGNALS: /*! emit this signal if a storage changed * * @param storageName name of the storage that changed */ void storageChange(QString aStorageName); private: QHash iNotifierMap; PluginManager* iPluginManager; }; } #endif buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/BackgroundSync.cpp0000644000015600001650000002225612634332753023663 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2014 Jolla Ltd * * Contact: Valerio Valerio * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "LogMacros.h" #include "BackgroundSync.h" #include #include // 24 hours const int MAX_FREQUENCY = 1440; BackgroundSync::BackgroundSync(QObject* aParent) : QObject(aParent) { FUNCTION_CALL_TRACE; } BackgroundSync::~BackgroundSync() { FUNCTION_CALL_TRACE; removeAll(); } void BackgroundSync::removeAll() { FUNCTION_CALL_TRACE; QStringList profNames; QMapIterator iter(iScheduledSyncs); while (iter.hasNext()) { iter.next(); profNames.append(iter.key()); } for(int i=0; istop(); delete tmp.backgroundActivity; iScheduledSyncs.remove(aProfName); return true; } bool BackgroundSync::set(const QString &aProfName, int seconds) { FUNCTION_CALL_TRACE; if( aProfName.isEmpty()) return false; if(iScheduledSyncs.contains(aProfName) == true) { // Can't schedule sync for such long interval removing existent profile if it exists, // new background activity will be added below if ((seconds / 60 > MAX_FREQUENCY)) { remove(aProfName); } else { BActivityStruct &newAct = iScheduledSyncs[aProfName]; BackgroundActivity::Frequency frequency = frequencyFromSeconds(seconds); if (newAct.frequency != frequency) { newAct.backgroundActivity->stop(); newAct.frequency = frequency; newAct.backgroundActivity->setWakeupFrequency(newAct.frequency); newAct.backgroundActivity->wait(); LOG_DEBUG("BackgroundSync::set(), Rescheduling for " << aProfName << " with frequency " << seconds / 60); return true; } else { newAct.backgroundActivity->wait(); LOG_DEBUG("Profile already with the same frequency... No new BackgroundSync"); return true; //returing 'true' - no immediate sync request to be sent. } } } BActivityStruct &newAct = iScheduledSyncs[aProfName]; newAct.backgroundActivity = new BackgroundActivity(this); newAct.id = newAct.backgroundActivity->id(); connect(newAct.backgroundActivity,SIGNAL(running()), this, SLOT(onBackgroundSyncStarted())); if (seconds / 60 > MAX_FREQUENCY) { LOG_DEBUG("BackgroundSync::set() without a frequency, profile name = " << aProfName); newAct.frequency = BackgroundActivity::Range; // 0 newAct.backgroundActivity->wait(seconds); } else { newAct.frequency = frequencyFromSeconds(seconds); newAct.backgroundActivity->setWakeupFrequency(newAct.frequency); newAct.backgroundActivity->wait(); LOG_DEBUG("BackgroundSync::set(), profile name = " << aProfName << " with frequency " << seconds / 60); } return true; } void BackgroundSync::onBackgroundSyncStarted() { FUNCTION_CALL_TRACE; BackgroundActivity *tempAct = static_cast(sender()); QString profName = getProfNameFromId(tempAct->id()); if (!profName.isEmpty()) { LOG_DEBUG("Background Sync started, for profile = " << profName); emit onBackgroundSyncRunning(profName); } else { LOG_DEBUG("Error profile for background activity not found"); } } void BackgroundSync::onBackgroundSyncCompleted(QString aProfName) { FUNCTION_CALL_TRACE; LOG_DEBUG("Background sync completed, removing activity, profile name = " << aProfName); remove(aProfName); } QString BackgroundSync::getProfNameFromId(const QString activityId) const { FUNCTION_CALL_TRACE; QMapIterator iter(iScheduledSyncs); while (iter.hasNext()) { iter.next(); const BActivityStruct& tmp = iter.value(); if(tmp.id == activityId) { return iter.key(); break; } } return QString(); } BackgroundActivity::Frequency BackgroundSync::frequencyFromSeconds(int seconds) { // Don't allow frequencies smaller than 5 mins. // In rare cases is possible that seconds is 0 int minutes = seconds / 60; if (minutes <= 5) return BackgroundActivity::FiveMinutes; else if (minutes <= 10) return BackgroundActivity::TenMinutes; else if (minutes <= 15) return BackgroundActivity::FifteenMinutes; else if (minutes <= 30) return BackgroundActivity::ThirtyMinutes; else if (minutes <= 60) return BackgroundActivity::OneHour; else if (minutes <= 2 * 60) return BackgroundActivity::TwoHours; else if (minutes <= 4 * 60) return BackgroundActivity::FourHours; else if (minutes <= 8 * 60) return BackgroundActivity::EightHours; else if (minutes <= 10 * 60) return BackgroundActivity::TenHours; else if (minutes <= 12 * 60) return BackgroundActivity::TwelveHours; else return BackgroundActivity::TwentyFourHours; } // sync switches void BackgroundSync::removeAllSwitches() { FUNCTION_CALL_TRACE; QStringList profNames; QMapIterator iter(iScheduledSwitch); while (iter.hasNext()) { iter.next(); profNames.append(iter.key()); } for(int i=0; istop(); delete tmp.backgroundActivity; iScheduledSwitch.remove(aProfName); return true; } bool BackgroundSync::setSwitch(const QString &aProfName, const QDateTime &aSwitchTime) { FUNCTION_CALL_TRACE; if(aProfName.isEmpty()) return false; if(iScheduledSwitch.contains(aProfName) == true) { BActivitySwitchStruct &newSwitch = iScheduledSwitch[aProfName]; if (newSwitch.nextSwitch != aSwitchTime) { // If activity's state was already Waiting, the state doesn't change, nothing happens and // the existing background activity keeps running until the previously set time expires, so we have to stop it. newSwitch.backgroundActivity->stop(); newSwitch.nextSwitch = aSwitchTime; newSwitch.backgroundActivity->wait(QDateTime::currentDateTime().secsTo(aSwitchTime)); LOG_DEBUG("BackgroundSync::setSwitch(), Rescheduling for " << aProfName << " at " << aSwitchTime.toString()); } else { LOG_DEBUG("Profile already with the same switch timer... No new switch timer"); } } else { BActivitySwitchStruct &newSwitch = iScheduledSwitch[aProfName]; newSwitch.backgroundActivity = new BackgroundActivity(this); newSwitch.id = newSwitch.backgroundActivity->id(); connect(newSwitch.backgroundActivity,SIGNAL(running()), this, SLOT(onBackgroundSwitchStarted())); newSwitch.nextSwitch = aSwitchTime; newSwitch.backgroundActivity->wait(QDateTime::currentDateTime().secsTo(aSwitchTime)); LOG_DEBUG("BackgroundSync::setSwitch(), profile name = " << aProfName << " at " << aSwitchTime.toString()); } return true; } void BackgroundSync::onBackgroundSwitchStarted() { FUNCTION_CALL_TRACE; BackgroundActivity *tempAct = static_cast(sender()); QString profName = getProfNameFromSwitchId(tempAct->id()); if (!profName.isEmpty()) { LOG_DEBUG("Background switch timer started, for profile = " << profName); emit onBackgroundSwitchRunning(profName); } else { LOG_DEBUG("Error profile for background switch timer not found"); } } QString BackgroundSync::getProfNameFromSwitchId(const QString activityId) const { FUNCTION_CALL_TRACE; QMapIterator iter(iScheduledSwitch); while (iter.hasNext()) { iter.next(); const BActivitySwitchStruct& tmp = iter.value(); if(tmp.id == activityId) { return iter.key(); break; } } return QString(); } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncDBusInterface.h0000644000015600001650000003641312634332753023727 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2015 Jolla Ltd * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCDBUSINTERFACE_H #define SYNCDBUSINTERFACE_H #include #include #include #include namespace Buteo { /*! * \brief Defines a D-Bus interface for the sync daemon. * * A XML file describing the interface can be generated from this class using * qdbuscpp2xml tool. This XML file can then be used to generate interface * adaptor and proxy classes using qdbusxml2cpp tool. */ class SyncDBusInterface : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "com.meego.msyncd") public: signals: /*! * \brief Notifies about a change in synchronization status. * * \param aProfileName Name of the profile used in the sync session whose * status has changed. * \param aStatus The new status. One of the following: * 0 (QUEUED): Sync request has been queued or was already in the * queue when sync start was requested. * 1 (STARTED): Sync session has been started. * 2 (PROGRESS): Sync session is progressing. * 3 (ERROR): Sync session has encountered an error and has been stopped, * or the session could not be started at all. * 4 (DONE): Sync session was successfully completed. * 5 (ABORTED): Sync session was aborted. * Statuses 3-5 are final, no more status changes will be sent from the * same sync session. * \param aMessage A message describing the status change in detail. This * can for example be shown to the user or written to a log * \param aMoreDetails * When aStatus is ERROR, this parameter contains a specific error code. * When aStatus is PROGRESS, this parameter contains more details about the progress */ void syncStatus(QString aProfileName, int aStatus, QString aMessage, int aMoreDetails); /*! \brief Notifies about progress in transferring items * * \param aProfileName Name of the profile where progress has occurred * \param aTransferDatabase Database to which transfer was made. One of the following: * 0 (LOCAL_DATABASE): Transfer was made from remote database to local database * 1 (REMOTE_DATABASE): Transfer was made from local database to remote database * \param aTransferType Type of transfer that was made. One of the following: * 0 (ADDITION): Addition was made to database * 1 (MODIFICATION): Modification was made to database * 2 (DELETION): Deletion was made to database * 3 (ERROR): Addition/Modification/Deletion was attempted, but it failed * \param aMimeType Mime type of the processed item * \param aCommittedItems No. of Items committed for this operation */ void transferProgress(QString aProfileName, int aTransferDatabase, int aTransferType , QString aMimeType, int aCommittedItems); /*! \brief Notifies about a change in profile. * * This signal is sent when the profile data is modified or when a profile * is added or deleted in msyncd. * \param aProfileName Name of the changed profile. * \param aChangeType * 0 (ADDITION): Profile was added. * 1 (MODIFICATION): Profile was modified. * 2 (DELETION): Profile was deleted. * \param aProfileAsXml Updated Profile Object is sent as xml * */ void signalProfileChanged(QString aProfileName, int aChangeType , QString aProfileAsXml); /*! \brief Notifies about Backup start. * * This signal is sent when the backup framework is backing the sync related * data */ void backupInProgress (); /*! \brief Notifies about Backup done. * * This signal is sent when the backup framework has completed backing the sync related * data. */ void backupDone(); /*! \brief Notifies about Restore start. * * This signal is sent when the backup framework is restoring the sync related * data */ void restoreInProgress(); /*! \brief Notifies about Restore Done. * * This signal is sent when the backup framework has restored the sync related * data */ void restoreDone(); /*! \brief Notifies about the availability of Results for a recent sync * * This signal is sent when the results are available for the last sync * only recent results ( SyncResults object) are sent as xml. * \param aProfileName Name of the profile for which results are available * \param aResultsAsXml results as an xml object */ void resultsAvailable(QString aProfileName , QString aResultsAsXml); /*! \brief Notifies sync status change for a set of account Ids * * This signal is sent when the status of a sync for a particular * account ID changes state Upon receiving this signal, the client is * expected to call the status method to check whether sync is * running/stopped for this account ID * * \param aAccountId The account IDs that changed state * \param aNewStatus The new status of sync for this account * \param aFailedReason This is an out parameter. In case the last sync has * failed, this will contain the code indicating the failure reason (TODO: * Define error codes). In case the last sync has not failed, this must be * ignored * \param aPrevSyncTime This is an out parameter. The previous sync time. * Invalid time is returned if there was no last sync. * \param aNextSyncTime This is an out parameter. The next sync time. */ void statusChanged(unsigned int aAccountId, int aNewStatus, int aFailedReason, qlonglong aPrevSyncTime, qlonglong aNextSyncTime); /*! \brief Returns the connectivity state of a specific medium like * bluetooth, USB or network. * \see SyncCommonDefs::ConnectivityType for arguments */ bool isConnectivityAvailable(int connectivityType); /*! \brief Notifies sync externally status for an account and client profile. * * This signal is sent when the sync externally status of a particular * account changes or a client queries for the state via 'isSyncedExternally' * * \param aAccountId The account IDs that changed state/was queried the state * \param aClientProfileName The name of the client profile resposible for the sync. * \param aState The current status of sync externally(on/off). */ void syncedExternallyStatus(uint aAccountId, const QString &aClientProfileName, bool aState); public slots: /*! * \brief Requests to starts synchronizing using a profile with the given * name. * * A status change signal (QUEUED, STARTED or ERROR) will be sent by the * daemon when the request is processed. If there is a sync already in * progress using the same resources that are needed by the given profile, * adds the sync request to a sync queue. Otherwise a sync session is * started immediately. * * \param aProfileId Id of the profile to use in sync. * \return True if a profile with the given id was found. Otherwise * false and no status change signals will follow from this request. */ virtual bool startSync(QString aProfileId) = 0; /*! * \brief Stops synchronizing the profile with the given name. * * If the sync request is still in queue and not yet started, the queue * entry is removed. * * \param aProfileId Name of the profile to stop syncing. */ virtual Q_NOREPLY void abortSync(QString aProfileId) = 0; /*! * \brief This function should be called when sync profile has to be deleted * * \param aProfileId Id of the profile to be deleted. * \return status of the remove operation */ virtual bool removeProfile(QString aProfileId) = 0; /*! * \brief This function should be called when sync profile information has * been changed by someone else than the sync daemon. * \note If profile does not exist prior to calling this function, a new profile file is created * * \param aProfileAsXml - Modified Profile Object as XML. * \return status of the update operation */ virtual bool updateProfile(QString aProfileAsXml) = 0; /*! * \brief Requests sync daemon to reserve storages for the caller. * * This function must be called if an external sync entity (like Active * Sync engine) wants to use the same storages that the sync daemon uses, * because concurrent access might lead to data corruption. If none of the * requested storages is currently used by the sync daemon, they are all * marked as reserved and can not be used by the daemon until the storages * are freed by calling releaseStorages. If one or more of the requested * storages is already in use, none of them is reserved. * * \param aStorageNames Names of the storages to reserve. * \return Success indicator. True if all requested storages were * successfully reserved. False if request failed and no storages were * reserved. */ virtual bool requestStorages(QStringList aStorageNames) = 0; /*! * \brief Releases the given storages so that sync daemon can again use * them freely. * * This function must be called after a successful requestStorages call, * when the reserved storages are not used by the caller any more. */ virtual Q_NOREPLY void releaseStorages(QStringList aStorageNames) = 0; /*! * \brief Gets the list of profile names of currently running syncs. * * \return Profile name list. */ virtual QStringList runningSyncs() = 0; /*! * \brief This function returns true if backup/restore in progress else * false. */ virtual bool getBackUpRestoreState() = 0; /*! * \brief sets the schedule for a profile * * This Function helps in setting a schedule to profile * this Function is to be used by the SyncInterface Client Library to * expose a user friendly API by abstracting the dbus mechanisms * involved with synchronizer * * \param aProfileId - Id of the profile for which schedule has to be set * \param aScheduleAsXml - Over the dbus the schedule object is transmitted as xml * * \return bool - status of the operation */ virtual bool setSyncSchedule(QString aProfileId , QString aScheduleAsXml) = 0; /*! * \brief Save SyncResults to log.xml file. * \param aProfileId to save result in corresponding file. * \param aSyncResults to save in the \code .log.xml. \endcode * \return status of the saveSyncResults */ virtual bool saveSyncResults(QString aProfileId,QString aSyncResults) = 0; /*! \brief To get lastSyncResult. * \param aProfileId * \return QString of syncResult. */ virtual QString getLastSyncResult(const QString &aProfileId) = 0; /*! \brief Gets all visible sync profiles. * * Returns all sync profiles that should be visible in sync ui. A profile * is visible if it has not been explicitly set as hidden. * \return The list of sync profiles. */ virtual QStringList allVisibleSyncProfiles() = 0; /*! \brief Gets a sync profile. * * Loads and merges also all sub-profiles that are referenced from the * main profile. Loads the log of finished synchronization sessions with * this profile. * \param aProfileId Name of the profile to get. * \return The sync profile as Xml string. */ virtual QString syncProfile(const QString &aProfileId) = 0; /*! \brief Gets a sync profiles matching the key-value. * * Loads and merges also all sub-profiles that are referenced from the * main profile. Loads the log of finished synchronization sessions with * this profile. * \param aKey Key to match for profile. * \param aValue Value to match for profile. * \return The sync profiles as Xml string list. */ virtual QStringList syncProfilesByKey(const QString &aKey, const QString &aValue) = 0; /*! \brief Gets a profiles matching the profile type. * * \param aType Type of the profile service/storage/sync. * \return The sync profile ids as string list. */ virtual QStringList syncProfilesByType(const QString &aType) = 0; /*! \brief Starts sync for all profiles matching the given account ID. * * \param aAccountId The account ID. */ virtual Q_NOREPLY void start(unsigned int aAccountId) = 0; /*! \brief Stops sync for all profiles matching the given account ID. * * \param aAccountId The account ID. */ virtual Q_NOREPLY void stop(unsigned int aAccountId) = 0; /*! \brief Returns the list of account IDs for which sync is ongoing * * \return The list of account IDs currectly syncing. */ virtual QList syncingAccounts() = 0; /*! \brief Returns the status of the sync for the given account Id * * \param aAccountId The account ID. * \param aFailedReason This is an out parameter. In case the last sync has * failed, this will contain the code indicating the failure reason (TODO: * Define error codes). In case the last sync has not failed, this must be * ignored * \param aPrevSyncTime This is an out parameter. The previous sync time. * Invalid time is returned if there was no last sync. * \param aNextSyncTime This is an out parameter. The next sync time. * \return The status of sync: 0 = Sync is running, * 1 = Last sync succeeded, 2 = last sync failed */ virtual int status(unsigned int aAccountId, int &aFailedReason, qlonglong &aPrevSyncTime, qlonglong &aNextSyncTime) = 0; /*! \brief Queries the sync externally status of a given account, * 'syncedExternallyStatus' signal is emitted with the reply is ready, clients should listen * to the later. * * \param aAccountId The account ID. * \param aClientProfileName The name of the client profile resposible for the sync, this is used to distinguish accounts * having several services enabled */ virtual Q_NOREPLY void isSyncedExternally(unsigned int aAccountId, const QString aClientProfileName) = 0; /*! \brief Create a sync profile for the account if it does not exists * * \param aAccountId The account ID. * \return The profile name if the profile was created successful or empty if it fails */ virtual QString createSyncProfileForAccount(uint aAccountId) = 0; }; } #endif // SYNCDBUSINTERFACE_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/unittest.pri0000644000015600001650000000036412634332753022632 0ustar pbuserpbgroup00000000000000CONFIG += link_prl msyncd_out_pwd = $${OUT_PWD}/$$relative_path($${PWD}, $$dirname(_PRO_FILE_)) LIBS += -L$${msyncd_out_pwd} -lmsyncd SOURCES += $$PWD/UnitTest.cpp # Already present in ../unittests/tests/tests.pro #DEFINES += SYNCFW_UNIT_TESTS buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/IPHeartBeat.h0000644000015600001650000000632012634332753022476 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef IPHEARTBEAT_H #define IPHEARTBEAT_H #include #include #include extern "C" { #include "iphbd/libiphb.h" } namespace Buteo { /// \brief IPHeartBeat implementation. /// /// This class manages heart beats for different profiles. class IPHeartBeat : public QObject { Q_OBJECT /// \brief Internal structure to hold profile-iphb stucture-notifier map struct BeatStruct { int sockfd; QSocketNotifier* sockNotifier; iphb_t iphbHandle; }; public: /*! \brief Constructor. * \param aParent Parent object. */ IPHeartBeat(QObject *aParent); /** * \brief Destructor */ virtual ~IPHeartBeat(); /*! \brief Schedules a heartbeat for this profile between minWaitTime and maxWaitTime. * * The beat will be generated between minWaitTime and maxWaitTime seconds * \param aProfName Name of the profile. * \param aMinWaitTime Minimum wait time in seconds. * \param aMaxWaitTime Minimum wait time in seconds. * \return Success indicator. */ bool setHeartBeat(const QString& aProfName, ushort aMinWaitTime, ushort aMaxWaitTime); /*! \brief Removes heart beat waiting for a profile. * * \param aProfName Name of the profile. */ void removeWait(const QString& aProfName); /*! \brief Removes heart beat waiting for all profiles. */ void removeAllWaits(); signals: /*! \brief This signal will be emitted when a heartbeat for particular profile is triggered. * * \param aProfName Name of the profile for which heart beat is triggered. */ void onHeartBeat(QString aProfName); private slots: /*! \brief This signal will be emitted when a socket descriptor gets an event notification. * * \param aSockFd Socket descriptor who got the event. */ void internalBeatTriggered(int aSockFd); private: /*! \brief Finds the name of the profile which uses particular file descriptor * * \param aSockFd Socket descriptor. * \param aProfName name of the profile. * \return true if profile name found, otherwise false */ bool getProfNameFromFd(int aSockFd, QString& aProfName); private: ///Map of structures waiting for heart beat QMap iBeatsWaiting; #ifdef SYNCFW_UNIT_TESTS friend class IPHeartBeatTest; #endif }; } #endif buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/ClientPluginRunner.cpp0000644000015600001650000001431312634332753024531 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ClientPluginRunner.h" #include "ClientThread.h" #include "ClientPlugin.h" #include "LogMacros.h" #include "PluginManager.h" using namespace Buteo; // Maximum time in milliseconds for a plugin to finish sync static const unsigned long long MAX_PLUGIN_SYNC_TIME = 1800000; //30 mins ClientPluginRunner::ClientPluginRunner(const QString &aPluginName, SyncProfile *aProfile, PluginManager *aPluginMgr, PluginCbInterface *aPluginCbIf, QObject *aParent) : PluginRunner(PLUGIN_CLIENT, aPluginName, aPluginMgr, aPluginCbIf, aParent), iProfile(aProfile), iPlugin(0), iThread(0) { FUNCTION_CALL_TRACE; } ClientPluginRunner::~ClientPluginRunner() { FUNCTION_CALL_TRACE; disconnect(); if (iPlugin != 0 && iPluginMgr != 0) { iPluginMgr->destroyClient(iPlugin); iPlugin = 0; } if (iThread != 0) { delete iThread; iThread = 0; } } bool ClientPluginRunner::init() { FUNCTION_CALL_TRACE; if (iInitialized) return true; if (iPluginMgr == 0 || iPluginCbIf == 0 || iProfile == 0) { LOG_WARNING("Invalid members, failed to initialize"); return false; } iPlugin = iPluginMgr->createClient(iPluginName, *iProfile, iPluginCbIf); if (iPlugin == 0) { LOG_WARNING("Failed to create client plug-in:" << iPluginName); return false; } iThread = new ClientThread(); if (iThread == 0) { LOG_WARNING("Failed to create client thread"); return false; } // Pass connectivity state change signal to the plug-in. connect(this, SIGNAL(connectivityStateChanged(Sync::ConnectivityType, bool)), iPlugin, SLOT(connectivityStateChanged(Sync::ConnectivityType, bool))); // Connect signals from the plug-in. connect(iPlugin, SIGNAL(transferProgress(const QString &, Sync::TransferDatabase, Sync::TransferType, const QString &, int)), this, SLOT(onTransferProgress(const QString &, Sync::TransferDatabase, Sync::TransferType, const QString &, int))); connect(iPlugin, SIGNAL(error(const QString &, const QString &, int)), this, SLOT(onError(const QString &, const QString &, int))); connect(iPlugin, SIGNAL(success(const QString &, const QString &)), this, SLOT(onSuccess(const QString &, const QString &))); connect(iPlugin, SIGNAL(accquiredStorage(const QString &)), this, SLOT(onStorageAccquired(const QString &))); connect(iPlugin,SIGNAL(syncProgressDetail(const QString &,int)), this ,SLOT(onSyncProgressDetail(const QString &,int))); // Connect signals from the thread. connect(iThread, SIGNAL(initError(const QString &, const QString &, int)), this, SLOT(onError(const QString &, const QString &, int))); #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) connect(iThread, SIGNAL(terminated()), this, SLOT(onThreadExit())); #endif connect(iThread, SIGNAL(finished()), this, SLOT(onThreadExit())); iInitialized = true; return true; } bool ClientPluginRunner::start() { FUNCTION_CALL_TRACE; bool rv = false; if (iInitialized && iThread != 0) { // Set a timer after which the sync session should stop QTimer::singleShot( MAX_PLUGIN_SYNC_TIME, this, SLOT(pluginTimeout()) ); rv = iThread->startThread(iPlugin); } return rv; } void ClientPluginRunner::stop() { FUNCTION_CALL_TRACE; if (iThread != 0) { iThread->stopThread(); iThread->wait(); } } void ClientPluginRunner::abort(Sync::SyncStatus aStatus) { FUNCTION_CALL_TRACE; if (iPlugin != 0) { iPlugin->abortSync(aStatus); } } SyncPluginBase *ClientPluginRunner::plugin() { FUNCTION_CALL_TRACE; return iPlugin; } SyncResults ClientPluginRunner::syncResults() { FUNCTION_CALL_TRACE; if (iPlugin != 0) { return iPlugin->getSyncResults(); } else { return SyncResults(); } } bool ClientPluginRunner::cleanUp() { FUNCTION_CALL_TRACE; bool retval = false; if (iPlugin != 0) { retval = iPlugin->cleanUp(); } return retval; } void ClientPluginRunner::onTransferProgress(const QString &aProfileName, Sync::TransferDatabase aDatabase, Sync::TransferType aType, const QString &aMimeType, int aCommittedItems) { FUNCTION_CALL_TRACE; emit transferProgress(aProfileName, aDatabase, aType, aMimeType, aCommittedItems); } void ClientPluginRunner::onError(const QString &aProfileName, const QString &aMessage, int aErrorCode) { FUNCTION_CALL_TRACE; emit error(aProfileName, aMessage, aErrorCode); stop(); } void ClientPluginRunner::onSuccess(const QString &aProfileName, const QString &aMessage) { FUNCTION_CALL_TRACE; emit success(aProfileName, aMessage); stop(); } void ClientPluginRunner::onStorageAccquired(const QString &aMimeType ) { FUNCTION_CALL_TRACE; emit storageAccquired(aMimeType); } void ClientPluginRunner::onSyncProgressDetail(const QString &aProfileName,int aProgressDetail) { FUNCTION_CALL_TRACE; emit syncProgressDetail(aProfileName,aProgressDetail); } void ClientPluginRunner::onThreadExit() { FUNCTION_CALL_TRACE; emit done(); } void ClientPluginRunner::pluginTimeout() { FUNCTION_CALL_TRACE; emit error(iProfile->name(), "Plugin timeout occured", Sync::SYNC_PLUGIN_TIMEOUT); stop(); } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/bin/0000755000015600001650000000000012634333235021000 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/bin/msyncd.service0000644000015600001650000000056712634332753023673 0ustar pbuserpbgroup00000000000000[Unit] Description=Sync FW daemon Requires=dbus.socket booster-qt5.service After=pre-user-session.target booster-qt5.service [Service] # -G (--global-syms) so that msyncd's plugins can find symbols in msyncd and # in the libraries msyncd is linked to. ExecStart=/usr/bin/invoker -G -o -s --type=qt5 /usr/bin/msyncd Restart=always [Install] WantedBy=user-session.target buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/AccountsHelper.cpp0000644000015600001650000004537712634332753023677 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "AccountsHelper.h" #include "LogMacros.h" #include "ProfileManager.h" #include "Profile.h" #include "ProfileEngineDefs.h" #include static const QString ACCOUNTS_GLOBAL_SERVICE("global"); using namespace Buteo; AccountsHelper::AccountsHelper(ProfileManager &aProfileManager, QObject *aParent) : QObject(aParent), iProfileManager(aProfileManager) { iAccountManager = new Accounts::Manager(this); // Connect to signal for account creation, deletion, and modification QObject::connect(iAccountManager, SIGNAL(accountCreated(Accounts::AccountId)), this, SLOT(createProfileForAccount(Accounts::AccountId))); QObject::connect(iAccountManager, SIGNAL(accountRemoved(Accounts::AccountId)), this, SLOT(slotAccountRemoved(Accounts::AccountId))); QObject::connect(iAccountManager, SIGNAL(accountUpdated(Accounts::AccountId)), this, SLOT(slotAccountUpdated(Accounts::AccountId))); // load accounts after return from contructor, to allow connection with class singals // that can be fired by 'registerAccountListeners' function QTimer::singleShot(0, this, SLOT(registerAccountListeners())); } AccountsHelper::~AccountsHelper() { iAcctWatchMap.clear(); delete iAccountManager; iAccountManager = 0; // There is no need to delete the accounts objects as they get deleted by // their parent, aka, the manager } QString AccountsHelper::createProfileForAccount(Accounts::AccountId id) { FUNCTION_CALL_TRACE; QString profileName; Accounts::Account *newAccount = iAccountManager->account(id); bool profileFoundAndCreated = false; if(0 != newAccount) { registerAccountListener(id); Accounts::ServiceList serviceList = newAccount->services(); foreach(Accounts::Service service, serviceList) { // Look for a sync profile that matches the service name (template) LOG_DEBUG("Looking for sync profile::" << service.name()); SyncProfile *syncProfile = iProfileManager.syncProfile(service.name()); LOG_DEBUG("Found profile::" << service.name()); if(0 != syncProfile && (true == syncProfile->boolKey(KEY_USE_ACCOUNTS, false)) ) { profileName = addAccountIfNotExists(newAccount, service, syncProfile); profileFoundAndCreated = true; } if(0 != syncProfile) { delete syncProfile; syncProfile = 0; } } if (profileFoundAndCreated == false) { // Fetch the key "remote_service_name" from the account settings and // use it to create a profile QString profileName = newAccount->valueAsString(REMOTE_SERVICE_NAME); LOG_DEBUG("Profile name from account setting:" << profileName); SyncProfile *syncProfile = iProfileManager.syncProfile( profileName ); if (syncProfile) { if (syncProfile->boolKey (KEY_USE_ACCOUNTS, false) == true) createProfileForAccount (newAccount, profileName, syncProfile); profileName = syncProfile->name(); delete syncProfile; } } } else { LOG_DEBUG("Account not found:" << id); } return profileName; } void AccountsHelper::slotAccountRemoved(Accounts::AccountId id) { FUNCTION_CALL_TRACE; // Delete the profile(s) with account ID QList syncProfiles = getProfilesByAccountId(id); foreach(SyncProfile *profile, syncProfiles) { LOG_DEBUG("Removing profile" << profile->name()); emit removeProfile(profile->name()); delete profile; } // remove corresponding watch from the Map QMap::iterator i = iAcctWatchMap.begin(); while (i != iAcctWatchMap.end()) { if (i.value() == id) { i = iAcctWatchMap.erase(i); break; } else ++i; } } void AccountsHelper::slotAccountEnabledChanged(const QString &serviceName, bool enabled) { FUNCTION_CALL_TRACE; // Get the sender account Accounts::Account *changedAccount = qobject_cast(this->sender()); if(0 != changedAccount) { LOG_DEBUG("Received account enabled changed signal" << serviceName << enabled << changedAccount->displayName()); if(serviceName == ACCOUNTS_GLOBAL_SERVICE || serviceName.isEmpty()) { LOG_DEBUG("Entire account state changed to " << enabled << changedAccount->displayName()); // Entire account disabled/enabled QList profiles = getProfilesByAccountId(changedAccount->id()); foreach(SyncProfile *profile, profiles) { // Check if the status really changed here // saving the account can trigger the emition of enabledChanged() if (profile->isEnabled() != enabled) { LOG_DEBUG("Changing profile enabled" << profile->name() << enabled); if(enabled) { // The service pointer must not be deleted here. It is // cached by the Account manager object Accounts::Service service = iAccountManager->service(profile->name()); changedAccount->selectService(service); bool serviceEnabled = changedAccount->enabled(); changedAccount->selectService(); addSetting(changedAccount->id(), KEY_PROFILE_ID, QVariant(profile->name())); LOG_DEBUG("Enabled status for service ::" << profile->name() << serviceEnabled); if(serviceEnabled) { profile->setEnabled(changedAccount->enabled()); iProfileManager.updateProfile(*profile); emit scheduleUpdated(profile->name()); } } else { // Unconditionally, set the profile as disabled profile->setEnabled(enabled); iProfileManager.updateProfile(*profile); emit removeScheduledSync(profile->name()); } } delete profile; } } else { // If the account is in disabled state that will take precedence over the changed value // since those signals can come later if (!changedAccount->enabled()) { LOG_DEBUG("Account in disabled state, profile will be also set as disabled " << changedAccount->displayName()); enabled = false; } // A service in the account is enabled/disabled QList profiles = getProfilesByAccountId(changedAccount->id()); foreach(SyncProfile *profile, profiles) { // See if the service name matches if(serviceName == profile->key(REMOTE_SERVICE_NAME)) { // Check if the status really changed here // saving the account can trigger the emition of enabledChanged() if (profile->isEnabled() != enabled) { profile->setEnabled(enabled); iProfileManager.updateProfile(*profile); if (enabled) { emit scheduleUpdated(profile->name()); } else { emit removeScheduledSync(profile->name()); } } } delete profile; } } } } void AccountsHelper::setSyncSchedule(SyncProfile *syncProfile, Accounts::AccountId id, bool aCreateNew) { FUNCTION_CALL_TRACE; Q_UNUSED (aCreateNew); Accounts::Account *account = iAccountManager->account(id); if(0 != account) { //Sync schedule settings should be global account->selectService(); SyncSchedule syncSchedule; int peakStart = account->valueAsInt(Buteo::SYNC_SCHEDULE_PEAK_START_TIME_KEY_INT); QTime startTime(peakStart/60 , peakStart%60); int peakEnd = account->valueAsInt(Buteo::SYNC_SCHEDULE_PEAK_END_TIME_KEY_INT); QTime endTime(peakEnd/60 , peakEnd%60); LOG_DEBUG ("Start time:" << startTime << "End Time :" << endTime); syncSchedule.setRushTime(startTime, endTime); SyncProfile::SyncType syncType = account->valueAsBool (Buteo::SYNC_SCHEDULE_ENABLED_KEY_BOOL) ? SyncProfile::SYNC_SCHEDULED:SyncProfile::SYNC_MANUAL ; syncProfile->setSyncType (syncType); LOG_DEBUG ("Sync Type :" << syncType ); syncSchedule.setRushEnabled(account->valueAsBool(Buteo::SYNC_SCHEDULE_PEAK_ENABLED_KEY_BOOL)); LOG_DEBUG ("Sync PEAK :" << account->valueAsBool(Buteo::SYNC_SCHEDULE_PEAK_ENABLED_KEY_BOOL)); syncSchedule.setScheduleEnabled(account->valueAsBool(Buteo::SYNC_SCHEDULE_OFFPEAK_ENABLED_KEY_BOOL)); LOG_DEBUG ("Sync OFFPEAK :" << account->valueAsBool(Buteo::SYNC_SCHEDULE_OFFPEAK_ENABLED_KEY_BOOL)); int scheduleInterval = 60; if(syncSchedule.scheduleEnabled()) scheduleInterval = account->valueAsInt(Buteo::SYNC_SCHEDULE_OFFPEAK_SCHEDULE_KEY_INT); syncSchedule.setInterval(scheduleInterval); LOG_DEBUG ("Sync Interval :" << scheduleInterval); scheduleInterval = 60; if(syncSchedule.rushEnabled()) scheduleInterval = account->valueAsInt (Buteo::SYNC_SCHEDULE_PEAK_SCHEDULE_KEY_INT); syncSchedule.setRushInterval(scheduleInterval); LOG_DEBUG ("Sync Rush Interval :" << scheduleInterval); int map = account->valueAsInt (Buteo::SYNC_SCHEDULE_PEAK_DAYS_KEY_INT); LOG_DEBUG ("Sync Days :" << account->valueAsInt (Buteo::SYNC_SCHEDULE_PEAK_DAYS_KEY_INT)); Buteo::DaySet rdays; Buteo::DaySet days; int lastDay = Qt::Sunday; while (lastDay > 0) { int val = 0; val |= (1 << (lastDay - 1)); days.insert(lastDay); if ((val & map)) { rdays.insert(lastDay); LOG_DEBUG ("Day :" << lastDay); } --lastDay; } syncSchedule.setRushDays(rdays); syncSchedule.setDays(days); syncProfile->setSyncSchedule (syncSchedule); } } void AccountsHelper::slotAccountUpdated(Accounts::AccountId id) { #if 0 // Not required for scheduler settings; they will be handled by slotSchedulerSettingsChanged FUNCTION_CALL_TRACE; QList syncProfiles = getProfilesByAccountId(id); foreach(SyncProfile *syncProfile, syncProfiles) { if (syncProfile) { setSyncSchedule(syncProfile, id); iProfileManager.updateProfile(*syncProfile); emit scheduleUpdated(syncProfile->name()); delete syncProfile; } } #else Q_UNUSED(id); #endif } QList AccountsHelper::getProfilesByAccountId(Accounts::AccountId id) { FUNCTION_CALL_TRACE; QList filters; ProfileManager::SearchCriteria filter; filter.iType = ProfileManager::SearchCriteria::EQUAL; filter.iKey = KEY_ACCOUNT_ID; filter.iValue = QString::number(id); filters.append(filter); return iProfileManager.getSyncProfilesByData(filters); } void AccountsHelper::createProfileForAccount(Accounts::Account *account, const QString profileName, const SyncProfile *baseProfile) { FUNCTION_CALL_TRACE; LOG_DEBUG("Creating new profile by cloning base profile"); // Create a new sync profile with username SyncProfile *newProfile = baseProfile->clone(); newProfile->setName(profileName + "-" + QString::number(account->id())); newProfile->setKey(KEY_DISPLAY_NAME, profileName + "-" + account->displayName()); newProfile->setKey(KEY_ACCOUNT_ID, QString::number(account->id())); // Check if account is enabled if (account->enabled()) newProfile->setEnabled(true); // Set profile as enabled setSyncSchedule (newProfile, account->id(), true); // Save the newly created profile iProfileManager.updateProfile(*newProfile); account->setValue(KEY_PROFILE_ID, newProfile->name()); account->syncAndBlock(); emit scheduleUpdated(newProfile->name()); if(newProfile->isSOCProfile()) { emit enableSOC(newProfile->name()); } delete newProfile; } QString AccountsHelper::addAccountIfNotExists(Accounts::Account *account, Accounts::Service service, const SyncProfile *baseProfile) { FUNCTION_CALL_TRACE; Profile *serviceProfile = iProfileManager.profile(service.name(), Profile::TYPE_SYNC); if (!serviceProfile) { LOG_DEBUG ("!!!! Service not supported !!!!"); return QString(); } QString profileName ; if (!service.name().isEmpty()) { QStringList keys; keys << QString::number(account->id()) << service.name(); serviceProfile->setName(keys); profileName = serviceProfile->name(); } delete serviceProfile; SyncProfile *profile = iProfileManager.syncProfile(profileName); LOG_DEBUG("profileName:"<clone(); newProfile->setName(profileName); newProfile->setKey(KEY_DISPLAY_NAME, service.name() + "-" + account->displayName()); // Add the account ID to the profile newProfile->setKey(KEY_ACCOUNT_ID, QString::number(account->id())); // Check if service is enabled LOG_DEBUG("Service:: " << service.displayName() << "enabled status::" << (account->enabled() && account->enabledServices().contains(service))); // Set profile as enabled newProfile->setEnabled(account->enabled() && account->enabledServices().contains(service)); setSyncSchedule (newProfile, account->id(), true); // Save the newly created profile iProfileManager.updateProfile(*newProfile); emit scheduleUpdated(newProfile->name()); if(newProfile->isSOCProfile()) { emit enableSOC(newProfile->name()); } delete newProfile; } else if(true == profile->boolKey(KEY_USE_ACCOUNTS, false)) { LOG_DEBUG("Profile already exist enable it"); // Set profile as enabled profile->setEnabled(true); iProfileManager.updateProfile(*profile); emit scheduleUpdated(profile->name()); if(profile->isSOCProfile()) { emit enableSOC(profile->name()); } } if(0 != profile) { delete profile; } return profileName; } void AccountsHelper::addSetting(Accounts::AccountId id, QString key, QVariant value) { FUNCTION_CALL_TRACE; LOG_DEBUG("Account Id " << id << " Key = " << key << " Value = " << value); Accounts::Account* account = iAccountManager->account(id); if (account != NULL) { account->setValue(key, value); bool success = account->syncAndBlock(); if (!success) { LOG_WARNING("Could not save settings to Account : Reason = " << iAccountManager->lastError().message()); } } } void AccountsHelper::registerAccountListeners() { FUNCTION_CALL_TRACE; // Populate all enabled accounts list (so that we can listen to changes) QList accountIds = iAccountManager->accountList(); foreach(Accounts::AccountId id, accountIds) { registerAccountListener(id); } } void AccountsHelper::slotSchedulerSettingsChanged(const char *aKey) { FUNCTION_CALL_TRACE; LOG_DEBUG("Key Changed" << QString(aKey)); Accounts::Watch *watch = qobject_cast(this->sender()); if(watch && iAcctWatchMap.contains(watch)) { Accounts::AccountId id = iAcctWatchMap.value(watch); QList syncProfiles = getProfilesByAccountId(id); foreach(SyncProfile *syncProfile, syncProfiles) { if (syncProfile) { setSyncSchedule(syncProfile, id); iProfileManager.updateProfile(*syncProfile); emit scheduleUpdated(syncProfile->name()); delete syncProfile; } } } } void AccountsHelper::registerAccountListener(Accounts::AccountId id) { FUNCTION_CALL_TRACE; Accounts::Account *account = iAccountManager->account(id); if (iAccountList.contains(account)) { return; } iAccountList.append(account); // Callback for account enabled/disabled QObject::connect(account, SIGNAL(enabledChanged(const QString&, bool)), SLOT(slotAccountEnabledChanged(const QString&, bool)), Qt::UniqueConnection); // Account SyncOnChange QList profiles = getProfilesByAccountId(id); foreach(SyncProfile *profile, profiles) { if(profile->isSOCProfile()) { emit enableSOC(profile->name()); } } account->selectService(); account->beginGroup("scheduler"); LOG_DEBUG("Watching Group :" << account->group()); Accounts::Watch *watch = account->watchKey(); if(!watch){ LOG_DEBUG("Failed to add watch for acct with id:" << id); return; } iAcctWatchMap[watch] = id; QObject::connect(watch, SIGNAL(notify(const char *)), SLOT(slotSchedulerSettingsChanged(const char *)), Qt::UniqueConnection); } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/IPHeartBeat.cpp0000644000015600001650000000752512634332753023041 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "IPHeartBeat.h" #include #include "LogMacros.h" using namespace Buteo; IPHeartBeat::IPHeartBeat(QObject* aParent) : QObject(aParent) { FUNCTION_CALL_TRACE; } IPHeartBeat::~IPHeartBeat() { FUNCTION_CALL_TRACE; removeAllWaits(); } void IPHeartBeat::removeAllWaits() { FUNCTION_CALL_TRACE; QStringList profNames; QMapIterator iter(iBeatsWaiting); while (iter.hasNext()) { iter.next(); profNames.append(iter.key()); } for(int i=0; i iter(iBeatsWaiting); while (iter.hasNext()) { iter.next(); const BeatStruct& tmp = iter.value(); if(tmp.sockfd == aSockFd) { aProfName = iter.key(); ret = true; break; } } return ret; } bool IPHeartBeat::setHeartBeat(const QString& aProfName, ushort aMinWaitTime, ushort aMaxWaitTime) { FUNCTION_CALL_TRACE; if( (aMinWaitTime > aMaxWaitTime) || aProfName.isEmpty() ) return false; LOG_DEBUG("setHeartBeat(), profile name = " << aProfName); if(iBeatsWaiting.contains(aProfName) == true) { LOG_DEBUG("Profile already in waiting... No new beat"); return true; //returing 'true' - no immediate sync request to be sent. } iphb_t iphbHandle = iphb_open(NULL); if(iphbHandle == 0) { LOG_DEBUG("iphb_open() failed.... No IP heartbeat available"); return false; } int sockfd = iphb_get_fd(iphbHandle); if(sockfd < 0) { LOG_DEBUG("iphb_get_fd() failed.... No IP heartbeat"); iphb_close(iphbHandle); return false; } BeatStruct& newBeat = iBeatsWaiting[aProfName]; newBeat.iphbHandle = iphbHandle; newBeat.sockfd = sockfd; newBeat.sockNotifier = new QSocketNotifier(sockfd,QSocketNotifier::Read,0); if(iphb_wait(iphbHandle,aMinWaitTime,aMaxWaitTime,0) == -1) { LOG_DEBUG("iphb_wait() failed .... No IP heartbeat"); removeWait(aProfName); return false; } connect(newBeat.sockNotifier,SIGNAL(activated(int)),this,SLOT(internalBeatTriggered(int))); LOG_DEBUG("IP Heartbeat set for profile"); return true; } void IPHeartBeat::internalBeatTriggered(int aSockFd) { FUNCTION_CALL_TRACE; QString profName; if(getProfNameFromFd(aSockFd,profName) == true) { removeWait(profName); LOG_DEBUG("Emitting IP Heartbeat, profile name = " << profName); emit onHeartBeat(profName); } } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/msyncd.pro0000644000015600001650000000046312634332753022256 0ustar pbuserpbgroup00000000000000TEMPLATE = subdirs CONFIG += ordered SUBDIRS = msyncd-lib.pro msyncd-app.pro # ##################################################################### # make coverage (debug) # ##################################################################### coverage.CONFIG += recursive QMAKE_EXTRA_TARGETS += coverage buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/ClientThread.cpp0000644000015600001650000001270612634332753023314 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "ClientThread.h" #include "ClientPlugin.h" #include "LogMacros.h" #include using namespace Buteo; ClientThread::ClientThread() : iClientPlugin( 0 ), iIdentity(NULL), iService(NULL), iSession(NULL), iRunning(false) { FUNCTION_CALL_TRACE; } ClientThread::~ClientThread() { FUNCTION_CALL_TRACE; if (iSession) { iIdentity->destroySession(iSession); } delete iIdentity; } QString ClientThread::getProfileName() const { FUNCTION_CALL_TRACE; QString profileName; if (iClientPlugin != 0) { profileName = iClientPlugin->getProfileName(); } return profileName; } ClientPlugin* ClientThread::getPlugin() const { FUNCTION_CALL_TRACE; return iClientPlugin; } bool ClientThread::startThread( ClientPlugin* aClientPlugin ) { FUNCTION_CALL_TRACE; if (aClientPlugin == 0) return false; { QMutexLocker locker(&iMutex); if( iRunning ) { return false; } else { iRunning = true; } } iClientPlugin = aClientPlugin; if (iClientPlugin == 0) { LOG_CRITICAL("Client plugin is NULL"); return false; } SyncProfile &profile = iClientPlugin->profile(); const QString prefix("sso-provider="); QString username = profile.key("Username"); if (username.startsWith(prefix)) { // Look up real username/password in SSO first, // before starting sync. This is better done // in the application thread, because this is where // this instance lives. iProvider = username.mid(prefix.size()); LOG_DEBUG("SSO provider::" << iProvider); iService = new SignOn::AuthService(this); connect(iService, SIGNAL(identities(const QList &)), this, SLOT(identities(const QList &))); iService->queryIdentities(); } else { // Move to client thread iClientPlugin->moveToThread( this ); start(); } return true; } void ClientThread::stopThread() { FUNCTION_CALL_TRACE; exit(); } void ClientThread::run() { FUNCTION_CALL_TRACE; if( !iClientPlugin->init() ) { LOG_DEBUG( "Could not initialize client plugin:" << iClientPlugin->getPluginName() ); emit initError( getProfileName(), "", 0 ); return; } if( !iClientPlugin->startSync() ) { LOG_DEBUG( "Could not start client plugin:" << iClientPlugin->getPluginName() ); emit initError( getProfileName(), "", 0 ); return; } exec(); iSyncResults = iClientPlugin->getSyncResults(); iClientPlugin->uninit(); // Move back to application thread iClientPlugin->moveToThread( QCoreApplication::instance()->thread() ); { QMutexLocker locker(&iMutex); iRunning = false; } } SyncResults ClientThread::getSyncResults() { FUNCTION_CALL_TRACE; return iSyncResults; } void ClientThread::identities(const QList &identityList) { FUNCTION_CALL_TRACE; for (int i = 0; i < identityList.size(); ++i) { const SignOn::IdentityInfo &info = identityList.at(i); LOG_DEBUG("Signon identity::" << info.caption()); if (info.caption() == iProvider) { iIdentity = SignOn::Identity::existingIdentity(info.id(), this); // Setup an authentication session using the "password" method iSession = iIdentity->createSession(QLatin1String("password")); connect(iSession, SIGNAL(response(const SignOn::SessionData &)), this, SLOT(identityResponse(const SignOn::SessionData &))); connect(iSession, SIGNAL(error(SignOn::Error)), this, SLOT(identityError(SignOn::Error))); // Get the password! iSession->process(SignOn::SessionData(), QLatin1String("password")); return; } } emit initError( getProfileName(), "credentials not found in SSO", 0 ); } void ClientThread::identityResponse(const SignOn::SessionData &sessionData) { FUNCTION_CALL_TRACE; // temporarily set real username/password, then invoke client SyncProfile &profile = iClientPlugin->profile(); LOG_DEBUG("Username::" << sessionData.UserName()); profile.setKey("Username", sessionData.UserName()); profile.setKey("Password", sessionData.Secret()); // delayed starting of thread iClientPlugin->moveToThread( this ); start(); } void ClientThread::identityError(SignOn::Error err) { FUNCTION_CALL_TRACE; emit initError( getProfileName(), err.message(), 0 ); } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/generate_dbus_proxy.sh0000755000015600001650000000167712634332753024656 0ustar pbuserpbgroup00000000000000#/* # * This file is part of buteo-syncfw package # * # * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). # * # * Contact: Sateesh Kavuri # * # * This library is free software; you can redistribute it and/or # * modify it under the terms of the GNU Lesser General Public License # * version 2.1 as published by the Free Software Foundation. # * # * This library is distributed in the hope that it will be useful, but # * WITHOUT ANY WARRANTY; without even the implied warranty of # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # * Lesser General Public License for more details. # * # * You should have received a copy of the GNU Lesser General Public # * License along with this library; if not, write to the Free Software # * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # * 02110-1301 USA # * # */ # qdbusxml2cpp -p SyncDaemonProxy -N -c SyncDaemonProxy com.meego.msyncd.xml buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncBackupAdaptor.h0000644000015600001650000000672412634332753023773 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ /* * This file was generated by qdbusxml2cpp version 0.7 * Command line was: qdbusxml2cpp -a SyncBackupAdaptor -c SyncBackupAdaptor com.nokia.syncbackup.xml * * qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * This is an auto-generated file. * This file may have been hand-edited. Look for HAND-EDIT comments * before re-generating it. */ #ifndef SYNCBACKUPADAPTOR_H_1277973475 #define SYNCBACKUPADAPTOR_H_1277973475 #include #include class QByteArray; template class QList; template class QMap; class QString; class QStringList; class QVariant; namespace Buteo { /*! * \brief Adaptor class for interface com.nokia.backupclient */ class SyncBackupAdaptor: public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "com.nokia.backupclient") Q_CLASSINFO("D-Bus Introspection", "" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""); public: //! \see SyncBackup::SyncBackup() SyncBackupAdaptor(QObject *parent); //! \see ~SyncBackup::SyncBackup() virtual ~SyncBackupAdaptor(); public: // PROPERTIES public Q_SLOTS: // METHODS //! \see ~SyncBackup::backupFinished() uchar backupFinished(const QDBusMessage &message); //! \see ~SyncBackup::backupStarts() uchar backupStarts(const QDBusMessage &message); //! \see ~SyncBackup::getBackUpRestoreState() bool getBackUpRestoreState(); //! \see ~SyncBackup::restoreFinished uchar restoreFinished(const QDBusMessage &message); //! \see ~SyncBackup::restoreStarts uchar restoreStarts(const QDBusMessage &message); Q_SIGNALS: // SIGNALS //! \see ~SyncBackup::backupDone void backupDone(); //! \see ~SyncBackup::backupInProgress void backupInProgress(); //! \see ~SyncBackup::restoreDone void restoreDone(); //! \see ~SyncBackup::restoreInProgress void restoreInProgress(); }; } #endif buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/ClientPluginRunner.h0000644000015600001650000000612612634332753024201 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef CLIENTPLUGINRUNNER_H #define CLIENTPLUGINRUNNER_H #include "PluginRunner.h" #include namespace Buteo { class ClientPlugin; class ClientThread; class SyncProfile; /*! \brief Class for running client sync plug-ins */ class ClientPluginRunner : public PluginRunner { Q_OBJECT public: /*! \brief Constructor * * @param aPluginName Name of the plug-in to run * @param aProfile Sync profile for the client plug-in. Ownership is NOT * transferred. * @param aPluginMgr PluginManager instance for creating and destroying * plug-ins by name * @param aPluginCbIf Callback interface that the created plug-in can use * @param aParent Parent object */ ClientPluginRunner(const QString &aPluginName, SyncProfile *aProfile, PluginManager *aPluginMgr, PluginCbInterface *aPluginCbIf, QObject *aParent = 0); //! \brief Destructor virtual ~ClientPluginRunner(); //! @see PluginRunner::init virtual bool init(); //! @see PluginRunner::start virtual bool start(); //! @see PluginRunner::stop virtual void stop(); //! @see PluginRunner::abort virtual void abort(Sync::SyncStatus aStatus = Sync::SYNC_ABORTED); //! @see PluginRunner::syncResults virtual SyncResults syncResults(); //! @see PluginRunner::plugin virtual SyncPluginBase *plugin(); //! @see PluginRunner::plugin virtual bool cleanUp(); private slots: // Slots for catching plug-in signals. void onTransferProgress(const QString &aProfileName, Sync::TransferDatabase aDatabase, Sync::TransferType aType, const QString &aMimeType, int aCommittedItems); void onError(const QString &aProfileName, const QString &aMessage, int aErrorCode); void onSuccess(const QString &aProfileName, const QString &aMessage); void onStorageAccquired(const QString &aMimeType); void onSyncProgressDetail(const QString &aProfileName,int aProgressDetail); // Slot for observing thread exit void onThreadExit(); void pluginTimeout(); private: SyncProfile *iProfile; ClientPlugin *iPlugin; ClientThread *iThread; #ifdef SYNCFW_UNIT_TESTS friend class ClientPluginRunnerTest; #endif }; } #endif // CLIENTPLUGINRUNNER_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncSession.cpp0000644000015600001650000003032212634332753023220 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncSession.h" #include "PluginRunner.h" #include "StorageBooker.h" #include "SyncProfile.h" #include "NetworkManager.h" #include "LogMacros.h" using namespace Buteo; SyncSession::SyncSession(SyncProfile *aProfile, QObject *aParent) : QObject(aParent), iProfile(aProfile), iPluginRunner(0), iStatus(Sync::SYNC_ERROR), iErrorCode(0), iPluginRunnerOwned(false), iScheduled(false), iAborted(false), iStarted(false), iFinished(false), iCreateProfile(false), iStorageBooker(0), iNetworkManager(0) { FUNCTION_CALL_TRACE; } SyncSession::~SyncSession() { FUNCTION_CALL_TRACE; if (iPluginRunnerOwned) { PluginRunner *runner = iPluginRunner; iPluginRunner = 0; delete runner; } if(iNetworkManager) { // Disconnect all slots connected to the network manager disconnect(iNetworkManager, SIGNAL(connectionSuccess()), this, SLOT(onNetworkSessionOpened())); disconnect(iNetworkManager, SIGNAL(connectionError()), this, SLOT(onNetworkSessionError())); iNetworkManager->disconnectSession(); } releaseStorages(); delete iProfile; iProfile = 0; } void SyncSession::setPluginRunner(PluginRunner *aPluginRunner, bool aTransferOwnership) { FUNCTION_CALL_TRACE; // Delete old owned plug-in runner, if any. if (iPluginRunnerOwned) { delete iPluginRunner; iPluginRunner = 0; } iPluginRunner = aPluginRunner; iPluginRunnerOwned = aTransferOwnership; if (iPluginRunner != 0) { // As we are setting a plugin runner, the pluginrunner should have // been started already iStarted = true; // Connect signals from plug-in runner. connect(iPluginRunner, SIGNAL(transferProgress(const QString &, Sync::TransferDatabase, Sync::TransferType, const QString &,int)), this, SLOT(onTransferProgress(const QString &, Sync::TransferDatabase, Sync::TransferType, const QString &,int))); connect(iPluginRunner, SIGNAL(error(const QString &, const QString &, int)), this, SLOT(onError(const QString &, const QString &, int))); connect(iPluginRunner, SIGNAL(success(const QString &, const QString &)), this, SLOT(onSuccess(const QString &, const QString &))); connect(iPluginRunner, SIGNAL(storageAccquired(const QString &)), this, SLOT(onStorageAccquired(const QString &))); connect(iPluginRunner,SIGNAL(syncProgressDetail(const QString &,int)), this ,SLOT(onSyncProgressDetail(const QString &,int))); connect(iPluginRunner, SIGNAL(done()), this, SLOT(onDone())); connect(iPluginRunner, SIGNAL(destroyed(QObject*)), this, SLOT(onDestroyed(QObject*))); } } PluginRunner *SyncSession::pluginRunner() { FUNCTION_CALL_TRACE; return iPluginRunner; } bool SyncSession::start() { FUNCTION_CALL_TRACE; bool rv = false; // If this is an online session, then we need to ensure that the network // session is opened before starting our plugin runner if((iProfile->destinationType() == SyncProfile::DESTINATION_TYPE_ONLINE) && !iScheduled) { iNetworkManager = new NetworkManager(this); Q_ASSERT(iNetworkManager); connect(iNetworkManager, SIGNAL(connectionSuccess()), SLOT(onNetworkSessionOpened()), Qt::QueuedConnection); connect(iNetworkManager, SIGNAL(connectionError()), SLOT(onNetworkSessionError()), Qt::QueuedConnection); // Return true here and wait for the session open status iNetworkManager->connectSession(iScheduled); rv = true; } else { rv = tryStart(); } return rv; } bool SyncSession::tryStart() { bool rv = false; if (iPluginRunner != 0) { iStarted = rv = iPluginRunner->start(); } if (!rv) { updateResults(SyncResults(QDateTime::currentDateTime(), SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::INTERNAL_ERROR)); if (iPluginRunner != 0) { disconnect(iPluginRunner, 0, this, 0); } } return rv; } bool SyncSession::isFinished() { return iFinished; } bool SyncSession::isAborted() { return iAborted; } void SyncSession::abort(Sync::SyncStatus aStatus) { FUNCTION_CALL_TRACE; if(!iStarted) { LOG_DEBUG("Client plugin runner not started, ignore abort"); updateResults(SyncResults(QDateTime::currentDateTime(), SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::ABORTED)); emit finished(profileName(), Sync::SYNC_ERROR, QString(), SyncResults::ABORTED); return; } else { iAborted = true; if (iPluginRunner != 0) { iPluginRunner->abort(aStatus); } } } QMap SyncSession::getStorageMap() { FUNCTION_CALL_TRACE return iStorageMap; } void SyncSession::setStorageMap(QMap &aStorageMap) { FUNCTION_CALL_TRACE iStorageMap = aStorageMap; } bool SyncSession::isProfileCreated() { FUNCTION_CALL_TRACE return iCreateProfile; } void SyncSession::setProfileCreated(bool aProfileCreated) { FUNCTION_CALL_TRACE iCreateProfile = aProfileCreated; } void SyncSession::stop() { FUNCTION_CALL_TRACE; if(!iStarted) { LOG_DEBUG("Plugin runner not yet started, ignoring stop."); } else if (iPluginRunner != 0) { iPluginRunner->stop(); } } SyncProfile *SyncSession::profile() const { FUNCTION_CALL_TRACE; return iProfile; } QString SyncSession::profileName() const { FUNCTION_CALL_TRACE; QString name; if (iProfile != 0) { name = iProfile->name(); } // no else return name; } SyncResults SyncSession::results() const { FUNCTION_CALL_TRACE; return iResults; } void SyncSession::setScheduled(bool aScheduled) { FUNCTION_CALL_TRACE; iScheduled = aScheduled; } bool SyncSession::isScheduled() const { FUNCTION_CALL_TRACE; return iScheduled; } void SyncSession::onSuccess(const QString &aProfileName, const QString &aMessage) { FUNCTION_CALL_TRACE; iErrorCode = 0; Q_UNUSED(aProfileName); iFinished = true; if (!iAborted) { iStatus = Sync::SYNC_DONE; } else { iStatus = Sync::SYNC_ABORTED; } iMessage = aMessage; if (iPluginRunner != 0) { updateResults(iPluginRunner->syncResults()); } emit finished(profileName(), iStatus, iMessage, iErrorCode); } void SyncSession::onError(const QString &aProfileName, const QString &aMessage, int aErrorCode) { FUNCTION_CALL_TRACE; Q_UNUSED(aProfileName); iFinished = true; iStatus = mapToSyncStatusError(aErrorCode); iMessage = aMessage; iErrorCode = aErrorCode; if (iPluginRunner != 0) { updateResults(iPluginRunner->syncResults()); } emit finished(profileName(), iStatus, iMessage, iErrorCode); } Sync::SyncStatus SyncSession::mapToSyncStatusError( int aErrorCode ) { Sync::SyncStatus status; SyncResults::MinorCode code = static_cast(aErrorCode); switch(code) { case SyncResults::UNSUPPORTED_SYNC_TYPE: { status = Sync::SYNC_NOTPOSSIBLE; break; } default: { status = Sync::SYNC_ERROR; break; } } return status; } void SyncSession::onTransferProgress(const QString &aProfileName, Sync::TransferDatabase aDatabase, Sync::TransferType aType, const QString &aMimeType, int aCommittedItems) { FUNCTION_CALL_TRACE; emit transferProgress(aProfileName, aDatabase, aType, aMimeType, aCommittedItems); } void SyncSession::onStorageAccquired (const QString &aMimeType) { FUNCTION_CALL_TRACE; emit storageAccquired (profileName(), aMimeType); } void SyncSession::onSyncProgressDetail(const QString &aProfileName,int aProgressDetail) { FUNCTION_CALL_TRACE; Q_UNUSED(aProfileName); emit syncProgressDetail (profileName(), aProgressDetail); } void SyncSession::onDone() { FUNCTION_CALL_TRACE; QString pluginName; if (iPluginRunner != 0) { disconnect(iPluginRunner, 0, this, 0); pluginName = iPluginRunner->pluginName(); } if (!iFinished) { LOG_WARNING("Plug-in terminated unexpectedly:" << pluginName); emit finished(profileName(), Sync::SYNC_ERROR, iMessage, 0); } } void SyncSession::onDestroyed(QObject *aPluginRunner) { if (iPluginRunner == aPluginRunner) { LOG_WARNING("Plug-in runner destroyed before sync session"); iPluginRunner = 0; } } void SyncSession::updateResults(const SyncResults &aResults) { FUNCTION_CALL_TRACE; iResults = aResults; iResults.setScheduled(iScheduled); iResults.setTargetId(aResults.getTargetId()); } void SyncSession::setFailureResult(int aMajorCode, int aMinorCode) { FUNCTION_CALL_TRACE; iResults.setMajorCode(aMajorCode); iResults.setMinorCode(aMinorCode); } bool SyncSession::reserveStorages(StorageBooker *aStorageBooker) { FUNCTION_CALL_TRACE; bool success = false; if (aStorageBooker != 0 && iProfile != 0 && aStorageBooker->reserveStorages(iProfile->storageBackendNames(), iProfile->name())) { success = true; iStorageBooker = aStorageBooker; } return success; } void SyncSession::releaseStorages() { // Release storages that were reserved earlier. if (iStorageBooker != 0 && iProfile != 0) { iStorageBooker->releaseStorages(iProfile->storageBackendNames()); } // Set storage booker to NULL. This indicates that we don't hold any // storage reservations. iStorageBooker = 0; } void SyncSession::onNetworkSessionOpened() { // Start the plugin runner now FUNCTION_CALL_TRACE; if(iNetworkManager) { // Disconnect all slots connected to the network manager disconnect(iNetworkManager, SIGNAL(connectionSuccess()), this, SLOT(onNetworkSessionOpened())); disconnect(iNetworkManager, SIGNAL(connectionError()), this, SLOT(onNetworkSessionError())); } if (false == tryStart()) { updateResults(SyncResults(QDateTime::currentDateTime(), SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::INTERNAL_ERROR)); emit finished(profileName(), Sync::SYNC_ERROR, QString(), SyncResults::INTERNAL_ERROR); } } void SyncSession::onNetworkSessionError() { FUNCTION_CALL_TRACE; if(iNetworkManager) { // Disconnect all slots connected to the network manager disconnect(iNetworkManager, SIGNAL(connectionSuccess()), this, SLOT(onNetworkSessionOpened())); disconnect(iNetworkManager, SIGNAL(connectionError()), this, SLOT(onNetworkSessionError())); iNetworkManager->disconnectSession(); } updateResults(SyncResults(QDateTime::currentDateTime(), SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::CONNECTION_ERROR)); // Update the session with connection error emit finished(profileName(), Sync::SYNC_ERROR, QString(), SyncResults::CONNECTION_ERROR); } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/ServerActivator.h0000644000015600001650000001040312634332753023526 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SERVERACTIVATOR_H_ #define SERVERACTIVATOR_H_ #include "TransportTracker.h" #include "ProfileManager.h" #include #include #include namespace Buteo { /*! \brief Keeps track of which server plug-ins should be enabled * * Finds out from server profiles which transports each server plug-in uses and * concludes the enabled/disabled state based on the transport states. A server * is enabled if any of the transports it uses is enabled or if there are any * references left to the server. Each matching and enabled transport creates * one reference to the server. References can be added and removed also * externally, so that a server won't become disabled if it is still in use but * all its transports are disabled. A signal is emitted when a server is enabled * or disabled. This class does not control the actual server plug-ins, it only * gives information about when a server plug-in should be enabled or disabled. */ class ServerActivator : public QObject { Q_OBJECT; public: /*! \brief Constructor * * @param aProfileManager Profile manager for accessing server profiles * @param aTransportTracker Transport tracker * @param aParent Parent object */ ServerActivator(ProfileManager &aProfileManager, TransportTracker &aTransportTracker, QObject *aParent = 0); //! \brief Destructor virtual ~ServerActivator(); /*! \brief Adds a reference to the given server * * If the reference count was zero, emits the serverEnabled signal, if the * emit signal paramater is true * @param aServerName Server profile name * @param emitSignal Controls the emission of the serverEnabled signal * @return Number of references after the addition */ int addRef(const QString &aServerName, bool emitSignal = true); /*! \brief Removes a reference from the given server * * If the reference count reaches zero, emits the serverDisabled signal, if the * emitSignal parameter is true * @param aServerName Server profile name * @param emitSignal Controls the emission of the serverDisabled signal * @return Number of references after the remove */ int removeRef(const QString &aServerName, bool emitSignal = true); /*! \brief Gets the list of enabled server * * @return Server profile names */ QStringList enabledServers() const; signals: /*! \brief Signal emitted when a server should be enabled * * @param aServerName Server profile name */ void serverEnabled(const QString &aServerName); /*! \brief Signal emitted when a server should be disabled * * @param aServerName Server profile name */ void serverDisabled(const QString &aServerName); public slots: /*! \brief Called when transport state changes * * @param aType Connectivity type * @param aState New state */ void onConnectivityStateChanged(Sync::ConnectivityType aType, bool aState); private: QList transportsFromProfile(const Profile *aProfile); ProfileManager &iProfileManager; TransportTracker &iTransportTracker; struct ServerData { ServerData() : iRefCount(0) { } QList iTransports; int iRefCount; }; QMap iServers; #ifdef SYNCFW_UNIT_TESTS friend class ServerActivatorTest; #endif }; } #endif /* SERVERACTIVATOR_H_ */ buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/generate_dbus_adaptor.sh0000755000015600001650000000177312634332753025124 0ustar pbuserpbgroup00000000000000#/* # * This file is part of buteo-syncfw package # * # * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). # * # * Contact: Sateesh Kavuri # * # * This library is free software; you can redistribute it and/or # * modify it under the terms of the GNU Lesser General Public License # * version 2.1 as published by the Free Software Foundation. # * # * This library is distributed in the hope that it will be useful, but # * WITHOUT ANY WARRANTY; without even the implied warranty of # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # * Lesser General Public License for more details. # * # * You should have received a copy of the GNU Lesser General Public # * License along with this library; if not, write to the Free Software # * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # * 02110-1301 USA # * # */ # qdbuscpp2xml -M -S SyncDBusInterface.h -o com.meego.msyncd.xml qdbusxml2cpp -a SyncDBusAdaptor -c SyncDBusAdaptor com.meego.msyncd.xml buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncAlarmInventory.h0000644000015600001650000000626012634332753024220 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCALARMINVENTORY_H #define SYNCALARMINVENTORY_H #include #include #include /*! \brief Class for storing alarms * * This class stores alarms for scheduled synchronizations. The main elements * are the sync time and the alarm id */ class SyncAlarmInventory : public QObject { Q_OBJECT public: /*! The alarm inventory constructor * Always Call init() before using other methods of this class */ SyncAlarmInventory(); /*! The alarm inventory destructor */ ~SyncAlarmInventory(); /*! \brief Creates and Initialize the alarms database. also Creates the timers * Please call this function to make sure the database is initialised properly * @return - status of the initialisation */ bool init(); /*! \brief Method to add an alarm * * @param alarmTime - time of the alarm as QDateTime * @return id of the alarm if alarm was added successfully. else 0 */ int addAlarm(QDateTime alarmTime); /*! Method to remove an alarm * * @param alarmId - id of the alarm to remove * @return status of the remove */ bool removeAlarm(int alarmId); /*! Method to remove all alarms * */ void removeAllAlarms(); signals: /*! \brief Signal triggered when an alarm expired * @param alarmId - id of the alarm that got triggered. * */ void triggerAlarm(int alarmId); private: /* Deletes the alarm from DB */ bool deleteAlarmFromDb( int alarmName ); /* Method to add an alarm to the database */ int addAlarmToDb( QDateTime timeStamp ); /* Method to fetch the database handle */ QSqlDatabase* getDbHandle(); /* Timer object to keep tracke of alarm timers */ QTimer* iTimer; /* Current alarm that is under work */ int currentAlarm; /* Number of times that the alarm triggers */ int triggerCount; /* Database handle */ QSqlDatabase iDbHandle; /* Database connection name */ QString iConnectionName; private slots: /*! Slot used whenever the timer object expires */ void timerTriggered(); }; #endif buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/msyncd-app.pro0000644000015600001650000000452012634332753023032 0ustar pbuserpbgroup00000000000000TEMPLATE = app TARGET = msyncd QT += xml \ dbus \ sql \ network QT -= gui CONFIG += \ link_pkgconfig \ link_prl DEPENDPATH += . INCLUDEPATH += . \ ../ \ ../libbuteosyncfw/pluginmgr \ ../libbuteosyncfw/common \ ../libbuteosyncfw/profile PKGCONFIG += dbus-1 equals(QT_MAJOR_VERSION, 4): { PKGCONFIG += libsignon-qt accounts-qt CONFIG += mobility MOBILITY += systeminfo LIBS += -lbuteosyncfw } equals(QT_MAJOR_VERSION, 5): { PKGCONFIG += libsignon-qt5 accounts-qt5 Qt5SystemInfo LIBS += -lbuteosyncfw5 packagesExist(qt5-boostable) { DEFINES += HAS_BOOSTER PKGCONFIG += qt5-boostable } else { warning("qt5-boostable not available; startup times will be slower") } } QMAKE_LIBDIR_QT += ../libsyncprofile/ LIBS += -L../libbuteosyncfw LIBS += -L. -lmsyncd # Input SOURCES += \ main.cpp \ UnitTest.cpp QMAKE_CXXFLAGS = -Wall \ -g \ -Wno-cast-align \ -O2 \ -finline-functions # install target.path = /usr/bin/ loglevel.files = bin/set_sync_log_level loglevel.path = /etc/buteo/ service.files = bin/msyncd.service service.path = /usr/lib/systemd/user/ syncwidget.path = /etc/syncwidget/ syncwidget.files = com.meego.msyncd gschemas.path = /usr/share/glib-2.0/schemas gschemas.files = gschemas/com.meego.msyncd.gschema.xml INSTALLS += target \ loglevel \ syncwidget \ service \ gschemas # ##################################################################### # make coverage (debug) # ##################################################################### coverage.CONFIG += recursive QMAKE_EXTRA_TARGETS += coverage CONFIG(debug,debug|release) { QMAKE_EXTRA_TARGETS += cov_cxxflags \ cov_lflags cov_cxxflags.target = coverage cov_cxxflags.depends = CXXFLAGS \ += \ -fprofile-arcs \ -ftest-coverage cov_lflags.target = coverage cov_lflags.depends = LFLAGS \ += \ -fprofile-arcs \ -ftest-coverage # QMAKE_CXXFLAGS += -fprofile-arcs -ftest-coverage # QMAKE_LFLAGS += -fprofile-arcs -ftest-coverage # -ftest-coverage coverage.commands = @echo \ "Built with coverage support..." build_pass|!debug_and_release:coverage.depends = all QMAKE_CLEAN += $(OBJECTS_DIR)/*.gcda \ $(OBJECTS_DIR)/*.gcno \ $(OBJECTS_DIR)/*.gcov } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncAlarmInventory.cpp0000644000015600001650000001763412634332753024562 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncAlarmInventory.h" #include "SyncCommonDefs.h" #include #include #include #include #include const QString ALARM_CONNECTION_NAME( "alarms" ); const int TRIGGER_COUNT = 1; SyncAlarmInventory::SyncAlarmInventory(): iTimer(0), currentAlarm(0), triggerCount(TRIGGER_COUNT) { // empty.explicitly call init } bool SyncAlarmInventory::init() { FUNCTION_CALL_TRACE; static unsigned connectionNumber = 0; iConnectionName = ALARM_CONNECTION_NAME + QString::number( connectionNumber++ ); iDbHandle = QSqlDatabase::addDatabase( "QSQLITE", iConnectionName ); // Make sure we have the .sync directory QDir configDir; configDir.mkdir(Sync::syncCacheDir()); QString path( Sync::syncCacheDir() ); path.append( QDir::separator() ).append( "alarms.db.sqlite" ); path = QDir::toNativeSeparators( path ); iDbHandle.setDatabaseName( path ); if (!iDbHandle.open()) { LOG_CRITICAL("Failed to OPEN DB. SCHEDULING WILL NOT WORK"); return false; } else { LOG_DEBUG("DB Opened Successfully"); } // Create the alarms table const QString createTableQuery( "CREATE TABLE IF NOT EXISTS alarms(alarmid INTEGER PRIMARY KEY AUTOINCREMENT, synctime DATETIME)" ); QSqlQuery query( createTableQuery, iDbHandle ); LOG_DEBUG("SQL Query::" << query.lastQuery()); if ( !query.exec() ) { LOG_WARNING("Failed to execute the createTableQuery"); return false; } // Clear any old alarms that may have lingered removeAllAlarms(); // Create the iTimer object iTimer = new QTimer(this); if(iTimer) { connect( iTimer, SIGNAL(timeout()), this, SLOT(timerTriggered()) ); currentAlarm = 0; return true; } else { LOG_WARNING("Failed to create a QTimer"); return false; } } SyncAlarmInventory::~SyncAlarmInventory() { FUNCTION_CALL_TRACE; iDbHandle.close(); iDbHandle = QSqlDatabase(); QSqlDatabase::removeDatabase( iConnectionName ); if (iTimer) { iTimer->stop(); delete iTimer; iTimer = 0; } } int SyncAlarmInventory::addAlarm( QDateTime alarmDate ) { FUNCTION_CALL_TRACE; // Check if alarmDate < QDateTime::currentDateTime() if ( QDateTime::currentDateTime().secsTo(alarmDate) < 0 ) { LOG_WARNING("alarmDate < QDateTime::currentDateTime()"); //Setting with current date time. alarmDate = QDateTime::currentDateTime(); } // Store the alarm int alarmId = 0; if ( (alarmId = addAlarmToDb(alarmDate)) == 0 ) { // Note: Even incase of an already existing profile, false is returned by the query // There is no way to detect a record insertion from an already existing alarm // If unable to add an alarm to db, set the iTimers // for already existing alarms LOG_WARNING("(alarmId = addAlarmToDb(alarmDate)) == 0"); } // Select all the alarms from the db sorted by alarm time QSqlQuery selectQuery( iDbHandle ); if ( selectQuery.exec("SELECT alarmid,synctime FROM alarms ORDER BY synctime ASC") ) { LOG_DEBUG("SQL Query::" << selectQuery.lastQuery()); if ( selectQuery.first() ) { int newAlarm = selectQuery.value(0).toInt(); QDateTime alarmTime = selectQuery.value(1).toDateTime(); // If the newAlarm != currentAlarm that is fetched from DB, stop the // previous iTimer if ( (currentAlarm != 0) && (newAlarm != currentAlarm) ) { if ( !iTimer->isActive() ) iTimer->stop(); } // This is a new alarm. Set the iTimer for the alarm currentAlarm = newAlarm; QDateTime now = QDateTime::currentDateTime(); int iTimerInterval = 0; if(now < alarmTime) { iTimerInterval = (now.secsTo( alarmTime ) / TRIGGER_COUNT) * 1000; // time interval in millisec } LOG_DEBUG("currentAlarm"<setInterval( iTimerInterval ); iTimer->start(); } } else { LOG_WARNING("Select Query Execution Failed" ); } return alarmId; } bool SyncAlarmInventory::removeAlarm(int alarmId) { FUNCTION_CALL_TRACE; if( alarmId <= 0 ) return false; deleteAlarmFromDb( alarmId ); return true; } void SyncAlarmInventory::removeAllAlarms() { FUNCTION_CALL_TRACE; QSqlQuery deleteAllQuery(QString("DELETE FROM alarms"), iDbHandle); LOG_DEBUG("SQL Query::" << deleteAllQuery.lastQuery()); if(!deleteAllQuery.exec()) { LOG_WARNING("Failed query to delete all alarms"); } } void SyncAlarmInventory::timerTriggered() { FUNCTION_CALL_TRACE; // Decrement the alarm counter triggerCount--; // Alarm expired. Trigger the alarm and delete it from DB and set the alarm for the next one if (triggerCount == 0) { LOG_DEBUG("Triggering the alarm " << currentAlarm ); emit triggerAlarm(currentAlarm); // Delete the alarm from DB if ( !deleteAlarmFromDb(currentAlarm) ) { } iTimer->stop(); currentAlarm = 0; // Set the new alarm iTimer // Select all the alarms from the db sorted by alarm time QSqlQuery selectQuery( iDbHandle ); if ( selectQuery.exec("SELECT alarmid,synctime FROM alarms ORDER BY synctime ASC") ) { LOG_DEBUG("SQL Query::" << selectQuery.lastQuery()); if ( selectQuery.first() ) { currentAlarm = selectQuery.value(0).toInt(); QDateTime alarmTime = selectQuery.value(1).toDateTime(); // Set the iTimer for the alarm QDateTime now = QDateTime::currentDateTime(); int iTimerInterval = 0; if(now < alarmTime) { iTimerInterval = (now.secsTo( alarmTime ) / TRIGGER_COUNT) * 1000; // time interval in millisec } triggerCount = TRIGGER_COUNT; LOG_DEBUG("Starting timer with interval::" << iTimerInterval); iTimer->setInterval( iTimerInterval ); iTimer->start(); } } } } bool SyncAlarmInventory::deleteAlarmFromDb( int alarmId ) { FUNCTION_CALL_TRACE; QSqlQuery removeQuery ( iDbHandle ); removeQuery.prepare( "DELETE FROM alarms WHERE alarmid=:alarmid" ); removeQuery.bindValue( ":alarmid", alarmId ); LOG_DEBUG("SQL Query::" << removeQuery.lastQuery()); if ( !removeQuery.exec() ) return false; else return true; } int SyncAlarmInventory::addAlarmToDb( QDateTime timeStamp ) { FUNCTION_CALL_TRACE; QSqlQuery insertQuery( iDbHandle ); insertQuery.prepare( "INSERT INTO alarms(synctime) VALUES(:synctime)" ); insertQuery.bindValue( ":synctime", timeStamp ); LOG_DEBUG("SQL Query::" << insertQuery.lastQuery()); if ( insertQuery.exec() ) return insertQuery.lastInsertId().toInt(); else return 0; } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/AccountsHelper.h0000644000015600001650000000777312634332753023342 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef ACCOUNTSHELPER_H #define ACCOUNTSHELPER_H #include #include #include namespace Buteo { class Profile; class AccountsHelperTest; class ProfileManager; class SyncProfile; const QString REMOTE_SERVICE_NAME("remote_service_name"); /*! \brief Helper Class towards Accounts::Manager and various SSO related * operations. */ class AccountsHelper : public QObject { Q_OBJECT public: /*! \brief Constructor * * \param aProfileManager - reference to Profile Manager Object * \param aParent - Parent object */ AccountsHelper(ProfileManager &aProfileManager, QObject *aParent = NULL); /*! \brief Destructor * */ virtual ~AccountsHelper(); /*! \brief Returns sync profiles that correspond to a given account ID * * \param id - The account ID. * \return A list of sync profiles. The caller should delete the profiles * after use. */ QList getProfilesByAccountId(Accounts::AccountId id); public Q_SLOTS: /*! \brief This method is used to create a profile for a specified * account * \param id Accounts Id * \return A string with the new profile name */ QString createProfileForAccount(Accounts::AccountId id); /*! \brief slot for Accounts::Manager accountRemoved signal * * \param id of the accounts */ void slotAccountRemoved(Accounts::AccountId id); /*! \brief slot for Accounts::Account enabledChanged signal * * \param serviceName The service that was enabled/disabled. Empty if the * entire account is enabled/disabled * \param enabled Boolean indicating enabled (true) or disabled (false) */ void slotAccountEnabledChanged(const QString &serviceName, bool enabled); /*! \brief slot for Accounts::Manager displayNameChanged signal * * * \param id of the accounts */ void slotAccountUpdated(Accounts::AccountId id); void slotSchedulerSettingsChanged(const char *aKey); Q_SIGNALS: void enableSOC(const QString& aProfileName); void scheduleUpdated(const QString& aProfileName); void removeProfile(QString profileId); void removeScheduledSync(const QString& profileId); private Q_SLOTS: void registerAccountListeners(); private: void createProfileForAccount(Accounts::Account* account, const QString profileName, const SyncProfile* baseProfile); QString addAccountIfNotExists(Accounts::Account *account, Accounts::Service service, const SyncProfile *baseProfile); void setSyncSchedule(SyncProfile *syncProfile, Accounts::AccountId id, bool aCreateNew = false); void addSetting(Accounts::AccountId id, QString key, QVariant value); void registerAccountListener(Accounts::AccountId id); Accounts::Manager *iAccountManager; ProfileManager &iProfileManager; QList iAccountList; QMap iAcctWatchMap; #ifdef SYNCFW_UNIT_TESTS friend class AccountsHelperTest; #endif }; } #endif // ACCOUNTSHELPER_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncOnChangeScheduler.h0000644000015600001650000000461712634332753024573 0ustar pbuserpbgroup00000000000000#ifndef SYNCONCHANGESCHEDULER_H #define SYNCONCHANGESCHEDULER_H #include #include #include #include "SyncScheduler.h" namespace Buteo { class SyncProfile; class SyncOnChangeScheduler : public SyncScheduler { Q_OBJECT; public: /*! \brief constructor */ SyncOnChangeScheduler(); /*! \brief destructor */ ~SyncOnChangeScheduler(); /*! \brief Call this method to schedule SOC for a profile * * There are 3 scheduling criteria - SOC after info from the * profile, default SOC after and sync now. * * The profile is first checked for sync on change after time, which * should be specified in seconds (0 means sync now). If none is specified * then we use a default of DEFAULT_SOC_AFTER_TIME * * If the profile has already been added and if it's SOC is scheduled, * calling this method again will just use the previous schedule, and in * this case the method will return false. * * Once the SOC is initiated (by sending a syncNow signal), the profile is * removed automatically * * @param aProfile pointer to sync profile * @return true if SOC could be scheduled, false otherwise */ bool addProfile(const SyncProfile* aProfile); /*! \brief call this method to disable SOC that has been scheduled * for a certain profile * * @param aProfileName name of the profile */ void removeProfile(const QString &aProfileName); private Q_SLOTS: /*! \brief slot to initiate sync when timeout criterion is being used * and the timeout occurs * * @param aProfile sync profile */ void sync(const SyncProfile* aProfile); private: QStringList iSOCProfileNames; QMap iSOCTimers; }; class SyncOnChangeTimer : public QObject { Q_OBJECT public: /*! \brief constructor */ SyncOnChangeTimer(const SyncProfile* aProfile, const quint32& aTimeout); /*! \brief destructor */ ~SyncOnChangeTimer(); /*! \brief fire the timer */ void fire(); Q_SIGNALS: /*! \brief emit this signal when the timeout occurs * * @param aProfile sync profile */ void timeout(const SyncProfile* aProfile); private Q_SLOTS: /*! \brief slot corresponding to timeout */ void onTimeout(); private: const SyncProfile* iSyncProfile; quint32 iTimeout; }; } #endif buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/com.meego.msyncd0000644000015600001650000000021412634332753023321 0ustar pbuserpbgroup00000000000000 com.meego.msyncd com.meego.msyncd /synchronizer buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/StorageBooker.h0000644000015600001650000001046012634332753023154 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef STORAGEBOOKER_H #define STORAGEBOOKER_H #include #include #include #include namespace Buteo { /*! \brief A helper class for managing storage reservations. * */ class StorageBooker { public: //! \brief Constructor StorageBooker(); //! \brief Destructor ~StorageBooker(); /*! \brief Tries to reserve one storage for the given client. * * If the reserve is successfull, the caller must call release when * it does not need the storage anymore. * The same client can call reserve multiple times. Internal reference * counter is increased in that case. For each reserve there must be a * release call later. Other clients calling reserve for the same storage * will fail, while the storage is reserved to some other client. * \param aStorageName Name of the requested storage. * \param aClientId ID of the requesting client. * \return Success indicator. */ bool reserveStorage(const QString &aStorageName, const QString &aClientId = ""); /*! \brief Tries to reserve multiple storages for the given client. * * If the reserve is successfull, the caller must call release for each * storage when it does not need the storages anymore. * The reserve is successfull only if all given storages are available. * If the reserve fails, no storages are reserved. * \param aStorageNames Names of the storages to reserve. * \param aClientId ID of the requesting client. * \return Success indicator. */ bool reserveStorages(const QStringList &aStorageNames, const QString &aClientId = ""); /*! \brief Releases the given storage. * * \param aStorageName Name of the storage to release. * \return Number of remaining references to the storage. If this is zero, * other clients can now reserve the storage. */ unsigned releaseStorage(const QString &aStorageName); /*! \brief Releases the given storages. * * \param aStorageNames Names of the storages to release. */ void releaseStorages(const QStringList &aStorageNames); /*! \brief Checks if the given storage is available for the given client. * * The storage is available if there are no reservations for it or if the * storage is already reserved for the same client. If the storage is * available, it can be reserved for the client by calling reserve. * \param aStorageName Name of the requested storage. * \param aClientId ID of the requesting client. * \return Is the storage available. */ bool isStorageAvailable(const QString &aStorageName, const QString &aClientId = "") const; /*! \brief Checks if the given storages are available for the given client. * * \param aStorageNames Names of the requested storages. * \param aClientId ID of the requesting client. * \return Are the storages available. */ bool storagesAvailable(const QStringList &aStorageNames, const QString &aClientId = "") const; private: struct StorageMapItem { QString iClientId; unsigned iRefCount; StorageMapItem() : iRefCount(0) { }; StorageMapItem(const QString &aClientId) : iClientId(aClientId), iRefCount(1) { }; }; QMap iStorageMap; mutable QMutex iMutex; }; } #endif // STORAGEBOOKER_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/synchronizer.cpp0000644000015600001650000021602112634332753023477 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2014-2015 Jolla Ltd * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include "synchronizer.h" #include "SyncDBusAdaptor.h" #include "SyncSession.h" #include "ClientPluginRunner.h" #include "ServerPluginRunner.h" #include "AccountsHelper.h" #include "NetworkManager.h" #include "TransportTracker.h" #include "ServerActivator.h" #include "SyncCommonDefs.h" #include "StoragePlugin.h" #include "SyncLog.h" #include "ClientPlugin.h" #include "ServerPlugin.h" #include "ProfileFactory.h" #include "ProfileEngineDefs.h" #include "LogMacros.h" #include "BtHelper.h" #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #include #else #include #endif #include #include #include using namespace Buteo; static const QString SYNC_DBUS_OBJECT = "/synchronizer"; static const QString SYNC_DBUS_SERVICE = "com.meego.msyncd"; static const QString BT_PROPERTIES_NAME = "Name"; // Maximum time in milliseconds to wait for a thread to stop static const unsigned long long MAX_THREAD_STOP_WAIT_TIME = 5000; Synchronizer::Synchronizer( QCoreApplication* aApplication ) : iNetworkManager(0), iSyncScheduler(0), iSyncBackup(0), iTransportTracker(0), iServerActivator(0), iAccounts(0), iClosing(false), iSOCEnabled(false), iSyncUIInterface(NULL) { iSettings = g_settings_new_with_path("com.meego.msyncd", "/com/meego/msyncd/"); FUNCTION_CALL_TRACE; this->setParent(aApplication); } Synchronizer::~Synchronizer() { FUNCTION_CALL_TRACE; //Clearing syncUiinterface if (iSyncUIInterface) { delete iSyncUIInterface; iSyncUIInterface = NULL; } g_object_unref(iSettings); } bool Synchronizer::initialize() { FUNCTION_CALL_TRACE; LOG_DEBUG("Starting msyncd"); // Create a D-Bus adaptor. It will get deleted when the Synchronizer is // deleted. new SyncDBusAdaptor(this); // Register our object on the session bus and expose interface to others. QDBusConnection dbus = QDBusConnection::sessionBus(); if (!dbus.registerObject(SYNC_DBUS_OBJECT, this) || !dbus.registerService(SYNC_DBUS_SERVICE)) { LOG_CRITICAL( "Failed to register to D-Bus (D-Bus not started or msyncd already running?), aborting start" ); return false; } else { LOG_DEBUG("Registered to D-Bus"); } // else ok connect(this, SIGNAL(syncStatus(QString, int, QString, int)), this, SLOT(slotSyncStatus(QString, int, QString, int)), Qt::QueuedConnection); // use queued connection because the profile will be stored after the signal connect(&iProfileManager ,SIGNAL(signalProfileChanged(QString,int,QString)), this, SLOT(slotProfileChanged(QString,int,QString)), Qt::QueuedConnection); iNetworkManager = new NetworkManager(this); Q_ASSERT(iNetworkManager); iTransportTracker = new TransportTracker(this); if (iTransportTracker != 0) { iServerActivator = new ServerActivator(iProfileManager, *iTransportTracker, this); connect(iTransportTracker, SIGNAL(networkStateChanged(bool,Sync::InternetConnectionType)), SLOT(onNetworkStateChanged(bool,Sync::InternetConnectionType))); } // Initialize account manager. iAccounts = new AccountsHelper(iProfileManager, this); // Deleted with parent. connect(iAccounts, SIGNAL(enableSOC(QString)), this, SLOT(enableSOCSlot(QString)), Qt::QueuedConnection); connect(iAccounts, SIGNAL(scheduleUpdated(QString)), this, SLOT(reschedule(QString)), Qt::QueuedConnection); connect(iAccounts, SIGNAL(removeProfile(QString)), this, SLOT(removeProfile(QString)), Qt::QueuedConnection); connect(iAccounts, SIGNAL(removeScheduledSync(QString)), this, SLOT(removeScheduledSync(QString)), Qt::QueuedConnection); connect(this, SIGNAL(storageReleased()), this, SLOT(onStorageReleased()), Qt::QueuedConnection); startServers(); // Initialize scheduler initializeScheduler(); // For Backup/restore handling iSyncBackup = new SyncBackup(); connect(iSyncBackup, SIGNAL(startBackup()),this, SLOT(backupStarts())); connect(iSyncBackup, SIGNAL(backupDone()),this, SLOT(backupFinished())); connect(iSyncBackup, SIGNAL(startRestore()),this, SLOT(restoreStarts())); connect(iSyncBackup, SIGNAL(restoreDone()),this, SLOT(restoreFinished())); //For Sync On Change // enable SOC for contacts only as of now QHash > aSOCStorageMap; //TODO can we do away with hard coding storage (plug-in) names, in other words do they //have to be well known this way QList SOCProfiles = iProfileManager.getSOCProfilesForStorage("./contacts"); if(SOCProfiles.count()) { // TODO Come up with a scheme to avoid hard-coding, this is not done just here // but also for storage plug-ins in onStorageAquired aSOCStorageMap["hcontacts"] = SOCProfiles; QStringList aFailedStorages; bool isSOCEnabled = iSyncOnChange.enable(aSOCStorageMap, &iSyncOnChangeScheduler, &iPluginManager, aFailedStorages); if(!isSOCEnabled) { foreach(const QString& aStorageName, aFailedStorages) { LOG_CRITICAL("Sync on change couldn't be enabled for storage" << aStorageName); } } else { QObject::connect(&iSyncOnChangeScheduler, SIGNAL(syncNow(QString)), this, SLOT(startScheduledSync(QString)), Qt::QueuedConnection); iSOCEnabled = true; } } else { LOG_DEBUG("No profiles interested in SOC"); } return true; } void Synchronizer::enableSOCSlot(const QString& aProfileName) { FUNCTION_CALL_TRACE; SyncProfile* profile = iProfileManager.syncProfile(aProfileName); if(profile->isSOCProfile() && !iSOCEnabled) { QHash > aSOCStorageMap; QList SOCProfiles; SOCProfiles.append(profile); aSOCStorageMap["hcontacts"] = SOCProfiles; QStringList aFailedStorages; bool isSOCEnabled = iSyncOnChange.enable(aSOCStorageMap, &iSyncOnChangeScheduler, &iPluginManager, aFailedStorages); if(!isSOCEnabled) { LOG_CRITICAL("Sync on change couldn't be enabled for profile" << aProfileName); delete profile; } else { QObject::connect(&iSyncOnChangeScheduler, SIGNAL(syncNow(const QString&)), this, SLOT(startScheduledSync(const QString&)), Qt::QueuedConnection); iSOCEnabled = true; LOG_DEBUG("Sync on change enabled for profile" << aProfileName); } } else if (profile->isSOCProfile()) { iSyncOnChange.addProfile("hcontacts", profile); } } void Synchronizer::close() { FUNCTION_CALL_TRACE; LOG_DEBUG("Stopping msyncd"); iClosing = true; // Stop running sessions if(iSOCEnabled) { iSyncOnChange.disable(); } QList sessions = iActiveSessions.values(); foreach (SyncSession* session, sessions) { if (session != 0) { session->stop(); } } qDeleteAll(sessions); iActiveSessions.clear(); stopServers(); delete iSyncScheduler; iSyncScheduler = 0; delete iSyncBackup; iSyncBackup = 0; // Unregister from D-Bus. QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.unregisterObject(SYNC_DBUS_OBJECT); if (!dbus.unregisterService(SYNC_DBUS_SERVICE)) { LOG_WARNING("Failed to unregister from D-Bus"); } else { LOG_DEBUG("Unregistered from D-Bus"); } } bool Synchronizer::startSync(QString aProfileName) { FUNCTION_CALL_TRACE; // Manually triggered sync. return startSync(aProfileName, false); } bool Synchronizer::startScheduledSync(QString aProfileName) { FUNCTION_CALL_TRACE; // All scheduled syncs are online syncs // Add this to the waiting online syncs and it will be started when we // receive a session connection status from the NetworkManager bool accept = acceptScheduledSync(iNetworkManager->isOnline(), iNetworkManager->connectionType()); if(accept) { startSync(aProfileName, true); } else if (!iWaitingOnlineSyncs.contains(aProfileName)) { LOG_DEBUG("Wait for internet connection:" << aProfileName); if (iNetworkManager->isOnline()) { LOG_DEBUG("Connection over mobile data plan. The sync will be postponed untill a full connection is available;"); } else { LOG_DEBUG("Device offline. Wait for internet connection."); } iWaitingOnlineSyncs.append(aProfileName); } return true; } bool Synchronizer::setSyncSchedule(QString aProfileId , QString aScheduleAsXml) { bool status = false; if(iProfileManager.setSyncSchedule(aProfileId , aScheduleAsXml)) { reschedule(aProfileId); status = true; } return status; } bool Synchronizer::saveSyncResults(QString aProfileId, QString aSyncResults) { QDomDocument doc; bool status = false; if (doc.setContent(aSyncResults, true)) { Buteo::SyncResults results(doc.documentElement()); status = iProfileManager.saveSyncResults(aProfileId , results); } else { LOG_CRITICAL("Invalid Profile Xml Received from msyncd"); } return status; } QString Synchronizer::createSyncProfileForAccount(uint aAccountId) { return iAccounts->createProfileForAccount(aAccountId); } bool Synchronizer::startSync(const QString &aProfileName, bool aScheduled) { FUNCTION_CALL_TRACE; bool success = false; if (isBackupRestoreInProgress()) { SyncResults syncResults(QDateTime::currentDateTime(), SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::BACKUP_IN_PROGRESS); iProfileManager.saveSyncResults(aProfileName, syncResults); return success; } LOG_DEBUG( "Start sync requested for profile:" << aProfileName ); // This function can be called from a client app as manual sync: // If we receive a manual sync to a profile that is peding to sync due a // data change we can remove it from the iSyncOnChangeScheduler, to avoid a // second sync. iSyncOnChangeScheduler.removeProfile(aProfileName); if (iActiveSessions.contains(aProfileName)) { LOG_DEBUG( "Sync already in progress" ); return true; } else if (iSyncQueue.contains(aProfileName)) { LOG_DEBUG( "Sync request already in queue" ); emit syncStatus(aProfileName, Sync::SYNC_QUEUED, "", 0); return true; } else if (!aScheduled && iWaitingOnlineSyncs.contains(aProfileName)) { // Manual sync is allowed to happen in any kind of connection // if sync is not scheduled remove it from iWaitingOnlineSyncs to avoid // sync it twice later iWaitingOnlineSyncs.removeOne(aProfileName); LOG_DEBUG("Removing" << aProfileName << "from online waiting list."); } SyncProfile *profile = iProfileManager.syncProfile(aProfileName); if (!profile) { LOG_WARNING( "Profile not found" ); SyncResults syncResults(QDateTime::currentDateTime(), SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::INTERNAL_ERROR); iProfileManager.saveSyncResults(aProfileName, syncResults); emit syncStatus(aProfileName, Sync::SYNC_ERROR, "Internal Error" , Buteo::SyncResults::INTERNAL_ERROR); return false; } else if(false == profile->isEnabled()) { LOG_WARNING("Profile is disabled, not stating sync"); SyncResults syncResults(QDateTime::currentDateTime(), SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::INTERNAL_ERROR); iProfileManager.saveSyncResults(aProfileName, syncResults); emit syncStatus(aProfileName, Sync::SYNC_ERROR, "Internal Error" , Buteo::SyncResults::INTERNAL_ERROR); delete profile; return false; } SyncSession *session = new SyncSession(profile, this); if (session == 0) { LOG_WARNING( "Failed to create sync session object" ); delete profile; profile = 0; SyncResults syncResults(QDateTime::currentDateTime(), SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::INTERNAL_ERROR); iProfileManager.saveSyncResults(aProfileName, syncResults); emit syncStatus(aProfileName, Sync::SYNC_ERROR, "Internal Error" , Buteo::SyncResults::INTERNAL_ERROR); return false; } session->setScheduled(aScheduled); if (clientProfileActive(profile->clientProfile()->name())) { LOG_DEBUG( "Sync request of the same type in progress, adding request to the sync queue" ); iSyncQueue.enqueue(session); emit syncStatus(aProfileName, Sync::SYNC_QUEUED, "", 0); return false; } // @todo: Complete profile with data from account manager. //iAccounts->addAccountData(*profile); #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) QBatteryInfo iDeviceInfo; QBatteryInfo::LevelStatus batteryStat = iDeviceInfo.levelStatus(); #elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(5, 2, 0) QBatteryInfo iDeviceInfo; QBatteryInfo::BatteryStatus batteryStat = iDeviceInfo.batteryStatus(0); #else QtMobility::QSystemDeviceInfo iDeviceInfo; QtMobility::QSystemDeviceInfo::BatteryStatus batteryStat = iDeviceInfo.batteryStatus(); #endif if (!profile->isValid()) { LOG_WARNING( "Profile is not valid" ); session->setFailureResult(SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::INTERNAL_ERROR); emit syncStatus(aProfileName, Sync::SYNC_ERROR, "Internal Error", Buteo::SyncResults::INTERNAL_ERROR); } #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) else if( aScheduled && ( (batteryStat == QBatteryInfo::LevelEmpty) || (batteryStat == QBatteryInfo::LevelLow) )) #elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(5, 2, 0) else if( aScheduled && ( (batteryStat == QBatteryInfo::BatteryEmpty) || (batteryStat == QBatteryInfo::BatteryLow) )) #else else if( aScheduled && ( (batteryStat != QtMobility::QSystemDeviceInfo::BatteryNormal) && (batteryStat != QtMobility::QSystemDeviceInfo::BatteryLow) )) #endif { LOG_DEBUG( "Low power, scheduled sync aborted" ); session->setFailureResult(SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::LOW_BATTERY_POWER); emit syncStatus(aProfileName, Sync::SYNC_ERROR, "Low battery", Buteo::SyncResults::LOW_BATTERY_POWER); } else if (!session->reserveStorages(&iStorageBooker)) { LOG_DEBUG( "Needed storage(s) already in use, queuing sync request" ); iSyncQueue.enqueue(session); emit syncStatus(aProfileName, Sync::SYNC_QUEUED, "", 0); success = true; } else { // Sync can be started now. success = startSyncNow(session); if (success) { emit syncStatus(aProfileName, Sync::SYNC_STARTED, "", 0); } else { session->setFailureResult(SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::INTERNAL_ERROR); emit syncStatus(aProfileName, Sync::SYNC_ERROR, "Internal Error", Buteo::SyncResults::INTERNAL_ERROR); } } if (!success) { cleanupSession(session, Sync::SYNC_ERROR ); } // no else return success; } bool Synchronizer::startSyncNow(SyncSession *aSession) { FUNCTION_CALL_TRACE; if (!aSession || isBackupRestoreInProgress()) { LOG_WARNING( "Session is null || backup in progress" ); return false; } SyncProfile *profile = aSession->profile(); if (!profile) { LOG_WARNING( "Profile in session is null" ); return false; } LOG_DEBUG( "Starting sync with profile" << aSession->profileName()); Profile *clientProfile = profile->clientProfile(); if (clientProfile == 0) { LOG_WARNING( "Could not find client sub-profile" ); return false; } LOG_DEBUG("Disable sync on change:" << iSOCEnabled << profile->isSOCProfile()); //As sync is ongoing, disable sync on change for now, we can query later if //there are changes. if(iSOCEnabled) { if(profile->isSOCProfile()) { iSyncOnChange.disable(); } else { iSyncOnChange.disableNext(); } } iProfileManager.addRetriesInfo(profile); PluginRunner *pluginRunner = new ClientPluginRunner( clientProfile->name(), aSession->profile(), &iPluginManager, this, this); aSession->setPluginRunner(pluginRunner, true); if (pluginRunner == 0 || !pluginRunner->init()) { LOG_WARNING( "Failed to initialize client plug-in runner" ); return false; } // Relay connectivity state change signal to plug-in runner. connect(iTransportTracker, SIGNAL(connectivityStateChanged(Sync::ConnectivityType, bool)), pluginRunner, SIGNAL(connectivityStateChanged(Sync::ConnectivityType, bool))); LOG_DEBUG( "Client plug-in runner initialized" ); // Connect signals from sync session. connect(aSession, SIGNAL(transferProgress(const QString &, Sync::TransferDatabase, Sync::TransferType, const QString &, int)), this, SLOT(onTransferProgress(const QString &, Sync::TransferDatabase, Sync::TransferType, const QString &, int))); connect(aSession, SIGNAL(storageAccquired(const QString &, const QString &)), this, SLOT(onStorageAccquired(const QString &, const QString &))); connect(aSession, SIGNAL(syncProgressDetail(const QString &,int)), this, SLOT(onSyncProgressDetail(const QString &,int))); connect(aSession, SIGNAL(finished(const QString &, Sync::SyncStatus, const QString &, int)), this, SLOT(onSessionFinished(const QString &, Sync::SyncStatus, const QString &, int))); if (aSession->start()) { // Get the DBUS interface for sync-UI. LOG_DEBUG( "sync-ui dbus interface is getting called" ); if (aSession->isScheduled() && !profile->isHidden()) { if (iSyncUIInterface == NULL) { LOG_DEBUG( "iSyncUIInterface is Null" ); iSyncUIInterface = new QDBusInterface("com.nokia.syncui", "/org/maemo/m", "com.nokia.MApplicationIf", QDBusConnection::sessionBus() ); Q_ASSERT(iSyncUIInterface); } else if(!iSyncUIInterface->isValid()) { LOG_DEBUG( "iSyncUIInterface is not valid" ); delete iSyncUIInterface; iSyncUIInterface = NULL; iSyncUIInterface = new QDBusInterface("com.nokia.syncui", "/org/maemo/m", "com.nokia.MApplicationIf", QDBusConnection::sessionBus() ); Q_ASSERT(iSyncUIInterface); } //calling launch with argument list QStringList list; list.append("launching"); QList argumentList; argumentList << qVariantFromValue(list); iSyncUIInterface->asyncCallWithArgumentList(QLatin1String("launch"), argumentList); } LOG_DEBUG( "Sync session started" ); iActiveSessions.insert(aSession->profileName(), aSession); } else { LOG_WARNING( "Failed to start sync session" ); return false; } return true; } void Synchronizer::onSessionFinished(const QString &aProfileName, Sync::SyncStatus aStatus, const QString &aMessage, int aErrorCode) { FUNCTION_CALL_TRACE; LOG_DEBUG( "Session finished:" << aProfileName << ", status:" << aStatus); if(iActiveSessions.contains(aProfileName)) { SyncSession *session = iActiveSessions[aProfileName]; if (session) { switch(aStatus) { case Sync::SYNC_DONE: { bool enabledUpdated = false, visibleUpdated = false; QMap storageMap = session->getStorageMap(); SyncProfile *sessionProf = session->profile(); iProfileManager.enableStorages(*sessionProf, storageMap, &enabledUpdated); // If caps have not been modified, i.e. fetched from the remote device yet, set // enabled storages also visible. If caps have been modified, we must not touch visibility anymore if (sessionProf->boolKey(KEY_CAPS_MODIFIED) == false) { iProfileManager.setStoragesVisible(*sessionProf, storageMap, &visibleUpdated); } if (enabledUpdated || visibleUpdated) { iProfileManager.updateProfile(*sessionProf); } iProfileManager.retriesDone(sessionProf->name()); break; } case Sync::SYNC_ABORTED: case Sync::SYNC_CANCELLED: { session->setFailureResult(SyncResults::SYNC_RESULT_CANCELLED, Buteo::SyncResults::ABORTED); if(session->isProfileCreated()) { iProfileManager.removeProfile(session->profileName()); } break; } case Sync::SYNC_ERROR: { session->setFailureResult(SyncResults::SYNC_RESULT_FAILED, aErrorCode); if(session->isProfileCreated()) { iProfileManager.removeProfile(session->profileName()); } QDateTime nextRetryInterval = iProfileManager.getNextRetryInterval(session->profile()); if(nextRetryInterval.isValid()) { iSyncScheduler->addProfileForSyncRetry(session->profile(), nextRetryInterval); } else { iProfileManager.retriesDone(session->profile()->name()); } break; } default: LOG_WARNING("Unhandled Status in onSessionFinished" << aStatus); break; } iActiveSessions.remove(aProfileName); if(session->isScheduled()) { // Calling this multiple times has no effect, even if the // session was not actually opened iNetworkManager->disconnectSession(); } cleanupSession(session, aStatus); if(iProfilesToRemove.contains(aProfileName)) { cleanupProfile(aProfileName); iProfilesToRemove.removeAll(aProfileName); } if (session->isAborted() && (iActiveSessions.size() == 0) && isBackupRestoreInProgress()) { stopServers(); iSyncBackup->sendReply(0); } } else { LOG_WARNING( "Session found in active sessions, but is NULL" ); } } else { LOG_WARNING( "Session not found from active sessions" ); } emit syncStatus(aProfileName, aStatus, aMessage, aErrorCode); emit syncDone(aProfileName); //Re-enable sync on change if(iSOCEnabled) { iSyncOnChange.enable(); } // Try starting new sync sessions waiting in the queue. while (startNextSync()) { //intentionally empty } } void Synchronizer::onSyncProgressDetail(const QString &aProfileName,int aProgressDetail) { FUNCTION_CALL_TRACE; LOG_DEBUG("aProfileName"<profile(); if (profile == 0) { LOG_WARNING( "Null profile found from queued session" ); cleanupSession(session, Sync::SYNC_ERROR); iSyncQueue.dequeue(); return true; } QString profileName = session->profileName(); LOG_DEBUG( "Trying to start next sync in queue. Profile:" << profileName << session->isScheduled()); #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) QBatteryInfo iDeviceInfo; QBatteryInfo::LevelStatus batteryStat = iDeviceInfo.levelStatus(); if (session->isScheduled() && ((batteryStat == QBatteryInfo::LevelEmpty) || (batteryStat == QBatteryInfo::LevelLow))) #elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(5, 2, 0) QBatteryInfo iDeviceInfo; QBatteryInfo::BatteryStatus batteryStat = iDeviceInfo.batteryStatus(0); if (session->isScheduled() && ((batteryStat == QBatteryInfo::BatteryEmpty) || (batteryStat == QBatteryInfo::BatteryLow))) #else QtMobility::QSystemDeviceInfo iDeviceInfo; QtMobility::QSystemDeviceInfo::BatteryStatus batteryStat = iDeviceInfo.batteryStatus(); if (session->isScheduled() && ((batteryStat != QtMobility::QSystemDeviceInfo::BatteryNormal) && (batteryStat != QtMobility::QSystemDeviceInfo::BatteryLow)) ) #endif { LOG_DEBUG( "Low power, scheduled sync aborted" ); iSyncQueue.dequeue(); session->setFailureResult(SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::LOW_BATTERY_POWER); cleanupSession(session, Sync::SYNC_ERROR); emit syncStatus(profileName, Sync::SYNC_ERROR, "Low Battery", Buteo::SyncResults::LOW_BATTERY_POWER); tryNext = true; } else if (!session->reserveStorages(&iStorageBooker)) { LOG_DEBUG( "Needed storage(s) already in use" ); tryNext = false; } else if (clientProfileActive(profile->clientProfile()->name())) { LOG_DEBUG( "Client profile active, wait for finish" ); return false; } else { // Sync can be started now. iSyncQueue.dequeue(); if (startSyncNow(session)) { emit syncStatus(profileName, Sync::SYNC_STARTED, "", 0); } else { session->setFailureResult(SyncResults::SYNC_RESULT_FAILED, Buteo::SyncResults::INTERNAL_ERROR); cleanupSession(session, Sync::SYNC_ERROR); emit syncStatus(profileName, Sync::SYNC_ERROR, "Internal Error", Buteo::SyncResults::INTERNAL_ERROR); } tryNext = true; } return tryNext; } void Synchronizer::cleanupSession(SyncSession *aSession, Sync::SyncStatus aStatus) { FUNCTION_CALL_TRACE; if (aSession != 0) { QString profileName = aSession->profileName(); if (!profileName.isEmpty()) { LOG_DEBUG("aStatus"<profile(); if ((profile->lastResults()==0) && (aStatus == Sync::SYNC_DONE)) { iProfileManager.saveRemoteTargetId(*profile, aSession->results().getTargetId()); } iProfileManager.saveSyncResults(profileName, aSession->results()); // UI needs to know that Sync Log has been updated. emit resultsAvailable(profileName,aSession->results().toString()); if ( aSession->isScheduled() ) { reschedule(profileName); emit signalProfileChanged(profileName, 1 ,QString()); } // no else } // no else aSession->setProfileCreated(false); aSession->releaseStorages(); aSession->deleteLater(); aSession = 0; } } void Synchronizer::abortSync(QString aProfileName) { FUNCTION_CALL_TRACE; LOG_DEBUG( "Abort sync requested for profile: " << aProfileName ); if (iActiveSessions.contains(aProfileName)) { iActiveSessions[aProfileName]->abort(); } else { LOG_WARNING( "No sync in progress with the given profile" ); // Check if sync was queued, in which case, remove it from the queue SyncSession *queuedSession = iSyncQueue.dequeue(aProfileName); if(queuedSession) { LOG_DEBUG("Removed queued sync" << aProfileName); delete queuedSession; } SyncResults syncResults(QDateTime::currentDateTime(), SyncResults::SYNC_RESULT_CANCELLED, Buteo::SyncResults::ABORTED); iProfileManager.saveSyncResults(aProfileName, syncResults); emit syncStatus(aProfileName, Sync::SYNC_CANCELLED, "", Buteo::SyncResults::ABORTED); } } bool Synchronizer::cleanupProfile(const QString &aProfileId) { FUNCTION_CALL_TRACE; // We assume this call is made on a Sync Profile SyncProfile *profile = iProfileManager.syncProfile (aProfileId); bool status = false; if(!aProfileId.isEmpty() && profile) { bool client = true ; Profile *subProfile = profile->clientProfile(); // client or server if ( !subProfile) { LOG_WARNING( "Could not find client sub-profile" ); subProfile = profile->serverProfile (); client = false; if (!subProfile){ LOG_WARNING( "Could not find server sub-profile" ); return status; } } if (profile->syncType() == SyncProfile::SYNC_SCHEDULED) { iSyncScheduler->removeProfile(aProfileId); } // Remove external sync status if it exist removeExternalSyncStatus(profile); PluginRunner *pluginRunner; if (client) { pluginRunner = new ClientPluginRunner(subProfile->name(), profile, &iPluginManager, this, this); } else { pluginRunner = new ServerPluginRunner(subProfile->name(), profile, &iPluginManager, this, iServerActivator, this); } if (pluginRunner == 0 || !pluginRunner->init()) { LOG_WARNING( "Failed to initialize client plug-in runner" ); delete profile; return status; } const SyncResults * syncResults = profile->lastResults(); if (!pluginRunner->cleanUp() && syncResults){ LOG_CRITICAL ("Error in removing anchors, sync session "); } else { LOG_DEBUG("Removing the profile"); iProfileManager.removeProfile(aProfileId); status = true; } delete profile; delete pluginRunner; } return status; } bool Synchronizer::clientProfileActive(const QString &clientProfileName) { QList activeSessions = iActiveSessions.values(); foreach(SyncSession *session, activeSessions) { if(session->profile()) { SyncProfile *profile = session->profile(); if (profile->clientProfile()->name() == clientProfileName) { return true; } } } return false; } bool Synchronizer::removeProfile(QString aProfileId) { FUNCTION_CALL_TRACE; bool status = true; // Check if a sync session is ongoing for this profile. if(iActiveSessions.contains(aProfileId)) { // If yes, abort that sync session first LOG_DEBUG("Sync still ongoing for profile" << aProfileId); LOG_DEBUG("Aborting sync for profile" << aProfileId); abortSync(aProfileId); iProfilesToRemove.append(aProfileId); } else { status = cleanupProfile(aProfileId); } return status; } bool Synchronizer::updateProfile(QString aProfileAsXml) { FUNCTION_CALL_TRACE bool status = false; QString address; if(!aProfileAsXml.isEmpty()) { // save the changes to persistent storage Profile *profile = iProfileManager.profileFromXml(aProfileAsXml); if(profile) { // Update storage info before giving it to profileManager /* Profile* service = 0; foreach(Profile* p, profile->allSubProfiles()) { if (p->type() == Profile::TYPE_SERVICE) { service = p; break; } } */ if (!profile->boolKey(Buteo::KEY_STORAGE_UPDATED)) { address = profile->key(Buteo::KEY_BT_ADDRESS); if (!address.isNull()) { if(profile->key(Buteo::KEY_UUID).isEmpty()) { QString uuid = QUuid::createUuid().toString(); uuid = uuid.remove(QRegExp("[{}]")); profile->setKey(Buteo::KEY_UUID, uuid); } if(profile->key(Buteo::KEY_REMOTE_NAME).isEmpty()) { profile->setKey(Buteo::KEY_REMOTE_NAME, profile->displayname()); } profile->setBoolKey(Buteo::KEY_STORAGE_UPDATED, true); } } QString profileId = iProfileManager.updateProfile(*profile); // if the profile changes are for schedule sync we need to reschedule if(!profileId.isEmpty()) { reschedule(profileId); status = true; } delete profile; } } return status; } bool Synchronizer::requestStorages(QStringList aStorageNames) { FUNCTION_CALL_TRACE; return iStorageBooker.reserveStorages(aStorageNames, ""); } void Synchronizer::releaseStorages(QStringList aStorageNames) { FUNCTION_CALL_TRACE; iStorageBooker.releaseStorages(aStorageNames); emit storageReleased(); } QStringList Synchronizer::runningSyncs() { FUNCTION_CALL_TRACE; return iActiveSessions.keys(); } void Synchronizer::onStorageReleased() { FUNCTION_CALL_TRACE; LOG_DEBUG( "Storage released" ); while (startNextSync()) { // Intentionally empty. } } void Synchronizer::onTransferProgress( const QString &aProfileName, Sync::TransferDatabase aDatabase, Sync::TransferType aType, const QString &aMimeType, int aCommittedItems ) { FUNCTION_CALL_TRACE; LOG_DEBUG( "Sync session progress" ); LOG_DEBUG( "Profile:" << aProfileName ); LOG_DEBUG( "Database:" << aDatabase ); LOG_DEBUG( "Transfer type:" << aType ); LOG_DEBUG( "Mime type:" << aMimeType ); emit transferProgress( aProfileName, aDatabase, aType, aMimeType, aCommittedItems ); } void Synchronizer::onStorageAccquired ( const QString &aProfileName, const QString &aMimeType ) { FUNCTION_CALL_TRACE; LOG_DEBUG( "Mime type:" << aMimeType ); LOG_DEBUG( "Profile:" << aProfileName ); if (!aProfileName.isEmpty() && !aMimeType.isEmpty()){ SyncSession *session = qobject_cast(QObject::sender()); if (session){ QMap storageMap = session->getStorageMap(); if (aMimeType.compare(QString("text/x-vcard"), Qt::CaseInsensitive) == 0) storageMap["hcontacts"] = true; else if (aMimeType.compare(QString("text/x-vcalendar"), Qt::CaseInsensitive) == 0) storageMap["hcalendar"] = true; else if (aMimeType.compare(QString("text/plain"), Qt::CaseInsensitive) == 0) storageMap["hnotes"] = true; #ifdef BM_SYNC else if (aMimeType.compare(QString("text/x-vbookmark"), Qt::CaseInsensitive) == 0) storageMap["hbookmarks"] = true; #endif #ifdef SMS_SYNC else if (aMimeType.compare(QString("text/x-vmsg"), Qt::CaseInsensitive) == 0) storageMap["hsms"] = true; #endif else LOG_DEBUG( "Unsupported mime type" << aMimeType ); session->setStorageMap(storageMap); } } } bool Synchronizer::requestStorage(const QString &aStorageName, const SyncPluginBase *aCaller) { FUNCTION_CALL_TRACE; return iStorageBooker.reserveStorage(aStorageName, aCaller->getProfileName()); } void Synchronizer::releaseStorage(const QString &aStorageName, const SyncPluginBase */*aCaller*/) { FUNCTION_CALL_TRACE; iStorageBooker.releaseStorage(aStorageName); emit storageReleased(); } StoragePlugin* Synchronizer::createStorage(const QString &aPluginName) { FUNCTION_CALL_TRACE; StoragePlugin* plugin = NULL; if (!aPluginName.isEmpty()) { plugin = iPluginManager.createStorage(aPluginName); } // no else return plugin; } void Synchronizer::initializeScheduler() { FUNCTION_CALL_TRACE; if (!iSyncScheduler) { iSyncScheduler = new SyncScheduler(this); connect(iSyncScheduler, SIGNAL(syncNow(QString)), this, SLOT(startScheduledSync(QString)), Qt::QueuedConnection); connect(iSyncScheduler, SIGNAL(externalSyncChanged(const SyncProfile*,bool)), this, SLOT(externalSyncStatus(const SyncProfile*,bool)), Qt::QueuedConnection); QList profiles = iProfileManager.allSyncProfiles(); foreach (SyncProfile *profile, profiles) { if (profile->syncType() == SyncProfile::SYNC_SCHEDULED) { iSyncScheduler->addProfile(profile); } // Emit external sync status for all profiles // on startup, in case of a crash/abort of this // process, we should update pontential listeners with // the correct status externalSyncStatus(profile, true); } qDeleteAll(profiles); } } void Synchronizer::destroyStorage(StoragePlugin *aStorage) { FUNCTION_CALL_TRACE; iPluginManager.destroyStorage(aStorage); } bool Synchronizer::isConnectivityAvailable( Sync::ConnectivityType aType ) { FUNCTION_CALL_TRACE; if (iTransportTracker != 0) { return iTransportTracker->isConnectivityAvailable(aType); } else { return false; } } void Synchronizer::startServers( bool resume ) { FUNCTION_CALL_TRACE; LOG_DEBUG( "Starting/Resuming server plug-ins" ); if (iServerActivator != 0) { if( false == resume ) { connect(iServerActivator, SIGNAL(serverEnabled(const QString &)), this, SLOT(startServer(const QString &)), Qt::QueuedConnection); connect(iServerActivator, SIGNAL(serverDisabled(const QString &)), this, SLOT(stopServer(const QString &)), Qt::QueuedConnection); } QStringList enabledServers = iServerActivator->enabledServers(); foreach (QString server, enabledServers) { if( false == resume ) { startServer(server); } else { ServerPluginRunner *pluginRunner = iServers[server]; if( pluginRunner ) { pluginRunner->resume(); } } } } else { LOG_CRITICAL("No server plug-in activator"); } } void Synchronizer::stopServers( bool suspend ) { FUNCTION_CALL_TRACE; LOG_DEBUG( "Stopping/Suspending all server plug-ins" ); if( false == suspend ) { iServerActivator->disconnect(); } QStringList activeServers = iServers.keys(); foreach (QString server, activeServers) { if( false == suspend ) { stopServer(server); } else { ServerPluginRunner *pluginRunner = iServers[server]; if( pluginRunner ) { pluginRunner->suspend(); } } } } void Synchronizer::startServer(const QString &aProfileName) { FUNCTION_CALL_TRACE; LOG_DEBUG("Starting server plug-in:" << aProfileName); if(iServers.contains(aProfileName)) { LOG_WARNING("Server thread already running for profile:" << aProfileName); // Remove reference from the activator iServerActivator->removeRef(aProfileName, false); return; } Profile* serverProfile = iProfileManager.profile( aProfileName, Profile::TYPE_SERVER ); if (!serverProfile) { // @todo: for now, do not enforce server plug-ins to have an XML profile LOG_WARNING( "Profile not found, creating an empty one" ); ProfileFactory pf; serverProfile = pf.createProfile(aProfileName, Profile::TYPE_SERVER); } else { iProfileManager.expand( *serverProfile ); } if( !serverProfile || !serverProfile->isValid() ) { LOG_WARNING( "Profile not found or not valid:" << aProfileName ); delete serverProfile; serverProfile = 0; return; } ServerPluginRunner *pluginRunner = new ServerPluginRunner(aProfileName, serverProfile, &iPluginManager, this, iServerActivator, this); if (pluginRunner == 0) { LOG_CRITICAL("Failed to create plug-in runner"); delete serverProfile; serverProfile = 0; return; } // Relay connectivity state change signal to plug-in runner. connect(iTransportTracker, SIGNAL(connectivityStateChanged(Sync::ConnectivityType, bool)), pluginRunner, SIGNAL(connectivityStateChanged(Sync::ConnectivityType, bool))); connect(pluginRunner, SIGNAL(done()), this, SLOT(onServerDone())); connect(pluginRunner, SIGNAL(newSession(const QString &)), this, SLOT(onNewSession(const QString &))); if (!pluginRunner->init() || !pluginRunner->start()) { LOG_CRITICAL("Failed to start plug-in"); delete pluginRunner; pluginRunner = 0; return; } iServers.insert(aProfileName, pluginRunner); } void Synchronizer::stopServer( const QString& aProfileName ) { FUNCTION_CALL_TRACE; LOG_DEBUG("Stopping server:" << aProfileName); if (iServers.contains(aProfileName)) { ServerPluginRunner *pluginRunner = iServers[aProfileName]; if( pluginRunner ) { pluginRunner->stop(); } LOG_DEBUG("Deleting server"); if (!iClosing) { // This function may have been invoked from a signal. The plugin runner // will only be deleted when the server thread returns. LOG_WARNING("The server thread for profile: " << aProfileName << "is still running. Server will be deleted later"); } else { iServers.remove(aProfileName); // Synchronizer is closing, this function is not invoked by a signal. // Delete plug-in runner immediately. delete pluginRunner; } pluginRunner = 0; } else { LOG_WARNING("Server not found"); } } void Synchronizer::onServerDone() { FUNCTION_CALL_TRACE; ServerPluginRunner *pluginRunner = qobject_cast(QObject::sender()); QString serverName = "unknown"; if (pluginRunner != 0) { serverName = pluginRunner->pluginName(); } LOG_DEBUG("Server stopped:" << serverName); if (iServers.values().contains(pluginRunner)) { LOG_DEBUG("Deleting server"); iServers.remove(iServers.key(pluginRunner)); pluginRunner->deleteLater(); pluginRunner = 0; } } bool syncProfilePointerLessThan(SyncProfile *&aLhs, SyncProfile *&aRhs) { if (aLhs && aRhs) { if (aLhs->isHidden() != aRhs->isHidden()) return !aLhs->isHidden(); if (aLhs->isEnabled() != aRhs->isEnabled()) return aLhs->isEnabled(); } return false; } void Synchronizer::onNewSession(const QString &aDestination) { FUNCTION_CALL_TRACE; LOG_DEBUG("New session from" << aDestination); bool createNewProfile = false; ServerPluginRunner *pluginRunner = qobject_cast(QObject::sender()); if (pluginRunner != 0) { SyncProfile *profile = 0; QList syncProfiles; BtHelper btHelp(aDestination); QMap mapVal = btHelp.getDeviceProperties(); uint classType = mapVal.value("Class").toInt(); uint computerclass = 0x100; //Major Device Class - Computer if(aDestination.contains("USB") || classType & computerclass) { syncProfiles = iProfileManager.getSyncProfilesByData( QString::null, QString::null, KEY_DISPLAY_NAME, PC_SYNC); } else { syncProfiles = iProfileManager.getSyncProfilesByData( QString::null, Profile::TYPE_SYNC, KEY_BT_ADDRESS, aDestination); } // no else if (syncProfiles.isEmpty()) { LOG_DEBUG( "No sync profiles found with a matching destination address" ); // destination a bt address profile = iProfileManager.createTempSyncProfile(aDestination, createNewProfile); profile->setKey(Buteo::KEY_UUID, iUUID); profile->setKey(Buteo::KEY_REMOTE_NAME, iRemoteName); if(createNewProfile) { iProfileManager.updateProfile(*profile); } } else { // Sort profiles to preference order. Visible and enabled are preferred. qSort(syncProfiles.begin(), syncProfiles.end(), syncProfilePointerLessThan); profile = syncProfiles.first(); LOG_DEBUG( "Found" << syncProfiles.count() << "sync profiles with a " "matching destination address. Selecting" << profile->name()); syncProfiles.removeFirst(); qDeleteAll(syncProfiles); } // If the profile is not hidden, UI must be informed. if(!profile->isHidden()) { // Get the DBUS interface for sync-UI. LOG_DEBUG( "sync-ui dbus interface is getting called" ); if (iSyncUIInterface == NULL) { LOG_DEBUG( "iSyncUIInterface is NULL" ); iSyncUIInterface = new QDBusInterface("com.nokia.syncui", "/org/maemo/m", "com.nokia.MApplicationIf", QDBusConnection::sessionBus() ); Q_ASSERT(iSyncUIInterface); } else if(!iSyncUIInterface->isValid()) { LOG_DEBUG( "iSyncUIInterface is not Valid()" ); delete iSyncUIInterface; iSyncUIInterface = NULL; iSyncUIInterface = new QDBusInterface("com.nokia.syncui", "/org/maemo/m", "com.nokia.MApplicationIf", QDBusConnection::sessionBus() ); Q_ASSERT(iSyncUIInterface); } //calling launch with argument list QStringList list; list.append("launching"); QList argumentList; argumentList << qVariantFromValue(list); iSyncUIInterface->asyncCallWithArgumentList(QLatin1String("launch"), argumentList); } SyncSession *session = new SyncSession(profile, this); if (session != 0) { LOG_DEBUG("Disable sync on change"); //As sync is ongoing, disable sync on change for now, we can query later if //there are changes. if(iSOCEnabled) { iSyncOnChange.disableNext(); } session->setProfileCreated(createNewProfile); // disable all storages // @todo : Can we remove hardcoding of the storageNames ??? QMap storageMap; storageMap["hcontacts"] = 0; storageMap["hcalendar"] = 0; storageMap["hnotes"] = 0; #ifdef BM_SYNC storageMap["hbookmarks"] = 0; #endif #ifdef SMS_SYNC storageMap["hsms"] = 0; #endif session->setStorageMap(storageMap); iActiveSessions.insert(profile->name(), session); // Connect signals from sync session. connect(session, SIGNAL(transferProgress(const QString &, Sync::TransferDatabase, Sync::TransferType, const QString &, int)), this, SLOT(onTransferProgress(const QString &, Sync::TransferDatabase, Sync::TransferType, const QString &, int))); connect(session, SIGNAL(storageAccquired(const QString &, const QString &)), this, SLOT(onStorageAccquired(const QString &, const QString &))); connect(session, SIGNAL(finished(const QString &, Sync::SyncStatus, const QString &, int)), this, SLOT(onSessionFinished(const QString &, Sync::SyncStatus, const QString &, int))); connect(session, SIGNAL(syncProgressDetail(const QString &,int)), this, SLOT(onSyncProgressDetail(const QString &,int))); // Associate plug-in runner with the new session. session->setPluginRunner(pluginRunner, false); emit syncStatus(profile->name(), Sync::SYNC_STARTED, "", 0); } else { delete profile; profile = 0; LOG_CRITICAL("Failed to create session object"); } } else { LOG_WARNING("Could not resolve server, session object not created"); } } void Synchronizer::slotProfileChanged(QString aProfileName, int aChangeType, QString aProfileAsXml) { // start sync when a new profile is added switch (aChangeType) { case ProfileManager::PROFILE_ADDED: { enableSOCSlot(aProfileName); SyncProfile *profile = iProfileManager.syncProfile(aProfileName); if (profile && profile->isEnabled()) { startSync(aProfileName); } } break; case ProfileManager::PROFILE_REMOVED: iSyncOnChangeScheduler.removeProfile(aProfileName); iWaitingOnlineSyncs.removeAll(aProfileName); break; case ProfileManager::PROFILE_MODIFIED: { // schedule a new sync in case of profile changed; // Example if the profile change from disable -> enable SyncProfile *profile = iProfileManager.syncProfile(aProfileName); if (profile->isEnabled()) { startScheduledSync(aProfileName); } } break; } emit signalProfileChanged(aProfileName, aChangeType, aProfileAsXml); } void Synchronizer::reschedule(const QString &aProfileName) { FUNCTION_CALL_TRACE; if (iSyncScheduler == 0) return; SyncProfile *profile = iProfileManager.syncProfile(aProfileName); if(profile && profile->syncType() == SyncProfile::SYNC_SCHEDULED && profile->isEnabled()) { iSyncScheduler->addProfile(profile); } else { LOG_DEBUG("Scheduled sync got disabled for" << aProfileName); iSyncScheduler->removeProfile(aProfileName); } if(profile) { externalSyncStatus(profile); LOG_DEBUG("Reschdule profile" << aProfileName << profile->syncType() << profile->isEnabled()); delete profile; profile = NULL; } } void Synchronizer::slotSyncStatus(QString aProfileName, int aStatus, QString /*aMessage*/, int /*aMoreDetails*/) { FUNCTION_CALL_TRACE; SyncProfile *profile = iProfileManager.syncProfile(aProfileName); if(profile) { QString accountId = profile->key(KEY_ACCOUNT_ID); if(!accountId.isNull()) { switch(aStatus) { case Sync::SYNC_QUEUED: case Sync::SYNC_STARTED: case Sync::SYNC_ERROR: case Sync::SYNC_DONE: case Sync::SYNC_ABORTED: case Sync::SYNC_CANCELLED: case Sync::SYNC_NOTPOSSIBLE: { LOG_DEBUG("Sync status changed for account" << accountId); qlonglong aPrevSyncTime; qlonglong aNextSyncTime; int aFailedReason; int aNewStatus = status(accountId.toUInt(), aFailedReason, aPrevSyncTime, aNextSyncTime); emit statusChanged(accountId.toUInt(), aNewStatus, aFailedReason, aPrevSyncTime, aNextSyncTime); } break; case Sync::SYNC_STOPPING: case Sync::SYNC_PROGRESS: default: break; } } delete profile; } } void Synchronizer::removeScheduledSync(const QString &aProfileName) { FUNCTION_CALL_TRACE; if (iSyncScheduler == 0) return; SyncProfile *profile = iProfileManager.syncProfile(aProfileName); if(profile && !profile->isEnabled()) { LOG_DEBUG("Sync got disabled for" << aProfileName); iSyncScheduler->removeProfile(aProfileName); } // Check if external sync status changed, profile might be turned to sync externally and thus buteo sync set to disable externalSyncStatus(profile); } bool Synchronizer::isBackupRestoreInProgress () { FUNCTION_CALL_TRACE; bool retVal = getBackUpRestoreState(); if (retVal) { LOG_DEBUG ("Backup-Restore o/p in progress - Failed to start manual sync"); } return retVal; } void Synchronizer::backupRestoreStarts () { LOG_DEBUG ("Synchronizer:backupRestoreStarts:"); iClosing = true; // No active sessions currently !! if (iActiveSessions.size() == 0) { LOG_DEBUG ("No active sync sessions "); stopServers( true ); iSyncBackup->sendReply(0); } else { // Stop running sessions QList sessions = iActiveSessions.values(); foreach (SyncSession* session, sessions) { if (session != 0) { session->abort(); } } } delete iSyncScheduler; iSyncScheduler = 0; } void Synchronizer::backupRestoreFinished() { LOG_DEBUG ("Synchronizer::backupFinished"); iClosing = false; startServers( true ); initializeScheduler(); iSyncBackup->sendReply(0); } void Synchronizer::backupStarts() { LOG_DEBUG ("Synchronizer::backupStarts"); emit backupInProgress(); backupRestoreStarts (); } void Synchronizer::backupFinished() { LOG_DEBUG ("Synchronizer::backupFinished"); backupRestoreFinished(); emit backupDone(); } void Synchronizer::restoreStarts() { LOG_DEBUG ("Synchronizer::restoreStarts"); emit restoreInProgress(); backupRestoreStarts(); } void Synchronizer::restoreFinished() { LOG_DEBUG ("Synchronizer::restoreFinished"); backupRestoreFinished(); emit restoreDone(); } bool Synchronizer::getBackUpRestoreState() { LOG_DEBUG ("Synchronizer::getBackUpRestoreState"); return iSyncBackup->getBackUpRestoreState(); } void Synchronizer::start(unsigned int aAccountId) { FUNCTION_CALL_TRACE; LOG_DEBUG("Start sync requested for account" << aAccountId); QList profileList = iAccounts->getProfilesByAccountId(aAccountId); foreach(SyncProfile *profile, profileList) { startSync(profile->name()); delete profile; } } void Synchronizer::stop(unsigned int aAccountId) { FUNCTION_CALL_TRACE; LOG_DEBUG("Stop sync requested for account" << aAccountId); QList profileList = iAccounts->getProfilesByAccountId(aAccountId); foreach(SyncProfile *profile, profileList) { abortSync(profile->name()); delete profile; } } int Synchronizer::status(unsigned int aAccountId, int &aFailedReason, qlonglong &aPrevSyncTime, qlonglong &aNextSyncTime) { FUNCTION_CALL_TRACE; int status = 1; // Initialize to Done QDateTime prevSyncTime; // Initialize to invalid QDateTime nextSyncTime; QList profileList = iAccounts->getProfilesByAccountId(aAccountId); foreach(SyncProfile *profile, profileList) { // First check if sync is going on for any profile corresponding to this // account ID if(iActiveSessions.contains(profile->name()) || iSyncQueue.contains(profile->name())) { LOG_DEBUG("Sync running for" << aAccountId); status = 0; break; } else { // Check if the last sync resulted in an error for any of the // profiles const SyncResults *lastResults = profile->lastResults(); if(lastResults && SyncResults::SYNC_RESULT_FAILED == lastResults->majorCode()) { status = 2; // TODO: Determine the set of failure enums needed here aFailedReason = lastResults->minorCode(); break; } } } if(status != 0) { // Need to return the next and last sync times foreach(SyncProfile *profile, profileList) { if(!prevSyncTime.isValid()) { prevSyncTime = profile->lastSyncTime(); } else { (prevSyncTime > profile->lastSyncTime()) ? prevSyncTime : profile->lastSyncTime(); } } if(prevSyncTime.isValid()) { // Doesn't really matter which profile we do this for, as all of // them have the same schedule SyncProfile *profile = profileList.first(); nextSyncTime = profile->nextSyncTime(prevSyncTime); } } aPrevSyncTime = prevSyncTime.toMSecsSinceEpoch(); aNextSyncTime = nextSyncTime.toMSecsSinceEpoch(); qDeleteAll(profileList); return status; } QList Synchronizer::syncingAccounts() { FUNCTION_CALL_TRACE; QList syncingAccountsList; // Check active sessions QList activeSessions = iActiveSessions.values(); foreach(SyncSession *session, activeSessions) { if(session->profile()) { SyncProfile *profile = session->profile(); QString accountId = profile->key(KEY_ACCOUNT_ID); if(!accountId.isNull()) { syncingAccountsList.append(accountId.toUInt()); } } } // Check queued syncs const QList queuedSessions = iSyncQueue.getQueuedSyncSessions(); foreach(const SyncSession *session, queuedSessions) { if(session->profile()) { SyncProfile *profile = session->profile(); QString accountId = profile->key(KEY_ACCOUNT_ID); if(!accountId.isNull()) { syncingAccountsList.append(accountId.toUInt()); } } } return syncingAccountsList; } QString Synchronizer::getLastSyncResult(const QString &aProfileId) { FUNCTION_CALL_TRACE; QString lastSyncResult; if(!aProfileId.isEmpty()) { SyncProfile *profile = iProfileManager.syncProfile (aProfileId); if(profile) { const SyncResults * syncResults = profile->lastResults(); if (syncResults) { lastSyncResult = syncResults->toString(); LOG_DEBUG("SyncResults found:"< profiles = iProfileManager.allVisibleSyncProfiles(); if(!profiles.isEmpty()) { foreach(Buteo::SyncProfile *profile , profiles) { if(profile) { profilesAsXml.append(profile->toString()); delete profile; profile = NULL; } } } LOG_DEBUG("allVisibleSyncProfiles profilesAsXml"<toString()); delete profile; profile = NULL; } else { LOG_DEBUG("No profile found with aProfileId"< filters; ProfileManager::SearchCriteria filter; filter.iType = ProfileManager::SearchCriteria::EQUAL; filter.iKey = aKey; filter.iValue = aValue; filters.append(filter); QList profiles = iProfileManager.getSyncProfilesByData(filters); if (profiles.size() > 0) { LOG_DEBUG("Found matching profiles :" << profiles.size()); foreach (SyncProfile *profile, profiles) { profilesAsXml.append(profile->toString()); } qDeleteAll(profiles); } else { LOG_DEBUG("No profile found with key :" << aKey << "Value : " << aValue ); } } return profilesAsXml; } QStringList Synchronizer::syncProfilesByType(const QString &aType) { FUNCTION_CALL_TRACE; LOG_DEBUG("Profile Type : "<< aType); return iProfileManager.profileNames(aType); } void Synchronizer::onNetworkStateChanged(bool aState, Sync::InternetConnectionType type) { FUNCTION_CALL_TRACE; LOG_DEBUG("Network state changed: OnLine:" << aState << " connection type:" << type); if (acceptScheduledSync(aState, type)) { LOG_DEBUG("Restart sync for profiles that need network"); QStringList profiles(iWaitingOnlineSyncs); iWaitingOnlineSyncs.clear(); foreach(QString profileName, profiles) { // start sync now, we do not need to call 'startScheduledSync' since that function // only checks for internet connection startSync(profileName, true); } } else if (!aState) { QList profiles = iActiveSessions.keys(); foreach(QString profileId, profiles) { //Getting profile SyncProfile *profile = iProfileManager.syncProfile (profileId); if ((profile) && (profile->destinationType() == Buteo::SyncProfile::DESTINATION_TYPE_ONLINE)) { iActiveSessions[profileId]->abort(Sync::SYNC_ERROR); delete profile; profile = NULL; } else { LOG_DEBUG("No profile found with aProfileId"< profiles; if("USB" == aAddress) { profiles = iProfileManager.getSyncProfilesByData( QString::null, QString::null, KEY_DISPLAY_NAME, PC_SYNC); } else { BtHelper btHelp(aAddress); QMap mapVal = btHelp.getDeviceProperties(); uint classType = mapVal.value("Class").toInt(); uint computerclass = 0x100; //Major Device Class - Computer if(classType & computerclass) { profiles = iProfileManager.getSyncProfilesByData( QString::null, QString::null, KEY_DISPLAY_NAME, PC_SYNC); } else { profiles = iProfileManager.getSyncProfilesByData("", Buteo::Profile::TYPE_SYNC, Buteo::KEY_BT_ADDRESS, aAddress); } } if(!profiles.isEmpty()) { profile = profiles.first(); } return profile; } QString Synchronizer::getValue(const QString& aAddress, const QString& aKey) { FUNCTION_CALL_TRACE; QString value; if(Buteo::KEY_UUID == aKey) { iUUID = QUuid::createUuid().toString(); iUUID = iUUID.remove(QRegExp("[{}]")); value = iUUID; } if(Buteo::KEY_REMOTE_NAME == aKey) { if("USB" == aAddress) { iRemoteName = PC_SYNC; } else { BtHelper btHelper(aAddress); QMap mapVal = btHelper.getDeviceProperties(); uint classType = mapVal.value("Class").toInt(); uint computerclass = 0x100; //Major Device Class - Computer if(classType & computerclass) { iRemoteName = PC_SYNC; } else { iRemoteName = btHelper.getDeviceProperties().value(BT_PROPERTIES_NAME).toString(); } } value = iRemoteName; } return value; } // Here we store profile names since they are unique, but can be anything, and we emit signals // containing the client profile name, since those are always associated with a // specific plugin, this way potential listeners of these signals can distinguish the signals // based on the accountId and client profile name. void Synchronizer::externalSyncStatus(const SyncProfile* aProfile, bool aQuery) { int accountId = aProfile->key(KEY_ACCOUNT_ID).toInt(); if (accountId) { const QString &profileName = aProfile->name(); const QString &clientProfile = aProfile->clientProfile()->name(); // Account in set to sync externally, buteo will let external process handle the syncs in this case if (aProfile->syncExternallyEnabled()) { if (!iExternalSyncProfileStatus.value(profileName)) { LOG_DEBUG("Sync externally status changed from false to true for profile:" << profileName); iExternalSyncProfileStatus.insert(profileName, true); emit syncedExternallyStatus(accountId, clientProfile, true); } else if (aQuery) { LOG_DEBUG("Account is in set to sync externally for profile:" << profileName); emit syncedExternallyStatus(accountId, clientProfile, true); } // Account set to sync externally in rush mode } else if (aProfile->syncExternallyDuringRush()) { // Check if we are currently inside rush bool isSyncExternally = aProfile->inExternalSyncRushPeriod(); if (iExternalSyncProfileStatus.contains(profileName)) { LOG_DEBUG("We already have this profile, lets check the status for profile:" << profileName); bool prevSyncExtState = iExternalSyncProfileStatus.value(profileName); if (prevSyncExtState != isSyncExternally) { iExternalSyncProfileStatus.insert(profileName, isSyncExternally); LOG_DEBUG("Sync externally status changed to " << isSyncExternally << "for profile:" << profileName); emit syncedExternallyStatus(accountId, clientProfile, isSyncExternally); } else if (aQuery){ LOG_DEBUG("Sync externally status did not change, current state is: " << prevSyncExtState << "for profile:" << profileName); emit syncedExternallyStatus(accountId, clientProfile, prevSyncExtState); } } else { iExternalSyncProfileStatus.insert(profileName, isSyncExternally); LOG_DEBUG("Inserting sync externally status:" << isSyncExternally << "for profile:" << profileName); emit syncedExternallyStatus(accountId, clientProfile, isSyncExternally); } } else { if (iExternalSyncProfileStatus.contains(profileName)) { iExternalSyncProfileStatus.remove(profileName); emit syncedExternallyStatus(accountId, clientProfile, false); LOG_DEBUG("Removing sync externally status for profile:" << profileName); } else if (aQuery) { LOG_DEBUG("Sync externally is off for profile:" << profileName); emit syncedExternallyStatus(accountId, clientProfile, false); } } } } void Synchronizer::removeExternalSyncStatus(const SyncProfile *aProfile) { int accountId = aProfile->key(KEY_ACCOUNT_ID).toInt(); if (accountId) { const QString &profileName = aProfile->name(); if (iExternalSyncProfileStatus.contains(profileName)) { // if profile was set to sync externally emit the change state signal if (iExternalSyncProfileStatus.value(profileName)) { emit syncedExternallyStatus(accountId, aProfile->clientProfile()->name(),false); } iExternalSyncProfileStatus.remove(profileName); LOG_DEBUG("Removing sync externally status for profile:" << profileName); } } } bool Synchronizer::acceptScheduledSync(bool aConnected, Sync::InternetConnectionType aType) const { static QList allowedTypes; if (allowedTypes.isEmpty()) { allowedTypes << Sync::INTERNET_CONNECTION_WLAN << Sync::INTERNET_CONNECTION_ETHERNET; } return (aConnected && (g_settings_get_boolean(iSettings, "allow-scheduled-sync-over-cellular") || allowedTypes.contains(aType))); } void Synchronizer::isSyncedExternally(unsigned int aAccountId, const QString aClientProfileName) { LOG_DEBUG("Received isSyncedExternally request for account:" << aAccountId); bool profileFound = false; QList syncProfiles = iAccounts->getProfilesByAccountId(aAccountId); if (!syncProfiles.isEmpty()) { foreach(SyncProfile *profile, syncProfiles) { if (profile->clientProfile()->name() == aClientProfileName) { externalSyncStatus(profile, true); profileFound = true; break; } } } if (!profileFound) { LOG_DEBUG("We don't have a profile for account:" << aAccountId << "emitting sync external status false"); emit syncedExternallyStatus(aAccountId, QString(), false); } qDeleteAll(syncProfiles); } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncScheduler.h0000644000015600001650000001257612634332753023173 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCSCHEDULER_H #define SYNCSCHEDULER_H #ifdef USE_KEEPALIVE #include "BackgroundSync.h" #include "ProfileManager.h" #else #include "SyncAlarmInventory.h" #include "IPHeartBeat.h" #endif #include #include #include #include class QDateTime; #ifdef USE_KEEPALIVE class BackgroundSync; #else class IPHeartBeat; #endif namespace Buteo { class SyncSession; class SyncSchedulerTest; class SyncProfile; /*! \brief SyncScheduler Object to be used to set Schedule via the framework */ class SyncScheduler : public QObject { Q_OBJECT public: //! \brief Constructor. SyncScheduler(QObject *aParent = 0); /** * \brief Destructor */ virtual ~SyncScheduler(); /*! \brief Adds a profile to the scheduler. * * Verifies that the profile is enabled and it has schedule enabled * before adding. If the profile is already added, removes the old schedule * and then adds the profile again. * A syncNow signal is sent when sync should be started based on the * schedule settings of the profile. The signal is sent only once. Call * this function again after the sync has finished to continue scheduling * syncs for the profile. * \param aProfile Profile to add to scheduler. * \return Success indicator. */ bool addProfile(const SyncProfile* aProfile); /* Schedule a retry for a failed sync if the profile has retries enabled * * @param aProfile sync profile * @param aNExtSyncTime retry after this duration */ void addProfileForSyncRetry(const SyncProfile* aProfile, QDateTime aNextSyncTime); /*! \brief Removes the profile with the given name from the scheduler. * * No new syncNow signals will be sent for the profile. Note that an * already sent signal may still be waiting in the event queue of receiving * thread. * \param aProfileName Name of the profile to remove from the scheduler. */ void removeProfile(const QString &aProfileName); private slots: #ifndef USE_KEEPALIVE /** * \brief Performs needed actions when scheduled alarm is triggered * * @param aAlarmEventID an ID that identifies the triggered alarm event */ void doAlarmActions(int aAlarmEventID); #endif /** * \brief Performs needed actions when a IP heart beat is triggered * * @param aProfileName Name of the profile on which heart beat received */ void doIPHeartbeatActions(QString aProfileName); #ifdef USE_KEEPALIVE /** * \brief Reschedule backgroundActivity for a profile * * @param aProfileName Name of the profile to reschedule */ void rescheduleBackgroundActivity(const QString& aProfileName); #endif signals: /*! \brief Signal emitted when a sync session should be launched based on * the sync schedule settings of the profile. * * \param aProfileName Name of the profile. */ void syncNow(QString aProfileName); /*! \brief Signal emitted when a sync session should be launched based on * the sync schedule settings of the profile. * * \param aProfileName Name of the profile. */ void externalSyncChanged(const SyncProfile* aProfile, bool aQuery=false); private: // functions /** * \brief Programs next alarm event to alarmd. * * @param aProfile The profile for which the alarm is programmed * @param aNextSyncTime use if provided, otherwise fetch the info from the profile * @return Unique alarm event ID or 0 in failure case. */ int setNextAlarm(const SyncProfile* aProfile, QDateTime aNextSyncTime = QDateTime()); /** * \brief Creates a DBUS adaptor for the scheduler */ void setupDBusAdaptor(); #ifndef USE_KEEPALIVE /** * \brief Removes an alarm from alarmd queue * @param aAlarmEventID ID of the alarm to be removed */ void removeAlarmEvent(int aAlarmEvent); /** * \brief A convenience method that removes all alarms from alarmd queue */ void removeAllAlarms(); #endif private: // data #ifdef USE_KEEPALIVE /// BackgroundSync management object BackgroundSync *iBackgroundActivity; ProfileManager iProfileManager; #else /// A list of sync schedule profiles QMap iSyncScheduleProfiles; /// Alarm factory object SyncAlarmInventory *iAlarmInventory; /// IP Heartbeat management object IPHeartBeat* iIPHeartBeatMan; #endif #ifdef SYNCFW_UNIT_TESTS friend class SyncSchedulerTest; #endif }; } #endif // SYNCSCHEDULER_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/PluginRunner.h0000644000015600001650000001206112634332753023035 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef PLUGINRUNNER_H #define PLUGINRUNNER_H #include #include "SyncCommonDefs.h" #include "SyncPluginBase.h" #include #include namespace Buteo { class PluginManager; class PluginCbInterface; /*! \brief Base class for running sync plug-ins. * * This class hides the details of thread/process handling when sync client * and server plug-ins are run. Specific client and server plug-in runner * classes are derived from this class. */ class PluginRunner : public QObject { Q_OBJECT public: //! Plug-in type: client or server enum PluginType { PLUGIN_CLIENT, PLUGIN_SERVER }; /*! \brief Constructor * * @param aPluginType Type of the plug-in to run * @param aPluginName Name of the plug-in to run * @param aPluginMgr PluginManager instance for creating and destroying * plug-ins by name * @param aPluginCbIf Callback interface that the created plug-in can use * @param aParent Parent object */ PluginRunner(PluginType aPluginType, const QString &aPluginName, PluginManager *aPluginMgr, PluginCbInterface *aPluginCbIf, QObject *aParent = 0); /*! \brief Initializes the plug-in runner * * Creates the plug-in that will be run and a thread or process * for running it. * @return Success indicator */ virtual bool init() = 0; /*! \brief Starts running the plug-in * * @return Success indicator. */ virtual bool start() = 0; /*! \brief Stops running the plug-in * * Returns when the plug-in is stopped. */ virtual void stop() = 0; /*! \brief Aborts running the plug-in * \param Status error. * The plug-in is requested to abort. This function will return when the * abort request is sent, but the plug-in will continue running until * it has gracefully aborted. */ virtual void abort(Sync::SyncStatus aStatus = Sync::SYNC_ABORTED) = 0; /*! \brief Gets the sync results from the plug-in. * * Should be called only after success or error signal is received from * this class. * @return Sync results */ virtual SyncResults syncResults() = 0; /*! \brief Calls the cleanup for the plugin * * The plug-in is requested to clean up. */ virtual bool cleanUp() = 0; /*! \brief Gets the plug-in type * * @return Plug-in type */ PluginType pluginType() const; /*! \brief Gets the plug-in name * * @return Plug-in name */ QString pluginName() const; /*! \brief Gets the plug-in associated with this plug-in runner * * @return Plug-in instance */ virtual SyncPluginBase *plugin() = 0; signals: //! @see SyncPluginBase::transferProgress void transferProgress(const QString &aProfileName, Sync::TransferDatabase aDatabase, Sync::TransferType aType, const QString &aMimeType, int aCommittedItems); //! @see SyncPluginBase::error void error(const QString &aProfileName, const QString &aMessage, int aErrorCode); //! @see SyncPluginBase::success void success(const QString &aProfileName, const QString &aMessage); //! @see SyncPluginBase::storageAcquired void storageAccquired (const QString &aMimeType); //! @see SyncPluginBase::syncProgressDetail void syncProgressDetail(const QString &aProfileName,int aProgressDetail); /*! \brief Signal sent when the plug-in runner has finished * * Sent when the thread or process running the plug-in has exited */ void done(); //! @see SyncPluginBase::newSession void newSession(const QString &aDestination); //! @see SyncPluginBase::connectivityStateChanged void connectivityStateChanged(Sync::ConnectivityType aType, bool aState); protected: //! Initialization status of the plugin bool iInitialized; //! pointer to an instance of plugin manager PluginManager *iPluginMgr; //! pointer to an instance of synchronizer PluginCbInterface *iPluginCbIf; //! type of the plugin PluginType iType; //! name of the plugin QString iPluginName; private: #ifdef SYNCFW_UNIT_TESTS friend class PluginRunnerTest; #endif }; } #endif // PLUGINRUNNER_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncBackupProxy.h0000644000015600001650000000643712634332753023523 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCBACKUPPROXY_H #define SYNCBACKUPPROXY_H #include #include #include namespace Buteo { /*! * \brief Defines a D-Bus backup proxy for the backupclient * * A XML file describing the interface can be generated from this class using * qdbuscpp2xml tool. This XML file can then be used to generate interface * adaptor and proxy classes using qdbusxml2cpp tool. */ class SyncBackupProxy : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "com.nokia.backupclient") public: signals: /*! \brief Notifies about completion of backup. * * This signal is sent when the backup is completed */ void backupDone(); /*! \brief Notifies about starting of backup. * * This signal is sent when the backup is started */ void startBackup(); /*! \brief Notifies about completion of restore opertaion. * * This signal is sent when the backup is completed */ void restoreDone(); /*! \brief Notifies about starting of restore operation. * * This signal is sent when the restore is started/in progress */ void startRestore(); public slots: /*! * \brief Sets the required params and stops the servers and any running sync * sessions. * * This function must be called when backup is initiated, * @param message Received dbus message */ virtual uchar backupStarts (const QDBusMessage &message) = 0; /*! * \brief Sets the required params and starts the servers. * * This function must be called when backup is completed. * @param message Received dbus message */ virtual uchar backupFinished (const QDBusMessage &message) = 0; /*! * \brief Sets the required params and stops the servers and any running sync * sessions. * * This function must be called when restore is initiated, * @param message Received dbus message */ virtual uchar restoreStarts (const QDBusMessage &message) = 0; /*! * \brief Sets the required params and starts the servers. * * This function must be called when restore is completed. * @param message Received dbus message */ virtual uchar restoreFinished (const QDBusMessage &message) = 0; /*! * \brief Requests the current state og backup/restore operation. */ virtual bool getBackUpRestoreState() = 0; }; } #endif // SYNCBACKUPPROXY_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncBackup.cpp0000644000015600001650000001061612634332753023006 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncBackup.h" #include "SyncBackupAdaptor.h" #include "LogMacros.h" #include "UnitTest.h" #include #include using namespace Buteo; const char* SyncBackup::DBUS_BACKUP_OBJECT = "/backup"; const QString BACKUP_SERVICE_NAME = "com.nokia.backup"; SyncBackup::SyncBackup() : iBackupRestore(false), iReply(0), iWatchService(0), iAdaptor(0) { FUNCTION_CALL_TRACE; iAdaptor = new SyncBackupAdaptor(this); if (!iAdaptor) { LOG_CRITICAL("Failed to initialize adaptor"); Q_ASSERT(false); } QDBusConnection dbus = QDBusConnection::sessionBus(); if (dbus.registerObject(DBUS_BACKUP_OBJECT, this)) { LOG_DEBUG("Registered sync backup to D-Bus"); } else { LOG_CRITICAL("Failed to register sync backup to D-Bus"); Q_ASSERT(false); } iWatchService = new QDBusServiceWatcher (BACKUP_SERVICE_NAME , dbus, QDBusServiceWatcher::WatchForUnregistration); if (!iWatchService) { LOG_CRITICAL("Failed to initialize watch service : backup"); Q_ASSERT(false); } connect(iWatchService, SIGNAL(serviceUnregistered(const QString&)), this, SLOT(backupServiceUnregistered(const QString&))); } SyncBackup::~SyncBackup() { FUNCTION_CALL_TRACE; iBackupRestore = false; //Unregister from D-Bus. QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.unregisterObject(DBUS_BACKUP_OBJECT); delete iWatchService; iWatchService = 0; delete iAdaptor; iAdaptor = 0; LOG_DEBUG("Unregistered backup from D-Bus"); } void SyncBackup::backupServiceUnregistered(const QString &serviceName) { FUNCTION_CALL_TRACE; Q_UNUSED (serviceName); if (iBackupRestore) { // Should not happen ; backup framework exitted abruptly emit restoreDone(); } iBackupRestore = false; } uchar SyncBackup::sendDelayReply (const QDBusMessage &message) { FUNCTION_CALL_TRACE; if (SYNCFW_UNIT_TESTS_RUNTIME) return 0; // coverity[unreachable] //Suppressing false positives with code annotations message.setDelayedReply(true); if (!iReply) iReply = new QDBusMessage; *iReply = message.createReply(); return 0; } void SyncBackup::sendReply (uchar aResult) { FUNCTION_CALL_TRACE; if (SYNCFW_UNIT_TESTS_RUNTIME) return ; // coverity[unreachable] //Suppressing false positives with code annotations if (iReply) { LOG_DEBUG ("Send Reply"); QList arguments; QVariant vt = QVariant::fromValue((uchar)aResult); arguments.append(vt); iReply->setArguments(arguments); QDBusConnection::sessionBus().send(*iReply); delete iReply; iReply = 0; } } uchar SyncBackup::backupStarts(const QDBusMessage &message) { FUNCTION_CALL_TRACE; iBackupRestore = true; sendDelayReply(message); emit startBackup(); return 0; } uchar SyncBackup::backupFinished(const QDBusMessage &message) { FUNCTION_CALL_TRACE; Q_UNUSED (message); iBackupRestore = false; sendDelayReply(message); emit backupDone(); return 0; } uchar SyncBackup::restoreStarts(const QDBusMessage &message) { FUNCTION_CALL_TRACE; iBackupRestore = true; sendDelayReply(message); emit startRestore(); return 0; } uchar SyncBackup::restoreFinished(const QDBusMessage &message) { FUNCTION_CALL_TRACE; Q_UNUSED (message); iBackupRestore = false; sendDelayReply(message); emit restoreDone(); return 0; } bool SyncBackup::getBackUpRestoreState() { FUNCTION_CALL_TRACE; return iBackupRestore; } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/StorageBooker.cpp0000644000015600001650000000774012634332753023516 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "StorageBooker.h" #include #include "LogMacros.h" using namespace Buteo; StorageBooker::StorageBooker() : iMutex(QMutex::Recursive) { FUNCTION_CALL_TRACE; } StorageBooker::~StorageBooker() { FUNCTION_CALL_TRACE; } bool StorageBooker::reserveStorage(const QString &aStorageName, const QString &aClientId) { FUNCTION_CALL_TRACE; QMutexLocker locker(&iMutex); bool success = false; if (iStorageMap.contains(aStorageName)) { StorageMapItem item = iStorageMap[aStorageName]; if (aClientId.isEmpty() || aClientId != item.iClientId) { // Already reserved for different client. success = false; } else { // Already reserved for the same client. Increase ref count. item.iRefCount++; iStorageMap[aStorageName] = item; success = true; } } else { // No reservations for the storage. Add a new entry to the storage // reservation map. iStorageMap.insert(aStorageName, aClientId); success = true; } return success; } bool StorageBooker::reserveStorages(const QStringList &aStorageNames, const QString &aClientId) { FUNCTION_CALL_TRACE; QMutexLocker locker(&iMutex); bool success = false; if (storagesAvailable(aStorageNames, aClientId)) { foreach (QString storage, aStorageNames) { reserveStorage(storage, aClientId); } success = true; } else { success = false; } return success; } unsigned StorageBooker::releaseStorage(const QString &aStorageName) { FUNCTION_CALL_TRACE; QMutexLocker locker(&iMutex); unsigned remainingRefCount = 0; if (iStorageMap.contains(aStorageName)) { StorageMapItem item = iStorageMap[aStorageName]; item.iRefCount--; remainingRefCount = item.iRefCount; if (remainingRefCount == 0) { iStorageMap.remove(aStorageName); } else { iStorageMap[aStorageName] = item; } } // no else return remainingRefCount; } void StorageBooker::releaseStorages(const QStringList &aStorageNames) { FUNCTION_CALL_TRACE; QMutexLocker locker(&iMutex); foreach (QString storage, aStorageNames) { releaseStorage(storage); } } bool StorageBooker::isStorageAvailable(const QString &aStorageName, const QString &aClientId) const { FUNCTION_CALL_TRACE; QMutexLocker locker(&iMutex); return (!iStorageMap.contains(aStorageName) || (!aClientId.isEmpty() && (aClientId == iStorageMap[aStorageName].iClientId))); } bool StorageBooker::storagesAvailable(const QStringList &aStorageNames, const QString &aClientId) const { FUNCTION_CALL_TRACE; QMutexLocker locker(&iMutex); foreach (QString storage, aStorageNames) { if (!isStorageAvailable(storage, aClientId)) return false; } return true; } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/ServerThread.h0000644000015600001650000000501612634332753023005 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SERVERTHREAD_H #define SERVERTHREAD_H #include #include namespace Buteo { class ServerPlugin; /*! \brief Thread for server plugin * */ class ServerThread : public QThread { Q_OBJECT; public: /*! \brief Constructor * */ ServerThread(); /*! \brief Destructor * */ virtual ~ServerThread(); /*! \brief Returns profile that this thread is running * * @return Profile name */ QString getProfileName() const; /*! \brief Returns plugin that this thread is running * * @return Plugin */ ServerPlugin* getPlugin() const; /*! \brief Starts server thread * * @param aServerPlugin Server plug-in to run. The plug-in is owned by the caller * and must not be deleted while the thread is running. * @return True on success, otherwise false */ bool startThread( ServerPlugin* aServerPlugin ); /*! \brief Stops server thread * */ void stopThread(); signals: /*! \brief Emitted when synchronization cannot be started due to an * error in plugin initialization * * @param aProfileName Name of the profile being synchronized * @param aMessage Message data related to error event * @param aErrorCode Error code */ void initError( const QString &aProfileName, const QString &aMessage, int aErrorCode); protected: //! overriding method of QThread::run virtual void run(); private: ServerPlugin *iServerPlugin; bool iRunning; mutable QMutex iMutex; #ifdef SYNCFW_UNIT_TESTS friend class ServerThreadTest; #endif }; } #endif // SERVERTHREAD_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncDBusAdaptor.h0000644000015600001650000002336012634332753023416 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2015 Jolla Ltd * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCDBUSADAPTOR_H_1383642656 #define SYNCDBUSADAPTOR_H_1383642656 #include #include class QByteArray; template class QList; template class QMap; class QString; class QStringList; class QVariant; /* * Adaptor class for interface com.meego.msyncd */ class SyncDBusAdaptor: public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "com.meego.msyncd") Q_CLASSINFO("D-Bus Introspection", "" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \" name=\"com.trolltech.QtDBus.QtTypeName.Out0\"/>\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "") public: SyncDBusAdaptor(QObject *parent); virtual ~SyncDBusAdaptor(); public: // PROPERTIES public Q_SLOTS: // METHODS Q_NOREPLY void abortSync(const QString &aProfileId); QStringList allVisibleSyncProfiles(); bool getBackUpRestoreState(); QString getLastSyncResult(const QString &aProfileId); bool isConnectivityAvailable(int connectivityType); Q_NOREPLY void releaseStorages(const QStringList &aStorageNames); bool removeProfile(const QString &aProfileId); bool requestStorages(const QStringList &aStorageNames); QStringList runningSyncs(); bool saveSyncResults(const QString &aProfileId, const QString &aSyncResults); bool setSyncSchedule(const QString &aProfileId, const QString &aScheduleAsXml); Q_NOREPLY void start(uint aAccountId); bool startSync(const QString &aProfileId); int status(uint aAccountId, int &aFailedReason, qlonglong &aPrevSyncTime, qlonglong &aNextSyncTime); Q_NOREPLY void stop(uint aAccountId); QString syncProfile(const QString &aProfileId); QStringList syncProfilesByKey(const QString &aKey, const QString &aValue); QStringList syncProfilesByType(const QString &aType); QList syncingAccounts(); bool updateProfile(const QString &aProfileAsXml); Q_NOREPLY void isSyncedExternally(uint aAccountId, const QString aClientProfileName); QString createSyncProfileForAccount(uint aAccountId); Q_SIGNALS: // SIGNALS void backupDone(); void backupInProgress(); void restoreDone(); void restoreInProgress(); void resultsAvailable(const QString &aProfileName, const QString &aResultsAsXml); void signalProfileChanged(const QString &aProfileName, int aChangeType, const QString &aProfileAsXml); void statusChanged(uint aAccountId, int aNewStatus, int aFailedReason, qlonglong aPrevSyncTime, qlonglong aNextSyncTime); void syncStatus(const QString &aProfileName, int aStatus, const QString &aMessage, int aMoreDetails); void transferProgress(const QString &aProfileName, int aTransferDatabase, int aTransferType, const QString &aMimeType, int aCommittedItems); void syncedExternallyStatus(uint aAccountId, const QString &aClientProfileName, bool aState); }; #endif buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/PluginRunner.cpp0000644000015600001650000000364712634332753023402 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "PluginRunner.h" #include "LogMacros.h" using namespace Buteo; PluginRunner::PluginRunner(PluginType aPluginType, const QString &aPluginName, PluginManager *aPluginMgr, PluginCbInterface *aPluginCbIf, QObject *aParent) : QObject(aParent), iInitialized(false), iPluginMgr(aPluginMgr), iPluginCbIf(aPluginCbIf), iType(aPluginType), iPluginName(aPluginName) { FUNCTION_CALL_TRACE; // register various metatypes used in DBus arguments qRegisterMetaType("Sync::SyncStatus"); qRegisterMetaType("Sync::TransferDatabase"); qRegisterMetaType("Sync::TransferType"); qRegisterMetaType("Sync::ConnectivityType"); qRegisterMetaType("QProcess::ProcessError"); qRegisterMetaType("QProcess::ExitStatus"); } PluginRunner::PluginType PluginRunner::pluginType() const { FUNCTION_CALL_TRACE; return iType; } QString PluginRunner::pluginName() const { FUNCTION_CALL_TRACE; return iPluginName; } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncSession.h0000644000015600001650000002036312634332753022671 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef SYNCSESSION_H #define SYNCSESSION_H #include "SyncCommonDefs.h" #include "SyncResults.h" #include #include namespace Buteo { class SyncProfile; class PluginRunner; class StorageBooker; class NetworkManager; /*! \brief Class representing a single sync session * * The session can be initiated by a client or server plug-in */ class SyncSession : public QObject { Q_OBJECT public: /*! \brief Constructor * * @param aProfile SyncProfile associated with the session. With server * plug-in initiated sessions this can be null, if there is no profile with * a matching destination address. * @param aParent Parent object */ explicit SyncSession(SyncProfile *aProfile, QObject *aParent = 0); //! \brief Destructor virtual ~SyncSession(); /*! \brief Associates a plug-in runner with this session * * @param aPluginRunner The plug-in runner to use for this session * @param aTransferOwnership Does this session become the owner of the plug-in * runner instance. If so, the plug-in runner is deleted when the session is * deleted. */ void setPluginRunner(PluginRunner *aPluginRunner, bool aTransferOwnership); /*! \brief Gets the plug-in runner associated with this session * * @return Plug-in runner */ PluginRunner *pluginRunner(); /*! \brief Returns if the sync session is finished or in process * * @return Finished indicator */ bool isFinished(); /*! \brief Returns if the sync session was aborted * * @return Aborted indicator */ bool isAborted(); /*! \brief Starts the session using the associated plug-in runner * * @return Success indicator */ bool start(); /*! \brief Aborts the session. Returns when the abort request is sent. * \param - Status. */ void abort(Sync::SyncStatus aStatus = Sync::SYNC_ABORTED); /*! \brief Stops the session. Returns when the session is stopped. */ void stop(); /*! \brief Gets the sync profile used by this session * * @return Sync profile */ SyncProfile *profile() const; /*! \brief Gets the name of the profile used by this session * * @return Profile name */ QString profileName() const; /*! \brief Gets the results of the finished session. * * This function should be called only after the finished signal is received * from the session. * @return Sync results */ SyncResults results() const; /*! \brief Sets if the session was started by the scheduler * * @param aScheduled True if scheduled, false otherwise */ void setScheduled(bool aScheduled); /*! \brief Checks if the session was started by the scheduler * * @return True if scheduled, false otherwise */ bool isScheduled() const; /*! \brief Sets the results for this session * * This function can be used in error situations to set the results to this * session, even if the session has failed to run. * @param aResults The results to set */ void updateResults(const SyncResults &aResults); /*! \brief Sets the results for this session using the provided error code * * This function can be used in error situations to set the results to this * session, even if the session has failed to run. Time stamp for the results * is set to current time. * \param aMajorCode Error code * \param aMinorCode failed reason */ void setFailureResult(int aMajorCode, int aMinorCode); /*! \brief Tries to reserve storages needed by the session * * Successfully reserved storages are automatically released when the session * is deleted. * @param aStorageBooker Storake booker to use for reserving the storages. * If reserving is successfull, this booker is saved internally and used * later to release the storages when the session is deleted. * @return Success indicator. True if all storages were successfully reserved. * When false, no storages were reserved, meaning one or more of the needed * storages were already in use. */ bool reserveStorages(StorageBooker *aStorageBooker); //! \brief Releases storages that were reserved earlier with reserveStorages void releaseStorages(); //! \brief returns the StorageMap used for this session QMap getStorageMap(); /*! \brief sets the storage map for this session * * @param aStorageMap - storage map to set */ void setStorageMap(QMap &aStorageMap); //! \brief returns the returns the status of the profile creation for this session bool isProfileCreated(); //! \brief sets Profile Created flag to true void setProfileCreated(bool aProfileCreated); //! \brief Maps sync failure error code from stack to SyncStatus Sync::SyncStatus mapToSyncStatusError(int aErrorCode); signals: //! @see SyncPluginBase::transferProgress void transferProgress(const QString &aProfileName, Sync::TransferDatabase aDatabase, Sync::TransferType aType, const QString &aMimeType, int aCommittedItems); /*! \brief Signal sent when a storage is accquired * * @param aProfileName Name of the profile used by the session * @param aMimeType Mimetype of the storage accquired. */ void storageAccquired (const QString &aProfileName , const QString &aMimeType) ; /*! \brief Signal sent when the session has finished * * @param aProfileName Name of the profile used by the session * @param aStatus Status of the finished session * @param aMessage Possible textual message * @param aErrorCode Error code, if the status is error */ void finished(const QString &aProfileName, Sync::SyncStatus aStatus, const QString &aMessage, int aErrorCode); /*! \brief Signal sent when the sync is in progress to indicate the detail of the progress * * @param aProfileName Name of the profile used by the session * @param aProgressDetail Detail of the progress. */ void syncProgressDetail(const QString &aProfileName,int aProgressDetail); private: bool tryStart(); private slots: // Slots for catching plug-in runner signals. void onSuccess(const QString &aProfileName, const QString &aMessage); void onError(const QString &aProfileName, const QString &aMessage, int aErrorCode); void onTransferProgress(const QString &aProfileName, Sync::TransferDatabase aDatabase, Sync::TransferType aType, const QString &aMimeType, int aCommittedItems); void onStorageAccquired (const QString &aMimeType); void onSyncProgressDetail(const QString &aProfileName,int aProgressDetail); void onDone(); void onDestroyed(QObject *aPluginRunner); void onNetworkSessionOpened(); void onNetworkSessionError(); private: SyncProfile *iProfile; PluginRunner *iPluginRunner; SyncResults iResults; Sync::SyncStatus iStatus; int iErrorCode; bool iPluginRunnerOwned; bool iScheduled; bool iAborted; bool iStarted; bool iFinished; bool iCreateProfile; QString iMessage; QString iRemoteId ; StorageBooker *iStorageBooker; QMap iStorageMap; NetworkManager *iNetworkManager; #ifdef SYNCFW_UNIT_TESTS friend class SyncSessionTest; #endif }; } #endif // SYNCSESSION_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/UnitTest.h0000644000015600001650000000234112634332753022164 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2014 Jolla Ltd * * Contact: Valerio Valerio * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef UNITTEST_H #define UNITTEST_H #include /*! * \brief A dirty hack to allow modified behavior during unit test execution. * * If you are writing a unit test, \c include(msyncd/unittest.pri) in your * project file. */ #define SYNCFW_UNIT_TESTS_RUNTIME Q_UNLIKELY(__SYNCFW_UNIT_TESTS_RUNTIME) /*! \cond __false */ extern bool __SYNCFW_UNIT_TESTS_RUNTIME; /*! \endcond __false */ #endif // UNITTEST_H buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/SyncDBusAdaptor.cpp0000644000015600001650000001621012634332753023745 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-syncfw package * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2015 Jolla Ltd * * Contact: Sateesh Kavuri * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "SyncDBusAdaptor.h" #include "synchronizer.h" #include #include #include #include #include #include #include using namespace Buteo; /* * Implementation of adaptor class SyncDBusAdaptor */ SyncDBusAdaptor::SyncDBusAdaptor(QObject *parent) : QDBusAbstractAdaptor(parent) { // constructor setAutoRelaySignals(true); } SyncDBusAdaptor::~SyncDBusAdaptor() { // destructor } void SyncDBusAdaptor::abortSync(const QString &aProfileId) { // handle method call com.meego.msyncd.abortSync QMetaObject::invokeMethod(parent(), "abortSync", Q_ARG(QString, aProfileId)); } QStringList SyncDBusAdaptor::allVisibleSyncProfiles() { // handle method call com.meego.msyncd.allVisibleSyncProfiles QStringList out0; QMetaObject::invokeMethod(parent(), "allVisibleSyncProfiles", Q_RETURN_ARG(QStringList, out0)); return out0; } bool SyncDBusAdaptor::getBackUpRestoreState() { // handle method call com.meego.msyncd.getBackUpRestoreState bool out0; QMetaObject::invokeMethod(parent(), "getBackUpRestoreState", Q_RETURN_ARG(bool, out0)); return out0; } QString SyncDBusAdaptor::getLastSyncResult(const QString &aProfileId) { // handle method call com.meego.msyncd.getLastSyncResult QString out0; QMetaObject::invokeMethod(parent(), "getLastSyncResult", Q_RETURN_ARG(QString, out0), Q_ARG(QString, aProfileId)); return out0; } bool SyncDBusAdaptor::isConnectivityAvailable(int connectivityType) { // handle method call com.meego.msyncd.isConnectivityAvailable bool out0; QMetaObject::invokeMethod(parent(), "isConnectivityAvailable", Q_RETURN_ARG(bool, out0), Q_ARG(int, connectivityType)); return out0; } void SyncDBusAdaptor::releaseStorages(const QStringList &aStorageNames) { // handle method call com.meego.msyncd.releaseStorages QMetaObject::invokeMethod(parent(), "releaseStorages", Q_ARG(QStringList, aStorageNames)); } bool SyncDBusAdaptor::removeProfile(const QString &aProfileId) { // handle method call com.meego.msyncd.removeProfile bool out0; QMetaObject::invokeMethod(parent(), "removeProfile", Q_RETURN_ARG(bool, out0), Q_ARG(QString, aProfileId)); return out0; } bool SyncDBusAdaptor::requestStorages(const QStringList &aStorageNames) { // handle method call com.meego.msyncd.requestStorages bool out0; QMetaObject::invokeMethod(parent(), "requestStorages", Q_RETURN_ARG(bool, out0), Q_ARG(QStringList, aStorageNames)); return out0; } QStringList SyncDBusAdaptor::runningSyncs() { // handle method call com.meego.msyncd.runningSyncs QStringList out0; QMetaObject::invokeMethod(parent(), "runningSyncs", Q_RETURN_ARG(QStringList, out0)); return out0; } bool SyncDBusAdaptor::saveSyncResults(const QString &aProfileId, const QString &aSyncResults) { // handle method call com.meego.msyncd.saveSyncResults bool out0; QMetaObject::invokeMethod(parent(), "saveSyncResults", Q_RETURN_ARG(bool, out0), Q_ARG(QString, aProfileId), Q_ARG(QString, aSyncResults)); return out0; } bool SyncDBusAdaptor::setSyncSchedule(const QString &aProfileId, const QString &aScheduleAsXml) { // handle method call com.meego.msyncd.setSyncSchedule bool out0; QMetaObject::invokeMethod(parent(), "setSyncSchedule", Q_RETURN_ARG(bool, out0), Q_ARG(QString, aProfileId), Q_ARG(QString, aScheduleAsXml)); return out0; } void SyncDBusAdaptor::start(uint aAccountId) { // handle method call com.meego.msyncd.start QMetaObject::invokeMethod(parent(), "start", Q_ARG(uint, aAccountId)); } bool SyncDBusAdaptor::startSync(const QString &aProfileId) { // handle method call com.meego.msyncd.startSync bool out0; QMetaObject::invokeMethod(parent(), "startSync", Q_RETURN_ARG(bool, out0), Q_ARG(QString, aProfileId)); return out0; } int SyncDBusAdaptor::status(uint aAccountId, int &aFailedReason, qlonglong &aPrevSyncTime, qlonglong &aNextSyncTime) { // handle method call com.meego.msyncd.status return static_cast(parent())->status(aAccountId, aFailedReason, aPrevSyncTime, aNextSyncTime); } void SyncDBusAdaptor::stop(uint aAccountId) { // handle method call com.meego.msyncd.stop QMetaObject::invokeMethod(parent(), "stop", Q_ARG(uint, aAccountId)); } QString SyncDBusAdaptor::syncProfile(const QString &aProfileId) { // handle method call com.meego.msyncd.syncProfile QString out0; QMetaObject::invokeMethod(parent(), "syncProfile", Q_RETURN_ARG(QString, out0), Q_ARG(QString, aProfileId)); return out0; } QStringList SyncDBusAdaptor::syncProfilesByKey(const QString &aKey, const QString &aValue) { // handle method call com.meego.msyncd.syncProfilesByKey QStringList out0; QMetaObject::invokeMethod(parent(), "syncProfilesByKey", Q_RETURN_ARG(QStringList, out0), Q_ARG(QString, aKey), Q_ARG(QString, aValue)); return out0; } QStringList SyncDBusAdaptor::syncProfilesByType(const QString &aType) { // handle method call com.meego.msyncd.syncProfilesByType QStringList out0; QMetaObject::invokeMethod(parent(), "syncProfilesByType", Q_RETURN_ARG(QStringList, out0), Q_ARG(QString, aType)); return out0; } QList SyncDBusAdaptor::syncingAccounts() { // handle method call com.meego.msyncd.syncingAccounts QList out0; QMetaObject::invokeMethod(parent(), "syncingAccounts", Q_RETURN_ARG(QList, out0)); return out0; } bool SyncDBusAdaptor::updateProfile(const QString &aProfileAsXml) { // handle method call com.meego.msyncd.updateProfile bool out0; QMetaObject::invokeMethod(parent(), "updateProfile", Q_RETURN_ARG(bool, out0), Q_ARG(QString, aProfileAsXml)); return out0; } void SyncDBusAdaptor::isSyncedExternally(uint aAccountId, const QString aClientProfileName) { // handle method call com.meego.msyncd.isSyncedExternally QMetaObject::invokeMethod(parent(), "isSyncedExternally", Q_ARG(uint, aAccountId), Q_ARG(QString, aClientProfileName)); } QString SyncDBusAdaptor::createSyncProfileForAccount(uint aAccountId) { // handle method call com.meego.msyncd.createSyncProfileForAccount QString out0; QMetaObject::invokeMethod(parent(), "createSyncProfileForAccount", Q_RETURN_ARG(QString, out0), Q_ARG(uint, aAccountId)); return out0; } buteo-syncfw-0.7.21+16.04.20151216.1/msyncd/StorageChangeNotifier.cpp0000644000015600001650000000677712634332753025173 0ustar pbuserpbgroup00000000000000#include "StorageChangeNotifier.h" #include "StorageChangeNotifierPlugin.h" #include "PluginManager.h" #include "LogMacros.h" #include using namespace Buteo; StorageChangeNotifier::StorageChangeNotifier() : iPluginManager(0) { FUNCTION_CALL_TRACE; } StorageChangeNotifier::~StorageChangeNotifier() { FUNCTION_CALL_TRACE; StorageChangeNotifierPlugin* plugin = 0; for(QHash::iterator storageNameItr = iNotifierMap.begin(); storageNameItr != iNotifierMap.end(); ++storageNameItr) { plugin = storageNameItr.value(); if(iPluginManager && plugin) { iPluginManager->destroyStorageChangeNotifier(plugin); } } } void StorageChangeNotifier::loadNotifiers(PluginManager* aPluginManager, const QStringList& aStorageNames) { FUNCTION_CALL_TRACE; StorageChangeNotifierPlugin* plugin = 0; iPluginManager = aPluginManager; for(QStringList::const_iterator storageNameItr = aStorageNames.constBegin(); storageNameItr != aStorageNames.constEnd(); ++storageNameItr) { if(iPluginManager) { plugin = iPluginManager->createStorageChangeNotifier(*storageNameItr); if(plugin) { iNotifierMap[*storageNameItr] = plugin; } } } } bool StorageChangeNotifier::startListen(QStringList& aFailedStorages) { FUNCTION_CALL_TRACE; bool success = true; StorageChangeNotifierPlugin* plugin = 0; if(!iNotifierMap.count()) { success = false; } for(QHash::iterator storageNameItr = iNotifierMap.begin(); storageNameItr != iNotifierMap.end(); ++storageNameItr) { plugin = storageNameItr.value(); if(plugin) { QObject::connect(plugin, SIGNAL(storageChange()), this, SLOT(storageChanged())); plugin->enable(); } else { aFailedStorages << storageNameItr.key(); success = false; } } return success; } void StorageChangeNotifier::stopListen(bool disableAfterNextChange) { FUNCTION_CALL_TRACE; StorageChangeNotifierPlugin* plugin = 0; for(QHash::iterator storageNameItr = iNotifierMap.begin(); storageNameItr != iNotifierMap.end(); ++storageNameItr) { plugin = storageNameItr.value(); if(plugin) { QObject::disconnect(plugin, SIGNAL(storageChange()), this, SLOT(storageChanged())); plugin->disable(disableAfterNextChange); } } } void StorageChangeNotifier::storageChanged() { FUNCTION_CALL_TRACE; StorageChangeNotifierPlugin* plugin = qobject_cast(sender()); if(plugin) { LOG_DEBUG("Change in storage" << plugin->name()); plugin->changesReceived(); emit storageChange(plugin->name()); } } void StorageChangeNotifier::checkForChanges() { FUNCTION_CALL_TRACE; StorageChangeNotifierPlugin* plugin = 0; for(QHash::iterator storageNameItr = iNotifierMap.begin(); storageNameItr != iNotifierMap.end(); ++storageNameItr) { plugin = storageNameItr.value(); if(plugin && plugin->hasChanges()) { plugin->changesReceived(); emit storageChange(plugin->name()); } } } buteo-syncfw-0.7.21+16.04.20151216.1/oneshot/0000755000015600001650000000000012634333235020412 5ustar pbuserpbgroup00000000000000buteo-syncfw-0.7.21+16.04.20151216.1/oneshot/msyncd-storage-perm0000644000015600001650000000075212634332753024245 0ustar pbuserpbgroup00000000000000#!/bin/sh DEF_UID=$(grep "^UID_MIN" /etc/login.defs | tr -s " " | cut -d " " -f2) DEVICEUSER=$(getent passwd $DEF_UID | sed 's/:.*//') if [ ! -d "/home/$DEVICEUSER/.cache" ]; then exit 1 fi storage_dir="/home/$DEVICEUSER/.cache/msyncd" mkdir -p $storage_dir chown privileged $storage_dir chgrp privileged $storage_dir chmod 770 $storage_dir sync_dir="/home/$DEVICEUSER/.cache/msyncd/sync" mkdir -p $sync_dir chown privileged $sync_dir chgrp privileged $sync_dir chmod 770 $sync_dir