From dd0008f6ab9d05df5f3a743357a6e2567bdeae01 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Tue, 12 Jun 2012 14:42:52 +0000 Subject: [PATCH 01/21] Fixed version Monotone-Parent: ea6bc80cdabf55c83e6df32dd1aaa4ecfa16a6e1 Monotone-Revision: 8d523fe7796cff9d335706a89105b900379bfa52 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-06-12T14:42:52 Monotone-Branch: ca.inverse.sogo --- Version | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Version b/Version index 0de612ba1..09503ebca 100644 --- a/Version +++ b/Version @@ -2,6 +2,6 @@ # This file is included by library makefiles to set the version information # of the executable. -MAJOR_VERSION=1 -MINOR_VERSION=3 -SUBMINOR_VERSION=17 +MAJOR_VERSION=2 +MINOR_VERSION=0 +SUBMINOR_VERSION=0 From 07cc5e8cf8f66a5ce0ad7d3b3fba676b9dac79c9 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 15 Jun 2012 13:33:21 +0000 Subject: [PATCH 02/21] Monotone-Parent: 8d523fe7796cff9d335706a89105b900379bfa52 Monotone-Revision: b664e2e19975c70a7614aa975a3a045f0bec7ec3 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-06-15T13:33:21 Monotone-Branch: ca.inverse.sogo --- OpenChange/MAPIStoreContext.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/OpenChange/MAPIStoreContext.m b/OpenChange/MAPIStoreContext.m index 17907e93b..1817658ea 100644 --- a/OpenChange/MAPIStoreContext.m +++ b/OpenChange/MAPIStoreContext.m @@ -294,9 +294,11 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) [MAPIStoreUserContext userContextWithUsername: username andTDBIndexing: indexingTdb]); +#if 0 mapistore_mgmt_backend_register_user (newConnInfo, "SOGo", [username UTF8String]); +#endif connInfo = newConnInfo; username = [NSString stringWithUTF8String: newConnInfo->username]; @@ -315,9 +317,12 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) - (void) dealloc { +#if 0 mapistore_mgmt_backend_unregister_user ([self connectionInfo], "SOGo", [[userContext username] UTF8String]); +#endif + [contextUrl release]; [userContext release]; [containersBag release]; From 988ca2d853e7563e99c74242507db9bf602aa0f0 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 18 Jun 2012 17:50:31 +0000 Subject: [PATCH 03/21] Monotone-Parent: 6748af72ecdd0c613acb968eb261448ae6b6982d Monotone-Revision: c69af3e4b83d5cab3a52701fb736c8a149697338 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-06-18T17:50:31 Monotone-Branch: ca.inverse.sogo --- ...Native Microsoft Outlook Configuration.odt | Bin 54208 -> 27917 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Documentation/SOGo Native Microsoft Outlook Configuration.odt b/Documentation/SOGo Native Microsoft Outlook Configuration.odt index 8b38028861faff71f12c9d13cfc555608f567b8b..a350de392a22f7c89e179aeacef624755b9329af 100644 GIT binary patch literal 27917 zcmaHS1F#^?mgco>+jjT0ZQHhO+qP}nwr$&1-|hd+>@4Q(R>W7CStq`O%#4ai963o~ z5EK9a2mpXUuLVAx5hfUN004l0@b?pdwYjyile?X-rl*qS;RJN_Tx{&(iTv*=`N zYh|ZzV{G;RvH0)e`^U-uy>tJ&wSUL@$J+m&h5v7&Gq=_^HFl&IFn6-nw{!eYM;fpFOVZ3xE>|+HR!*QPzq&cUDFg-*N1{XUi}$1On4P)DpBn6P@$LIF;uFzp zL`;PI3yB07Md=39HFwb%QOrn0L}Y}U*!QPaRxy_QL9ET+x681plQZhmWv}heo7?H( zcjwCfOmEIt>@Ij=yyYV?d0isq>tPD+8{=1M|7y?g1lw5GgfU(yn*9jk-3$kNluc3o zvG1qKLW4_UEJ+^3`>n-4Sy<@3G4G01pNuWE-j=Y>q9r54wb^4C=RQ%^faSxpqW(9d zvICaUKz$!9O$M%j5#+Paytubg2fw*4oJ<%;f+$u8Om0PWwMpoySsbF z{CRlEI%rj-&Z$yM9Gn4n6aVpQn=&WNxQGe1mMm)^{j1Vrq&3U!p7)=pq8H(ey@~U@r0&22p@wPF zzG#2+J6ReqO9#A#$Es_kaMDya>22S`1H-9IUZjogbwN9!S$T0^mM~T;EBu&{u&D$n z4r~Z9NHn;1l8Z5e=iE6I1o3y_FdjNvD6J5E%|QRRHTZ6`90|apA~C8Ep*G0tpE1}>YD-4PPS;aIV^5~1C6ZLNk1+njbjj^( z0EvV9P|aIaLF}%YK0yynQ#`zjTDhdvCYB?<3aZ->i=Eq1YDSQa6)-k#yX@9F$XsY& z>s4PFfJl$~d;N1rOBTU{_!i#n)`R_l`n+TSn>Ogi({Q~5?#Rtjg$Ck#NyVWkwL^Hf z*>m;kGg;Is&d11uAL5(8I-|#j4Xp<8nohD>H8ek}1vMds!JhOUdoFmFb>8AS;w;!5 zUQifc&5(_<>hhOmel{4qC!1O6TvbGU>8vU|tw{~S>;TmLH&ulS(mr!k%`Z1v2OEfn zitZLb7F-Pru!Cz>K`DL_n3M3y+}`y}3g@N?h%yc!RcwQZmZ4&>KtpGBwv3KNBEplD1` zeFRY>HMMR*f6_GHxylkvxp;XwmgD^hb?CIPnr z1U~+d8abR`##mAmP2onnyW~VJqK_$d-;7bb&_v|fN0US)t*{1s>o2&}4opi35TI2k z5U&2i14d`{cD_NQA&4WSOrxW;bu=`-%aTv7ZeQZvE4gO9@!>TE{s#jAn3eTaA91(c zdr0$(AZFwbTDh%BybJJO8~``H6c&P$@iOa9$gqHsXNJTS%K!#I52h+UFWvyy4AxnM z-H-v&xzlZ1JA=aVT`5t=s^^y%wv$Ra_s%PRJ*&Evfz>Nim?JhY3ZlN%8{2_C=G`d1 zNzh5W$}kT1_BcV4@S*%Tg}E>sx7X{6WI(gdYv|e^y2O<^ST-MTTw7qdubJ`c{3{=e z6c`r953JKv56|<1ht(u{@ADEsI?v7``ISAhSY>!m@jPo_=czSest$uN(v-kjImph< zCNobypX`|1)@06PgYpOcS3DS&lvJ7RtU-7IcIW&YVgy>}*MdHq>uVL{n$9U6w^f0{ z=#-k2CBj8%^npp}88<2x6)hyRlockPc5Oe<=7%FH8mE|Pw#}!%F}@TWy>bT1Go)zi zKnhjNq8HK70cI0puHtsf+(>3h;qGP^*_Dyuf7_Jv&&X7ac;#TVJ4{KV2u9org$opJ zey3)W*36lkdD<3x<)_1PBp=;Tj|;>?vhZPQQgRiuTWQ4U4^_)n6XJs+j^2zmw`{9) z@T10L<`tnXx>Eu%w=NJuCF;`C=63%+S@9tQ-Ti)6U(BixY4Gg@;gJZ7<~Ww1whttH z}9&MhK59y(z4bU!*i3ZkwfeH@9y-F% z@{dWs)C(Xg?_Q=wQ88zFa?KWBN;Hr(lXO({NtbT1YV2Wo0>2*o1MstYefv7q9e6^< z9Faniw$!gRqFR=5YYZJPt?u2xbt(E~tf@l{0|JL8HE-J8_{Mgi6P&;aN%T4j(D)9O zTR2%z!0!yN_$wlyY>%jw`xnhFUs`Y${W@%ztu)@+UP_Ck*!?nQ@hnnJl@#&c2zP`RGKxrS{woqJ3Z z9eFDyjYifK&v0+Ap5s5G9&9~1t_+XGurzywd~vj?AKhjJ8ZV{xC;)=8>g>n$6VI}p zM=_N#d}HfH+5_n0OR~k(Uy$A;XFrJu|`UR4FbO(8jO!|V|7+^D8 zpZ;2D^Y$@9enZeA^(+D7Oz6MYK}rQ_L4FALeR41xzT{qx+K8U2+^NM3ko@s{g_uB- z19YfAHy)7y%Zf%8&UKle7|{XA2(fz84C$z9Oih+VVBu7`KXYX)!% zYx9>mg90bK8s60X)?=G8O6^aHA$Cn0%IZhYCNLl{*qYB8MmEdABhW9J8DxeN6jtmH zK>T*|eU9%21{~X|2tnFT?NjsacUA8w6WKJHYd)S$2YHYpV|PwRCn6gdiv$y)R<8CTJgJsRKx9o?sONr>$A?=>&5)4>I_z? zkffpg%Z(q9q2pXNs29!nSF-`!n%_Fzp!^ht+OZmY?S;C{rt^6+-CU|2A&c(IRa)S zpf@Z+XEPrSh(FV??VtjWVgEENw2cbwTSxLIc)%pYjWwTLEg^_%LF7}V#8T4_oZRBB zU&fzWbXHGnxyIK^;psnMOIJT0@{VAL#sG?Rn7YSA1}Wvzin?TUYC3;;Ca484bzQ{op4OBr(_ z`gwa)4!WO~yiWB@w9Paq#RDeC!7vXLp7jmBX3hh&4?kp#g!Po! zFTD`nls3F{Ty2PW0PH|o*K--|^O>D4 z8VxS>2Z>FIjO$05a0Y};t}alPYuY*L7z6jKf!PR5YaE5hdbE%&`uei-BvG~$+%`#p zDsSN@f2?TZ(dNG2WBn8zPDL?t)rw1Z_OcaZ$r-;Sc_v9w1pyi6@Ar2PRSgxSOlnT+ z(~rFk9tr$!3J5S|DFpV=j=2k_=&s37hwfE}=N2^FYEDNm+R_I*5KQ4ki1$?f&MZ(j zT3HYb{6{447OVjb@numtGvZ*x_5&s<6i@*IiqwLlL>i`)P{g=cTV5@0p8d0Y#gQyzm z=WQ7apE%Hbv{Fs}_hUf<>J~ov$7;bpLLp(KYy*CT3oSHE!$H>KP5K{VV)8<9j(#U> zonffQOn@g4RopDcYlI_Exa0={b%Y4%P`D42Reob^C{{IL(N&^V!eAk*u3lpy@(hB6 z5!IoNart76DjoS&v%(`ZNS*^U{X8JN10v09-$4Q8g1;Qj1|?Ziz1|woAd4Zy6#b1f z@I!>na|9m5`)6owug_aM?{>%Xd3T64&gfqUhhh2mb}D&^;4J*~zQ$1*WsVO9&!_1+ zKkevs4byqISfKI)n%9;-f?(sL!}^HlN7_hFFeshf&Oja618Ay8i8U}c1{GnzGzt5x zB+He3KZxfHkvgAv$lsOa;ZQRQOt#zic?A+=mHX#iWuka_c;$b<6$z5m1ZVKI;}0c!lLc&&Upq z$SLh5M1`R70oUe_zGkEfu=&(KW#{bzs+e21NSUJF05Nz9+2GF>CmDpquDUe`hk1|# z{Ggvsvm&!fl=+x|benRQ^oe*sUL*pIp+~0_G)Hz263t?p{k`?^m6Y{vx!2#%GCNkJZF9G@ zV)Rm*Et9~vb*&J47*dUaUCJw2tQ{mNB~~Z;<*8A0@1E*{56fk)}!G>KN;s8%P7_yu>X_fZzadK9N?* zZ$mju4YXp0MV>MOYU|#tyV<4Ak;tq$@~OvXaPOCz@48CN!9b`O4t6LoSIfh#CX{FK zcL=BE^CEkGhD2LFsFYaL$<&EFOaZd9OpG#1?L53z_0PwJj#0a^agO5@wiRcz25wz0qtvYm@^=K)Q*uPt5}a)Wf6uX?-aEm3$JO#I@Avaf zXwW16``_9%YxU%){=q0Db5sip@H7K34%=hgr`OR)dy7Wi1txw%Es%88 z4_%j#ew~*LCG@pN`aw<7i-!u7EUH&~@9LYBW6tt*>$t|Jdg*)E zk7^ujDO&lrwLB4Np-E|UsAhbJh4XCEN%^2@Nk$;Vd~OlU>Tn0T7Rw7rl$n6exXte) z($@qTuw(k{*>D)AxEKwEI<55!(J_(|XnPKRBB-%M?_Xn^kozsLA+n->AR$uSgch;P z;7bh^A!c1EQNKySEJ^bNp%;(x^neOqoq9}`n!<1DHg5_Qb_8gFi~0H;QV9$N4RK21 z-XTdD#lX%S-49HHe^ZdknJrFh$V_m@3^)l=>m9TBqoD&4jJ{#zqz?f^t?ytnY_?b( z`RCqgkQmw1WQ`(oH4FZjvwy{h8>QWB)<=SW>03r|{TT|W@iUdtD-`X1CgBP}PxtFF zWuCHgNwHSER=r@7qP-&1hdt&}2C-9zZ4n;&qMDMTo;SRB)b?tUcrIo=G zYdd&&J6-)gr_-Zs>~J^qH+_+VOaU_n!%xLH6jL;GFR6BvRY&V8+!}iBv`U7F5X2n( z1Z{$Nnd&pedn=gzF&3t*8k@!Ktq}~fCiW#WM*wSuRlS;vF`}=4ZEw%w0%lgs@e4rB zF#mJ%QBeDivPbS#$3%|S>=Vx$`Pl=+r+U9;q802k?gRm#;km-y8POzkI%8hW1iH37 z!4B5fZH;kz&|0(BygIx77KKfj3t3QZ;I&vi@fC$GbYm1#zLJ z{=R@!LgG@8Nie80+a2ld91(2A!f3G6Gjlsg#X!k4EUf{qIkP~~hG)T2>~)X=b3%%d zd%ucBk=kSlXa3uOT+nLcF|qS6%6dP$sH(AJtbHYEyui5dAxJ)TecDWMo$+h&KA%TH zq#<28Bz17UINLSbkvn+vH5nJLml9}8Z3ewHB8rYZvn;!nqXHA1(fxLk z1@aUvviMjdL#tsdn;xGZI~VWgn}hGc>GN+@()ph8$ohs^-W^|O@8&ga!LhAGB6CtT(>aS z498cusEwqXgI4%Xb9xrVHz8g(>#x0cP4H$L*3tU8wT4N{57L<)x`Jxg{FzhGV0|6( ze5>;EGl<}ueRvg9TmC|gU8I|WB0KU~HDj#NdDiLCV zH!|tD)i&)U%sHqneEOa`qGFtr8hb*Rmaxsn@uaCm1Q#zY&7!=seOOpVdYHT{)-WoZisngmD!vz0BYB!Stho`v)u2nS;RT$;}z#vBvRoog6(eA73hqc5xvApX-?V+;DXYcP; zH&!w>tMfw^P>KRS7FMAyL;3@7GD)(p<`O==qOY|gVrln<|GmB>Sg>GfH-}=(!TV)l zv0|>+$Kwdj*k|%x8kzbfMwE=KI}Nab1m9YvTu61M#t$$o%J+?{AITfJsG=hUyHz^& zXlqvaC4FHKL>apn7&Zaqm7NmckWV2WhPE_syN<2Mb5@GWj&`HJ4rY|Lzl7F2160ll z>X?;FCnl5ArjqUVg*q)EGC9Qo!Gwc8_Mz#>4#!YMR4OXV#jj%=rWfqt3x6_|+rU0c zxcJaZgmlq6=!C+-?>FB!;cgF!bkP~yOcDaatyB)Gif@d+RdXO z4{D5*EDYtCQL4SyIt~L+Shf};?#B7_YCU#I$(NDhM#JdviyaLzM?uCfC?!0Wh>7N? zy^m+Au>d;*?VG?>up>r{h0ATzCZ4TQkvY*)gd( zPXo-!I2mL@Wx`c(+b}LNXKKp|N8hQl2-_g9@}NtWa)rqB@#qk&$A(Lghi1jjmdDQ6 z9QmXTkc>98HUh`MB9l^o6C3(@sp4u-Qu%g{wuI zRl#a$BRq~Fd}DbUtr|0TZ@Zi`b+EG7n~dia#XIU@srN^&tc;r$rM*~E_vhBk9&0GA zXRUVo)}h~zs;l@j*cVQ*9NIYv9F#$o6!1ng1xhwED$vB#4+-V|%p1IQ9^b%)owL$A z*w>gYwm*8ly|!1Ig+1HM@L+;FC~PmSz+6WO=_mlVE}dPQ*cyf2i|hC>Q{Ue;_I=gB zJZroxB7^&3nV%-Na)m1vh32Xvy5#tNOz&nQ$dYA`RPGte5SWz{(?!n-!?b5to!GWA zKI*)V*9Q74@8;gG)|k<`B2P2cfKUVubre)}%f>IY2r~1?&D*Boa22 z4kEr_ML)rloVRXf59rUFD*ot&e?%VbV7S9)klmi1U0OC-R2MCw3L9SJVyO)C10_MV z5l)Eip-WiPq#&p(_J|a=2x2CAB8UN=-E>Cr+GErA7g5tmmNG6H{YWQOB-Wc>U6wt5 zatPS!ik@8)!<*lc*s0o#kWTjcJN9j7a0avWmjEkXx`Xy+iN5iSNK?FtAuirI~|yhTR&GZfrdg&lET+i z*y^VBwaF3su%CMk0t{cIDY;%^{u!X9u2w;qnBIAK?hqO)9yrvKivn^V;&S;0F%O3m zpjAduBt6DeZ?IC{E=IM>`Vdl&2wI4$Xp5s0SDWI zTmdJ`O+}4RIrWn*vTv}KFpMyNo<7V)(JU6ndt-Xl9gieKBinNcXXH?0aHx zCWIx9J}xzRUx3J{UcYv>x3kAH9ydTz45Eerj->w`{fL1uycr2)11cz<>a>w+6X9O@ zXrah#H5Lcv;mE!}n!|)pow)W2yUu*!A!=y_lu3OBo=!fFA)$ryiV?1&mUG*stRX#y z#*VIpsPf{#bj8I4+czC9)}Ua&0Y?$@=SCLJt*3siQ)X>Y@JLBuCMLv&K(rn}N>RmK zajBoJEl=1gG*PNUuFjrW5@F=I*ZnX$G!YZEICmC$H1P03zPIBB70})q7nC)a3wZXg zDI4M6CR|w>`eTLLT-FVYkqHMv4rifUl#0{$Y5u>hX4Z<_juzC`VL@4xlLempN)0Yx zTpz)EAfS73?h&F{Di$q2qI6RLG9L0?V6qEeP&T+w*Yl^Fs@Tf{5z%@Yhdi2}^iDyJ zP3=psPgEWra;CueEP-MNLa<8pD@DW&brS(5%K;CptNT=A#onT92sQN%3}RHV+SIJ4 ze@JZC#gd85_bE`KwUlrUNNIc1Y}(0&MNaS9WsF+Mi94n1Spz14VFl`ZMXfs$sRD3^ zyh=^vQ)y>{mxyf?OBs-sWjh5WX3%5}{aKb8bIKKW$FEpB_}mGRGbAV14QW%GdGvw| zug;lvM%E%_(o6A{-KnypYJX$klk#XUB&c}CoZT=p6^CtWvau82#S~9}Zc0I`yY;Ij z*}kkbYz!rQJedhU)Urj%E3B9x-u`6BdGu~eKCGEw;yq-I?qwBO^ z^cg6L|C9J<6rrz4cocq|*<{J!%k+y^xmc}t~PZ~eFEAC!PI z;o22)Yl57%=@ge_j1C{sdNxc5z%73h1i;eE6DvCEa;0`!=q6~-#PlT$seO7_?Y%oX zV9S!s`uyY^@U5}g*E*uLFvM$PWvFtu@4t&)=gjWPjw+gnKaaA$gZ(Wxn6IN1F^y1P z!je|4Zqc$8*Ty$M)#S8wH)4&KHSFWXgE zOj}GL?+x^D7AW6fqW%D3B$Vg+xA4FfyhHahPz=_!0sup4LvIMv2Ll&sg6a7J`t3#- zy=~3{{N1NN)B}uaoPQ&Oy0Za22l=720w-k`Qbt)X`enGOcU=JH)hoNwjCLspcE-~m{{nDUnB6X9`7 zgkijUpu5AVuAE|hHXs>FrS56g{auub?>J2P-NHraf(>&2V6Qu5_gZf06#29GqG-L8 z@TvmjYX7*ue{-1mx%v2LTn2_j^(dUjVfCyk;Oy-ET!+@c4XJ)BOn_bGfAzKfd>zW^ z;`!&#!OfqSSZcz{UI?Q+qgt@*kL}LM$Vx|-kC$uOFx!`tkB`fXz+-^joyk7aD*sw! zoJp>!OEart^hH;zpEZXph}#O}H{!aGr`=XBBT_sTyy!YhC1hfnmOo(3vid-u^vr;b z4SDHVecS7*r?$L0IX^Mof(2fW_caFJu;IIbserYuU|o;AILd{;#bTJh?@^e$@2~{Y zmn6}H8ddRky}CMn);f4RqszFfpg&=C(%RLZb%8&KwbfLou~E^ntbmuXs+|Te2du^W zX2;^?)=BWxZ+yjDsHwc}gm9_vBS(e7%UeQoap_$Pe% zR{tzS>lTlv-eOKIR-(GyS)m9qciEG78(}FL*X4+3(6r7mZY$K@oWw(HPtzYreIKZq z1J!Qvm6RmngsvjXH9^o@QGg;fBoR4vawdQ-L#<3;r)WNSDD)qapkP48OdvN3-J2PB zzzg||R*Js9Ok}pwq*Dh1v!CPUhDkLR(ru5Qi<``i@1OA~<{`Gg=EipsL-1jTpbXVv?tP=JUXMx?c{Sh0;6R*b-9yeGN-= z#zyKJ=h%TCK;?78%Oqeo3C{xvgEr9DGzL{aLGd^HDSIVIf4Fq<%W(%Jcnp1KnI2o2)~ zgezGroJ##FbD;o$M}=j5a9EdBcMa_1!& zM^8uZm)*zxpUPU9+8v#oT^=u2@6YGWNKFxT7=nEcpDStoOesE>-*~m@9U6E@_R-iU zC>_uRA7wi0rYL*U0t%U5sXyO;9zLeekAKRAZPuaK#2p|uagV_O62bB#h2jxKHUaq? zgy`NalN37?%djAx`Ia1X4EUzCkmusjWO5<9w!M1Xu)%aL)u+^F5?qQWmXi`ZK&8vo z7|J51D=N}#{;qenay$XI2{eHXH1Uw^jZ;!|^7ev^t3hZ@BZ4Qj=Iz zq_hc(BHngMuf(1uJivN4lkRjksj3o{Z;J8xbI!|XJvCa$?U&?k0w=FWN8Iz|cXPbC zh`mCIBg>OaInXX4H$CJub40DsZ}~Jq{@6M`upnN|=&WEVmA!n?noV3 z?nwI=^nbzl7tDXb`WL?<-Pg=05uviZ`Im%$v5fF{M?M@aNA!Vmm&E8DtV`=f$Hd8F zbzmhnYq$yQ23jM}#sYhPDs+C9U$zdU178i}k0u&gKQ=M|OyeW^OA!3&b8%LB8SVjK z`h(I@ca81|;`hzhZFys)5g1f2%2-FgFy)g0S3%iaTSOm(<;_#*M-f=JQBW+>iEd$hk$6?~nYD(vbwK0Z;b_N=wOPGS{%{ zu;EXz<(0e{RT^_M7idrs4OKJY*J$o3acXT5=`lz%Df{e}{Re1-@p#W24i1G|nNdGI)doYdS|X^Z9vR+wbRMxY*}HX|f~8lE zM59*P(3Oj4=i4n{b@@7L$L!eAOmkLo{6iantN5f;;x4^ST)BLo#CzfLz(Kh2+_b#s zkbgtfyBu0hT=~x$k9B<}6`{iS)0S)`))=!)`lhIF&barTDI>VG_XhLNl@MLcYp%Q1 zPddsYl%$?>gP#oOT+YhytP)@nOh7`pN<$Hp*$hqo7_RIAjQ2~7$-UJ&owWU7$0}`oEMp_ zeI54XDIO;o;Ag+XBJV6?J))95r`VTUIh5S6Tx#AXJ)FxZ_m<1$T;^&^KbHWPA){fJ zaG<8coyYGKJu4`@$}zj@=Bw;X-mmAj5G(l=|skk#g|U<#E$jSmIu-=L1+=Xqr(>N5(6L*^$7}B+yzjXn+6ydqo&w4UL>@5O?5rMT&#KM)}5P0IkyidQ!`er`&>gwMuLnfctkGW2nggTzQBsKh5Llq6z} zG#48NSXhJAb4p~Ro-JW%sO7|l#|17QQW7H|%AT{hA)|1nUeJx8NQl^UZkJ#H(SfM= zHxL1Z_)z!wuL;xpgrEk8D(U7{73MFay|4N(Jgx^LK#n{Rx%|B+p)M#-d=jU=Gh2=1 zg=~+lqHeoH)%_+|PC%3`<8T+iqvq+ATDTvzawt=L|q|IXOt5Werh1{M2 zg_K)tpPekEC?5SuUH*5gqe5oBQNAqCRk^f@R)?CPZXhXDu%uaj#Xn?3FTb}+45)#W zbL{zqBuH0G12HvN!di_QeGINJYa*27QW-cBtcIX67D!wSuKD-Wn0El0q7pQ5mk#2w zQ5i={7E8Bk2nPPEG018v4u*S3*6oQ!^%@Ucj1unRPx>xwKyo5_E~S+QIhGqOf?_RJ ze;!ZQDNjzr%J$Eut#C6)9aRGcrpo>yO`^JaFjM9P=CFgJG6x;JizMhs{Q=e88;kh? z*xQT&>~>YAQyeC~0pMd-2ZJ~~0yX7(t18U24(8trU^};&-7WLiJtnTd$Uf~{vw3$1 zPw#*@zJ;XlC;$Z?a{br6f5m@M!^Vo~&OQh&8Mk&)NQU#r9*GmNk&Y6qG)^WXhO*}c zmlm@g;S!cLHm&0>FeUJ`=ZP6Y#5f%6w2jKIn>V`FX-)iU%WNf);4iAI{&g%Mmr$1#vy@I$VcwjIhOEc*t#*#7 zq&S;-_=X%Os|R$kL{x(mb}Ek|B%soQR2*J@6kIWgm9&eHZxm6I*N-q^v=h}Nnax7G z7i=bWEhTX=;s|ec(xJdgX`9=is9WF~f>`qR0;_DGXnU^s(rW!DbEcpXxqG8N~m!_V*R#3b;Zf${^gYJbFf zj_m$c#kIm%t*#nY!D4_T`6M6!WptpG$Vw|L?{Xc~I#i`4j`ibPr*Sb;MzziLiK#1U z=Eqxml}b}3(M+vs0E#s9#27|}`fPbSa4wYd;52(G4L%$qLh4|X_MY9Axm^g9PM6mQEHn_oSOO! zNX71XevwINNhbkpYREF}%Ps{A`g5E{iE1gR&w8$pQ#OLc*S%5qEjIy~i(gZ#!g!%o zi4Fc4EbA@7zfIc+^Hp-Exi~?Xlvh8l5p6 zm#GJF){of;)=Z>-D8}jKPs-T<8R7u6Z-7KUo7ty=%>kJYI1I-9A)>Cwbu9xH zpBT|u-ck%6o^6o5u??*7;SYL-XRxx#@@0A4T(dWZ)8t6tyA>}ouzY~$C zj}!MGx=XnFAeqx8dcdS`oQ+N!9Zi9)N_VJ<7$^h==wB0k{#pDn41_oI?*{8nH0zK z%M3Cg2)}X%AJ-h3XTjDdIPovD1|VaMiW4Kr9@&d|dP;>sfkHyoH+?_v&|az(NupJ|Fca)oAt zP$s5{ogD=5kjJ!iZ7`_(mRMG7drFDXwpCT?>MF2cA(2xQg^XFDl@j%nUc`hhk7HQN z1H3euT=yjYj#8vxrKC0aFJ%QTM5thn#^i-{Se60s8)kAI4ONBgM5+mMI_ zpt!|fk&O$QfZ_mzo`yIA?%DQm-w0eHCbEPTeugt=%hKSu*W(Lieva3|+g@*ux7PkV zNq6ah{% zfX$zNjht_1?me96BuVXW2nRG+fu~Gk&4-a{LSDD>&m#u$--#9YONX z-A{aIbpp=CW(uotB|J!^!`Y*`i?`n~j!NeMHF+@Ft~|XN_q$L}`KVuf0Wip*Tb7`& za%aaVv925ifLYAI5ZJs5jLCCBK2C>ItIPq3X-4csmX5%nOZJY>8z!A~>fL6wAlnGL zs{ue9k(htiqaEKEdxNT^pNld-$5DXCMA;>gQmrF{^voNkipby+G*wjw>-&)y0j`+* z@)E%iC?PK4?S4?_N%Es5YIWl8GExDMMPRxpC9L*)}>a5EK5zP&}N*ASBdnO@V`Br>xJ>~u4A4L>xbY0sdJUDH2Nkg zS=g+F%JPTBj@obqC}F;8=ypGULMS>Xs9@-WGFNs|>!;iSroS~81 zKuEFzwk6UZw&f(;n~A3Z(5#EOg`0!=gZuhbuvXGbjutI*yFgp_qc=D}IdgURZN#JP z#%X=3Sd9GDG8H0*ylEc573O$tEfqk2Crwe5cVkU`M?5@R85m$`!(j=l#9W%E3bAVs z(NI0j3~q^15;#Jt+c(MxF09`$UF@G>bBZrl50R9DAYcI z_Ikh&lEzK!3Bksvpk|2W1t^94Yg|n;tY8|?IuBB`sz35oUW5;4$1ByBXNKTUBX8jC zu-qv!m9F?{6X6VBe3m*4O5$V>{b=D8(&ShUMGTAL0uEnTENtRQcZT^R51T#zQbI@i znchsWc;#=v{TA7AG*KF$M!}=NQVV9QG=-3Uy0P9qxr+qvBV$h7v%e_T^;L>4* zh{S8~yevd*5TYK-(z*18Ju<#o0eug#)PZ)e)RU|S(bS}bqIKUHsxqr-_#ws835;r!x)yj(_^E1I1>D@nz4B|ur)D!MdNP9psHf#J$^$nhS8;yp)s?F4Dg zBn9@839_c6BKdVfAS~NZH&_@0O`UnMfN3<1(_@vn4~vaTdx8BJ?Q;F>+19+s6<;g!f>1K17UPK+H$EWqka6UHyVC9K&;CZyg4@ zCwa!hP~>SJvQb{cHEa(_zKH0En)L99!>XWeej&0qUyD>PxlRrG6o6UPi`1_3LmI30 z!sy6OC-A~k_yYTY+{L9=odSx8TgqY{${00(Dyp24=%4P(!HpajgE?tIXX5*@#V+vs zKDHw*%*$_%zCqVLv#w1L=4DduI>0JSoG)G;j?8^@%Q%-7fjF3@fbiL80js12)P|b0 z$(Xh~1ZaiP6efId0UTIy0}?El2rBZ&O-6*08H*Xu45f+*9y?Y_SYO;bBBFk~(DL{} ztz>W-j8ZQaV3$(k$@$bJG;yzyq*2YcxM3&uLVv)ldQ|?z3{@oxvHWUVdM_wBf|VMv z^wg-7?60DlDs~RgEPmH(t*BLWUT{T1zEg^h0_eE^V!BCf_X1H5`@HKsC|v%kiv3hB znV~%b1j-6RtCbZ8i5(x*L!K97Nv-g`U8GUtU1yQ-FNQK`>SiR68Nfmwd<$8Bw>SoO4^UVQ5! zwoz)2ueswlEd(DDiT30t*Q{n*2vlca+&f;=kt+1and{T1A~TZ}KRa_LA)q-`(4bf( zx#uxtfw?E1KK_lm!lSqWd~i526?&y_W(&%j{r+{yOd@0^v!b)_EM?@dtXB&j2J)Un zj5;dRqSB+qma2ukPQkmCtuu`~PrhL5^}v-$p)q{4Y{KT=T%UL01xT2fJfgp+sXM*y zt5GVfdWx|~uODtGvH*oP!KStJK@IjTGR7yqNbHeCWz}*v^Slb&au6~*2hQunsUe(LxoQeiMWUMZg#ZAfw72+g+DzZcc{dfYLvR3tHH_vx67ZAKYpamPv4;=x+N zpn|DaR571+pSxLcie6=u`>XDJWF@3J$G&Nmtu_U2d11F$+Ce^#Y85yekImgEVNz+O zDf&eP^5EAHAn^8uQt6ENhhTj6GLq7H?{D_g#mwPnh*6sdL+oav*Mko&r()OI4VYfT zWC9%y>*<*ut&kpA8%APF`cyZR01n01<#(97wI-#(s1*(&JS!LqVWp59>Zt3hsI1ev zVd>Y2gXz2pgTb>L%4l6hsiI2{D|;6T97o+)`rnQA@qG=HZEZq&<~apDPEu;Sb~@W! zr5}Ib3T9gSO=LZAA;;FaEOWT&DZt5Tfa$ftebYjIz!&fObOq05iYCG*67A$TyCrdrHiP=I%DB(xUp| z$>^@joLwfgj|DDjXB0}u@gPj$Zv$lO@8*+qPv92_Xi%*|U=w=Q8buABbS3Cha2KDl zvAEJ#5EsED=boFzF<*nUXEn>niy7u(6C_1Ox~OZxBn@$K;V6jcvP@pGKh%}Nua+gl zdRr)?G(q1VvYtIecjS~*wd0&%2@4!<8L=ZWJ$IZj>Btp_Y{C-dfNyjgdC{aowv2C{ zx@6k)Sv%-->?*lWn(FPJEcN$v6tfSneDHTNlb-eNh7y|K59MZ9v^n&)GE zjcKrLHunAD95SM0Vi9#Ax5cmZQ&%caVW)jQJ-Y$ID3dtC z*xnB0{zfu*aGt!%ipALmN-qZbLR9+38~mP%Q&_+mqtH8*zpCpdFd(q#ptHfZKQZzm zs_qJsO8lkj9Ds5>rlY@s_tE(Bg0daQrY|kKqJ85OXHH8S!H^G9dA<%4X(I}q}-dE7sWml{WCn= zEC?q! zdC%OGSfY_<8+6wl&|RjmuFO}G^F4Q$R(2@(Vb4zGDJABm*K=gE-Br5$5TFn+$tzL# zG#3(#H0w3ngk!D9DH~!ISYS$vBf^}LrFaihMB(1MtidG3;n>C{Uw=Qyp6MX~RlD#7 zd5eRX0I7Xyq%3Z^6ioxos7obbFOaAE5&aNOCGY|)_(t}KHD2V+FeFQ_DiiBeU?CA} z3E@Mh=L7r)j)Pm)R@!uqHrjOTFBj?68regV&PipdoN}FoZ2aWMtP3+h`i zO{yJmjgG2kr@=FCc_z;Snz`G=n7iKQYLc{^9Sa&rkhF*<_L8TnWozq)5r$onJ%Hu* zwsjSYfPtN13|)4<872iQ)5W+~hJkb=y%Cu?C@;Lf5pVTT%eZB8opl;g&z@(vAYKsE z8G*ydc-OA|XUCk7fz7F<1QuKC4Y&Q93Q090^e2EI@b_HTi_&f2xpTBm=2PpLSLNU% zQO8~ZScP+$QQ|9_v8S0H6F{cIo&5EKJlW4}&(gT!kx{79Xl@LluEB^8;8eHMUh@xR z4^BHO%qb@C#-7U6ZkIZ(eeilrTA3k^@?JN-3{dgp6cBS3!0k#I0`xVC^H>Rc_^{AK zRs!DJoeEuJp!VX>QI5UNGU7N#gI9@Z3gp-oK1t>9PHZYc)Jt*nZ2^_d#Z0+8l~eaK z%<+xArvXPTrCPn~Huy3uV>f7Pk1O+*okfDr^{tGt0xGnu)c_L6$%FR6bq_>7iAVrC{U;)GhWXKd@aJWD4mX@_NHY} zY1a}p5T=PLM5Z0*yt%{=zDXvZO6CkZ27dA?3rrC@&T?1rQ({bzy~)K*d^)uF#0{JH z`*rISny0u2;ZvF~o9yB=Ml7*Oi-S{d_NamQD&uiTWs%l~1hJFF1_p<(vM*%k$jrrz z#47S>h7k&g!kMn;*xj#xOhue$hM}>bLO~JR+{1L-BhpA83B$d600njT`GH7dV6Suc zuQnGizjkPTk4=>XMa})n6Umjp2_W{^*X8o}QWRkL~-OjBNGw&Hs}BWdIOaCORWJeI0up zT1OKDCt@OkKjZjIz!`}Mei#7R+v-@_8Cuy|`~dd(%N``;5Qv~Vpdi{iSAID0hZV?h z8R?wu%O85vmq0rbNoVWMkmApK)!(lG&THW(=jp8TQG2t@Hv>6>(esQGjh@WX6X;hdbS2X+@*yK(trmtV0Kyt zdRj(i1$qt;Gb4!UUhnU6f9M+Mo7h7RGg>_tJ;=ll4-*6XpPl^H@ONqbKar1kWEce$ z*hLr_B^lY|e^dNRhD%=$^rvSGjI{Kuv<&PD3=AM9RuDZK7u|j1A07O4Y~KMYnb_Hz z=n?!Fvb~P3xdE-7m4l@{HxcCY_)+--spq~DvI~DyTIt?}?!NY3)Ea{6b6>~!v(i?_ z$X3VN_^%cY=0B}CS=s9URm;xE^k=o6v5u{d9t3IWuQqxXW)32{KXrVK?#@DPB2i02D*}f9CdT;R#F+k@81tRjpQj4wEc8VnDR=RnWne%- z-F@zml@yE}EOae(Ow8@*?C-w&%isQFE@7V$0yTvW<^Mez`}eajTRA@ltnimuhZrISYl@3?QT=@XL6E6$zvJ|2O{M#vm{IwW`upF#$q0 z@A{;W)NR_}s@R+~oaixn_U?;NTi7^Jb8% z_L4IZK`7TwbyFwZK&Y)EL6f1-*2FH?Uor^~Zpu6qGjijj+T7gsuG?uGqtWQZ3UpD= z>C#(NR}cwY>wEMk{BkcJi$XK}?;8q$T3)eEoDTb2Rf1gJZ+OR}YKcBelSyy-Ja4;zMe;gwtymS+%*L&!l_x8lp zonnWfv!OFO61U{s)Tmrk7?1~l*^^*t`A*+Www}th35cIG+iU%HXc?5sNK)hFG=0KE zkh`O7n0u3Vl&uK2DTZ?pzw*#;8B3|B#3&3@=v0zadn;8kVi051mv?qPQy&i8DHQ}=#FdAnblNM(cW4bO|i~4(oQx(k`Ps>qt@5}@pmcbTWQk(w4cnQJs zlN)76y_@=vOe*cc#rBF*kLg~h?yC;ON!XdtxS-1DV*`l8r#>&=<8_nG;vv5SlQE>$Kc2)`ZqWVjp2_QJPcXtZ5wT?MZmu^rc0 z_Re2EJ7(BnYb({TmHBjL<93B%7Tn}P>!z5VD^p8_p=b5M&Uil@Q22PrFR7=Pfb4co zIJU6wQYImg`f;d8m+*l^n^|z9jsn$rGMu;1P)7k4x0Px49H>$?S?%)WAhNtJ(0OHk z(Sp2DzCK&kqIO|#sumw8axScFdA&Q^d2M{WA7kmNs}B#{^E=K|hqH4@jNhVMJj-~b z8pCrNG`mz+wJ+e2s;GzAJ@~*q?lyk$x^PhmVCl@|q810AS1eZDM8+fQFr$0E`+g;W ziewpSadrM0yInD6nm8@ZRwX~24Lu|rYs7bZ zl_kZU=upv*jhd${P+D;0ZQtX>zzlc7q4%1TDl`qVgDGExkDoI$omZ7NVeV5dG(VNT zHUveeR|=bG8JM#==zuZ(kN3OBda{VlWtB+ARW)0Kj^LC#r1<0-ONwu>hRl^}`dt*e z=lGakktm~QA$#h5M;o(9-1+?35wh02KwAO8dg(c!dwPxyrwk(52@- z4Nbb>^Q*3^ubG-1--~!R#zJO(#`|#Q)^aqlDBp}_eIfBuI0Wn2@Orv-P$CtkVX@7& zK}>^&J7&(!YEE|XrkxGADI%IKT0KAW@B;>LdmHe|S#zYAoVE6(q^_N6 zw)af%3i2)t5QgAUY@OVukal!Rz@yw?(Vy(K>vX+NM?hI@hsYPzvav!VsAL)BzzV~1 zf!bbQ&rIVWxBzf8qitDl7tZ93ai6RC_B44$p|sXKlYLIvHBhFcLpUUf&0J`+n2@p< z4J-RBq_2lfK@Iu4^7A}oPtWnAj>uj5@sPLAZ)GeHIzD^6lKHyfjqIH%r&HF)WO@yz zWP$+gyp+=-pN)H7UHoJ>pPP%t@Dp1RNCl0+)Be1}%-qi*lb1@1hMnvv#a8s_+@dig zqdv@FTx;^>a&7cRV%ZC6)LQ92hXrS;-APTmn#r?7vpEwtZ+3wmJlF8gGulXz2fi6; z&Pr_%bgQb(pk=YmFk0~CR_?1)`wmHY68dJR<*7`UhOI`}SqR3{;F?Ul&om^aA&A&r zIj|XeJNg0XAM6J9PX}F*lhKoRHr2Dbhs|eIO2G~}N^USMxw|4C@?sNH2`RN}D5!eo z=mvFpD7`*Je|a=I$$8SP%C37-_$bcIwLM=S{&7b^EaXIz^&aA-*Wr72wNrs-v)SkJ zH6hJRkK7}&K@Ef~gqe?N&QgBbd;QV&G$bRjgK}EE@km_KZ&Su_0;fBCEe3a!u89~= z^!yv_wbfO|8cxiYUiVSX6UK&4jng0g@a;P?SGZrAnm6=0OkRAIdTu-Pv(7>k9B zW|<~DWfNVmd|g1^cTq~^6$_bt&0rto5*E*NmSUlJd6;+VXs;WRx7>)nRf(O+YY=Uz zo{KDJ#xKYJFbq{12f$FT{nEUEM~s@+CQDY2MPb6sK+8FH@41JB#>2=?jceJkPGCyxVgy$KbrdGQ)VzB8h>{St4^#bvQEc8dvrcO)LksEk#lRQ1Uh{sx`)S?><&DNFHzc-@v z;qAO<-!#hP-$seGNL8xcEqe#n7LCWi>5pk)Pi)(lj+igL%ISbSK{#lw^@n#fmR6e1 zC44IOq?n^j<|C#BW`bC*?4MFswoS2N6oKh+)nj?A_*VP0t?L-!H^0vmGWukEp3BIw z-zOeZsIR;jJ0|6Qk;h3kF3D$?Za=Iw<_|Q{N!z&CO7u*rW#6vld{(?QjJnCrR>z?d zJhZrC6rmtJV0*I1e{BV1joZzcyv<+uw(obPcPv<6sG;^I&mbOo9V)&T7nTY|x#~0d zY2WFRJzJ~h;&~sO%}!XEseqgpF!16--Gvsd=Q!>OquKEzyNQ&^f>blca8`5`b0HfL z-JXyD)_(2B^8JWwI-w0da-l32XB-&jn}Ui}QA1%~a9$V9h@-z|zBLwS++JopZsTqXQyLw$FkZu%g6Tz~9Jnz=d>lG=Ha@^g zr=q_x47SA+J*)SeUxH~Z@+uHmX?-$ozBgA8My1mkt9oP(swG-bP0N%y*o_n=_z|`1 ztiSG{%%{!m(MYO0i5XT+(NL}dlE~mpQlJEFJEfIxe9F>|YWZZpLl}7>A9R8Q;01=@ z&a5$8<&U3w4qPOYs%Of7o8fw%SCN0n(?GnR7d@YWQjMR9yt$2^GWMRlx+p=c1ltY= zamm^m%t85nNE^d#A^Jnk~YqZW_#0eRcdj*6jO+m2}>;h+!p$XZEsPupAGDvXMk2Re~9wWY=;6kL|CxV?sd9-s2zA!7XYkB6FLQ)~M((!Pr5J zkn|cjCwERAc_EI4mIC6~iKK~M0

> z(z-sT;iQWPt<@;GunBTA0>94pS|2|^&g2y<&MeU|f8C5kiJGDn)F}=#azm4ihKtz2 zB?>-t6DU5ZXp*1C+Ewrpu{9Sb-#!)}oaOg;ZTO)N$Mqq9_MF8x+lCjB-HpJ~yyC;AfI;6r zX_pUC+;A{Ctq+hX*bJ#@%!_B#u8#E*-gsqli@QaxCU_%f`Vk+v^}+dr2zWB1L<50= zc$f3@m&&-RpR8AWk62mq!)UAbtCZ#N`Q@~Z*4|95?s&<1VlG}R-B9hymk)hrwg8T# z_|CAXTIn~ff^Ot@biZ;CG1qFpb~j=Jmu_vnI<+jGO_eVdUVszW@ANNWHF5~7eFlk5 za-g!i>St1Z_jd9o+~T8N!$hRfIsHOSE?X(U!*JFUER>n+O${v@6UlwgF^S-&NO`S* zQDlY-()5&umALrW_Gi9M&!T&^AtOSD#3^~wY9N3-JK=yfD=NszM=VjXQP0$4!8=;m za@WnbN?e{gvE^o=b9cqCfmnqK-R&8!N3&c}@a6@ubXco1e-I!sb^c(N3{3fWPw57mfJ#l)Lb8yGdQN9V>22P+e$dZM1FaP)CJ#2 zaZ6Wac82r1bfcC(J9RlIElwa@)qQrF+$)*9f$2?iLX}9@W>Hu&Gw1Svp#QAG2Zh=i z%s7xj)zU&x!~O!lWnkTr_uf-j^x4IMubg6qisxBTZpi*k?6sK{z{IEKgRK)i>#Fr0 zwoN!iyWOIUVml&9liy3~t*r@{%p)6wAdmLa$e*VcD`>ed!lufzRK~jFcul^kDLA|t z@q72M_2eXcZ5x1>i*&qdFO_0t|AE?~azu&Wx3X$LgKXP9Qc7Odq2%)uEAbHA>-GS0 zMFFce9`5xlil#m6%T=V6ppy7|G8+IEZ z6CKjY7gNuO`VfjfhG87eXC%kJXoTsW^5J_}@cddA-}kg)I|1^j=4--_*XQ1#0J>NX z;YJeaa?-*&ktyK0sP(a895qE%f{{yBTS^O+dd%$2&O<7Y)xhTZ%Cz`x^5@)?4_NV4 z>&*ss<+1LH6=61$QsL1eUD-RkPMS?`c*;>5DX(puEJz7Z1N8{$Ld0w2+-;u#`lrN| zpM6A9PvNoBRhY~dRvu;jUUelT(ltgVnA~*!2_x9sGWf9R?9-WgyP-9A zw}btlHN5lKTN%i6Tl3uIjI-68*}OI?y(F zthR}bzBef{i}X!1YcJM$U|U{raF zHsc%ES*F=#bm9wd7;A94n59tkz5E*a%s#euU93`$7T?I7y`>EbMvT$8=R=bBtS!9| z`j-r4HY&d*cYA&L1-=&Ihb4U%?;d%Sob!GA1QQ2MLpUC)=;%jkhJa)0pwi0ubyTTR zHo8%3hRiOEt(I=TD;1c}5ov|5Py{RjhWiDr=s}YUi|LL$sz3w#cu8@TUd+wtX zyDc3fw!E>yzQp^qJUjJLEJ72mDDa*wEbiKn%PkC#Pf%WC$r$p zVB&KT)K_n;2#l#eTeprf0UKB2JgUc{W3Hh_I$EerT?v)CWjt;Y2PYA{dF0Kp7*dr& ztXnCH)d}9g&hDb=B=7{kti; z!FfqKvM?1`48yeQmF&l0bYto{$cjk<4b{wJFN~1s@2`ZNCa_wsUGa7V>JD>Bu27JL z6xz;(GZN&zJeHsp5pfulbGe{Qk!flnKo3=FG8pFRQ8k4|F!IKXw+?yqvk?l}-%}mfHz3U3DCPC@Ud;p2jFlf?l$ALHYuT*#37P;a3VSga7R#IYCk$HM@8J& z-<^@dYed8?XZt=CA5@;=gqHI)XY)%Nj`Aep67Z{tlC0=M()cre&0f;%!E_lg|Ni7& zW+H51lVKr_o9!VK=k~(fNs+fO^0g8n&R{kb!q?zW!iwqzk0{^5o5b$hT@Xe zAkPd(ChmL#j`bo$CxC88MvcfRp={ck<+unLb?#x_}Udz$3)X z_R$H!K|!Su-sb@P6IBP~+wjh@u06Q&&M#`I+^FOO|xGCatVvXFykhcUe$S3-w=B$HM5&#ELcWmfPCD;CaN zE@1cJ%q@|4>q*h8J1$0HVm(f8EhP#a?S7DKlJBF$5|e{)+S*%3uG&Km)>O!|e>vr6 z(_VaF9e-ddNq)J8I@_EC4-=#V%Y%yd?xnAZDK}u?lHpKcdP`090m$GjJ=(M?K=U&; z1KRA=tll@LLH$D8*D}jr!(%!(xN$U0*BJHyqR%zH<=K!zGSLwSLc+^~%AU!;ZE`vD zKNEVs*J6MT=M$=c_(Z*4PS?EDeTxA%$zz`RA!3DY$~@e z@+0OSt}UF{7+rvek6AZDp>tufmr9QZggg8HoB;@PwY(T%V2;KMolXvD< z9-*IheE-4Dzpx7ZZ_eGl<^5T@Azy!Y?kC64y=(uIh3D@>gn@+ecPqbg4*k2GJJz9} z1@upL{>DM{@0R}V(cRAd-J@SQiT>Tr-{Vv84|aZX6#Z8#i~nHdCuh;WTe)K``dP3b z$Hnhs`X3xde;B*J2>r?=boUVWvnWE0{mN-{f5_Zx{<_ZUj%ny;*@S57{p20GSA_bL zIO+FgSU(lvS^vH$>t6G|%U|=U?h*}t7E-of7cAZD{ad!xPrXPXNRNiqqg`*+P>FF|+rho2?Z@L%p0GU9L#@5G)#e)1rx KWs=5sTmJ{=M;VU* delta 52213 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[E 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 From 8ef875b1f4580c5c89f346f7114e3ddb538fd020 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 28 Jun 2012 14:45:12 +0000 Subject: [PATCH 04/21] Monotone-Parent: c69af3e4b83d5cab3a52701fb736c8a149697338 Monotone-Revision: 58f1829bce5e146423ed657d76063f09b7596385 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-06-28T14:45:12 Monotone-Branch: ca.inverse.sogo --- SOPE/GDLContentStore/GCSSpecialQueries.h | 11 +++++++++++ SOPE/GDLContentStore/GCSSpecialQueries.m | 21 ++++++--------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/SOPE/GDLContentStore/GCSSpecialQueries.h b/SOPE/GDLContentStore/GCSSpecialQueries.h index 08c2c3e69..24330d848 100644 --- a/SOPE/GDLContentStore/GCSSpecialQueries.h +++ b/SOPE/GDLContentStore/GCSSpecialQueries.h @@ -48,4 +48,15 @@ @end +/* interfaces exposed so that categories can be created from them */ +@interface GCSPostgreSQLSpecialQueries : GCSSpecialQueries +@end + +@interface GCSMySQLSpecialQueries : GCSSpecialQueries +@end + +@interface GCSOracleSpecialQueries : GCSSpecialQueries +@end + + #endif /* GCSSPECIALQUERIES_H */ diff --git a/SOPE/GDLContentStore/GCSSpecialQueries.m b/SOPE/GDLContentStore/GCSSpecialQueries.m index 7bd771c7c..34a29ab82 100644 --- a/SOPE/GDLContentStore/GCSSpecialQueries.m +++ b/SOPE/GDLContentStore/GCSSpecialQueries.m @@ -27,15 +27,6 @@ #import "GCSSpecialQueries.h" -@interface GCSPostgreSQLSpecialQueries : GCSSpecialQueries -@end - -@interface GCSMySQLSpecialQueries : GCSSpecialQueries -@end - -@interface GCSOracleSpecialQueries : GCSSpecialQueries -@end - @implementation EOAdaptorChannel (GCSSpecialQueries) - (GCSSpecialQueries *) specialQueries @@ -163,7 +154,7 @@ { static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (\n" - @" c_name VARCHAR (255) NOT NULL PRIMARY KEY,\n" + @" c_name VARCHAR (255) PRIMARY KEY,\n" @" c_content TEXT NOT NULL,\n" @" c_creationdate INT4 NOT NULL,\n" @" c_lastmodified INT4 NOT NULL,\n" @@ -190,7 +181,7 @@ { static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (" - @" c_id VARCHAR(255) NOT NULL PRIMARY KEY," + @" c_id VARCHAR(255) PRIMARY KEY," @" c_value VARCHAR(255) NOT NULL," @" c_creationdate INT4 NOT NULL," @" c_lastseen INT4 NOT NULL)"); @@ -257,7 +248,7 @@ { static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (\n" - @" c_name VARCHAR (255) NOT NULL PRIMARY KEY,\n" + @" c_name VARCHAR (255) PRIMARY KEY,\n" @" c_content MEDIUMTEXT NOT NULL,\n" @" c_creationdate INT NOT NULL,\n" @" c_lastmodified INT NOT NULL,\n" @@ -284,7 +275,7 @@ { static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (" - @" c_id VARCHAR(255) NOT NULL PRIMARY KEY," + @" c_id VARCHAR(255) PRIMARY KEY," @" c_value VARCHAR(255) NOT NULL," @" c_creationdate INT NOT NULL," @" c_lastseen INT NOT NULL)"); @@ -351,7 +342,7 @@ { static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (\n" - @" c_name VARCHAR2 (255) NOT NULL PRIMARY KEY,\n" + @" c_name VARCHAR2 (255) PRIMARY KEY,\n" @" c_content CLOB NOT NULL,\n" @" c_creationdate INTEGER NOT NULL,\n" @" c_lastmodified INTEGER NOT NULL,\n" @@ -377,7 +368,7 @@ { static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (" - @" c_id VARCHAR2(255) NOT NULL PRIMARY KEY," + @" c_id VARCHAR2(255) PRIMARY KEY," @" c_value VARCHAR2(255) NOT NULL," @" c_creationdate INTEGER NOT NULL," @" c_lastseen INTEGER NOT NULL)"); From 79552f8ff36f7cebeffc4aa57eecb599caba6f2a Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 28 Jun 2012 14:45:45 +0000 Subject: [PATCH 05/21] Monotone-Parent: 58f1829bce5e146423ed657d76063f09b7596385 Monotone-Revision: 8bf030be6f5baed09d67ad6a173017ace25834c7 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-06-28T14:45:45 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 5 +++++ SoObjects/SOGo/SOGoObject.m | 3 +++ 2 files changed, 8 insertions(+) diff --git a/ChangeLog b/ChangeLog index 0dcca9839..f749e20f6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2012-06-28 Wolfgang Sourdeau + + * SoObjects/SOGo/SOGoObject.m (-initWithName:inContainer:): make + sure that "_name" is neither nil nor empty. + 2012-06-12 Wolfgang Sourdeau * SoObjects/SOGo/SOGoGroup.m diff --git a/SoObjects/SOGo/SOGoObject.m b/SoObjects/SOGo/SOGoObject.m index 8736cf050..b7b42ba9f 100644 --- a/SoObjects/SOGo/SOGoObject.m +++ b/SoObjects/SOGo/SOGoObject.m @@ -177,6 +177,9 @@ { if ((self = [self init])) { + if ([_name length] == 0) + [NSException raise: NSInvalidArgumentException + format: @"'_name' must not be an empty string"]; context = [[WOApplication application] context]; nameInContainer = [_name copy]; container = _container; From 591306481b520cb11a1896c9cafb75d0a14d4abe Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 28 Jun 2012 14:46:37 +0000 Subject: [PATCH 06/21] Monotone-Parent: 8bf030be6f5baed09d67ad6a173017ace25834c7 Monotone-Revision: 3f8608c82d9c379cc9e0fea6ffe853cc1949a24b Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-06-28T14:46:37 Monotone-Branch: ca.inverse.sogo --- Main/SOGo.m | 1 + 1 file changed, 1 insertion(+) diff --git a/Main/SOGo.m b/Main/SOGo.m index f2b3f9e0a..8fe51d242 100644 --- a/Main/SOGo.m +++ b/Main/SOGo.m @@ -201,6 +201,7 @@ static BOOL debugLeaks; fileSuffix = [channelURL scheme]; tc = [cm acquireOpenChannelForURL: channelURL]; + /* FIXME: make use of [EOChannelAdaptor describeTableNames] instead */ tableName = [url lastPathComponent]; if ([tc evaluateExpressionX: [NSString stringWithFormat: @"SELECT count(*) FROM %@", From 95a443d89a87b03a27f9f1fa166a4ff681522680 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 29 Jun 2012 17:59:38 +0000 Subject: [PATCH 07/21] Monotone-Parent: 3f8608c82d9c379cc9e0fea6ffe853cc1949a24b Monotone-Revision: 905276f295d6f28a6946297f6a7af9ad60f71842 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-06-29T17:59:38 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 8 + OpenChange/EOQualifier+MAPI.h | 4 +- OpenChange/EOQualifier+MAPI.m | 30 +- OpenChange/GCSSpecialQueries+OpenChange.h | 34 ++ OpenChange/GCSSpecialQueries+OpenChange.m | 102 ++++ OpenChange/GNUmakefile | 34 +- OpenChange/MAPIStoreCalendarAttachment.m | 6 +- OpenChange/MAPIStoreCalendarMessage.m | 4 +- OpenChange/MAPIStoreContactsMessage.m | 4 +- OpenChange/MAPIStoreContext.m | 33 +- ...BaseContext.h => MAPIStoreDBBaseContext.h} | 12 +- OpenChange/MAPIStoreDBBaseContext.m | 116 +++++ ...APIStoreFSFolder.h => MAPIStoreDBFolder.h} | 12 +- ...APIStoreFSFolder.m => MAPIStoreDBFolder.m} | 87 ++-- ...FolderTable.h => MAPIStoreDBFolderTable.h} | 12 +- ...FolderTable.m => MAPIStoreDBFolderTable.m} | 8 +- ...IStoreFSMessage.h => MAPIStoreDBMessage.h} | 14 +- ...IStoreFSMessage.m => MAPIStoreDBMessage.m} | 108 +++- ...ssageTable.h => MAPIStoreDBMessageTable.h} | 12 +- ...ssageTable.m => MAPIStoreDBMessageTable.m} | 16 +- OpenChange/MAPIStoreFAIMessage.h | 4 +- OpenChange/MAPIStoreFAIMessageTable.h | 4 +- OpenChange/MAPIStoreFSBaseContext.m | 79 --- OpenChange/MAPIStoreFallbackContext.h | 4 +- OpenChange/MAPIStoreFallbackContext.m | 15 +- OpenChange/MAPIStoreFolder.h | 21 +- OpenChange/MAPIStoreFolder.m | 190 ++++--- OpenChange/MAPIStoreGCSFolder.h | 2 +- OpenChange/MAPIStoreGCSFolder.m | 12 +- OpenChange/MAPIStoreMailAttachment.h | 2 + OpenChange/MAPIStoreMailAttachment.m | 9 +- OpenChange/MAPIStoreMailFolder.h | 2 +- OpenChange/MAPIStoreMailFolder.m | 36 +- OpenChange/MAPIStoreMailMessage.m | 6 +- OpenChange/MAPIStoreMailMessageTable.m | 2 +- OpenChange/MAPIStoreMailVolatileMessage.h | 4 +- OpenChange/MAPIStoreMailVolatileMessage.m | 184 +++++-- OpenChange/MAPIStoreMessage.h | 4 +- OpenChange/MAPIStoreMessage.m | 12 +- OpenChange/MAPIStoreMessageTable.h | 1 + OpenChange/MAPIStoreMessageTable.m | 57 +++ OpenChange/MAPIStoreNotesContext.h | 4 +- OpenChange/MAPIStoreNotesFolder.h | 4 +- OpenChange/MAPIStoreNotesMessage.h | 4 +- OpenChange/MAPIStoreNotesMessage.m | 17 - OpenChange/MAPIStoreObject.h | 44 +- OpenChange/MAPIStoreObject.m | 224 ++------ OpenChange/MAPIStorePermissionsTable.m | 4 +- OpenChange/MAPIStoreSOGoObject.h | 89 ++++ OpenChange/MAPIStoreSOGoObject.m | 255 +++++++++ OpenChange/MAPIStoreTable.h | 2 - OpenChange/MAPIStoreTable.m | 55 -- OpenChange/MAPIStoreUserContext.h | 7 + OpenChange/MAPIStoreUserContext.m | 77 ++- OpenChange/MAPIStoreVolatileMessage.h | 37 -- OpenChange/MAPIStoreVolatileMessage.m | 208 -------- OpenChange/NSObject+PropertyList.m | 182 +++++++ ...{SOGoMAPIFSFolder.h => SOGoMAPIDBFolder.h} | 41 +- OpenChange/SOGoMAPIDBFolder.m | 402 +++++++++++++++ OpenChange/SOGoMAPIDBObject.h | 83 +++ OpenChange/SOGoMAPIDBObject.m | 483 ++++++++++++++++++ OpenChange/SOGoMAPIFSFolder.m | 439 ---------------- OpenChange/SOGoMAPIFSMessage.h | 47 -- OpenChange/SOGoMAPIFSMessage.m | 238 --------- ...MAPIVolatileMessage.h => SOGoMAPIObject.h} | 26 +- ...MAPIVolatileMessage.m => SOGoMAPIObject.m} | 50 +- OpenChange/plreader.m | 151 +----- 67 files changed, 2606 insertions(+), 1873 deletions(-) create mode 100644 OpenChange/GCSSpecialQueries+OpenChange.h create mode 100644 OpenChange/GCSSpecialQueries+OpenChange.m rename OpenChange/{MAPIStoreFSBaseContext.h => MAPIStoreDBBaseContext.h} (76%) create mode 100644 OpenChange/MAPIStoreDBBaseContext.m rename OpenChange/{MAPIStoreFSFolder.h => MAPIStoreDBFolder.h} (78%) rename OpenChange/{MAPIStoreFSFolder.m => MAPIStoreDBFolder.m} (75%) rename OpenChange/{MAPIStoreFSFolderTable.h => MAPIStoreDBFolderTable.h} (76%) rename OpenChange/{MAPIStoreFSFolderTable.m => MAPIStoreDBFolderTable.m} (85%) rename OpenChange/{MAPIStoreFSMessage.h => MAPIStoreDBMessage.h} (74%) rename OpenChange/{MAPIStoreFSMessage.m => MAPIStoreDBMessage.m} (59%) rename OpenChange/{MAPIStoreFSMessageTable.h => MAPIStoreDBMessageTable.h} (76%) rename OpenChange/{MAPIStoreFSMessageTable.m => MAPIStoreDBMessageTable.m} (92%) delete mode 100644 OpenChange/MAPIStoreFSBaseContext.m create mode 100644 OpenChange/MAPIStoreSOGoObject.h create mode 100644 OpenChange/MAPIStoreSOGoObject.m delete mode 100644 OpenChange/MAPIStoreVolatileMessage.h delete mode 100644 OpenChange/MAPIStoreVolatileMessage.m create mode 100644 OpenChange/NSObject+PropertyList.m rename OpenChange/{SOGoMAPIFSFolder.h => SOGoMAPIDBFolder.h} (52%) create mode 100644 OpenChange/SOGoMAPIDBFolder.m create mode 100644 OpenChange/SOGoMAPIDBObject.h create mode 100644 OpenChange/SOGoMAPIDBObject.m delete mode 100644 OpenChange/SOGoMAPIFSFolder.m delete mode 100644 OpenChange/SOGoMAPIFSMessage.h delete mode 100644 OpenChange/SOGoMAPIFSMessage.m rename OpenChange/{SOGoMAPIVolatileMessage.h => SOGoMAPIObject.h} (63%) rename OpenChange/{SOGoMAPIVolatileMessage.m => SOGoMAPIObject.m} (58%) diff --git a/ChangeLog b/ChangeLog index f749e20f6..a2d6e0ffe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2012-06-29 Wolfgang Sourdeau + + * OpenChange/SOGoMAPIDBObject.m: new class module that replaced + SOGoMAPIFSMessage. + + * OpenChange/SOGoMAPIDBFolder.m: new class module that replaced + SOGoMAPIFSFolder. + 2012-06-28 Wolfgang Sourdeau * SoObjects/SOGo/SOGoObject.m (-initWithName:inContainer:): make diff --git a/OpenChange/EOQualifier+MAPI.h b/OpenChange/EOQualifier+MAPI.h index cbe65b915..72f368b63 100644 --- a/OpenChange/EOQualifier+MAPI.h +++ b/OpenChange/EOQualifier+MAPI.h @@ -25,11 +25,11 @@ #import -@class SOGoMAPIVolatileMessage; +@class SOGoMAPIDBObject; @interface EOQualifier (MAPIStoreRestrictions) -- (BOOL) evaluateMAPIVolatileMessage: (SOGoMAPIVolatileMessage *) message; +- (BOOL) evaluateSOGoMAPIDBObject: (SOGoMAPIDBObject *) object; @end diff --git a/OpenChange/EOQualifier+MAPI.m b/OpenChange/EOQualifier+MAPI.m index 0584e2789..8707dd788 100644 --- a/OpenChange/EOQualifier+MAPI.m +++ b/OpenChange/EOQualifier+MAPI.m @@ -28,28 +28,28 @@ #import -#import "SOGoMAPIVolatileMessage.h" +#import "EOBitmaskQualifier.h" +#import "SOGoMAPIDBObject.h" #import "EOQualifier+MAPI.h" -#import "EOBitmaskQualifier.h" @implementation EOQualifier (MAPIStoreRestrictions) -- (BOOL) _evaluateMAPIVolatileMessageProperties: (NSDictionary *) properties +- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties { [self subclassResponsibility: _cmd]; return NO; } -- (BOOL) evaluateMAPIVolatileMessage: (SOGoMAPIVolatileMessage *) message +- (BOOL) evaluateSOGoMAPIDBObject: (SOGoMAPIDBObject *) object { NSDictionary *properties; BOOL rc; - [self logWithFormat: @"evaluating message '%@'", message]; + [self logWithFormat: @"evaluating object '%@'", object]; - properties = [message properties]; - rc = [self _evaluateMAPIVolatileMessageProperties: properties]; + properties = [object properties]; + rc = [self _evaluateSOGoMAPIDBObject: properties]; [self logWithFormat: @" evaluation result: %d", rc]; @@ -60,7 +60,7 @@ @implementation EOAndQualifier (MAPIStoreRestrictionsPrivate) -- (BOOL) _evaluateMAPIVolatileMessageProperties: (NSDictionary *) properties +- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties { NSUInteger i; BOOL rc; @@ -69,7 +69,7 @@ for (i = 0; rc && i < count; i++) rc = [[qualifiers objectAtIndex: i] - _evaluateMAPIVolatileMessageProperties: properties]; + _evaluateSOGoMAPIDBObject: properties]; return rc; } @@ -78,7 +78,7 @@ @implementation EOOrQualifier (MAPIStoreRestrictionsPrivate) -- (BOOL) _evaluateMAPIVolatileMessageProperties: (NSDictionary *) properties +- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties { NSUInteger i; BOOL rc; @@ -87,7 +87,7 @@ for (i = 0; !rc && i < count; i++) rc = [[qualifiers objectAtIndex: i] - _evaluateMAPIVolatileMessageProperties: properties]; + _evaluateSOGoMAPIDBObject: properties]; return rc; } @@ -96,9 +96,9 @@ @implementation EONotQualifier (MAPIStoreRestrictionsPrivate) -- (BOOL) _evaluateMAPIVolatileMessageProperties: (NSDictionary *) properties +- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties { - return ![qualifier _evaluateMAPIVolatileMessageProperties: properties]; + return ![qualifier _evaluateSOGoMAPIDBObject: properties]; } @end @@ -107,7 +107,7 @@ typedef BOOL (*EOComparator) (id, SEL, id); -- (BOOL) _evaluateMAPIVolatileMessageProperties: (NSDictionary *) properties +- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties { id finalKey; id propValue; @@ -136,7 +136,7 @@ typedef BOOL (*EOComparator) (id, SEL, id); @implementation EOBitmaskQualifier (MAPIStoreRestrictionsPrivate) -- (BOOL) _evaluateMAPIVolatileMessageProperties: (NSDictionary *) properties +- (BOOL) _evaluateSOGoMAPIDBObject: (NSDictionary *) properties { NSNumber *propTag; id propValue; diff --git a/OpenChange/GCSSpecialQueries+OpenChange.h b/OpenChange/GCSSpecialQueries+OpenChange.h new file mode 100644 index 000000000..4caaca54b --- /dev/null +++ b/OpenChange/GCSSpecialQueries+OpenChange.h @@ -0,0 +1,34 @@ +/* GCSSpecialQueries+OpenChange.h - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef GCSSPECIALQUERIES_OPENCHANGE_H +#define GCSSPECIALQUERIES_OPENCHANGE_H + +#import + +@interface GCSSpecialQueries (OpenChangeHelpers) + +- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName; + +@end + +#endif /* GCSSPECIALQUERIES_OPENCHANGE_H */ diff --git a/OpenChange/GCSSpecialQueries+OpenChange.m b/OpenChange/GCSSpecialQueries+OpenChange.m new file mode 100644 index 000000000..aa336bf99 --- /dev/null +++ b/OpenChange/GCSSpecialQueries+OpenChange.m @@ -0,0 +1,102 @@ +/* GCSSpecialQueries+OpenChange.m - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import + +#import "GCSSpecialQueries+OpenChange.h" + +@interface GCSPostgreSQLSpecialQueries (OpenChangeHelpers) +@end + +@interface GCSMySQLSpecialQueries (OpenChangeHelpers) +@end + +@interface GCSOracleSpecialQueries (OpenChangeHelpers) +@end + +@implementation GCSSpecialQueries (OpenChangeHelpers) + +- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName +{ + [self subclassResponsibility: _cmd]; + + return nil; +} + +@end + +@implementation GCSPostgreSQLSpecialQueries (OpenChangeHelpers) + +- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName +{ + static NSString *sqlFolderFormat + = (@"CREATE TABLE %@ (" + @" c_path VARCHAR(255) PRIMARY KEY," + @" c_type SMALLINT NOT NULL," + @" c_creationdate INT4 NOT NULL," + @" c_lastmodified INT4 NOT NULL," + @" c_version INT4 NOT NULL DEFAULT 0," + @" c_deleted SMALLINT NOT NULL DEFAULT 0," + @" c_content TEXT)"); + + return [NSString stringWithFormat: sqlFolderFormat, tableName]; +} + +@end + +@implementation GCSMySQLSpecialQueries (OpenChangeHelpers) + +- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName +{ + static NSString *sqlFolderFormat + = (@"CREATE TABLE %@ (" + @" c_path VARCHAR(255) PRIMARY KEY," + @" c_type TINYINT NOT NULL," + @" c_creationdate INT NOT NULL," + @" c_lastmodified INT NOT NULL," + @" c_version INT NOT NULL DEFAULT 0," + @" c_deleted TINYINT NOT NULL DEFAULT 0," + @" c_content TEXT)"); + + return [NSString stringWithFormat: sqlFolderFormat, tableName]; +} + +@end + +@implementation GCSOracleSpecialQueries (OpenChangeHelpers) + +- (NSString *) createOpenChangeFSTableWithName: (NSString *) tableName +{ + static NSString *sqlFolderFormat + = (@"CREATE TABLE %@ (" + @" c_path VARCHAR2(255) PRIMARY KEY," + @" c_type SMALLINT NOT NULL," + @" c_creationdate INT4 NOT NULL," + @" c_lastmodified INT4 NOT NULL," + @" c_version INT4 NOT NULL DEFAULT 0," + @" c_deleted SMALLINT NOT NULL DEFAULT 0," + @" c_content CLOB)"); + + return [NSString stringWithFormat: sqlFolderFormat, tableName]; +} + +@end diff --git a/OpenChange/GNUmakefile b/OpenChange/GNUmakefile index 0f3fb1a39..6f4803ba9 100644 --- a/OpenChange/GNUmakefile +++ b/OpenChange/GNUmakefile @@ -41,9 +41,11 @@ $(SOGOBACKEND)_OBJC_FILES += \ MAPIStoreSamDBUtils.m \ MAPIStoreUserContext.m \ \ - SOGoMAPIVolatileMessage.m \ - SOGoMAPIFSFolder.m \ - SOGoMAPIFSMessage.m \ + SOGoMAPIObject.m \ + \ + SOGoMAPIDBObject.m \ + SOGoMAPIDBMessage.m \ + SOGoMAPIDBFolder.m \ \ MAPIStoreAppointmentWrapper.m \ MAPIStoreAttachment.m \ @@ -53,18 +55,17 @@ $(SOGOBACKEND)_OBJC_FILES += \ MAPIStoreFolder.m \ MAPIStoreMessage.m \ MAPIStoreObject.m \ + MAPIStoreSOGoObject.m \ MAPIStoreTable.m \ MAPIStoreMessageTable.m \ MAPIStoreFolderTable.m \ MAPIStorePermissionsTable.m \ \ - MAPIStoreVolatileMessage.m \ - \ - MAPIStoreFSBaseContext.m \ - MAPIStoreFSFolder.m \ - MAPIStoreFSFolderTable.m \ - MAPIStoreFSMessage.m \ - MAPIStoreFSMessageTable.m \ + MAPIStoreDBBaseContext.m \ + MAPIStoreDBFolder.m \ + MAPIStoreDBFolderTable.m \ + MAPIStoreDBMessage.m \ + MAPIStoreDBMessageTable.m \ \ MAPIStoreFAIMessage.m \ MAPIStoreFAIMessageTable.m \ @@ -113,7 +114,10 @@ $(SOGOBACKEND)_OBJC_FILES += \ NSValue+MAPIStore.m \ \ EOBitmaskQualifier.m \ - EOQualifier+MAPI.m \ + \ + GCSSpecialQueries+OpenChange.m\ + \ + EOQualifier+MAPI.m $(SOGOBACKEND)_RESOURCE_FILES += \ @@ -145,7 +149,13 @@ PLREADER_TOOL = plreader $(PLREADER_TOOL)_OBJC_FILES += \ plreader.m \ -TEST_TOOL_NAME += $(PLREADER_TOOL) +DBMSGREADER_TOOL = dbmsgreader +$(DBMSGREADER_TOOL)_OBJC_FILES += \ + dbmsgreader.m + +$(DBMSGREADER_TOOL)_LIB_DIRS += -L../SoObjects/SOGo/ -lSOGo -lNGObjWeb + +TEST_TOOL_NAME += $(PLREADER_TOOL) $(DBMSGREADER_TOOL) ### cflags and libs LIBMAPI_CFLAGS = $(shell pkg-config libmapi --cflags) diff --git a/OpenChange/MAPIStoreCalendarAttachment.m b/OpenChange/MAPIStoreCalendarAttachment.m index 63aa68df8..1ba40ac17 100644 --- a/OpenChange/MAPIStoreCalendarAttachment.m +++ b/OpenChange/MAPIStoreCalendarAttachment.m @@ -67,10 +67,10 @@ { MAPIStoreEmbeddedMessage *msg; - if (isNew) - msg = nil; - else + // if (isNew) msg = nil; + // else + // msg = nil; return msg; } diff --git a/OpenChange/MAPIStoreCalendarMessage.m b/OpenChange/MAPIStoreCalendarMessage.m index daa9cc83d..5d2eb48a6 100644 --- a/OpenChange/MAPIStoreCalendarMessage.m +++ b/OpenChange/MAPIStoreCalendarMessage.m @@ -1111,9 +1111,7 @@ newAid = [[self attachmentKeys] count]; newAttachment = [MAPIStoreCalendarAttachment - mapiStoreObjectWithSOGoObject: nil - inContainer: self]; - [newAttachment setIsNew: YES]; + mapiStoreObjectInContainer: self]; [newAttachment setAID: newAid]; newKey = [NSString stringWithFormat: @"%ul", newAid]; [attachmentParts setObject: newAttachment diff --git a/OpenChange/MAPIStoreContactsMessage.m b/OpenChange/MAPIStoreContactsMessage.m index c8af5c05b..99f297ea1 100644 --- a/OpenChange/MAPIStoreContactsMessage.m +++ b/OpenChange/MAPIStoreContactsMessage.m @@ -35,6 +35,7 @@ #import #import +#import "MAPIStoreAttachment.h" #import "MAPIStoreContactsAttachment.h" #import "MAPIStoreContactsFolder.h" #import "MAPIStorePropertySelectors.h" @@ -767,8 +768,7 @@ || [encoding isEqualToString: @"BASE64"]) { attachment = [MAPIStoreContactsAttachment - mapiStoreObjectWithSOGoObject: nil - inContainer: self]; + mapiStoreObjectInContainer: self]; [attachment setAID: 0]; [attachment setPhoto: photo]; [attachmentParts setObject: attachment forKey: @"photo"]; diff --git a/OpenChange/MAPIStoreContext.m b/OpenChange/MAPIStoreContext.m index 1817658ea..a3704289b 100644 --- a/OpenChange/MAPIStoreContext.m +++ b/OpenChange/MAPIStoreContext.m @@ -28,11 +28,9 @@ #import #import +#import #import -#import "SOGoMAPIFSFolder.h" -#import "SOGoMAPIFSMessage.h" - #import "MAPIStoreAttachment.h" // #import "MAPIStoreAttachmentTable.h" #import "MAPIStoreFallbackContext.h" @@ -433,25 +431,29 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) [self ensureContextFolder]; currentFolder = [self rootSOGoFolder]; + [containersBag addObject: currentFolder]; path = [contextUrl path]; if ([path hasPrefix: @"/"]) path = [path substringFromIndex: 1]; if ([path hasSuffix: @"/"]) path = [path substringToIndex: [path length] - 1]; - pathComponents = [path componentsSeparatedByString: @"/"]; - max = [pathComponents count]; - for (count = 0; currentFolder && count < max; count++) + if ([path length] > 0) { - [woContext setClientObject: currentFolder]; - currentFolder - = [currentFolder lookupName: [pathComponents objectAtIndex: count] - inContext: woContext + pathComponents = [path componentsSeparatedByString: @"/"]; + max = [pathComponents count]; + for (count = 0; currentFolder && count < max; count++) + { + [woContext setClientObject: currentFolder]; + currentFolder = [currentFolder + lookupName: [pathComponents objectAtIndex: count] + inContext: woContext acquire: NO]; - if ([currentFolder isKindOfClass: SOGoObjectK]) /* class common to all - SOGo folder types */ - [containersBag addObject: currentFolder]; - else - currentFolder = nil; + if ([currentFolder isKindOfClass: SOGoObjectK]) /* class common to all + SOGo folder types */ + [containersBag addObject: currentFolder]; + else + currentFolder = nil; + } } if (currentFolder) @@ -460,7 +462,6 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri) mapiStoreObjectWithSOGoObject: currentFolder inContainer: nil]; [baseFolder setContext: self]; - *folderPtr = baseFolder; rc = MAPISTORE_SUCCESS; } diff --git a/OpenChange/MAPIStoreFSBaseContext.h b/OpenChange/MAPIStoreDBBaseContext.h similarity index 76% rename from OpenChange/MAPIStoreFSBaseContext.h rename to OpenChange/MAPIStoreDBBaseContext.h index ecc63d1e6..62f5bd489 100644 --- a/OpenChange/MAPIStoreFSBaseContext.h +++ b/OpenChange/MAPIStoreDBBaseContext.h @@ -1,6 +1,6 @@ -/* MAPIStoreFSBaseContext.h - this file is part of SOGo +/* MAPIStoreDBBaseContext.h - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. + * Copyright (C) 2012 Inverse inc. * * Author: Wolfgang Sourdeau * @@ -20,13 +20,13 @@ * Boston, MA 02111-1307, USA. */ -#ifndef MAPISTOREFSBASECONTEXT_H -#define MAPISTOREFSBASECONTEXT_H +#ifndef MAPISTOREDBBASECONTEXT_H +#define MAPISTOREDBBASECONTEXT_H #import "MAPIStoreContext.h" -@interface MAPIStoreFSBaseContext : MAPIStoreContext +@interface MAPIStoreDBBaseContext : MAPIStoreContext @end -#endif /* MAPISTOREFSBASECONTEXT_H */ +#endif /* MAPISTOREDBBASECONTEXT_H */ diff --git a/OpenChange/MAPIStoreDBBaseContext.m b/OpenChange/MAPIStoreDBBaseContext.m new file mode 100644 index 000000000..d82538083 --- /dev/null +++ b/OpenChange/MAPIStoreDBBaseContext.m @@ -0,0 +1,116 @@ +/* MAPIStoreDBBaseContext.m - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* A generic parent class for all context that will store their data on the + disk in the form of a plist. */ + +#import +#import +#import + +#import + +#import "MAPIStoreDBFolder.h" +#import "MAPIStoreMapping.h" +#import "MAPIStoreUserContext.h" +#import "SOGoMAPIDBFolder.h" + +#import "MAPIStoreDBBaseContext.h" + +#undef DEBUG +#include + +static Class MAPIStoreDBFolderK; + +@implementation MAPIStoreDBBaseContext + ++ (void) initialize +{ + MAPIStoreDBFolderK = [MAPIStoreDBFolder class]; +} + ++ (NSString *) MAPIModuleName +{ + return nil; +} + +- (Class) MAPIStoreFolderClass +{ + return MAPIStoreDBFolderK; +} + +- (void) ensureContextFolder +{ + SOGoMAPIDBFolder *currentFolder; + NSArray *parts; + NSMutableArray *folders; + NSString *folderName; + NSUInteger count, max; + + parts = [[contextUrl path] componentsSeparatedByString: @"/"]; + max = [parts count]; + folders = [NSMutableArray arrayWithCapacity: max]; + + /* build the folder chain */ + currentFolder = [self rootSOGoFolder]; + [folders addObject: currentFolder]; + for (count = 1; count < max; count++) + { + folderName = [parts objectAtIndex: count]; + if ([folderName length] > 0) + { + currentFolder = [SOGoMAPIDBFolder objectWithName: folderName + inContainer: currentFolder]; + [folders addObject: currentFolder]; + } + } + + /* ensure each folder in the chain actually exists, so that it becomes + "listable" in further operations */ + max = [folders count]; + for (count = 0; count < max; count++) + { + currentFolder = [folders objectAtIndex: count]; + [currentFolder reloadIfNeeded]; + if ([currentFolder isNew]) + [currentFolder save]; + } +} + +- (id) rootSOGoFolder +{ + SOGoMAPIDBFolder *folder; + + [userContext ensureFolderTableExists]; + + folder = [SOGoMAPIDBFolder objectWithName: [isa MAPIModuleName] + inContainer: nil]; + [folder setTableUrl: [userContext folderTableURL]]; + // [folder reloadIfNeeded]; + + /* we don't need to set the "path prefix" of the folder since the module + name is used as the label for the top folder */ + + return folder; +} + +@end diff --git a/OpenChange/MAPIStoreFSFolder.h b/OpenChange/MAPIStoreDBFolder.h similarity index 78% rename from OpenChange/MAPIStoreFSFolder.h rename to OpenChange/MAPIStoreDBFolder.h index 75e3f08e5..2415af934 100644 --- a/OpenChange/MAPIStoreFSFolder.h +++ b/OpenChange/MAPIStoreDBFolder.h @@ -1,6 +1,6 @@ -/* MAPIStoreFSFolder.h - this file is part of SOGo +/* MAPIStoreDBFolder.h - this file is part of SOGo * - * Copyright (C) 2011 Inverse inc + * Copyright (C) 2012 Inverse inc * * Author: Wolfgang Sourdeau * @@ -20,14 +20,14 @@ * Boston, MA 02111-1307, USA. */ -#ifndef MAPISTOREFSFOLDER_H -#define MAPISTOREFSFOLDER_H +#ifndef MAPISTOREDBFOLDER_H +#define MAPISTOREDBFOLDER_H #import "MAPIStoreFolder.h" -@interface MAPIStoreFSFolder : MAPIStoreFolder +@interface MAPIStoreDBFolder : MAPIStoreFolder @end -#endif /* MAPISTOREFSFOLDER_H */ +#endif /* MAPISTOREDBFOLDER_H */ diff --git a/OpenChange/MAPIStoreFSFolder.m b/OpenChange/MAPIStoreDBFolder.m similarity index 75% rename from OpenChange/MAPIStoreFSFolder.m rename to OpenChange/MAPIStoreDBFolder.m index 68f57545f..641b9dc59 100644 --- a/OpenChange/MAPIStoreFSFolder.m +++ b/OpenChange/MAPIStoreDBFolder.m @@ -1,4 +1,4 @@ -/* MAPIStoreFSFolder.m - this file is part of SOGo +/* MAPIStoreDBFolder.m - this file is part of SOGo * * Copyright (C) 2011 Inverse inc * @@ -31,15 +31,15 @@ #import #import "EOQualifier+MAPI.h" #import "MAPIStoreContext.h" -#import "MAPIStoreFSFolderTable.h" -#import "MAPIStoreFSMessage.h" -#import "MAPIStoreFSMessageTable.h" +#import "MAPIStoreDBFolderTable.h" +#import "MAPIStoreDBMessage.h" +#import "MAPIStoreDBMessageTable.h" #import "MAPIStoreTypes.h" #import "MAPIStoreUserContext.h" -#import "SOGoMAPIFSFolder.h" -#import "SOGoMAPIFSMessage.h" +#import "SOGoMAPIDBFolder.h" +#import "SOGoMAPIDBMessage.h" -#import "MAPIStoreFSFolder.h" +#import "MAPIStoreDBFolder.h" #undef DEBUG #include @@ -57,39 +57,34 @@ static NSString *MAPIStoreRightCreateSubfolders = @"RightsCreateSubfolders"; static NSString *MAPIStoreRightFolderOwner = @"RightsFolderOwner"; static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; -@implementation MAPIStoreFSFolder +@implementation MAPIStoreDBFolder + (void) initialize { EOKeyValueQualifierK = [EOKeyValueQualifier class]; } +- (void) setupAuxiliaryObjects +{ + [super setupAuxiliaryObjects]; + ASSIGN (sogoObject, dbFolder); +} + - (MAPIStoreMessageTable *) messageTable { - return [MAPIStoreFSMessageTable tableForContainer: self]; + return [MAPIStoreDBMessageTable tableForContainer: self]; } - (MAPIStoreFolderTable *) folderTable { - return [MAPIStoreFSFolderTable tableForContainer: self]; + return [MAPIStoreDBFolderTable tableForContainer: self]; } - (enum mapistore_error) createFolder: (struct SRow *) aRow withFID: (uint64_t) newFID andKey: (NSString **) newKeyP { - NSString *newKey, *urlString; - NSURL *childURL; - SOGoMAPIFSFolder *childFolder; - - newKey = [NSString stringWithFormat: @"0x%.16"PRIx64, (unsigned long long) newFID]; - - urlString = [NSString stringWithFormat: @"%@/%@", [self url], newKey]; - childURL = [NSURL URLWithString: [urlString stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]]; - childFolder = [SOGoMAPIFSFolder folderWithURL: childURL - andTableType: MAPISTORE_MESSAGE_TABLE]; - [childFolder ensureDirectory]; - *newKeyP = newKey; + *newKeyP = [NSString stringWithFormat: @"0x%.16"PRIx64, (unsigned long long) newFID]; return MAPISTORE_SUCCESS; } @@ -97,14 +92,15 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; - (MAPIStoreMessage *) createMessage { MAPIStoreMessage *newMessage; - SOGoMAPIFSMessage *fsObject; + SOGoMAPIDBMessage *fsObject; NSString *newKey; newKey = [NSString stringWithFormat: @"%@.plist", [SOGoObject globallyUniqueObjectId]]; - fsObject = [SOGoMAPIFSMessage objectWithName: newKey + fsObject = [SOGoMAPIDBMessage objectWithName: newKey inContainer: sogoObject]; - newMessage = [MAPIStoreFSMessage mapiStoreObjectWithSOGoObject: fsObject + [fsObject setObjectType: MAPIDBObjectTypeMessage]; + newMessage = [MAPIStoreDBMessage mapiStoreObjectWithSOGoObject: fsObject inContainer: self]; return newMessage; @@ -119,9 +115,10 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; ownerUser = [[self userContext] sogoUser]; if ([[context activeUser] isEqual: ownerUser] || [self subscriberCanReadMessages]) - keys = [(SOGoMAPIFSFolder *) sogoObject - toOneRelationshipKeysMatchingQualifier: qualifier - andSortOrderings: sortOrderings]; + keys = [(SOGoMAPIDBFolder *) sogoObject childKeysOfType: MAPIDBObjectTypeMessage + includeDeleted: NO + matchingQualifier: qualifier + andSortOrderings: sortOrderings]; else keys = [NSArray array]; @@ -131,39 +128,17 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; - (NSArray *) folderKeysMatchingQualifier: (EOQualifier *) qualifier andSortOrderings: (NSArray *) sortOrderings { - NSArray *entries; - NSMutableArray *filteredEntries; - NSUInteger count, max; - MAPIStoreFSFolder *subfolder; - SOGoMAPIFSMessage *propertiesMessage; - NSString *subfolderKey; - - entries = [(SOGoMAPIFSFolder *) sogoObject toManyRelationshipKeys]; - if (qualifier) - { - max = [entries count]; - filteredEntries = [NSMutableArray arrayWithCapacity: max]; - for (count = 0; count < max; count++) - { - subfolderKey = [entries objectAtIndex: count]; - subfolder = [self lookupFolder: subfolderKey]; - propertiesMessage = [subfolder propertiesMessage]; - if ([qualifier evaluateMAPIVolatileMessage: propertiesMessage]) - [filteredEntries addObject: subfolderKey]; - } - entries = filteredEntries; - } - if (sortOrderings) - [self errorWithFormat: @"sort orderings are not used for folders"]; - - return entries; + return [dbFolder childKeysOfType: MAPIDBObjectTypeFolder + includeDeleted: NO + matchingQualifier: qualifier + andSortOrderings: sortOrderings]; } - (NSDate *) lastMessageModificationTime { NSUInteger count, max; NSDate *date, *fileDate; - MAPIStoreFSMessage *msg; + MAPIStoreDBMessage *msg; NSArray *messageKeys; messageKeys = [self messageKeys]; @@ -189,7 +164,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact"; - (SOGoFolder *) aclFolder { - return propsFolder; + return sogoObject; } - (NSArray *) rolesForExchangeRights: (uint32_t) rights diff --git a/OpenChange/MAPIStoreFSFolderTable.h b/OpenChange/MAPIStoreDBFolderTable.h similarity index 76% rename from OpenChange/MAPIStoreFSFolderTable.h rename to OpenChange/MAPIStoreDBFolderTable.h index f5a5dfb4e..afc3018e2 100644 --- a/OpenChange/MAPIStoreFSFolderTable.h +++ b/OpenChange/MAPIStoreDBFolderTable.h @@ -1,6 +1,6 @@ -/* MAPIStoreFSFolderTable.h - this file is part of SOGo +/* MAPIStoreDBFolderTable.h - this file is part of SOGo * - * Copyright (C) 2011 Inverse inc + * Copyright (C) 2012 Inverse inc * * Author: Wolfgang Sourdeau * @@ -20,12 +20,12 @@ * Boston, MA 02111-1307, USA. */ -#ifndef MAPISTOREFSFOLDERTABLE_H -#define MAPISTOREFSFOLDERTABLE_H +#ifndef MAPISTOREDBFOLDERTABLE_H +#define MAPISTOREDBFOLDERTABLE_H #import "MAPIStoreFolderTable.h" -@interface MAPIStoreFSFolderTable : MAPIStoreFolderTable +@interface MAPIStoreDBFolderTable : MAPIStoreFolderTable @end -#endif /* MAPISTOREFSFOLDERTABLE_H */ +#endif /* MAPISTOREDBFOLDERTABLE_H */ diff --git a/OpenChange/MAPIStoreFSFolderTable.m b/OpenChange/MAPIStoreDBFolderTable.m similarity index 85% rename from OpenChange/MAPIStoreFSFolderTable.m rename to OpenChange/MAPIStoreDBFolderTable.m index a834c5fb4..01a40b7f0 100644 --- a/OpenChange/MAPIStoreFSFolderTable.m +++ b/OpenChange/MAPIStoreDBFolderTable.m @@ -1,6 +1,6 @@ -/* MAPIStoreFSFolderTable.m - this file is part of SOGo +/* MAPIStoreDBFolderTable.m - this file is part of SOGo * - * Copyright (C) 2011 Inverse inc + * Copyright (C) 2012 Inverse inc * * Author: Wolfgang Sourdeau * @@ -24,9 +24,9 @@ #import "MAPIStoreTypes.h" -#import "MAPIStoreFSFolderTable.h" +#import "MAPIStoreDBFolderTable.h" -@implementation MAPIStoreFSFolderTable +@implementation MAPIStoreDBFolderTable - (NSString *) backendIdentifierForProperty: (enum MAPITAGS) property { diff --git a/OpenChange/MAPIStoreFSMessage.h b/OpenChange/MAPIStoreDBMessage.h similarity index 74% rename from OpenChange/MAPIStoreFSMessage.h rename to OpenChange/MAPIStoreDBMessage.h index 20084a4f2..532f53a8e 100644 --- a/OpenChange/MAPIStoreFSMessage.h +++ b/OpenChange/MAPIStoreDBMessage.h @@ -1,6 +1,6 @@ -/* MAPIStoreFSMessage.h - this file is part of SOGo +/* MAPIStoreDBMessage.h - this file is part of SOGo * - * Copyright (C) 2011 Inverse inc + * Copyright (C) 2012 Inverse inc * * Author: Wolfgang Sourdeau * @@ -20,12 +20,12 @@ * Boston, MA 02111-1307, USA. */ -#ifndef MAPISTOREFSMESSAGE_H -#define MAPISTOREFSMESSAGE_H +#ifndef MAPISTOREDBMESSAGE_H +#define MAPISTOREDBMESSAGE_H -#import "MAPIStoreVolatileMessage.h" +#import "MAPIStoreMessage.h" -@interface MAPIStoreFSMessage : MAPIStoreVolatileMessage +@interface MAPIStoreDBMessage : MAPIStoreMessage @end -#endif /* MAPISTOREFSMESSAGE_H */ +#endif /* MAPISTOREDBMESSAGE_H */ diff --git a/OpenChange/MAPIStoreFSMessage.m b/OpenChange/MAPIStoreDBMessage.m similarity index 59% rename from OpenChange/MAPIStoreFSMessage.m rename to OpenChange/MAPIStoreDBMessage.m index 7b7939884..945b41c8e 100644 --- a/OpenChange/MAPIStoreFSMessage.m +++ b/OpenChange/MAPIStoreDBMessage.m @@ -1,4 +1,4 @@ -/* MAPIStoreFSMessage.m - this file is part of SOGo +/* MAPIStoreDBMessage.m - this file is part of SOGo * * Copyright (C) 2011 Inverse inc * @@ -21,24 +21,25 @@ */ #import +#import #import #import #import #import "MAPIStoreContext.h" #import "MAPIStorePropertySelectors.h" -#import "SOGoMAPIFSMessage.h" +#import "SOGoMAPIDBMessage.h" -#import "MAPIStoreFSFolder.h" -#import "MAPIStoreFSMessage.h" +#import "MAPIStoreDBFolder.h" +#import "MAPIStoreDBMessage.h" #import "MAPIStoreTypes.h" -#import "NSData+MAPIStore.h" +#import "NSObject+MAPIStore.h" #undef DEBUG #include #include -@implementation MAPIStoreFSMessage +@implementation MAPIStoreDBMessage + (int) getAvailableProperties: (struct SPropTagArray **) propertiesP inMemCtx: (TALLOC_CTX *) memCtx @@ -60,13 +61,96 @@ /* FIXME (hack): append a few undocumented properties that can be added to FAI messages */ for (count = 0; count < 8; count++) - properties->aulPropTag[MAPIStoreSupportedPropertiesCount+count] = faiProperties[count]; + properties->aulPropTag[MAPIStoreSupportedPropertiesCount+count] + = faiProperties[count]; *propertiesP = properties; return MAPISTORE_SUCCESS; } +- (id) initWithSOGoObject: (id) newSOGoObject + inContainer: (MAPIStoreObject *) newContainer +{ + if ((self = [super initWithSOGoObject: newSOGoObject + inContainer: newContainer])) + { + [properties release]; + properties = [newSOGoObject properties]; + [properties retain]; + } + + return self; +} + +- (uint64_t) objectVersion +{ + NSNumber *versionNbr; + uint64_t objectVersion; + + [(SOGoMAPIDBMessage *) sogoObject reloadIfNeeded]; + versionNbr = [properties objectForKey: @"version"]; + if (versionNbr) + objectVersion = [versionNbr unsignedLongLongValue]; + else + objectVersion = ULLONG_MAX; + + return objectVersion; +} + +- (int) getProperties: (struct mapistore_property_data *) data + withTags: (enum MAPITAGS *) tags + andCount: (uint16_t) columnCount + inMemCtx: (TALLOC_CTX *) memCtx +{ + [sogoObject reloadIfNeeded]; + + return [super getProperties: data + withTags: tags + andCount: columnCount + inMemCtx: memCtx]; +} + +- (int) getProperty: (void **) data + withTag: (enum MAPITAGS) propTag + inMemCtx: (TALLOC_CTX *) memCtx +{ + id value; + int rc; + + value = [properties objectForKey: MAPIPropertyKey (propTag)]; + if (value) + rc = [value getValue: data forTag: propTag inMemCtx: memCtx]; + else + rc = [super getProperty: data withTag: propTag inMemCtx: memCtx]; + + return rc; +} + +- (int) getPidTagSubject: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + id value; + int rc; + + value = [properties + objectForKey: MAPIPropertyKey (PidTagNormalizedSubject)]; + if (value) + rc = [value getValue: data forTag: PidTagNormalizedSubject + inMemCtx: memCtx]; + else + rc = MAPISTORE_ERR_NOT_FOUND; + + return rc; +} + +- (void) addProperties: (NSDictionary *) newNewProperties +{ + [sogoObject reloadIfNeeded]; + + [super addProperties: newNewProperties]; +} + - (void) save { uint64_t newVersion; @@ -76,13 +160,11 @@ newVersion = exchange_globcnt ([[self context] getNewChangeNumber] >> 16); [properties setObject: [NSNumber numberWithUnsignedLongLong: newVersion] - forKey: @"version"]; + forKey: @"version"]; [self logWithFormat: @"%d props in dict", [properties count]]; - [sogoObject appendProperties: properties]; [sogoObject save]; - [properties removeAllObjects]; } - (BOOL) _messageIsFreeBusy @@ -91,7 +173,7 @@ /* This is a HACK until we figure out how to determine a message position in the mailbox hierarchy.... (missing: folderid and role) */ - msgClass = [[sogoObject properties] + msgClass = [properties objectForKey: MAPIPropertyKey (PR_MESSAGE_CLASS_UNICODE)]; return [msgClass isEqualToString: @"IPM.Microsoft.ScheduleData.FreeBusy"]; @@ -115,12 +197,12 @@ - (NSDate *) creationTime { - return [sogoObject creationTime]; + return [sogoObject creationDate]; } - (NSDate *) lastModificationTime { - return [sogoObject lastModificationTime]; + return [sogoObject lastModified]; } @end diff --git a/OpenChange/MAPIStoreFSMessageTable.h b/OpenChange/MAPIStoreDBMessageTable.h similarity index 76% rename from OpenChange/MAPIStoreFSMessageTable.h rename to OpenChange/MAPIStoreDBMessageTable.h index 452c8ffdb..4b9f66480 100644 --- a/OpenChange/MAPIStoreFSMessageTable.h +++ b/OpenChange/MAPIStoreDBMessageTable.h @@ -1,6 +1,6 @@ -/* MAPIStoreFSMessageTable.h - this file is part of SOGo +/* MAPIStoreDBMessageTable.h - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc + * Copyright (C) 2012 Inverse inc * * Author: Wolfgang Sourdeau * @@ -20,12 +20,12 @@ * Boston, MA 02111-1307, USA. */ -#ifndef MAPISTOREFSMESSAGETABLE_H -#define MAPISTOREFSMESSAGETABLE_H +#ifndef MAPISTOREDBMESSAGETABLE_H +#define MAPISTOREDBMESSAGETABLE_H #import "MAPIStoreMessageTable.h" -@interface MAPIStoreFSMessageTable : MAPIStoreMessageTable +@interface MAPIStoreDBMessageTable : MAPIStoreMessageTable @end -#endif /* MAPISTOREFSMESSAGETABLE_H */ +#endif /* MAPISTOREDBMESSAGETABLE_H */ diff --git a/OpenChange/MAPIStoreFSMessageTable.m b/OpenChange/MAPIStoreDBMessageTable.m similarity index 92% rename from OpenChange/MAPIStoreFSMessageTable.m rename to OpenChange/MAPIStoreDBMessageTable.m index 7c8504c67..9f791d68a 100644 --- a/OpenChange/MAPIStoreFSMessageTable.m +++ b/OpenChange/MAPIStoreDBMessageTable.m @@ -1,6 +1,6 @@ -/* MAPIStoreFSMessageTable.m - this file is part of SOGo +/* MAPIStoreDBMessageTable.m - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc + * Copyright (C) 2012 Inverse inc * * Author: Wolfgang Sourdeau * @@ -25,25 +25,25 @@ #import #import "MAPIStoreTypes.h" -#import "MAPIStoreFSMessage.h" +#import "MAPIStoreDBMessage.h" -#import "MAPIStoreFSMessageTable.h" +#import "MAPIStoreDBMessageTable.h" #undef DEBUG #include -static Class MAPIStoreFSMessageK = Nil; +static Class MAPIStoreDBMessageK = Nil; -@implementation MAPIStoreFSMessageTable +@implementation MAPIStoreDBMessageTable + (void) initialize { - MAPIStoreFSMessageK = [MAPIStoreFSMessage class]; + MAPIStoreDBMessageK = [MAPIStoreDBMessage class]; } + (Class) childObjectClass { - return MAPIStoreFSMessageK; + return MAPIStoreDBMessageK; } - (NSString *) backendIdentifierForProperty: (enum MAPITAGS) property diff --git a/OpenChange/MAPIStoreFAIMessage.h b/OpenChange/MAPIStoreFAIMessage.h index 4ac10e143..fd4d08930 100644 --- a/OpenChange/MAPIStoreFAIMessage.h +++ b/OpenChange/MAPIStoreFAIMessage.h @@ -23,9 +23,9 @@ #ifndef MAPISTOREFAIMESSAGE_H #define MAPISTOREFAIMESSAGE_H -#import "MAPIStoreFSMessage.h" +#import "MAPIStoreDBMessage.h" -@interface MAPIStoreFAIMessage : MAPIStoreFSMessage +@interface MAPIStoreFAIMessage : MAPIStoreDBMessage @end #endif /* MAPISTOREFAIMESSAGE_H */ diff --git a/OpenChange/MAPIStoreFAIMessageTable.h b/OpenChange/MAPIStoreFAIMessageTable.h index 70e12f43f..0aa6d7230 100644 --- a/OpenChange/MAPIStoreFAIMessageTable.h +++ b/OpenChange/MAPIStoreFAIMessageTable.h @@ -23,9 +23,9 @@ #ifndef MAPISTOREFAIMESSAGETABLE_H #define MAPISTOREFAIMESSAGETABLE_H -#import "MAPIStoreFSMessageTable.h" +#import "MAPIStoreDBMessageTable.h" -@interface MAPIStoreFAIMessageTable : MAPIStoreFSMessageTable +@interface MAPIStoreFAIMessageTable : MAPIStoreDBMessageTable @end #endif /* MAPISTOREFAIMESSAGETABLE_H */ diff --git a/OpenChange/MAPIStoreFSBaseContext.m b/OpenChange/MAPIStoreFSBaseContext.m deleted file mode 100644 index a3e448082..000000000 --- a/OpenChange/MAPIStoreFSBaseContext.m +++ /dev/null @@ -1,79 +0,0 @@ -/* MAPIStoreFSBaseContext.m - this file is part of SOGo - * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * This file 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/* A generic parent class for all context that will store their data on the - disk in the form of a plist. */ - -#import -#import - -#import - -#import "MAPIStoreFSFolder.h" -#import "MAPIStoreMapping.h" -#import "MAPIStoreUserContext.h" -#import "SOGoMAPIFSFolder.h" - -#import "MAPIStoreFSBaseContext.h" - -#undef DEBUG -#include - -static Class MAPIStoreFSFolderK; - -@implementation MAPIStoreFSBaseContext - -+ (void) initialize -{ - MAPIStoreFSFolderK = [MAPIStoreFSFolder class]; -} - -+ (NSString *) MAPIModuleName -{ - return nil; -} - -- (Class) MAPIStoreFolderClass -{ - return MAPIStoreFSFolderK; -} - -- (void) ensureContextFolder -{ - SOGoMAPIFSFolder *contextFolder; - - contextFolder = [SOGoMAPIFSFolder folderWithURL: contextUrl - andTableType: MAPISTORE_MESSAGE_TABLE]; - [contextFolder ensureDirectory]; -} - -- (id) rootSOGoFolder -{ - NSString *urlString; - - urlString = [NSString stringWithFormat: @"sogo://%@@%@/", - [userContext username], [isa MAPIModuleName]]; - return [SOGoMAPIFSFolder folderWithURL: [NSURL URLWithString: urlString] - andTableType: MAPISTORE_MESSAGE_TABLE]; -} - -@end diff --git a/OpenChange/MAPIStoreFallbackContext.h b/OpenChange/MAPIStoreFallbackContext.h index da8e03c0d..eaf992bb7 100644 --- a/OpenChange/MAPIStoreFallbackContext.h +++ b/OpenChange/MAPIStoreFallbackContext.h @@ -23,9 +23,9 @@ #ifndef MAPISTOREFALLBACKCONTEXT_H #define MAPISTOREFALLBACKCONTEXT_H -#import "MAPIStoreFSBaseContext.h" +#import "MAPIStoreDBBaseContext.h" -@interface MAPIStoreFallbackContext : MAPIStoreFSBaseContext +@interface MAPIStoreFallbackContext : MAPIStoreDBBaseContext @end diff --git a/OpenChange/MAPIStoreFallbackContext.m b/OpenChange/MAPIStoreFallbackContext.m index c5270d055..37da58d7d 100644 --- a/OpenChange/MAPIStoreFallbackContext.m +++ b/OpenChange/MAPIStoreFallbackContext.m @@ -26,7 +26,7 @@ #import "MAPIStoreUserContext.h" #import "NSString+MAPIStore.h" -#import "SOGoMAPIFSFolder.h" +#import "SOGoMAPIDBFolder.h" #import "MAPIStoreFallbackContext.h" @@ -51,10 +51,11 @@ inMemCtx: (TALLOC_CTX *) memCtx { struct mapistore_contexts_list *firstContext = NULL, *context; - SOGoMAPIFSFolder *root; + SOGoMAPIDBFolder *root; NSArray *names; NSUInteger count, max; NSString *baseURL, *url, *name; + MAPIStoreUserContext *userContext; baseURL = [NSString stringWithFormat: @"sogo://%@@fallback/", userName]; @@ -67,11 +68,15 @@ DLIST_ADD_END (firstContext, context, void); - /* Maybe emsmdbp_provisioning should be fixed in order to only take the uri returned above to avoid deleting its entries... */ - root = [SOGoMAPIFSFolder folderWithURL: [NSURL URLWithString: baseURL] - andTableType: MAPISTORE_MESSAGE_TABLE]; + root = [SOGoMAPIDBFolder objectWithName: [self MAPIModuleName] + inContainer: nil]; + [root setOwner: userName]; + userContext = [MAPIStoreUserContext userContextWithUsername: userName + andTDBIndexing: indexingTdb]; + [userContext ensureFolderTableExists]; + [root setTableUrl: [userContext folderTableURL]]; names = [root toManyRelationshipKeys]; max = [names count]; for (count = 0; count < max; count++) diff --git a/OpenChange/MAPIStoreFolder.h b/OpenChange/MAPIStoreFolder.h index 41b3bdd61..b29a93d3d 100644 --- a/OpenChange/MAPIStoreFolder.h +++ b/OpenChange/MAPIStoreFolder.h @@ -38,29 +38,34 @@ @class MAPIStoreMessageTable; @class MAPIStorePermissionsTable; @class SOGoFolder; -@class SOGoMAPIFSFolder; -@class SOGoMAPIFSMessage; +@class SOGoMAPIDBFolder; +@class SOGoMAPIDBMessage; -#import "MAPIStoreObject.h" +#import "MAPIStoreSOGoObject.h" -@interface MAPIStoreFolder : MAPIStoreObject +@interface MAPIStoreFolder : MAPIStoreSOGoObject { MAPIStoreContext *context; // NSArray *messageKeys; // NSArray *faiMessageKeys; // NSArray *folderKeys; - SOGoMAPIFSFolder *faiFolder; - SOGoMAPIFSFolder *propsFolder; - SOGoMAPIFSMessage *propsMessage; + SOGoMAPIDBFolder *dbFolder; + // SOGoMAPIDBFolder *faiFolder; + // SOGoMAPIDBFolder *propsFolder; + // SOGoMAPIDBMessage *propsMessage; } - (void) setContext: (MAPIStoreContext *) newContext; +- (void) setupAuxiliaryObjects; + +- (SOGoMAPIDBFolder *) dbFolder; + - (NSArray *) activeMessageTables; - (NSArray *) activeFAIMessageTables; -- (SOGoMAPIFSMessage *) propertiesMessage; +// - (SOGoMAPIDBMessage *) propertiesMessage; - (id) lookupMessageByURL: (NSString *) messageURL; - (id) lookupFolderByURL: (NSString *) folderURL; diff --git a/OpenChange/MAPIStoreFolder.m b/OpenChange/MAPIStoreFolder.m index 3e480f11d..f4128695d 100644 --- a/OpenChange/MAPIStoreFolder.m +++ b/OpenChange/MAPIStoreFolder.m @@ -48,8 +48,8 @@ #import "NSDate+MAPIStore.h" #import "NSString+MAPIStore.h" #import "NSObject+MAPIStore.h" -#import "SOGoMAPIFSFolder.h" -#import "SOGoMAPIFSMessage.h" +#import "SOGoMAPIDBFolder.h" +#import "SOGoMAPIDBMessage.h" #include @@ -79,33 +79,67 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe // messageKeys = nil; // faiMessageKeys = nil; // folderKeys = nil; - faiFolder = nil; + dbFolder = nil; context = nil; - propsFolder = nil; - propsMessage = nil; + // propsFolder = nil; + // propsMessage = nil; } return self; } -- (void) _setupAuxiliaryObjects +- (void) setupAuxiliaryObjects { - NSURL *propsURL; - NSString *urlString; + NSURL *folderURL; + NSMutableString *pathPrefix; + NSString *path, *folderName; + NSArray *parts; + NSUInteger lastPartIdx; + MAPIStoreUserContext *userContext; + + folderURL = [NSURL URLWithString: [self url]]; + path = [folderURL path]; + path = [path substringFromIndex: 1]; + if ([path length] > 0) + { + parts = [path componentsSeparatedByString: @"/"]; + lastPartIdx = [parts count] - 1; + if ([path hasSuffix: @"/"]) + lastPartIdx--; + folderName = [parts objectAtIndex: lastPartIdx]; + } + else + folderName = [folderURL host]; + + userContext = [self userContext]; + [userContext ensureFolderTableExists]; + + ASSIGN (dbFolder, + [SOGoMAPIDBFolder objectWithName: folderName + inContainer: [container dbFolder]]); + [dbFolder setTableUrl: [userContext folderTableURL]]; + if (!container && [path length] > 0) + { + pathPrefix = [NSMutableString stringWithCapacity: 64]; + [pathPrefix appendFormat: @"/%@", [folderURL host]]; + parts = [parts subarrayWithRange: NSMakeRange (0, lastPartIdx)]; + if ([parts count] > 0) + [pathPrefix appendFormat: @"/%@", [parts componentsJoinedByString: @"/"]]; + [dbFolder setPathPrefix: pathPrefix]; + } + [dbFolder reloadIfNeeded]; + + /* propsMessage and self share the same properties dictionary */ + // ASSIGN (propsMessage, + // [SOGoMAPIDBMessage objectWithName: @"properties.plist" + // inContainer: dbFolder]); + // [propsMessage setObjectType: MAPIDBObjectTypeInternal]; + // [propsMessage reloadIfNeeded]; + [properties release]; + properties = [dbFolder properties]; + [properties retain]; - urlString = [[self url] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; - propsURL = [NSURL URLWithString: urlString]; - [self logWithFormat: @"_setupAuxiliaryObjects: %@", propsURL]; - ASSIGN (faiFolder, - [SOGoMAPIFSFolder folderWithURL: propsURL - andTableType: MAPISTORE_FAI_TABLE]); - ASSIGN (propsFolder, - [SOGoMAPIFSFolder folderWithURL: propsURL - andTableType: MAPISTORE_FOLDER_TABLE]); - ASSIGN (propsMessage, - [SOGoMAPIFSMessage objectWithName: @"properties.plist" - inContainer: propsFolder]); [self setupVersionsMessage]; } @@ -119,7 +153,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe inContainer: newContainer]) && newContainer) { - [self _setupAuxiliaryObjects]; + [self setupAuxiliaryObjects]; } return self; @@ -129,13 +163,13 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe { ASSIGN (context, newContext); if (newContext) - [self _setupAuxiliaryObjects]; + [self setupAuxiliaryObjects]; } - (MAPIStoreContext *) context { if (!context) - [self setContext: [container context]]; + [self setContext: (MAPIStoreContext *) [container context]]; return context; } @@ -145,29 +179,31 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe // [messageKeys release]; // [faiMessageKeys release]; // [folderKeys release]; - [propsMessage release]; - [propsFolder release]; - [faiFolder release]; + // [propsMessage release]; + [dbFolder release]; [context release]; [super dealloc]; } +- (SOGoMAPIDBFolder *) dbFolder +{ + return dbFolder; +} + /* backend interface */ -- (SOGoMAPIFSMessage *) propertiesMessage -{ - return propsMessage; -} +// - (SOGoMAPIDBMessage *) propertiesMessage +// { +// return propsMessage; +// } - (uint64_t) objectVersion { NSNumber *value; - NSDictionary *props; uint64_t cn; - props = [propsMessage properties]; - value = [props objectForKey: MAPIPropertyKey (PidTagChangeNumber)]; + value = [properties objectForKey: MAPIPropertyKey (PidTagChangeNumber)]; if (value) cn = [value unsignedLongLongValue]; else @@ -175,10 +211,10 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe [self logWithFormat: @"no value for PidTagChangeNumber, adding one now"]; cn = [[self context] getNewChangeNumber]; value = [NSNumber numberWithUnsignedLongLong: cn]; - props = [NSDictionary dictionaryWithObject: value - forKey: MAPIPropertyKey (PidTagChangeNumber)]; - [propsMessage appendProperties: props]; - [propsMessage save]; + + [properties setObject: value + forKey: MAPIPropertyKey (PidTagChangeNumber)]; + [dbFolder save]; } return cn >> 16; @@ -186,21 +222,24 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe - (id) lookupFolder: (NSString *) folderKey { - MAPIStoreFolder *childFolder = nil; + MAPIStoreFolder *childFolder; SOGoFolder *sogoFolder; WOContext *woContext; if ([[self folderKeys] containsObject: folderKey]) { woContext = [[self userContext] woContext]; - sogoFolder = [sogoObject lookupName: folderKey - inContext: woContext + sogoFolder = [sogoObject lookupName: folderKey inContext: woContext acquire: NO]; - [sogoFolder setContext: woContext]; if (sogoFolder && ![sogoFolder isKindOfClass: NSExceptionK]) - childFolder = [isa mapiStoreObjectWithSOGoObject: sogoFolder - inContainer: self]; + { + [sogoFolder setContext: woContext]; + childFolder = [isa mapiStoreObjectWithSOGoObject: sogoFolder + inContainer: self]; + } } + else + childFolder = nil; return childFolder; } @@ -264,9 +303,9 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe { if ([[self faiMessageKeys] containsObject: messageKey]) { - msgObject = [faiFolder lookupName: messageKey - inContext: nil - acquire: NO]; + msgObject = [dbFolder lookupName: messageKey + inContext: nil + acquire: NO]; childMessage = [MAPIStoreFAIMessageK mapiStoreObjectWithSOGoObject: msgObject inContainer: self]; @@ -383,9 +422,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe - (int) deleteFolder { - [propsMessage delete]; - [propsFolder delete]; - [faiFolder delete]; + // [propsMessage delete]; + [dbFolder delete]; [self cleanupCaches]; @@ -1004,7 +1042,11 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe /* TODO: this should no longer be required once mapistore v2 API is in place, when we can then do this from -dealloc below */ + [dbFolder reloadIfNeeded]; + propsCopy = [newProperties mutableCopy]; + [propsCopy autorelease]; + currentProp = bannedProps; while (*currentProp) { @@ -1012,9 +1054,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe currentProp++; } - [propsMessage appendProperties: propsCopy]; - [propsMessage save]; - [propsCopy release]; + [properties addEntriesFromDictionary: propsCopy]; + [dbFolder save]; } - (NSArray *) messageKeys @@ -1039,9 +1080,10 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe - (NSArray *) faiMessageKeysMatchingQualifier: (EOQualifier *) qualifier andSortOrderings: (NSArray *) sortOrderings { - return [faiFolder - toOneRelationshipKeysMatchingQualifier: qualifier - andSortOrderings: sortOrderings]; + return [dbFolder childKeysOfType: MAPIDBObjectTypeFAI + includeDeleted: NO + matchingQualifier: qualifier + andSortOrderings: sortOrderings]; } - (NSArray *) faiMessageKeys @@ -1287,6 +1329,19 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe return MAPISTORE_SUCCESS; } +- (int) getProperties: (struct mapistore_property_data *) data + withTags: (enum MAPITAGS *) tags + andCount: (uint16_t) columnCount + inMemCtx: (TALLOC_CTX *) memCtx +{ + [dbFolder reloadIfNeeded]; + + return [super getProperties: data + withTags: tags + andCount: columnCount + inMemCtx: memCtx]; +} + - (int) getProperty: (void **) data withTag: (enum MAPITAGS) propTag inMemCtx: (TALLOC_CTX *) memCtx @@ -1294,8 +1349,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe int rc; id value; - value = [[propsMessage properties] - objectForKey: MAPIPropertyKey (propTag)]; + value = [properties objectForKey: MAPIPropertyKey (propTag)]; if (value) rc = [value getValue: data forTag: propTag inMemCtx: memCtx]; else @@ -1307,13 +1361,15 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe - (MAPIStoreMessage *) _createAssociatedMessage { MAPIStoreMessage *newMessage; - SOGoMAPIFSMessage *fsObject; + SOGoMAPIDBMessage *dbObject; NSString *newKey; newKey = [NSString stringWithFormat: @"%@.plist", [SOGoObject globallyUniqueObjectId]]; - fsObject = [SOGoMAPIFSMessage objectWithName: newKey inContainer: faiFolder]; - newMessage = [MAPIStoreFAIMessageK mapiStoreObjectWithSOGoObject: fsObject + dbObject = [SOGoMAPIDBMessage objectWithName: newKey inContainer: dbFolder]; + [dbObject setObjectType: MAPIDBObjectTypeFAI]; + [dbObject setIsNew: YES]; + newMessage = [MAPIStoreFAIMessageK mapiStoreObjectWithSOGoObject: dbObject inContainer: self]; return newMessage; @@ -1328,9 +1384,15 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe newMessage = [self _createAssociatedMessage]; else newMessage = [self createMessage]; - [newMessage setIsNew: YES]; + /* FIXME: this is ugly as the specifics of message creation should all be + delegated to subclasses */ + if ([newMessage respondsToSelector: @selector (setIsNew:)]) + [newMessage setIsNew: YES]; woContext = [[self userContext] woContext]; - [[newMessage sogoObject] setContext: woContext]; + /* FIXME: this is ugly too as the specifics of message creation should all + be delegated to subclasses */ + if ([newMessage respondsToSelector: @selector (sogoObject:)]) + [[newMessage sogoObject] setContext: woContext]; return newMessage; } @@ -1598,12 +1660,12 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe - (NSDate *) creationTime { - return [propsMessage creationTime]; + return [dbFolder creationDate]; } - (NSDate *) lastModificationTime { - return [propsMessage lastModificationTime]; + return [dbFolder lastModified]; } /* subclasses */ diff --git a/OpenChange/MAPIStoreGCSFolder.h b/OpenChange/MAPIStoreGCSFolder.h index 0d0c1e6df..d889b78a9 100644 --- a/OpenChange/MAPIStoreGCSFolder.h +++ b/OpenChange/MAPIStoreGCSFolder.h @@ -34,7 +34,7 @@ @interface MAPIStoreGCSFolder : MAPIStoreFolder { - SOGoMAPIFSMessage *versionsMessage; + SOGoMAPIDBMessage *versionsMessage; NSArray *activeUserRoles; EOQualifier *componentQualifier; } diff --git a/OpenChange/MAPIStoreGCSFolder.m b/OpenChange/MAPIStoreGCSFolder.m index e5f6bbd65..b94f10ad5 100644 --- a/OpenChange/MAPIStoreGCSFolder.m +++ b/OpenChange/MAPIStoreGCSFolder.m @@ -40,7 +40,7 @@ #import "NSData+MAPIStore.h" #import "NSDate+MAPIStore.h" #import "NSString+MAPIStore.h" -#import "SOGoMAPIFSMessage.h" +#import "SOGoMAPIDBMessage.h" #import "MAPIStoreGCSFolder.h" @@ -71,8 +71,9 @@ static Class NSNumberK; - (void) setupVersionsMessage { ASSIGN (versionsMessage, - [SOGoMAPIFSMessage objectWithName: @"versions.plist" - inContainer: propsFolder]); + [SOGoMAPIDBMessage objectWithName: @"versions.plist" + inContainer: dbFolder]); + [versionsMessage setObjectType: MAPIDBObjectTypeInternal]; } - (void) dealloc @@ -288,7 +289,8 @@ static Class NSNumberK; forKey: @"PredecessorChangeList"]; [changeList release]; } - [changeList setObject: globCnt forKey: guid]; + [changeList setObject: globCnt + forKey: guid]; } - (EOQualifier *) componentQualifier @@ -349,6 +351,7 @@ static Class NSNumberK; [sortOrdering retain]; } + [versionsMessage reloadIfNeeded]; currentProperties = [versionsMessage properties]; lastModificationDate = [currentProperties objectForKey: @"SyncLastModificationDate"]; @@ -451,7 +454,6 @@ static Class NSNumberK; forKey: @"SyncLastSynchronisationDate"]; [currentProperties setObject: lastModificationDate forKey: @"SyncLastModificationDate"]; - [versionsMessage appendProperties: currentProperties]; [versionsMessage save]; } } diff --git a/OpenChange/MAPIStoreMailAttachment.h b/OpenChange/MAPIStoreMailAttachment.h index 5fded594c..f9f0011e9 100644 --- a/OpenChange/MAPIStoreMailAttachment.h +++ b/OpenChange/MAPIStoreMailAttachment.h @@ -30,9 +30,11 @@ @interface MAPIStoreMailAttachment : MAPIStoreAttachment { NSDictionary *bodyInfo; + SOGoMailBodyPart *bodyPart; } - (void) setBodyInfo: (NSDictionary *) newBodyInfo; +- (void) setBodyPart: (SOGoMailBodyPart *) newBodyPart; @end diff --git a/OpenChange/MAPIStoreMailAttachment.m b/OpenChange/MAPIStoreMailAttachment.m index 7b9641548..6834b8c1d 100644 --- a/OpenChange/MAPIStoreMailAttachment.m +++ b/OpenChange/MAPIStoreMailAttachment.m @@ -52,6 +52,7 @@ if ((self = [super init])) { bodyInfo = nil; + bodyPart = nil; } return self; @@ -60,6 +61,7 @@ - (void) dealloc { [bodyInfo release]; + [bodyPart release]; [super dealloc]; } @@ -68,6 +70,11 @@ ASSIGN (bodyInfo, newBodyInfo); } +- (void) setBodyPart: (SOGoMailBodyPart *) newBodyPart +{ + ASSIGN (bodyPart, newBodyPart); +} + - (int) getPidTagAttachMethod: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { @@ -187,7 +194,7 @@ - (int) getPidTagAttachDataBinary: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - *data = [[sogoObject fetchBLOBWithPeek: YES] asBinaryInMemCtx: memCtx]; + *data = [[bodyPart fetchBLOBWithPeek: YES] asBinaryInMemCtx: memCtx]; return MAPISTORE_SUCCESS; } diff --git a/OpenChange/MAPIStoreMailFolder.h b/OpenChange/MAPIStoreMailFolder.h index 7e47e8c09..ab6430489 100644 --- a/OpenChange/MAPIStoreMailFolder.h +++ b/OpenChange/MAPIStoreMailFolder.h @@ -36,7 +36,7 @@ @interface MAPIStoreMailFolder : MAPIStoreFolder { - SOGoMAPIFSMessage *versionsMessage; + SOGoMAPIDBMessage *versionsMessage; } - (BOOL) ensureFolderExists; diff --git a/OpenChange/MAPIStoreMailFolder.m b/OpenChange/MAPIStoreMailFolder.m index f5cfe3340..8fffd8d9d 100644 --- a/OpenChange/MAPIStoreMailFolder.m +++ b/OpenChange/MAPIStoreMailFolder.m @@ -54,9 +54,8 @@ #import "MAPIStoreTypes.h" #import "NSData+MAPIStore.h" #import "NSString+MAPIStore.h" -#import "SOGoMAPIFSMessage.h" +#import "SOGoMAPIDBMessage.h" -#import "SOGoMAPIVolatileMessage.h" #import "MAPIStoreMailVolatileMessage.h" #import "MAPIStoreMailFolder.h" @@ -97,8 +96,9 @@ static Class SOGoMailFolderK, MAPIStoreOutboxFolderK; - (void) setupVersionsMessage { ASSIGN (versionsMessage, - [SOGoMAPIFSMessage objectWithName: @"versions.plist" - inContainer: propsFolder]); + [SOGoMAPIDBMessage objectWithName: @"versions.plist" + inContainer: dbFolder]); + [versionsMessage setObjectType: MAPIDBObjectTypeInternal]; } - (BOOL) ensureFolderExists @@ -119,6 +119,9 @@ static Class SOGoMailFolderK, MAPIStoreOutboxFolderK; && ![[(SOGoMailFolder *) sogoObject displayName] isEqualToString: newDisplayName]) { + [NSException raise: @"MAPIStoreIOException" + format: @"renaming a mail folder via OpenChange is" + @" currently a bad idea"]; [(SOGoMailFolder *) sogoObject renameTo: newDisplayName]; propsCopy = [newProperties mutableCopy]; [propsCopy removeObjectForKey: key]; @@ -489,10 +492,8 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) now = [NSCalendarDate date]; [now setTimeZone: utcTZ]; - currentProperties = [[versionsMessage properties] mutableCopy]; - if (!currentProperties) - currentProperties = [NSMutableDictionary new]; - [currentProperties autorelease]; + [versionsMessage reloadIfNeeded]; + currentProperties = [versionsMessage properties]; messages = [currentProperties objectForKey: @"Messages"]; if (!messages) { @@ -613,7 +614,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data) ti = [NSNumber numberWithDouble: [now timeIntervalSince1970]]; [currentProperties setObject: ti forKey: @"SyncLastSynchronisationDate"]; - [versionsMessage appendProperties: currentProperties]; [versionsMessage save]; } @@ -1004,20 +1004,16 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP) - (MAPIStoreMessage *) createMessage { - MAPIStoreMailVolatileMessage *newMessage; - SOGoMAPIVolatileMessage *newObject; + SOGoMAPIObject *childObject; - newObject = [SOGoMAPIVolatileMessage - objectWithName: [SOGoObject globallyUniqueObjectId] - inContainer: sogoObject]; - newMessage - = [MAPIStoreMailVolatileMessage mapiStoreObjectWithSOGoObject: newObject - inContainer: self]; - - return newMessage; + childObject = [SOGoMAPIObject objectWithName: [SOGoMAPIObject + globallyUniqueObjectId] + inContainer: sogoObject]; + return [MAPIStoreMailVolatileMessage + mapiStoreObjectWithSOGoObject: childObject + inContainer: self]; } - - (NSArray *) rolesForExchangeRights: (uint32_t) rights { NSMutableArray *roles; diff --git a/OpenChange/MAPIStoreMailMessage.m b/OpenChange/MAPIStoreMailMessage.m index 85081c2db..8e8070ae2 100644 --- a/OpenChange/MAPIStoreMailMessage.m +++ b/OpenChange/MAPIStoreMailMessage.m @@ -373,7 +373,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) if (uid) { changeNumber = [(MAPIStoreMailFolder *) container - changeNumberForMessageUID: uid]; + changeNumberForMessageUID: uid]; if (!changeNumber) { [self warnWithFormat: @"attempting to get change number" @@ -1529,8 +1529,8 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data) if (currentPart) { attachment = [MAPIStoreMailAttachment - mapiStoreObjectWithSOGoObject: currentPart - inContainer: self]; + mapiStoreObjectInContainer: self]; + [attachment setBodyPart: currentPart]; [attachment setBodyInfo: [attachmentParts objectForKey: childKey]]; [attachment setAID: [[self attachmentKeys] indexOfObject: childKey]]; } diff --git a/OpenChange/MAPIStoreMailMessageTable.m b/OpenChange/MAPIStoreMailMessageTable.m index 62dc406fd..c1561bbd5 100644 --- a/OpenChange/MAPIStoreMailMessageTable.m +++ b/OpenChange/MAPIStoreMailMessageTable.m @@ -332,7 +332,7 @@ static Class MAPIStoreMailMessageK, NSDataK, NSStringK; if (!fetchedCoreInfos) { fetchedCoreInfos = YES; - [(SOGoMailFolder *) [container sogoObject] + [(SOGoMailFolder *) [(MAPIStoreMailFolder *) container sogoObject] prefetchCoreInfosForMessageKeys: [self restrictedChildKeys]]; } diff --git a/OpenChange/MAPIStoreMailVolatileMessage.h b/OpenChange/MAPIStoreMailVolatileMessage.h index 82959cc6f..53a51847f 100644 --- a/OpenChange/MAPIStoreMailVolatileMessage.h +++ b/OpenChange/MAPIStoreMailVolatileMessage.h @@ -23,9 +23,9 @@ #ifndef MAPISTOREMAILVOLATILEMESSAGE_H #define MAPISTOREMAILVOLATILEMESSAGE_H -#import "MAPIStoreVolatileMessage.h" +#import "MAPIStoreMessage.h" -@interface MAPIStoreMailVolatileMessage : MAPIStoreVolatileMessage +@interface MAPIStoreMailVolatileMessage : MAPIStoreMessage - (int) submitWithFlags: (enum SubmitFlags) flags; diff --git a/OpenChange/MAPIStoreMailVolatileMessage.m b/OpenChange/MAPIStoreMailVolatileMessage.m index 3a3d66be3..b3bb2145f 100644 --- a/OpenChange/MAPIStoreMailVolatileMessage.m +++ b/OpenChange/MAPIStoreMailVolatileMessage.m @@ -51,6 +51,7 @@ #import #import "MAPIStoreAttachment.h" +#import "MAPIStoreAttachmentTable.h" #import "MAPIStoreContext.h" #import "MAPIStoreMailFolder.h" #import "MAPIStoreMIME.h" @@ -60,7 +61,7 @@ #import "NSData+MAPIStore.h" #import "NSObject+MAPIStore.h" #import "NSString+MAPIStore.h" -#import "SOGoMAPIVolatileMessage.h" +#import "SOGoMAPIObject.h" #import "MAPIStoreMailVolatileMessage.h" @@ -68,6 +69,8 @@ #include #include +static Class NSNumberK = Nil; + static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" }; // @@ -242,6 +245,106 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" }; @implementation MAPIStoreMailVolatileMessage ++ (void) initialize +{ + NSNumberK = [NSNumber class]; +} + +- (id) initWithSOGoObject: (id) newSOGoObject + inContainer: (MAPIStoreObject *) newContainer +{ + if ((self = [super initWithSOGoObject: newSOGoObject + inContainer: newContainer])) + { + ASSIGN (properties, [sogoObject properties]); + } + + return self; +} + +- (void) addProperties: (NSDictionary *) newProperties +{ + [super addProperties: newProperties]; + [sogoObject adjustLastModified]; +} + +- (BOOL) canGetProperty: (enum MAPITAGS) propTag +{ + return ([super canGetProperty: propTag] + || [properties objectForKey: MAPIPropertyKey (propTag)] != nil); +} + +- (uint64_t) objectVersion +{ + NSNumber *version; + + version = [properties objectForKey: @"version"]; + + return (version + ? exchange_globcnt ([version unsignedLongLongValue]) + : ULLONG_MAX); +} + +- (int) getPidTagSubject: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +{ + /* if we get here, it means that the properties file didn't contain a + relevant value */ + return [self getEmptyString: data inMemCtx: memCtx]; +} + +- (int) getPidTagMessageClass: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +{ + *data = [@"IPM.Note" asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagChangeKey: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +{ + NSData *changeKey; + int rc; + + changeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)]; + if (changeKey) + { + *data = [changeKey asBinaryInMemCtx: memCtx]; + rc = MAPISTORE_SUCCESS; + } + else + rc = [super getPidTagChangeKey: data inMemCtx: memCtx]; + + return rc; +} + +- (NSArray *) attachmentsKeysMatchingQualifier: (EOQualifier *) qualifier + andSortOrderings: (NSArray *) sortOrderings +{ + NSDictionary *attachments; + + attachments = [properties objectForKey: @"attachments"]; + + return [attachments allKeys]; +} + +- (NSDate *) creationTime +{ + return [sogoObject creationDate]; +} + +- (NSDate *) lastModificationTime +{ + return [sogoObject lastModified]; +} + +- (id) lookupAttachment: (NSString *) childKey +{ + NSDictionary *attachments; + + attachments = [properties objectForKey: @"attachments"]; + + return [attachments objectForKey: childKey]; +} + - (void) getMessageData: (struct mapistore_message **) dataPtr inMemCtx: (TALLOC_CTX *) memCtx { @@ -258,9 +361,11 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" }; samCtx = [[self context] connectionInfo]->sam_ctx; - [super getMessageData: &msgData inMemCtx: memCtx]; + // [super getMessageData: &msgData inMemCtx: memCtx]; - allRecipients = [[sogoObject properties] objectForKey: @"recipients"]; + msgData = talloc_zero (memCtx, struct mapistore_message); + + allRecipients = [properties objectForKey: @"recipients"]; msgData->columns = set_SPropTagArray (msgData, 9, PR_OBJECT_TYPE, PR_DISPLAY_TYPE, @@ -660,10 +765,13 @@ MakeTextPartBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, return textBody; } +// static id +// MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, +// NSString **contentType) static id -MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, - NSString **contentType) +MakeMessageBody (NSDictionary *mailProperties, NSString **contentType) { + NSDictionary *attachmentParts; id messageBody, textBody; NSString *textContentType; NSArray *parts; @@ -671,6 +779,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NGMutableHashMap *headers; NSUInteger count, max; + attachmentParts = [mailProperties objectForKey: @"attachments"]; textBody = MakeTextPartBody (mailProperties, attachmentParts, &textContentType); @@ -707,22 +816,20 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, - (NGMimeMessage *) _generateMessage { - NSDictionary *mailProperties; NSString *contentType; NGMimeMessage *message; NGMutableHashMap *headers; id messageBody; - mailProperties = [sogoObject properties]; - headers = [[NGMutableHashMap alloc] initWithCapacity: 16]; - FillMessageHeadersFromProperties (headers, mailProperties, + FillMessageHeadersFromProperties (headers, properties, [[self context] connectionInfo]); message = [[NGMimeMessage alloc] initWithHeader: headers]; [message autorelease]; [headers release]; - messageBody = MakeMessageBody (mailProperties, attachmentParts, &contentType); + messageBody = MakeMessageBody (properties, &contentType); + // messageBody = MakeMessageBody (mailProperties, attachmentParts, &contentType); if (messageBody) { [headers setObject: contentType forKey: @"content-type"]; @@ -775,7 +882,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, - (int) submitWithFlags: (enum SubmitFlags) flags { - NSDictionary *mailProperties, *recipients; + NSDictionary *recipients; NSData *messageData; NSMutableArray *recipientEmails; NSArray *list; @@ -785,19 +892,17 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, // SOGoMailFolder *sentFolder; SOGoDomainDefaults *dd; NSException *error; - MAPIStoreMapping *mapping; + // MAPIStoreMapping *mapping; - mailProperties = [sogoObject properties]; - msgClass = [mailProperties objectForKey: MAPIPropertyKey (PidTagMessageClass)]; + msgClass = [properties objectForKey: MAPIPropertyKey (PidTagMessageClass)]; if ([msgClass isEqualToString: @"IPM.Note"]) /* we skip invitation replies */ { /* send mail */ messageData = [self _generateMailDataWithBcc: NO]; - mailProperties = [sogoObject properties]; recipientEmails = [NSMutableArray arrayWithCapacity: 32]; - recipients = [mailProperties objectForKey: @"recipients"]; + recipients = [properties objectForKey: @"recipients"]; for (count = 0; count < 3; count++) { recId = recTypes[count]; @@ -819,11 +924,11 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, if (error) [self logWithFormat: @"an error occurred: '%@'", error]; - mapping = [self mapping]; - [mapping unregisterURLWithID: [self objectId]]; - [self setIsNew: NO]; - [properties removeAllObjects]; - [[self container] cleanupCaches]; + // mapping = [self mapping]; + // [mapping unregisterURLWithID: [self objectId]]; + // [self setIsNew: NO]; + // [properties removeAllObjects]; + [(MAPIStoreMailFolder *) [self container] cleanupCaches]; } else [self logWithFormat: @"skipping submit of message with class '%@'", @@ -834,14 +939,14 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, - (void) save { - NSString *folderName, *flag, *newIdString; + NSString *folderName, *flag, *newIdString, *messageKey; NSData *changeKey, *messageData; NGImap4Connection *connection; NGImap4Client *client; SOGoMailFolder *containerFolder; NSDictionary *result, *responseResult; - MAPIStoreMapping *mapping; - uint64_t mid; + // MAPIStoreMapping *mapping; + // uint64_t mid; messageData = [self _generateMailDataWithBcc: YES]; @@ -860,21 +965,24 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, flag = [responseResult objectForKey: @"flag"]; newIdString = [[flag componentsSeparatedByString: @" "] objectAtIndex: 2]; - mid = [self objectId]; - mapping = [self mapping]; - [mapping unregisterURLWithID: mid]; - [sogoObject setNameInContainer: [NSString stringWithFormat: @"%@.eml", newIdString]]; - [mapping registerURL: [self url] withID: mid]; - } + // mid = [self objectId]; + // mapping = [self mapping]; + // [mapping unregisterURLWithID: mid]; + // [sogoObject setNameInContainer: ]; + messageKey = [NSString stringWithFormat: @"%@.eml", newIdString]; + // [mapping registerURL: [NSString stringWithFormat: @"%@%@", + // [(MAPIStoreMailFolder *) container url], messageKey] + // withID: mid]; - /* synchronise the cache and update the change key with the one provided by - the client */ - [(MAPIStoreMailFolder *) container synchroniseCache]; - changeKey = [[sogoObject properties] - objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)]; - if (changeKey) - [(MAPIStoreMailFolder *) container - setChangeKey: changeKey forMessageWithKey: [self nameInContainer]]; + /* synchronise the cache and update the change key with the one provided + by the client */ + [(MAPIStoreMailFolder *) container synchroniseCache]; + changeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)]; + if (changeKey) + [(MAPIStoreMailFolder *) container + setChangeKey: changeKey + forMessageWithKey: messageKey]; + } } @end diff --git a/OpenChange/MAPIStoreMessage.h b/OpenChange/MAPIStoreMessage.h index d1494c060..b7ba24ae7 100644 --- a/OpenChange/MAPIStoreMessage.h +++ b/OpenChange/MAPIStoreMessage.h @@ -35,9 +35,9 @@ @class MAPIStoreAttachmentTable; @class MAPIStoreFolder; -#import "MAPIStoreObject.h" +#import "MAPIStoreSOGoObject.h" -@interface MAPIStoreMessage : MAPIStoreObject +@interface MAPIStoreMessage : MAPIStoreSOGoObject { NSArray *attachmentKeys; NSMutableDictionary *attachmentParts; diff --git a/OpenChange/MAPIStoreMessage.m b/OpenChange/MAPIStoreMessage.m index 35ea1036d..0d42eb048 100644 --- a/OpenChange/MAPIStoreMessage.m +++ b/OpenChange/MAPIStoreMessage.m @@ -36,6 +36,7 @@ #import "MAPIStoreAttachmentTable.h" #import "MAPIStoreContext.h" #import "MAPIStoreFolder.h" +#import "MAPIStoreMessageTable.h" #import "MAPIStorePropertySelectors.h" #import "MAPIStoreSamDBUtils.h" #import "MAPIStoreTypes.h" @@ -116,7 +117,6 @@ rtf2html (NSData *compressedRTF) @interface SOGoObject (MAPIStoreProtocol) -- (NSString *) davEntityTag; - (NSString *) davContentLength; @end @@ -304,6 +304,7 @@ rtf2html (NSData *compressedRTF) NSData *htmlData, *rtfData; static NSNumber *htmlKey = nil, *rtfKey = nil; + /* we intercept any RTF content and convert it to HTML */ [super addProperties: newNewProperties]; if (!htmlKey) @@ -339,10 +340,8 @@ rtf2html (NSData *compressedRTF) newAid = [[self attachmentKeys] count]; - newAttachment = [MAPIStoreAttachment - mapiStoreObjectWithSOGoObject: nil - inContainer: self]; - [newAttachment setIsNew: YES]; + newAttachment = [MAPIStoreAttachment mapiStoreObjectInContainer: self]; + // [newAttachment setIsNew: YES]; [newAttachment setAID: newAid]; newKey = [NSString stringWithFormat: @"%ul", newAid]; [attachmentParts setObject: newAttachment @@ -497,7 +496,6 @@ rtf2html (NSData *compressedRTF) [[containerTables objectAtIndex: count] notifyChangesForChild: self]; [self setIsNew: NO]; - [properties removeAllObjects]; [container cleanupCaches]; rc = MAPISTORE_SUCCESS; } @@ -792,7 +790,7 @@ rtf2html (NSData *compressedRTF) - (int) getPidTagOriginalMessageClass: (void **) data inMemCtx: (TALLOC_CTX *) memCtx { - return [self getPidTagMessageClass: data inMemCtx: memCtx]; + return [self getProperty: data withTag: PidTagMessageClass inMemCtx: memCtx]; } - (int) getPidTagHasAttachments: (void **) data diff --git a/OpenChange/MAPIStoreMessageTable.h b/OpenChange/MAPIStoreMessageTable.h index 7c3935bba..0779afaef 100644 --- a/OpenChange/MAPIStoreMessageTable.h +++ b/OpenChange/MAPIStoreMessageTable.h @@ -28,6 +28,7 @@ @interface MAPIStoreMessageTable : MAPIStoreTable - (void) setSortOrder: (const struct SSortOrderSet *) set; +- (void) notifyChangesForChild: (MAPIStoreMessage *) child; @end diff --git a/OpenChange/MAPIStoreMessageTable.m b/OpenChange/MAPIStoreMessageTable.m index 33703dc23..3c08e2bab 100644 --- a/OpenChange/MAPIStoreMessageTable.m +++ b/OpenChange/MAPIStoreMessageTable.m @@ -27,6 +27,7 @@ #import #import +#import "MAPIStoreContext.h" #import "MAPIStoreFolder.h" #import "MAPIStoreTypes.h" #import "NSData+MAPIStore.h" @@ -83,4 +84,60 @@ return [(MAPIStoreFolder *) container lookupMessage: childKey]; } +- (void) notifyChangesForChild: (MAPIStoreMessage *) child +{ + NSUInteger currentChildRow, newChildRow; + NSArray *list; + NSString *childName; + struct mapistore_table_notification_parameters notif_parameters; + struct mapistore_context *mstoreCtx; + + mstoreCtx = [[(MAPIStoreFolder *) container context] + connectionInfo]->mstore_ctx; + + notif_parameters.table_type = tableType; + notif_parameters.handle = handleId; + notif_parameters.folder_id = [(MAPIStoreFolder *) container objectId]; + notif_parameters.object_id = [child objectId]; + notif_parameters.instance_id = 0; /* TODO: always 0 ? */ + + childName = [child nameInContainer]; + list = [self restrictedChildKeys]; + currentChildRow = [list indexOfObject: childName]; + notif_parameters.row_id = currentChildRow; + + [self cleanupCaches]; + list = [self restrictedChildKeys]; + newChildRow = [list indexOfObject: childName]; + + if (currentChildRow == NSNotFound) + { + if (newChildRow != NSNotFound) + { + notif_parameters.row_id = newChildRow; + mapistore_push_notification (mstoreCtx, + MAPISTORE_TABLE, + MAPISTORE_OBJECT_CREATED, + ¬if_parameters); + } + } + else + { + if (newChildRow == NSNotFound) + mapistore_push_notification (mstoreCtx, + MAPISTORE_TABLE, + MAPISTORE_OBJECT_DELETED, + ¬if_parameters); + else + { + /* the fact that the row order has changed has no impact here */ + notif_parameters.row_id = newChildRow; + mapistore_push_notification (mstoreCtx, + MAPISTORE_TABLE, + MAPISTORE_OBJECT_MODIFIED, + ¬if_parameters); + } + } +} + @end diff --git a/OpenChange/MAPIStoreNotesContext.h b/OpenChange/MAPIStoreNotesContext.h index ad4a581e1..3f44d575d 100644 --- a/OpenChange/MAPIStoreNotesContext.h +++ b/OpenChange/MAPIStoreNotesContext.h @@ -23,9 +23,9 @@ #ifndef MAPISTORENOTESCONTEXT_H #define MAPISTORENOTESCONTEXT_H -#import "MAPIStoreFSBaseContext.h" +#import "MAPIStoreDBBaseContext.h" -@interface MAPIStoreNotesContext : MAPIStoreFSBaseContext +@interface MAPIStoreNotesContext : MAPIStoreDBBaseContext @end diff --git a/OpenChange/MAPIStoreNotesFolder.h b/OpenChange/MAPIStoreNotesFolder.h index de2eb2748..9baebab27 100644 --- a/OpenChange/MAPIStoreNotesFolder.h +++ b/OpenChange/MAPIStoreNotesFolder.h @@ -23,9 +23,9 @@ #ifndef MAPISTORENOTESFOLDER_H #define MAPISTORENOTESFOLDER_H -#import "MAPIStoreFSFolder.h" +#import "MAPIStoreDBFolder.h" -@interface MAPIStoreNotesFolder : MAPIStoreFSFolder +@interface MAPIStoreNotesFolder : MAPIStoreDBFolder @end #endif /* MAPISTORENOTESFOLDER_H */ diff --git a/OpenChange/MAPIStoreNotesMessage.h b/OpenChange/MAPIStoreNotesMessage.h index 81adfc0dc..d4ec7e1ba 100644 --- a/OpenChange/MAPIStoreNotesMessage.h +++ b/OpenChange/MAPIStoreNotesMessage.h @@ -23,9 +23,9 @@ #ifndef MAPISTORENOTESMESSAGE_H #define MAPISTORENOTESMESSAGE_H -#import "MAPIStoreFSMessage.h" +#import "MAPIStoreDBMessage.h" -@interface MAPIStoreNotesMessage : MAPIStoreFSMessage +@interface MAPIStoreNotesMessage : MAPIStoreDBMessage @end #endif /* MAPISTORENOTESMESSAGE_H */ diff --git a/OpenChange/MAPIStoreNotesMessage.m b/OpenChange/MAPIStoreNotesMessage.m index d59d9a2f8..7cac637b8 100644 --- a/OpenChange/MAPIStoreNotesMessage.m +++ b/OpenChange/MAPIStoreNotesMessage.m @@ -53,21 +53,4 @@ return MAPISTORE_SUCCESS; } -- (int) getPidTagSubject: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - id value; - int rc; - - value = [[sogoObject properties] - objectForKey: MAPIPropertyKey (PidTagNormalizedSubject)]; - if (value) - rc = [value getValue: data forTag: PidTagNormalizedSubject - inMemCtx: memCtx]; - else - rc = MAPISTORE_ERR_NOT_FOUND; - - return rc; -} - @end diff --git a/OpenChange/MAPIStoreObject.h b/OpenChange/MAPIStoreObject.h index cb7ed7809..709396e9f 100644 --- a/OpenChange/MAPIStoreObject.h +++ b/OpenChange/MAPIStoreObject.h @@ -33,56 +33,38 @@ @class NSMutableArray; @class NSMutableDictionary; -@class EOQualifier; - @class MAPIStoreContext; -@class MAPIStoreFolder; @class MAPIStoreMapping; -@class MAPIStoreTable; @class MAPIStoreUserContext; +@class MAPIStoreSOGoObject; @interface MAPIStoreObject : NSObject { const IMP *classGetters; NSMutableArray *parentContainersBag; - MAPIStoreObject *container; - id sogoObject; + id container; NSMutableDictionary *properties; - BOOL isNew; } -+ (id) mapiStoreObjectWithSOGoObject: (id) newSOGoObject - inContainer: (MAPIStoreObject *) newContainer; ++ (id) mapiStoreObjectInContainer: (MAPIStoreObject *) newContainer; +- (id) initInContainer: (MAPIStoreObject *) newContainer; + + (int) getAvailableProperties: (struct SPropTagArray **) propertiesP inMemCtx: (TALLOC_CTX *) memCtx; -- (id) initWithSOGoObject: (id) newSOGoObject - inContainer: (MAPIStoreObject *) newFolder; - -- (void) setIsNew: (BOOL) newIsNew; -- (BOOL) isNew; - -- (NSString *) nameInContainer; - -- (id) sogoObject; - (MAPIStoreObject *) container; - (MAPIStoreContext *) context; - (MAPIStoreUserContext *) userContext; - (MAPIStoreMapping *) mapping; -- (void) cleanupCaches; - -- (uint64_t) objectId; -- (NSString *) url; - /* properties */ - (BOOL) canGetProperty: (enum MAPITAGS) propTag; - (void) addProperties: (NSDictionary *) newProperties; -- (NSDictionary *) properties; +- (NSMutableDictionary *) properties; /* ops */ - (int) getAvailableProperties: (struct SPropTagArray **) propertiesP @@ -104,26 +86,12 @@ fromGlobCnt: (uint64_t) objectCnt inMemCtx: (TALLOC_CTX *) memCtx; -/* implemented getters */ -- (int) getPidTagDisplayName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx; -- (int) getPidTagSearchKey: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx; -- (int) getPidTagGenerateExchangeViews: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx; -- (int) getPidTagParentSourceKey: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx; -- (int) getPidTagSourceKey: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx; -- (int) getPidTagChangeKey: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx; - (int) getPidTagCreationTime: (void **) data inMemCtx: (TALLOC_CTX *) memCtx; - (int) getPidTagLastModificationTime: (void **) data inMemCtx: (TALLOC_CTX *) memCtx; /* subclasses */ -- (uint64_t) objectVersion; - (NSDate *) creationTime; - (NSDate *) lastModificationTime; diff --git a/OpenChange/MAPIStoreObject.m b/OpenChange/MAPIStoreObject.m index e3d379d6b..ac782cdc8 100644 --- a/OpenChange/MAPIStoreObject.m +++ b/OpenChange/MAPIStoreObject.m @@ -58,13 +58,11 @@ static Class NSExceptionK, MAPIStoreFolderK; MAPIStoreFolderK = [MAPIStoreFolder class]; } -+ (id) mapiStoreObjectWithSOGoObject: (id) newSOGoObject - inContainer: (MAPIStoreObject *) newContainer ++ (id) mapiStoreObjectInContainer: (MAPIStoreObject *) newContainer { id newObject; - newObject = [[self alloc] initWithSOGoObject: newSOGoObject - inContainer: newContainer]; + newObject = [[self alloc] initInContainer: newContainer]; [newObject autorelease]; return newObject; @@ -106,22 +104,18 @@ static Class NSExceptionK, MAPIStoreFolderK; classGetters = (IMP *) MAPIStorePropertyGettersForClass (isa); parentContainersBag = [NSMutableArray new]; container = nil; - sogoObject = nil; properties = [NSMutableDictionary new]; - isNew = NO; } - [self logWithFormat: @"-init"]; + // [self logWithFormat: @"-init"]; return self; } -- (id) initWithSOGoObject: (id) newSOGoObject - inContainer: (MAPIStoreObject *) newContainer +- (id) initInContainer: (MAPIStoreObject *) newContainer { if ((self = [self init])) { - ASSIGN (sogoObject, newSOGoObject); ASSIGN (container, newContainer); } @@ -130,42 +124,21 @@ static Class NSExceptionK, MAPIStoreFolderK; - (void) dealloc { - [self logWithFormat: @"-dealloc"]; - [sogoObject release]; + // [self logWithFormat: @"-dealloc"]; [properties release]; [parentContainersBag release]; [container release]; [super dealloc]; } -- (void) setIsNew: (BOOL) newIsNew -{ - isNew = newIsNew; -} - -- (BOOL) isNew -{ - return isNew; -} - -- (id) sogoObject -{ - return sogoObject; -} - - (MAPIStoreObject *) container { return container; } -- (NSString *) nameInContainer -{ - return [sogoObject nameInContainer]; -} - - (MAPIStoreContext *) context { - return [container context]; + return (MAPIStoreContext *) [container context]; } - (MAPIStoreUserContext *) userContext @@ -178,47 +151,14 @@ static Class NSExceptionK, MAPIStoreFolderK; return [[self userContext] mapping]; } -- (void) cleanupCaches -{ -} - /* helpers */ -- (uint64_t) objectId -{ - uint64_t objectId; - - if ([container isKindOfClass: MAPIStoreFolderK]) - objectId = [(MAPIStoreFolder *) container - idForObjectWithKey: [sogoObject nameInContainer]]; - else - { - [self errorWithFormat: @"%s: container is not a folder", __PRETTY_FUNCTION__]; - objectId = (uint64_t) -1; - } - - return objectId; -} - -- (NSString *) url -{ - NSString *containerURL, *format; - - containerURL = [container url]; - if ([containerURL hasSuffix: @"/"]) - format = @"%@%@"; - else - format = @"%@/%@"; - - return [NSString stringWithFormat: format, - containerURL, [self nameInContainer]]; -} - (void) addProperties: (NSDictionary *) newNewProperties { [properties addEntriesFromDictionary: newNewProperties]; } -- (NSDictionary *) properties +- (NSMutableDictionary *) properties { return properties; } @@ -248,124 +188,8 @@ static Class NSExceptionK, MAPIStoreFolderK; return rc; } -/* helper getters */ -- (NSData *) getReplicaKeyFromGlobCnt: (uint64_t) objectCnt -{ - struct mapistore_connection_info *connInfo; - NSMutableData *replicaKey; - char buffer[6]; - NSUInteger count; - - connInfo = [[self context] connectionInfo]; - - for (count = 0; count < 6; count++) - { - buffer[count] = objectCnt & 0xff; - objectCnt >>= 8; - } - - replicaKey = [NSMutableData dataWithCapacity: 22]; - [replicaKey appendBytes: &connInfo->replica_guid - length: sizeof (struct GUID)]; - [replicaKey appendBytes: buffer length: 6]; - - return replicaKey; -} - -- (int) getReplicaKey: (void **) data - fromGlobCnt: (uint64_t) objectCnt - inMemCtx: (TALLOC_CTX *) memCtx -{ - *data = [[self getReplicaKeyFromGlobCnt: objectCnt] asBinaryInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -/* getters */ -- (int) getPidTagDisplayName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - *data = [[sogoObject displayName] asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagSearchKey: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - NSString *stringValue; - - stringValue = [sogoObject nameInContainer]; - *data = [[stringValue dataUsingEncoding: NSASCIIStringEncoding] - asBinaryInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagGenerateExchangeViews: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getNo: data inMemCtx: memCtx]; -} - -- (int) getPidTagParentSourceKey: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getReplicaKey: data fromGlobCnt: [container objectId] >> 16 - inMemCtx: memCtx]; -} - -- (int) getPidTagSourceKey: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - return [self getReplicaKey: data fromGlobCnt: [self objectId] >> 16 - inMemCtx: memCtx]; -} - -- (uint64_t) objectVersion -{ - [self subclassResponsibility: _cmd]; - - return ULLONG_MAX; -} - -- (int) getPidTagChangeKey: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - int rc; - uint64_t obVersion; - - obVersion = [self objectVersion]; - if (obVersion == ULLONG_MAX) - rc = MAPISTORE_ERR_NOT_FOUND; - else - rc = [self getReplicaKey: data fromGlobCnt: obVersion - inMemCtx: memCtx]; - - return rc; -} - -- (int) getPidTagChangeNumber: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx -{ - int rc; - uint64_t obVersion; - - obVersion = [self objectVersion]; - if (obVersion == ULLONG_MAX) - rc = MAPISTORE_ERR_NOT_FOUND; - else - { - *data = MAPILongLongValue (memCtx, ((obVersion << 16) - | 0x0001)); - rc = MAPISTORE_SUCCESS; - } - - return rc; -} - - (int) getPidTagCreationTime: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { *data = [[self creationTime] asFileTimeInMemCtx: memCtx]; @@ -474,6 +298,38 @@ static Class NSExceptionK, MAPIStoreFolderK; return MAPISTORE_SUCCESS; } +- (NSData *) getReplicaKeyFromGlobCnt: (uint64_t) objectCnt +{ + struct mapistore_connection_info *connInfo; + NSMutableData *replicaKey; + char buffer[6]; + NSUInteger count; + + connInfo = [[self context] connectionInfo]; + + for (count = 0; count < 6; count++) + { + buffer[count] = objectCnt & 0xff; + objectCnt >>= 8; + } + + replicaKey = [NSMutableData dataWithCapacity: 22]; + [replicaKey appendBytes: &connInfo->replica_guid + length: sizeof (struct GUID)]; + [replicaKey appendBytes: buffer length: 6]; + + return replicaKey; +} + +- (int) getReplicaKey: (void **) data + fromGlobCnt: (uint64_t) objectCnt + inMemCtx: (TALLOC_CTX *) memCtx +{ + *data = [[self getReplicaKeyFromGlobCnt: objectCnt] asBinaryInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + /* subclasses */ - (NSDate *) creationTime { diff --git a/OpenChange/MAPIStorePermissionsTable.m b/OpenChange/MAPIStorePermissionsTable.m index 6d4c57c55..2385ee8a5 100644 --- a/OpenChange/MAPIStorePermissionsTable.m +++ b/OpenChange/MAPIStorePermissionsTable.m @@ -50,7 +50,7 @@ MAPIStorePermissionEntry *newEntry; newEntry = [[self alloc] initWithUserId: newUserId andMemberId: newMemberId - forFolder: newFolder]; + forFolder: newFolder]; [newEntry autorelease]; return newEntry; @@ -60,7 +60,7 @@ andMemberId: (uint64_t) newMemberId forFolder: (MAPIStoreFolder *) newFolder { - if ((self = [self initWithSOGoObject: nil inContainer: newFolder])) + if ((self = [self initInContainer: newFolder])) { ASSIGN (userId, newUserId); memberId = newMemberId; diff --git a/OpenChange/MAPIStoreSOGoObject.h b/OpenChange/MAPIStoreSOGoObject.h new file mode 100644 index 000000000..90524558f --- /dev/null +++ b/OpenChange/MAPIStoreSOGoObject.h @@ -0,0 +1,89 @@ +/* MAPIStoreObject.h - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef MAPISTORESOGOOBJECT_H +#define MAPISTORESOGOOBJECT_H + +#include + +#import "MAPIStoreObject.h" + +@class NSDate; +@class NSData; +@class NSString; +@class NSMutableArray; +@class NSMutableDictionary; + +@class EOQualifier; + +@class MAPIStoreContext; +@class MAPIStoreFolder; +@class MAPIStoreMapping; +@class MAPIStoreTable; +@class MAPIStoreUserContext; + +@interface MAPIStoreSOGoObject : MAPIStoreObject +{ + id sogoObject; + BOOL isNew; +} + ++ (id) mapiStoreObjectWithSOGoObject: (id) newSOGoObject + inContainer: (MAPIStoreObject *) newContainer; + +- (id) initWithSOGoObject: (id) newSOGoObject + inContainer: (MAPIStoreObject *) newFolder; + +- (void) setIsNew: (BOOL) newIsNew; +- (BOOL) isNew; + +- (id) sogoObject; + +- (NSString *) nameInContainer; + +- (MAPIStoreObject *) container; + +- (void) cleanupCaches; + +- (uint64_t) objectId; +- (NSString *) url; + +/* implemented getters */ +- (int) getPidTagDisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidTagSearchKey: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidTagGenerateExchangeViews: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidTagParentSourceKey: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidTagSourceKey: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; +- (int) getPidTagChangeKey: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx; + +/* subclasses */ +- (uint64_t) objectVersion; + +@end + +#endif /* MAPISTORESOGOOBJECT_H */ diff --git a/OpenChange/MAPIStoreSOGoObject.m b/OpenChange/MAPIStoreSOGoObject.m new file mode 100644 index 000000000..d2d3b4790 --- /dev/null +++ b/OpenChange/MAPIStoreSOGoObject.m @@ -0,0 +1,255 @@ +/* MAPIStoreObject.m - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import +#import +#import +#import +#import + +#import "MAPIStoreContext.h" +#import "MAPIStoreFolder.h" +#import "MAPIStorePropertySelectors.h" +#import "MAPIStoreTypes.h" +#import "MAPIStoreUserContext.h" +#import "NSDate+MAPIStore.h" +#import "NSData+MAPIStore.h" +#import "NSObject+MAPIStore.h" +#import "NSString+MAPIStore.h" + +#import "MAPIStoreSOGoObject.h" + +#undef DEBUG +#include +#include +#include +#include +#include + +@implementation MAPIStoreSOGoObject + +static Class MAPIStoreFolderK; + ++ (void) initialize +{ + MAPIStoreFolderK = [MAPIStoreFolder class]; +} + ++ (id) mapiStoreObjectWithSOGoObject: (id) newSOGoObject + inContainer: (MAPIStoreObject *) newContainer +{ + id newObject; + + newObject = [[self alloc] initWithSOGoObject: newSOGoObject + inContainer: newContainer]; + [newObject autorelease]; + + return newObject; +} + +- (id) init +{ + if ((self = [super init])) + { + sogoObject = nil; + isNew = NO; + } + + [self logWithFormat: @"-init"]; + + return self; +} + +- (id) initWithSOGoObject: (id) newSOGoObject + inContainer: (MAPIStoreObject *) newContainer +{ + if ((self = [self initInContainer: newContainer])) + { + ASSIGN (sogoObject, newSOGoObject); + } + + return self; +} + +- (void) dealloc +{ + // [self logWithFormat: @"-dealloc"]; + [sogoObject release]; + [super dealloc]; +} + +- (void) setIsNew: (BOOL) newIsNew +{ + isNew = newIsNew; +} + +- (BOOL) isNew +{ + return isNew; +} + +- (id) sogoObject +{ + return sogoObject; +} + +- (MAPIStoreObject *) container +{ + return container; +} + +- (NSString *) nameInContainer +{ + return [sogoObject nameInContainer]; +} + +- (void) cleanupCaches +{ +} + +/* helpers */ +- (uint64_t) objectId +{ + uint64_t objectId; + + if ([container isKindOfClass: MAPIStoreFolderK]) + objectId = [(MAPIStoreFolder *) container + idForObjectWithKey: [sogoObject nameInContainer]]; + else + { + [self errorWithFormat: @"%s: container is not a folder", __PRETTY_FUNCTION__]; + objectId = (uint64_t) -1; + } + + return objectId; +} + +- (NSString *) url +{ + NSString *containerURL, *format; + + containerURL = (NSString *) [container url]; + if ([containerURL hasSuffix: @"/"]) + format = @"%@%@"; + else + format = @"%@/%@"; + + return [NSString stringWithFormat: format, + containerURL, [self nameInContainer]]; +} + +/* getters */ +- (int) getPidTagDisplayName: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + *data = [[sogoObject displayName] asUnicodeInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagSearchKey: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + NSString *stringValue; + + stringValue = [sogoObject nameInContainer]; + *data = [[stringValue dataUsingEncoding: NSASCIIStringEncoding] + asBinaryInMemCtx: memCtx]; + + return MAPISTORE_SUCCESS; +} + +- (int) getPidTagGenerateExchangeViews: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getNo: data inMemCtx: memCtx]; +} + +- (int) getPidTagParentSourceKey: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getReplicaKey: data fromGlobCnt: [container objectId] >> 16 + inMemCtx: memCtx]; +} + +- (int) getPidTagSourceKey: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + return [self getReplicaKey: data fromGlobCnt: [self objectId] >> 16 + inMemCtx: memCtx]; +} + +/* helper getters */ +- (int) getPidTagChangeKey: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + int rc; + uint64_t obVersion; + + obVersion = [self objectVersion]; + if (obVersion == ULLONG_MAX) + rc = MAPISTORE_ERR_NOT_FOUND; + else + rc = [self getReplicaKey: data fromGlobCnt: obVersion + inMemCtx: memCtx]; + + return rc; +} + +- (int) getPidTagChangeNumber: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx +{ + int rc; + uint64_t obVersion; + + obVersion = [self objectVersion]; + if (obVersion == ULLONG_MAX) + rc = MAPISTORE_ERR_NOT_FOUND; + else + { + *data = MAPILongLongValue (memCtx, ((obVersion << 16) + | 0x0001)); + rc = MAPISTORE_SUCCESS; + } + + return rc; +} + +/* subclasses */ +- (uint64_t) objectVersion +{ + [self subclassResponsibility: _cmd]; + + return ULLONG_MAX; +} + +/* logging */ +- (NSString *) loggingPrefix +{ + return [NSString stringWithFormat:@"<%@:%p:%@>", + NSStringFromClass (isa), self, [self nameInContainer]]; +} + +@end diff --git a/OpenChange/MAPIStoreTable.h b/OpenChange/MAPIStoreTable.h index fbf13a680..7695c79f0 100644 --- a/OpenChange/MAPIStoreTable.h +++ b/OpenChange/MAPIStoreTable.h @@ -100,8 +100,6 @@ typedef enum { - (int) getRowCount: (uint32_t *) countP withQueryType: (enum mapistore_query_type) queryType; -- (void) notifyChangesForChild: (MAPIStoreObject *) child; - /* helpers */ - (SEL) operatorFromRestrictionOperator: (uint32_t) resOp; diff --git a/OpenChange/MAPIStoreTable.m b/OpenChange/MAPIStoreTable.m index 620b8f248..b47ce8a72 100644 --- a/OpenChange/MAPIStoreTable.m +++ b/OpenChange/MAPIStoreTable.m @@ -874,61 +874,6 @@ static Class NSDataK, NSStringK; return MAPISTORE_SUCCESS; } -- (void) notifyChangesForChild: (MAPIStoreObject *) child -{ - NSUInteger currentChildRow, newChildRow; - NSArray *list; - NSString *childName; - struct mapistore_table_notification_parameters notif_parameters; - struct mapistore_context *mstoreCtx; - - mstoreCtx = [[container context] connectionInfo]->mstore_ctx; - - notif_parameters.table_type = tableType; - notif_parameters.handle = handleId; - notif_parameters.folder_id = [container objectId]; - notif_parameters.object_id = [child objectId]; - notif_parameters.instance_id = 0; /* TODO: always 0 ? */ - - childName = [child nameInContainer]; - list = [self restrictedChildKeys]; - currentChildRow = [list indexOfObject: childName]; - notif_parameters.row_id = currentChildRow; - - [self cleanupCaches]; - list = [self restrictedChildKeys]; - newChildRow = [list indexOfObject: childName]; - - if (currentChildRow == NSNotFound) - { - if (newChildRow != NSNotFound) - { - notif_parameters.row_id = newChildRow; - mapistore_push_notification (mstoreCtx, - MAPISTORE_TABLE, - MAPISTORE_OBJECT_CREATED, - ¬if_parameters); - } - } - else - { - if (newChildRow == NSNotFound) - mapistore_push_notification (mstoreCtx, - MAPISTORE_TABLE, - MAPISTORE_OBJECT_DELETED, - ¬if_parameters); - else - { - /* the fact that the row order has changed has no impact here */ - notif_parameters.row_id = newChildRow; - mapistore_push_notification (mstoreCtx, - MAPISTORE_TABLE, - MAPISTORE_OBJECT_MODIFIED, - ¬if_parameters); - } - } -} - /* subclasses */ - (NSString *) backendIdentifierForProperty: (enum MAPITAGS) property { diff --git a/OpenChange/MAPIStoreUserContext.h b/OpenChange/MAPIStoreUserContext.h index 5cdd38710..70f142b3d 100644 --- a/OpenChange/MAPIStoreUserContext.h +++ b/OpenChange/MAPIStoreUserContext.h @@ -28,6 +28,7 @@ @class NSMutableDictionary; @class NSString; @class NSTimeZone; +@class NSURL; @class WOContext; @@ -52,6 +53,9 @@ MAPIStoreMapping *mapping; + BOOL userDbTableExists; + NSURL *folderTableURL; + WOContext *woContext; MAPIStoreAuthenticator *authenticator; } @@ -71,8 +75,11 @@ - (NSDictionary *) rootFolders; +- (NSURL *) folderTableURL; - (MAPIStoreMapping *) mapping; +- (void) ensureFolderTableExists; + /* SOGo hacky magic */ - (void) activateWithUser: (SOGoUser *) activeUser; - (MAPIStoreAuthenticator *) authenticator; diff --git a/OpenChange/MAPIStoreUserContext.m b/OpenChange/MAPIStoreUserContext.m index dcb3cfb72..bf5d97f0c 100644 --- a/OpenChange/MAPIStoreUserContext.m +++ b/OpenChange/MAPIStoreUserContext.m @@ -6,7 +6,7 @@ * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) + * the Free Software Foundation; either version 3, or (at your option) * any later version. * * This file is distributed in the hope that it will be useful, @@ -23,17 +23,21 @@ #import #import #import +#import #import #import #import +#import +#import #import #import #import #import +#import "GCSSpecialQueries+OpenChange.h" #import "MAPIApplication.h" #import "MAPIStoreAuthenticator.h" #import "MAPIStoreMapping.h" @@ -80,6 +84,9 @@ static NSMapTable *contextsTable = nil; mapping = nil; + userDbTableExists = NO; + folderTableURL = nil; + authenticator = nil; woContext = [WOContext contextWithRequest: nil]; [woContext retain]; @@ -117,6 +124,8 @@ static NSMapTable *contextsTable = nil; [authenticator release]; [mapping release]; + [folderTableURL release]; + [sogoUser release]; [contextsTable removeObjectForKey: username]; @@ -213,6 +222,72 @@ static NSMapTable *contextsTable = nil; return mapping; } + +/* OpenChange db table */ + +- (NSURL *) folderTableURL +{ + NSString *urlString, *ocFSTableName; + NSMutableArray *parts; + SOGoUser *user; + + if (!folderTableURL) + { + user = [self sogoUser]; + urlString = [[user domainDefaults] folderInfoURL]; + parts = [[urlString componentsSeparatedByString: @"/"] + mutableCopy]; + [parts autorelease]; + if ([parts count] == 5) + { + /* If "OCSFolderInfoURL" is properly configured, we must have 5 + parts in this url. */ + ocFSTableName = [NSString stringWithFormat: @"socfs_%@", username]; + [parts replaceObjectAtIndex: 4 withObject: ocFSTableName]; + folderTableURL + = [NSURL URLWithString: [parts componentsJoinedByString: @"/"]]; + [folderTableURL retain]; + } + else + [NSException raise: @"MAPIStoreIOException" + format: @"'OCSFolderInfoURL' is not set"]; + } + + return folderTableURL; +} + +- (void) ensureFolderTableExists +{ + GCSChannelManager *cm; + EOAdaptorChannel *channel; + NSString *tableName, *query; + GCSSpecialQueries *queries; + + [self folderTableURL]; + + cm = [GCSChannelManager defaultChannelManager]; + channel = [cm acquireOpenChannelForURL: folderTableURL]; + + /* FIXME: make use of [EOChannelAdaptor describeTableNames] instead */ + tableName = [[folderTableURL path] lastPathComponent]; + if ([channel evaluateExpressionX: + [NSString stringWithFormat: @"SELECT count(*) FROM %@", + tableName]]) + { + queries = [channel specialQueries]; + query = [queries createOpenChangeFSTableWithName: tableName]; + if ([channel evaluateExpressionX: query]) + [NSException raise: @"MAPIStoreIOException" + format: @"could not create special table '%@'", tableName]; + } + else + [channel cancelFetch]; + + + [cm releaseChannel: channel]; +} + +/* SOGo context objects */ - (WOContext *) woContext { return woContext; diff --git a/OpenChange/MAPIStoreVolatileMessage.h b/OpenChange/MAPIStoreVolatileMessage.h deleted file mode 100644 index 71e02c5f8..000000000 --- a/OpenChange/MAPIStoreVolatileMessage.h +++ /dev/null @@ -1,37 +0,0 @@ -/* MAPIStoreVolatileMessage.h - this file is part of SOGo - * - * Copyright (C) 2011 Inverse inc - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * This file 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef MAPISTOREVOLATILEMESSAGE_H -#define MAPISTOREVOLATILEMESSAGE_H - -#import "MAPIStoreMessage.h" - -@interface MAPIStoreVolatileMessage : MAPIStoreMessage -{ - BOOL fetchedAttachments; - NSDate *creationTime; - NSDate *lastModificationTime; -} - -@end - -#endif /* MAPISTOREVOLATILEMESSAGE_H */ diff --git a/OpenChange/MAPIStoreVolatileMessage.m b/OpenChange/MAPIStoreVolatileMessage.m deleted file mode 100644 index b248ef12a..000000000 --- a/OpenChange/MAPIStoreVolatileMessage.m +++ /dev/null @@ -1,208 +0,0 @@ -/* MAPIStoreVolatileMessage.m - this file is part of SOGo - * - * Copyright (C) 2011 Inverse inc - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * This file 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -#import "MAPIStoreContext.h" -#import "MAPIStoreMailFolder.h" -#import "MAPIStoreMapping.h" -#import "MAPIStoreTypes.h" -#import "NSData+MAPIStore.h" -#import "NSObject+MAPIStore.h" -#import "NSString+MAPIStore.h" -#import "SOGoMAPIVolatileMessage.h" - -#import "MAPIStoreVolatileMessage.h" - -#undef DEBUG -#include -#include - -Class NSNumberK; - -@implementation MAPIStoreVolatileMessage - -+ (void) initialize -{ - NSNumberK = [NSNumber class]; -} - -- (id) init -{ - if ((self = [super init])) - { - fetchedAttachments = NO; - ASSIGN (creationTime, [NSDate date]); - lastModificationTime = [creationTime copy]; - } - - return self; -} - -- (void) dealloc -{ - [creationTime release]; - [lastModificationTime release]; - [super dealloc]; -} - -- (void) addProperties: (NSDictionary *) newProperties -{ - [super addProperties: newProperties]; - [sogoObject appendProperties: properties]; - [properties removeAllObjects]; - ASSIGN (lastModificationTime, [NSDate date]); -} - -- (BOOL) canGetProperty: (enum MAPITAGS) propTag -{ - return ([super canGetProperty: propTag] - || [[sogoObject properties] objectForKey: MAPIPropertyKey (propTag)]); -} - -- (uint64_t) objectVersion -{ - NSNumber *version; - - version = [[sogoObject properties] objectForKey: @"version"]; - - return (version - ? exchange_globcnt ([version unsignedLongLongValue]) - : ULLONG_MAX); -} - -- (int) getProperty: (void **) data - withTag: (enum MAPITAGS) propTag - inMemCtx: (TALLOC_CTX *) memCtx -{ - id value; - int rc; - - value = [[sogoObject properties] objectForKey: MAPIPropertyKey (propTag)]; - if (value) - rc = [value getValue: data forTag: propTag inMemCtx: memCtx]; - else - rc = [super getProperty: data withTag: propTag inMemCtx: memCtx]; - - return rc; -} - -- (int) getPidTagSubject: (void **) data inMemCtx: (TALLOC_CTX *) memCtx -{ - /* if we get here, it means that the properties file didn't contain a - relevant value */ - return [self getEmptyString: data inMemCtx: memCtx]; -} - -- (int) getPidTagMessageClass: (void **) data inMemCtx: (TALLOC_CTX *) memCtx -{ - *data = [@"IPM.Note" asUnicodeInMemCtx: memCtx]; - - return MAPISTORE_SUCCESS; -} - -- (int) getPidTagChangeKey: (void **) data inMemCtx: (TALLOC_CTX *) memCtx -{ - NSData *changeKey; - int rc; - - changeKey = [[sogoObject properties] - objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)]; - if (changeKey) - { - *data = [changeKey asBinaryInMemCtx: memCtx]; - rc = MAPISTORE_SUCCESS; - } - else - rc = [super getPidTagChangeKey: data inMemCtx: memCtx]; - - return rc; -} - -- (NSArray *) attachmentsKeysMatchingQualifier: (EOQualifier *) qualifier - andSortOrderings: (NSArray *) sortOrderings -{ - NSDictionary *attachments; - NSArray *keys; - NSString *key, *newKey; - NSUInteger count, max, aid; - MAPIStoreAttachment *attachment; - - if (!fetchedAttachments) - { - attachments = [[sogoObject properties] objectForKey: @"attachments"]; - keys = [attachments allKeys]; - max = [keys count]; - if (max > 0) - { - aid = [keys count]; - for (count = 0; count < max; count++) - { - key = [keys objectAtIndex: count]; - attachment = [attachments objectForKey: key]; - newKey = [NSString stringWithFormat: @"%ul", (aid + count)]; - [attachmentParts setObject: attachment forKey: newKey]; - } - } - fetchedAttachments = YES; - } - - return [super attachmentKeysMatchingQualifier: qualifier - andSortOrderings: sortOrderings]; -} - -- (NSDate *) creationTime -{ - return creationTime; -} - -- (NSDate *) lastModificationTime -{ - return lastModificationTime; -} - -- (id) lookupAttachment: (NSString *) childKey -{ - return [attachmentParts objectForKey: childKey]; -} - -- (void) save -{ - [self subclassResponsibility: _cmd]; -} - -@end diff --git a/OpenChange/NSObject+PropertyList.m b/OpenChange/NSObject+PropertyList.m new file mode 100644 index 000000000..9f9762508 --- /dev/null +++ b/OpenChange/NSObject+PropertyList.m @@ -0,0 +1,182 @@ +/* dbmsgdump.m - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* A format-agnostic property list dumper. + Usage: dbmsgdump [filename] */ + +#import +#import +#import +#import +#import +#import +#import +#import +#import + +const char *indentationStep = " "; + +@interface NSObject (plext) + +- (void) displayWithIndentation: (NSInteger) anInt; + +@end + +@implementation NSObject (plext) + +- (void) _outputIndentation: (NSInteger) anInt +{ + NSInteger i; + + for (i = 0; i < anInt; i++) + printf ("%s", indentationStep); +} + +- (void) displayWithIndentation: (NSInteger) anInt +{ + printf ("(%s) %s", + [NSStringFromClass (isa) UTF8String], + [[self description] UTF8String]); +} + +@end + +@implementation NSDictionary (plext) + +- (void) displayKey: (NSString *) key + withIndentation: (NSInteger) anInt +{ + [self _outputIndentation: anInt]; + + printf ("%s ", [[key description] UTF8String]); + if ([key isKindOfClass: [NSValue class]]) + printf ("(%s: 0x%.8x) ", [(NSValue *) key objCType], [key intValue]); + + printf ("= "); +} + +- (void) displayWithIndentation: (NSInteger) anInt +{ + NSUInteger i, max; + NSArray *keys; + NSInteger subIndent; + NSString *key; + + keys = [self allKeys]; + max = [keys count]; + + printf ("{ (%ld) items\n", (long) max); + + subIndent = anInt + 1; + + for (i = 0; i < max; i++) + { + key = [keys objectAtIndex: i]; + [self displayKey: key withIndentation: subIndent]; + [[self objectForKey: key] displayWithIndentation: subIndent]; + if (i < (max - 1)) + printf (","); + printf ("\n"); + } + + [self _outputIndentation: anInt]; + printf ("}"); +} + +@end + +@implementation NSArray (plext) + +- (void) displayCount: (NSUInteger) count + withIndentation: (NSInteger) anInt +{ + [self _outputIndentation: anInt]; + printf ("%lu = ", (unsigned long) count); +} + +- (void) displayWithIndentation: (NSInteger) anInt +{ + NSUInteger i, max; + NSInteger subIndent; + + max = [self count]; + + printf ("[ (%ld) items\n", (long) max); + + subIndent = anInt + 1; + + for (i = 0; i < max; i++) + { + [self displayCount: i withIndentation: subIndent]; + [[self objectAtIndex: i] displayWithIndentation: subIndent]; + if (i < (max - 1)) + printf (","); + printf ("\n"); + } + + [self _outputIndentation: anInt]; + printf ("]"); +} + +@end + +static void +OCDumpPListData (NSData *content) +{ + NSDictionary *d; + NSPropertyListFormat format; + NSString *error = nil; + const char *formatName; + + d = [NSPropertyListSerialization propertyListFromData: content + mutabilityOption: NSPropertyListImmutable + format: &format + errorDescription: &error]; + if (d) + { + switch (format) + { + case NSPropertyListOpenStepFormat: + formatName = "OpenStep"; + break; + case NSPropertyListXMLFormat_v1_0: + formatName = "XML"; + break; + case NSPropertyListBinaryFormat_v1_0: + formatName = "Binary"; + break; + case NSPropertyListGNUstepFormat: + formatName = "GNUstep"; + break; + case NSPropertyListGNUstepBinaryFormat: + formatName = "GNUstep binary"; + break; + default: formatName = "unknown"; + } + + printf ("File format is: %s\n", formatName); + [d displayWithIndentation: 0]; + printf ("\n"); + } + else + printf ("an error occurred: %s\n", [error UTF8String]); +} diff --git a/OpenChange/SOGoMAPIFSFolder.h b/OpenChange/SOGoMAPIDBFolder.h similarity index 52% rename from OpenChange/SOGoMAPIFSFolder.h rename to OpenChange/SOGoMAPIDBFolder.h index 244094129..e2d29058f 100644 --- a/OpenChange/SOGoMAPIFSFolder.h +++ b/OpenChange/SOGoMAPIDBFolder.h @@ -1,6 +1,6 @@ -/* SOGoMAPIFSFolder.h - this file is part of SOGo +/* SOGoMAPIDBFolder.h - this file is part of SOGo * - * Copyright (C) 2010 Inverse inc. + * Copyright (C) 2012 Inverse inc. * * Author: Wolfgang Sourdeau * @@ -20,41 +20,38 @@ * Boston, MA 02111-1307, USA. */ -#ifndef SOGOMAPIFSFOLDER_H -#define SOGOMAPIFSFOLDER_H +#ifndef SOGOMAPIDBFOLDER_H +#define SOGOMAPIDBFOLDER_H -#import +#import "SOGoMAPIDBObject.h" @class NSArray; +@class NSMutableString; @class NSString; @class NSURL; @class EOQualifier; -@class SOGoMAPIFSMessage; +@class SOGoMAPIDBMessage; -@interface SOGoMAPIFSFolder : SOGoFolder +@interface SOGoMAPIDBFolder : SOGoMAPIDBObject { - NSString *directory; - BOOL directoryIsSane; + NSString *pathPrefix; /* for root folders */ + SOGoMAPIDBObject *aclMessage; } -+ (id) folderWithURL: (NSURL *) url - andTableType: (uint8_t) tableType; -- (id) initWithURL: (NSURL *) url - andTableType: (uint8_t) tableType; +- (void) setPathPrefix: (NSString *) newPathPrefix; -- (NSString *) directory; +- (NSMutableString *) pathForChild: (NSString *) childName; -- (SOGoMAPIFSMessage *) newMessage; -- (void) ensureDirectory; +- (NSArray *) toOneRelationshipKeys; +- (NSArray *) toManyRelationshipKeys; -- (NSCalendarDate *) creationTime; -- (NSCalendarDate *) lastModificationTime; - -- (NSArray *) toOneRelationshipKeysMatchingQualifier: (EOQualifier *) qualifier - andSortOrderings: (NSArray *) sortOrderings; +- (NSArray *) childKeysOfType: (MAPIDBObjectType) type + includeDeleted: (BOOL) includeDeleted + matchingQualifier: (EOQualifier *) qualifier + andSortOrderings: (NSArray *) sortOrderings; @end -#endif /* SOGOMAPIFSFOLDER_H */ +#endif /* SOGOMAPIDBFOLDER_H */ diff --git a/OpenChange/SOGoMAPIDBFolder.m b/OpenChange/SOGoMAPIDBFolder.m new file mode 100644 index 000000000..ddd6086a6 --- /dev/null +++ b/OpenChange/SOGoMAPIDBFolder.m @@ -0,0 +1,402 @@ +/* SOGoMAPIDBFolder.m - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import +// #import + +#import +#import +#import +#import "EOQualifier+MAPI.h" +#import "GCSSpecialQueries+OpenChange.h" +#import "SOGoMAPIDBMessage.h" + +#import "SOGoMAPIDBFolder.h" + +#undef DEBUG +#include +#include +#include +#include +#include +#include +#include + +Class SOGoMAPIDBObjectK = Nil; + +@implementation SOGoMAPIDBFolder + ++ (void) initialize +{ + SOGoMAPIDBObjectK = [SOGoMAPIDBObject class]; +} + +- (id) init +{ + if ((self = [super init])) + { + pathPrefix = nil; + } + + return self; +} + +- (id) initWithName: (NSString *) name inContainer: (id) newContainer +{ + if ((self = [super initWithName: name inContainer: newContainer])) + { + objectType = MAPIDBObjectTypeFolder; + aclMessage = [SOGoMAPIDBObject objectWithName: @"permissions" + inContainer: self]; + [aclMessage setObjectType: MAPIDBObjectTypeInternal]; + [aclMessage retain]; + } + + return self; +} + +- (void) dealloc +{ + [aclMessage release]; + [pathPrefix release]; + [super dealloc]; +} + +- (BOOL) isFolderish +{ + return YES; +} + +- (void) setPathPrefix: (NSString *) newPathPrefix +{ + ASSIGN (pathPrefix, newPathPrefix); +} + +- (NSMutableString *) pathForChild: (NSString *) childName +{ + NSMutableString *path; + + path = [self path]; + [path appendFormat: @"/%@", childName]; + + return path; +} + +- (NSMutableString *) path +{ + NSMutableString *path; + + path = [super path]; + if (pathPrefix) + [path insertString: pathPrefix atIndex: 0]; + + return path; +} + +// - (SOGoMAPIDBMessage *) newMessage +// { +// NSString *newFilename; + +// newFilename = [NSString stringWithFormat: @"%@.plist", +// [SOGoObject globallyUniqueObjectId]]; + +// return [SOGoMAPIDBMessage objectWithName: filename inContainer: self]; +// } + +- (NSArray *) childKeysOfType: (MAPIDBObjectType) type + includeDeleted: (BOOL) includeDeleted + matchingQualifier: (EOQualifier *) qualifier + andSortOrderings: (NSArray *) sortOrderings +{ + NSMutableArray *childKeys; + NSMutableString *sql// , *qualifierClause + ; + NSString *childPathPrefix, *childPath, *childKey; + NSMutableArray *whereClause; + NSArray *records; + NSDictionary *record; + NSUInteger childPathPrefixLen, count, max; + SOGoMAPIDBObject *currentChild; + + /* query construction */ + sql = [NSMutableString stringWithCapacity: 256]; + [sql appendFormat: @"SELECT * FROM %@", [self tableName]]; + + whereClause = [NSMutableArray arrayWithCapacity: 2]; + childPathPrefix = [NSString stringWithFormat: @"%@/", [self path]]; + [whereClause addObject: [NSString stringWithFormat: @"c_path LIKE '%@%%'", + childPathPrefix]]; + [whereClause addObject: [NSString stringWithFormat: @"c_type = %d", type]]; + if (!includeDeleted) + [whereClause addObject: @"c_deleted = 0"]; + + [sql appendFormat: @" WHERE %@", + [whereClause componentsJoinedByString: @" AND "]]; + + /* results */ + records = [self performSQLQuery: sql]; + if (records) + { + max = [records count]; + childKeys = [NSMutableArray arrayWithCapacity: max]; + childPathPrefixLen = [childPathPrefix length]; + for (count = 0; count < max; count++) + { + record = [records objectAtIndex: count]; + childPath = [record objectForKey: @"c_path"]; + childKey = [childPath substringFromIndex: childPathPrefixLen]; + if ([childKey rangeOfString: @"/"].location == NSNotFound) + { + if (qualifier) + { + currentChild = [SOGoMAPIDBObject objectWithName: childKey + inContainer: self]; + [currentChild setupFromRecord: record]; + if ([qualifier evaluateSOGoMAPIDBObject: currentChild]) + [childKeys addObject: childKey]; + } + else + [childKeys addObject: childKey]; + } + } + } + else + childKeys = nil; + + return childKeys; +} + +- (NSArray *) toManyRelationshipKeys +{ + return [self childKeysOfType: MAPIDBObjectTypeFolder + includeDeleted: NO + matchingQualifier: nil + andSortOrderings: nil]; +} + +- (NSArray *) toOneRelationshipKeys +{ + return [self childKeysOfType: MAPIDBObjectTypeMessage + includeDeleted: NO + matchingQualifier: nil + andSortOrderings: nil]; +} + +// - (NSArray *) toOneRelationshipKeysMatchingQualifier: (EOQualifier *) qualifier +// andSortOrderings: (NSArray *) sortOrderings +// { +// NSArray *allKeys; +// NSMutableArray *keys; +// NSUInteger count, max; +// NSString *messageKey; +// SOGoMAPIDBMessage *message; + +// if (sortOrderings) +// [self warnWithFormat: @"sorting is not handled yet"]; + +// allKeys = [self toOneRelationshipKeys]; +// if (qualifier) +// { +// [self logWithFormat: @"%s: getting restricted FAI keys", __PRETTY_FUNCTION__]; +// max = [allKeys count]; +// keys = [NSMutableArray arrayWithCapacity: max]; +// for (count = 0; count < max; count++) +// { +// messageKey = [allKeys objectAtIndex: count]; +// message = [self lookupName: messageKey +// inContext: nil +// acquire: NO]; +// if ([qualifier evaluateMAPIVolatileMessage: message]) +// [keys addObject: messageKey]; +// } +// } +// else +// keys = (NSMutableArray *) allKeys; + +// return keys; +// } + +- (id) lookupName: (NSString *) childName + inContext: (WOContext *) woContext + acquire: (BOOL) acquire +{ + id object; + Class objectClass; + NSString *childPath; + NSDictionary *record; + + childPath = [self pathForChild: childName]; + record = [self lookupRecord: childPath newerThanVersion: -1]; + if (record) + { + if ([[record objectForKey: @"c_type"] intValue] == MAPIDBObjectTypeFolder) + objectClass = isa; + else + objectClass = SOGoMAPIDBObjectK; + + object = [objectClass objectWithName: childName + inContainer: self]; + [object setupFromRecord: record]; + } + else + object = nil; + + return object; +} + +- (id) lookupFolder: (NSString *) folderName + inContext: (WOContext *) woContext +{ + id object; + + object = [SOGoMAPIDBFolder objectWithName: folderName + inContainer: self]; + [object reloadIfNeeded]; + + return object; +} + +// - (id) _fileAttributeForKey: (NSString *) key +// { +// NSDictionary *attributes; + +// attributes = [[NSFileManager defaultManager] +// fileAttributesAtPath: directory +// traverseLink: NO]; + +// return [attributes objectForKey: key]; +// } + +// - (NSCalendarDate *) creationTime +// { +// return [self _fileAttributeForKey: NSFileCreationDate]; +// } + +// - (NSCalendarDate *) lastModificationTime +// { +// return [self _fileAttributeForKey: NSFileModificationDate]; +// } + +- (NSException *) delete +{ + [self notImplemented: _cmd]; + + // NSFileManager *fm; + // NSException *error; + + // fm = [NSFileManager defaultManager]; + + // if (![fm removeFileAtPath: directory handler: NULL]) + // error = [NSException exceptionWithName: @"MAPIStoreIOException" + // reason: @"could not delete folder" + // userInfo: nil]; + // else + // error = nil; + + // return error; + return nil; +} + +/* acl */ +- (NSString *) defaultUserID +{ + return @"default"; +} + +- (NSMutableDictionary *) _aclEntries +{ + NSMutableDictionary *aclEntries; + + [aclMessage reloadIfNeeded]; + aclEntries = [aclMessage properties]; + if (![aclEntries objectForKey: @"users"]) + [aclEntries setObject: [NSMutableArray array] forKey: @"users"]; + if (![aclEntries objectForKey: @"entries"]) + [aclEntries setObject: [NSMutableDictionary dictionary] + forKey: @"entries"]; + + return aclEntries; +} + +- (void) addUserInAcls: (NSString *) user +{ + NSMutableDictionary *acl; + NSMutableArray *users; + + acl = [self _aclEntries]; + users = [acl objectForKey: @"users"]; + [users addObjectUniquely: user]; + [aclMessage save]; +} + +- (void) removeAclsForUsers: (NSArray *) oldUsers +{ + NSDictionary *acl; + NSMutableDictionary *entries; + NSMutableArray *users; + + acl = [self _aclEntries]; + entries = [acl objectForKey: @"entries"]; + [entries removeObjectsForKeys: oldUsers]; + users = [acl objectForKey: @"users"]; + [users removeObjectsInArray: oldUsers]; + [aclMessage save]; +} + +- (NSArray *) aclUsers +{ + return [[self _aclEntries] objectForKey: @"users"]; +} + +- (NSArray *) aclsForUser: (NSString *) uid +{ + NSDictionary *entries; + + entries = [[self _aclEntries] objectForKey: @"entries"]; + + return [entries objectForKey: uid]; +} + +- (void) setRoles: (NSArray *) roles + forUser: (NSString *) uid +{ + NSMutableDictionary *acl; + NSMutableDictionary *entries; + + acl = [self _aclEntries]; + entries = [acl objectForKey: @"entries"]; + [entries setObject: roles forKey: uid]; + [aclMessage save]; +} + +@end diff --git a/OpenChange/SOGoMAPIDBObject.h b/OpenChange/SOGoMAPIDBObject.h new file mode 100644 index 000000000..ea0a7a58a --- /dev/null +++ b/OpenChange/SOGoMAPIDBObject.h @@ -0,0 +1,83 @@ +/* SOGoMAPIDBObject.h - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef SOGOMAPIDBOBJECT_H +#define SOGOMAPIDBOBJECT_H + +#import "SOGoMAPIObject.h" + +@class NSArray; +@class NSMutableDictionary; +@class NSMutableString; +@class NSString; +@class NSURL; + +@class EOAdaptor; + +typedef enum { + MAPIDBObjectTypeFolder = 1, + MAPIDBObjectTypeMessage = 2, + MAPIDBObjectTypeFAI = 3, + MAPIDBObjectTypeInternal = 99 /* object = property list */ +} MAPIDBObjectType; + +@interface SOGoMAPIDBObject : SOGoMAPIObject +{ + NSURL *tableUrl; + + BOOL initialized; /* safe guard */ + MAPIDBObjectType objectType; + NSInteger version; + BOOL deleted; +} + +/* actions */ +- (void) setupFromRecord: (NSDictionary *) record; + +- (void) reloadIfNeeded; +- (void) save; + +/* accessors */ +- (NSMutableString *) path; /* full filename */ + +- (void) setTableUrl: (NSURL *) newTableUrl; +- (NSURL *) tableUrl; +- (NSString *) tableName; + +- (NSArray *) performSQLQuery: (NSString *) sql; +- (NSDictionary *) lookupRecord: (NSString *) path + newerThanVersion: (NSInteger) startVersion; + +- (void) setObjectType: (MAPIDBObjectType) newObjectType; +- (MAPIDBObjectType) objectType; /* message, fai, folder */ + +/* automatically set from actions */ +- (BOOL) deleted; + +/* db helpers */ +- (EOAdaptor *) tableChannelAdaptor; +- (NSArray *) performSQLQuery: (NSString *) sql; + + +@end + +#endif /* SOGOMAPIDBOBJECT_H */ diff --git a/OpenChange/SOGoMAPIDBObject.m b/OpenChange/SOGoMAPIDBObject.m new file mode 100644 index 000000000..b80af8fc5 --- /dev/null +++ b/OpenChange/SOGoMAPIDBObject.m @@ -0,0 +1,483 @@ +/* SOGoMAPIDBObject.m - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import "GCSSpecialQueries+OpenChange.h" +#import "MAPIStoreTypes.h" +#import "SOGoMAPIDBFolder.h" + +#import "SOGoMAPIDBObject.h" + +static EOAttribute *textColumn = nil; + +@implementation SOGoMAPIDBObject + ++ (void) initialize +{ + NSDictionary *description; + + if (!textColumn) + { + /* TODO: this is a hack for providing an EOAttribute definition that is + compatible with all the backends that we support. We should make use + of EOModel instead. */ + description = [NSDictionary dictionaryWithObjectsAndKeys: + @"c_textfield", @"columnName", + @"VARCHAR", @"externalType", + nil]; + textColumn = [EOAttribute attributeFromPropertyList: description]; + [textColumn retain]; + } +} + +/* + = (@"CREATE TABLE %@ (" + @" c_path VARCHAR(255) PRIMARY KEY," + @" c_type VARCHAR(20) NOT NULL," + @" c_creationdate INT4 NOT NULL," + @" c_lastmodified INT4 NOT NULL," + @" c_version INT4 NOT NULL DEFAULT 0," + @" c_deleted SMALLINT NOT NULL DEFAULT 0," +*/ + +/* indexes: + c_path (primary key) + c_counter + c_path, c_type + c_path, c_creationdate */ + +- (id) init +{ + if ((self = [super init])) + { + tableUrl = nil; + initialized = NO; + objectType = -1; + deleted = NO; + version = 0; + } + + return self; +} + +- (void) dealloc +{ + [tableUrl release]; + [super dealloc]; +} + +- (void) setTableUrl: (NSURL *) newTableUrl +{ + ASSIGN (tableUrl, newTableUrl); +} + +- (NSURL *) tableUrl +{ + if (!tableUrl) + { + tableUrl = [container tableUrl]; + [tableUrl retain]; + if (!tableUrl) + [NSException raise: @"MAPIStoreIOException" + format: @"table url is not set for object '%@'", self]; + } + + return tableUrl; +} + +- (NSString *) tableName +{ + NSArray *parts; + + [self tableUrl]; + parts = [[tableUrl path] componentsSeparatedByString: @"/"]; + + return [parts lastObject]; +} + +- (void) setupFromRecord: (NSDictionary *) record +{ + NSInteger intValue; + NSString *propsValue, *error; + NSDictionary *newValues; + NSPropertyListFormat format; + + objectType = [[record objectForKey: @"c_type"] intValue]; + intValue = [[record objectForKey: @"c_creationdate"] intValue]; + ASSIGN (creationDate, + [NSCalendarDate + dateWithTimeIntervalSince1970: (NSTimeInterval) intValue]); + intValue = [[record objectForKey: @"c_lastmodified"] intValue]; + ASSIGN (lastModified, + [NSCalendarDate + dateWithTimeIntervalSince1970: (NSTimeInterval) intValue]); + deleted = ([[record objectForKey: @"c_deleted"] intValue] > 0); + version = [[record objectForKey: @"c_version"] intValue]; + propsValue = [record objectForKey: @"c_content"]; + if ([propsValue isNotNull]) + { + newValues = [NSPropertyListSerialization propertyListFromData: [propsValue dataByDecodingBase64] + mutabilityOption: NSPropertyListMutableContainers + format: &format + errorDescription: &error]; + [properties addEntriesFromDictionary: newValues]; + // [properties addEntriesFromDictionary: [propsValue + // objectFromJSONString]]; + } + else + [properties removeAllObjects]; + + initialized = YES; +} + +/* accessors */ +- (NSMutableString *) path +{ + NSMutableString *path; + + if (container) + path = [container pathForChild: nameInContainer]; + else + path = [NSMutableString stringWithFormat: @"/%@", nameInContainer]; + + if ([path rangeOfString: @"//"].location != NSNotFound) + [NSException raise: @"MAPIStoreIOException" + format: @"object path has not been properly set for" + " folder '%@' (%@)", + self, path]; + + return path; +} + +- (void) setObjectType: (MAPIDBObjectType) newObjectType +{ + objectType = newObjectType; +} + +- (MAPIDBObjectType) objectType /* message, fai, folder */ +{ + return objectType; +} + +- (NSCalendarDate *) creationDate +{ + if (!initialized) + [NSException raise: @"MAPIStoreIOException" + format: @"record has not been initialized: %@", self]; + + return creationDate; +} + +- (NSCalendarDate *) lastModified +{ + if (!initialized) + [NSException raise: @"MAPIStoreIOException" + format: @"record has not been initialized: %@", self]; + + return lastModified; +} + +- (BOOL) deleted +{ + return deleted; +} + +- (Class) mapistoreMessageClass +{ + NSString *className, *mapiMsgClass; + + switch (objectType) + { + case MAPIDBObjectTypeMessage: + mapiMsgClass = [properties + objectForKey: MAPIPropertyKey (PidTagMessageClass)]; + if (mapiMsgClass) + { + if ([mapiMsgClass isEqualToString: @"IPM.StickyNote"]) + className = @"MAPIStoreNotesMessage"; + else + className = @"MAPIStoreDBMessage"; + [self logWithFormat: @"PidTagMessageClass = '%@', returning '%@'", + mapiMsgClass, className]; + } + else + { + [self warnWithFormat: @"PidTagMessageClass is not set, falling back" + @" to 'MAPIStoreDBMessage'"]; + className = @"MAPIStoreDBMessage"; + } + break; + case MAPIDBObjectTypeFAI: + className = @"MAPIStoreFAIMessage"; + break; + default: + [NSException raise: @"MAPIStoreIOException" + format: @"message class should not be queried for objects" + @" of type '%d'", objectType]; + } + + return NSClassFromString (className); +} + +/* actions */ +- (EOAdaptor *) tableChannelAdaptor +{ + GCSChannelManager *cm; + EOAdaptor *adaptor; + EOAdaptorChannel *channel; + + cm = [GCSChannelManager defaultChannelManager]; + channel = [cm acquireOpenChannelForURL: [self tableUrl]]; + adaptor = [[channel adaptorContext] adaptor]; + [cm releaseChannel: channel]; + + return adaptor; +} + +- (NSArray *) performSQLQuery: (NSString *) sql +{ + NSMutableArray *records; + GCSChannelManager *cm; + EOAdaptorChannel *channel; + NSException *error; + NSArray *attrs; + NSDictionary *record; + + cm = [GCSChannelManager defaultChannelManager]; + channel = [cm acquireOpenChannelForURL: [self tableUrl]]; + + error = [channel evaluateExpressionX: sql]; + if (error) + { + records = nil; + [self logWithFormat: + @"an exception occurred when executing query '%@'", + sql]; + [self logWithFormat: @"exception is '%@'", error]; + } + else + { + records = [NSMutableArray arrayWithCapacity: 256]; + attrs = [channel describeResults: NO]; + while ((record = [channel fetchAttributes: attrs withZone: NULL])) + [records addObject: record]; + } + [cm releaseChannel: channel]; + + return records; +} + +- (NSDictionary *) lookupRecord: (NSString *) path + newerThanVersion: (NSInteger) startVersion +{ + NSDictionary *record; + NSArray *records; + NSString *tableName, *pathValue; + NSMutableString *sql; + EOAdaptor *adaptor; + + if ([path hasSuffix: @"/"]) + [NSException raise: @"MAPIStoreIOException" + format: @"path ends with a slash: %@", path]; + + tableName = [self tableName]; + adaptor = [self tableChannelAdaptor]; + pathValue = [adaptor formatValue: path + forAttribute: textColumn]; + + /* query */ + sql = [NSMutableString stringWithFormat: + @"SELECT * FROM %@ WHERE c_path = %@", + tableName, pathValue]; + if (startVersion > -1) + [sql appendFormat: @" AND c_version > %d", startVersion]; + + /* execution */ + records = [self performSQLQuery: sql]; + if ([records count] > 0) + record = [records objectAtIndex: 0]; + else + record = nil; + + return record; +} + +- (void) reloadIfNeeded +{ + /* if object is uninitialized: reload without condition, otherwise, load if + c_version > :version */ + NSDictionary *record; + + if (initialized) + { + if (!isNew) + { + record = [self lookupRecord: [self path] + newerThanVersion: version]; + if (record) + [self setupFromRecord: record]; + } + } + else + { + record = [self lookupRecord: [self path] + newerThanVersion: -1]; + if (record) + { + [self setupFromRecord: record]; + isNew = NO; + } + else + isNew = YES; + initialized = YES; + } +} + +- (NSException *) delete +{ + deleted = YES; + [properties removeAllObjects]; + [self save]; + + return nil; +} + +- (void) save +{ + NSString *sql; + NSData *content; + NSCalendarDate *now; + GCSChannelManager *cm; + EOAdaptor *adaptor; + EOAdaptorChannel *channel; + NSInteger creationDateValue, lastModifiedValue, deletedValue; + NSString *tableName, *pathValue, *propsValue; + NSException *result; + + if (!initialized) + [NSException raise: @"MAPIStoreIOException" + format: @"record has not been initialized: %@", self]; + + cm = [GCSChannelManager defaultChannelManager]; + + channel = [cm acquireOpenChannelForURL: [self tableUrl]]; + + tableName = [self tableName]; + + now = [NSCalendarDate date]; + ASSIGN (lastModified, now); + + /* +- (NSException *)insertRowX:(NSDictionary *)_row forEntity:(EOEntity *)_entity; +- (NSException *)updateRowX:(NSDictionary*)aRow + describedByQualifier:(EOSQLQualifier*)aQualifier; + */ + + adaptor = [[channel adaptorContext] adaptor]; + pathValue = [adaptor formatValue: [self path] + forAttribute: textColumn]; + + lastModifiedValue = (NSInteger) [lastModified timeIntervalSince1970]; + + if (objectType == -1) + [NSException raise: @"MAPIStoreIOException" + format: @"object type has not been set for object '%@'", + self]; + + if ([properties count] > 0) + { + content = [NSPropertyListSerialization + dataFromPropertyList: properties + format: NSPropertyListBinaryFormat_v1_0 + errorDescription: NULL]; + propsValue = [adaptor formatValue: [content stringByEncodingBase64] + forAttribute: textColumn]; + } + else + propsValue = @"NULL"; + + if (isNew) + { + ASSIGN (creationDate, now); + creationDateValue = (NSInteger) [creationDate timeIntervalSince1970]; + sql = [NSString stringWithFormat: + (@"INSERT INTO %@" + @" (c_path, c_type, c_creationdate, c_lastmodified," + @" c_deleted, c_version, c_content)" + @" VALUES (%@, %d, %d, %d, 0, 0, %@" + @")"), + tableName, + pathValue, objectType, + creationDateValue, lastModifiedValue, + propsValue]; + isNew = NO; + } + else + { + version++; + deletedValue = (deleted ? 1 : 0); + sql = [NSString stringWithFormat: + (@"UPDATE %@" + @" SET c_lastmodified = %d, c_deleted = %d," + @" c_version = %d, c_content = %@" + @" WHERE c_path = %@"), + tableName, + lastModifiedValue, deletedValue, version, propsValue, + pathValue]; + } + + result = [channel evaluateExpressionX: sql]; + if (result) + [self errorWithFormat: @"could not insert/update record for record %@" + @" in %@: %@", pathValue, tableName, result]; + // @" c_path VARCHAR(255) PRIMARY KEY," + // @" c_type SMALLINT NOT NULL," + // @" c_creationdate INT4 NOT NULL," + // @" c_lastmodified INT4 NOT NULL," + // @" c_deleted SMALLINT NOT NULL DEFAULT 0," + // @" c_content BLOB"); + + [cm releaseChannel: channel]; +} + +@end diff --git a/OpenChange/SOGoMAPIFSFolder.m b/OpenChange/SOGoMAPIFSFolder.m deleted file mode 100644 index b26e0ea9d..000000000 --- a/OpenChange/SOGoMAPIFSFolder.m +++ /dev/null @@ -1,439 +0,0 @@ -/* SOGoMAPIFSFolder.m - this file is part of SOGo - * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * This file 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#import -#import -#import -#import -#import -#import -#import -#import -#import - -#import -#import - -#import "EOQualifier+MAPI.h" -#import "SOGoMAPIFSMessage.h" - -#import "SOGoMAPIFSFolder.h" - -#undef DEBUG -#include -#include -#include -#include -#include -#include -#include - -static NSString *privateDir = nil; - -@implementation SOGoMAPIFSFolder - -+ (void) initialize -{ - struct loadparm_context *lpCtx; - const char *cPrivateDir; - - if (!privateDir) - { - lpCtx = loadparm_init_global (true); - cPrivateDir = lpcfg_private_dir (lpCtx); - privateDir = [NSString stringWithUTF8String: cPrivateDir]; - [privateDir retain]; - } -} - -+ (id) folderWithURL: (NSURL *) url - andTableType: (uint8_t) tableType -{ - SOGoMAPIFSFolder *newFolder; - - newFolder = [[self alloc] initWithURL: url - andTableType: tableType]; - [newFolder autorelease]; - - return newFolder; -} - -- (id) init -{ - if ((self = [super init])) - { - directory = nil; - directoryIsSane = NO; - } - - return self; -} - -- (void) dealloc -{ - [directory release]; - [super dealloc]; -} - -- (id) initWithURL: (NSURL *) url - andTableType: (uint8_t) tableType -{ - NSString *path, *username, *tableParticle; - - if ((self = [self init])) - { - if (tableType == MAPISTORE_MESSAGE_TABLE) - tableParticle = @"message"; - else if (tableType == MAPISTORE_FAI_TABLE) - tableParticle = @"fai"; - else if (tableType == MAPISTORE_FOLDER_TABLE) - tableParticle = @"folder"; - else - { - [NSException raise: @"MAPIStoreIOException" - format: @"unsupported table type: %d", tableType]; - tableParticle = nil; - } - - path = [url path]; - if (![path hasSuffix: @"/"]) - path = [NSString stringWithFormat: @"%@/", path]; - username = [url user]; - directory = [NSString stringWithFormat: @"%@/mapistore/SOGo/%@/%@/%@%@", - privateDir, username, tableParticle, - [url host], path]; - [self setOwner: username]; - [self logWithFormat: @"directory: %@", directory]; - [directory retain]; - ASSIGN (nameInContainer, [path stringByDeletingLastPathComponent]); - } - - return self; -} - -- (id) initWithName: (NSString *) newName - inContainer: (id) newContainer -{ - if ((self = [super initWithName: newName inContainer: newContainer])) - { - directory = [[newContainer directory] - stringByAppendingPathComponent: newName]; - [directory retain]; - } - - return self; -} - -- (NSString *) directory -{ - return directory; -} - -- (SOGoMAPIFSMessage *) newMessage -{ - NSString *filename; - - filename = [NSString stringWithFormat: @"%@.plist", - [SOGoObject globallyUniqueObjectId]]; - - return [SOGoMAPIFSMessage objectWithName: filename inContainer: self]; -} - -- (void) ensureDirectory -{ - NSFileManager *fm; - NSDictionary *attributes; - BOOL isDir; - - if (!directory) - [NSException raise: @"MAPIStoreIOException" - format: @"directory is nil"]; - - fm = [NSFileManager defaultManager]; - if ([fm fileExistsAtPath: directory isDirectory: &isDir]) - { - if (!isDir) - [NSException raise: @"MAPIStoreIOException" - format: @"object at path '%@' is not a directory", - directory]; - } - else - { - attributes - = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: 0700] - forKey: NSFilePosixPermissions]; - [fm createDirectoryAtPath: directory - attributes: attributes]; - } - - directoryIsSane = YES; -} - -- (NSArray *) _objectsInDirectory: (BOOL) dirs -{ - NSFileManager *fm; - NSArray *contents; - NSMutableArray *files; - NSUInteger count, max; - NSString *file, *fullName; - BOOL isDir; - - if (!directoryIsSane) - [self ensureDirectory]; - - fm = [NSFileManager defaultManager]; - contents = [fm directoryContentsAtPath: directory]; - max = [contents count]; - files = [NSMutableArray arrayWithCapacity: max]; - for (count = 0; count < max; count++) - { - file = [contents objectAtIndex: count]; - if (![file isEqualToString: @"permissions.plist"]) - { - fullName = [directory stringByAppendingPathComponent: file]; - if ([fm fileExistsAtPath: fullName - isDirectory: &isDir] - && dirs == isDir) - [files addObject: file]; - } - } - - return files; -} - -- (NSArray *) toManyRelationshipKeys -{ - return [self _objectsInDirectory: YES]; -} - -- (NSArray *) toOneRelationshipKeys -{ - return [self _objectsInDirectory: NO]; -} - -- (NSArray *) toOneRelationshipKeysMatchingQualifier: (EOQualifier *) qualifier - andSortOrderings: (NSArray *) sortOrderings -{ - NSArray *allKeys; - NSMutableArray *keys; - NSUInteger count, max; - NSString *messageKey; - SOGoMAPIFSMessage *message; - - if (sortOrderings) - [self warnWithFormat: @"sorting is not handled yet"]; - - allKeys = [self toOneRelationshipKeys]; - if (qualifier) - { - [self logWithFormat: @"%s: getting restricted FAI keys", __PRETTY_FUNCTION__]; - max = [allKeys count]; - keys = [NSMutableArray arrayWithCapacity: max]; - for (count = 0; count < max; count++) - { - messageKey = [allKeys objectAtIndex: count]; - message = [self lookupName: messageKey - inContext: nil - acquire: NO]; - if ([qualifier evaluateMAPIVolatileMessage: message]) - [keys addObject: messageKey]; - } - } - else - keys = (NSMutableArray *) allKeys; - - return keys; -} - -- (id) lookupName: (NSString *) fileName - inContext: (WOContext *) woContext - acquire: (BOOL) acquire -{ - NSFileManager *fm; - NSString *fullName; - id object; - BOOL isDir; - - if (!directoryIsSane) - [self ensureDirectory]; - - fm = [NSFileManager defaultManager]; - fullName = [directory stringByAppendingPathComponent: fileName]; - if ([fm fileExistsAtPath: fullName - isDirectory: &isDir]) - { - if (isDir) - object = [isa objectWithName: fileName - inContainer: self]; - else - object = [SOGoMAPIFSMessage objectWithName: fileName - inContainer: self]; - } - else - object = nil; - - return object; -} - -- (id) _fileAttributeForKey: (NSString *) key -{ - NSDictionary *attributes; - - attributes = [[NSFileManager defaultManager] - fileAttributesAtPath: directory - traverseLink: NO]; - - return [attributes objectForKey: key]; -} - -- (NSCalendarDate *) creationTime -{ - return [self _fileAttributeForKey: NSFileCreationDate]; -} - -- (NSCalendarDate *) lastModificationTime -{ - return [self _fileAttributeForKey: NSFileModificationDate]; -} - -- (NSException *) delete -{ - NSFileManager *fm; - NSException *error; - - fm = [NSFileManager defaultManager]; - - if (![fm removeFileAtPath: directory handler: NULL]) - error = [NSException exceptionWithName: @"MAPIStoreIOException" - reason: @"could not delete folder" - userInfo: nil]; - else - error = nil; - - return error; -} - -/* acl */ -- (NSString *) defaultUserID -{ - return @"default"; -} - -- (NSMutableDictionary *) _aclEntries -{ - NSMutableDictionary *aclEntries; - NSData *content; - NSString *error, *filename; - NSPropertyListFormat format; - - filename = [directory stringByAppendingPathComponent: @"permissions.plist"]; - content = [NSData dataWithContentsOfFile: filename]; - if (content) - aclEntries = [NSPropertyListSerialization propertyListFromData: content - mutabilityOption: NSPropertyListMutableContainers - format: &format - errorDescription: &error]; - else - aclEntries = nil; - if (!aclEntries) - { - aclEntries = [NSMutableDictionary dictionary]; - [aclEntries setObject: [NSMutableArray array] forKey: @"users"]; - [aclEntries setObject: [NSMutableDictionary dictionary] - forKey: @"entries"]; - } - - return aclEntries; -} - -- (void) _saveAcl: (NSDictionary *) acl -{ - NSString *filename; - NSData *content; - - filename = [directory stringByAppendingPathComponent: @"permissions.plist"]; - [self ensureDirectory]; - - if (acl) - content = [NSPropertyListSerialization - dataFromPropertyList: acl - format: NSPropertyListBinaryFormat_v1_0 - errorDescription: NULL]; - else - content = [NSData data]; - if (![content writeToFile: filename atomically: NO]) - [NSException raise: @"MAPIStoreIOException" - format: @"could not save acl"]; -} - -- (void) addUserInAcls: (NSString *) user -{ - NSMutableDictionary *acl; - NSMutableArray *users; - - acl = [self _aclEntries]; - users = [acl objectForKey: @"users"]; - [users addObjectUniquely: user]; - [self _saveAcl: acl]; -} - -- (void) removeAclsForUsers: (NSArray *) oldUsers -{ - NSDictionary *acl; - NSMutableDictionary *entries; - NSMutableArray *users; - - acl = [self _aclEntries]; - entries = [acl objectForKey: @"entries"]; - [entries removeObjectsForKeys: oldUsers]; - users = [acl objectForKey: @"users"]; - [users removeObjectsInArray: oldUsers]; - [self _saveAcl: acl]; -} - -- (NSArray *) aclUsers -{ - return [[self _aclEntries] objectForKey: @"users"]; -} - -- (NSArray *) aclsForUser: (NSString *) uid -{ - NSDictionary *entries; - - entries = [[self _aclEntries] objectForKey: @"entries"]; - - return [entries objectForKey: uid]; -} - -- (void) setRoles: (NSArray *) roles - forUser: (NSString *) uid -{ - NSMutableDictionary *acl; - NSMutableDictionary *entries; - - acl = [self _aclEntries]; - entries = [acl objectForKey: @"entries"]; - [entries setObject: roles forKey: uid]; - [self _saveAcl: acl]; -} - -@end diff --git a/OpenChange/SOGoMAPIFSMessage.h b/OpenChange/SOGoMAPIFSMessage.h deleted file mode 100644 index ec6494938..000000000 --- a/OpenChange/SOGoMAPIFSMessage.h +++ /dev/null @@ -1,47 +0,0 @@ -/* SOGoMAPIFSMessage.h - this file is part of SOGo - * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * This file 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef SOGOMAPIFSMESSAGE_H -#define SOGOMAPIFSMESSAGE_H - -#import "SOGoMAPIVolatileMessage.h" - -@class NSDate; -@class NSString; - -@interface SOGoMAPIFSMessage : SOGoMAPIVolatileMessage -{ - NSString *completeFilename; - NSUInteger inode; - NSData *lastModificationTime; -} - -- (void) save; - -- (NSString *) completeFilename; - -- (NSDate *) creationTime; -- (NSDate *) lastModificationTime; - -@end - -#endif /* SOGOMAPIFSMESSAGE_H */ diff --git a/OpenChange/SOGoMAPIFSMessage.m b/OpenChange/SOGoMAPIFSMessage.m deleted file mode 100644 index 8a854fa1d..000000000 --- a/OpenChange/SOGoMAPIFSMessage.m +++ /dev/null @@ -1,238 +0,0 @@ -/* SOGoMAPIFSMessage.m - this file is part of SOGo - * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) - * any later version. - * - * This file 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#import -#import -#import -#import -#import -#import -#import - -#import - -#import "SOGoMAPIFSFolder.h" - -#import "SOGoMAPIFSMessage.h" - -@implementation SOGoMAPIFSMessage - -- (id) init -{ - if ((self = [super init])) - { - completeFilename = nil; - inode = 0; - lastModificationTime = nil; - } - - return self; -} - -- (void) dealloc -{ - [completeFilename release]; - [super dealloc]; -} - -- (Class) mapistoreMessageClass -{ - NSArray *dirMembers; - NSString *className; - - /* FIXME: this method is a bit dirty */ - dirMembers = [[container directory] componentsSeparatedByString: @"/"]; - if ([dirMembers containsObject: @"fai"]) /* should not occur as FAI message - are instantiated directly in - MAPIStoreFolder */ - className = @"MAPIStoreFAIMessage"; - else if ([dirMembers containsObject: @"notes"]) - className = @"MAPIStoreNotesMessage"; - else - className = @"MAPIStoreFSMessage"; - - return NSClassFromString (className); -} - -- (NSString *) completeFilename -{ - if (!completeFilename) - { - completeFilename = [[container directory] - stringByAppendingPathComponent: nameInContainer]; - [completeFilename retain]; - } - - return completeFilename; -} - -- (BOOL) _readFileChangesDataWithDate: (NSDate **) newLMTime - andInode: (NSUInteger *) newInode -{ - BOOL rc; - NSDictionary *attributes; - - attributes = [[NSFileManager defaultManager] - fileAttributesAtPath: [self completeFilename] - traverseLink: NO]; - if (attributes) - { - *newLMTime = [attributes fileModificationDate]; - *newInode = [attributes fileSystemFileNumber]; - rc = YES; - } - else - rc = NO; - - return rc; -} - -- (BOOL) _checkFileChangesDataWithDate: (NSDate **) newLMTime - andInode: (NSUInteger *) newInode -{ - BOOL hasChanged = NO; - NSDate *lastLMTime; - NSUInteger lastInode; - - if ([self _readFileChangesDataWithDate: &lastLMTime - andInode: &lastInode]) - { - if (inode != lastInode - || ![lastModificationTime isEqual: lastLMTime]) - { - if (lastLMTime) - *newLMTime = lastLMTime; - if (newInode) - *newInode = lastInode; - hasChanged = YES; - } - } - - return hasChanged; -} - -- (NSMutableDictionary *) properties -{ - NSData *content; - NSString *error; - NSPropertyListFormat format; - NSDate *lastLMTime; - NSUInteger lastInode; - - if ([self _checkFileChangesDataWithDate: &lastLMTime - andInode: &lastInode]) - { - [self logWithFormat: @"file '%@' new or modified: rereading properties", - [self completeFilename]]; - [properties release]; - properties = nil; - content = [NSData dataWithContentsOfFile: [self completeFilename]]; - if (content) - { - properties = [NSPropertyListSerialization propertyListFromData: content - mutabilityOption: NSPropertyListMutableContainers - format: &format - errorDescription: &error]; - [properties retain]; - if (!properties) - [self logWithFormat: @"an error occurred during deserialization" - @" of message: '%@'", error]; - } - ASSIGN (lastModificationTime, lastLMTime); - inode = lastInode; - } - - return [super properties]; -} - -- (void) save -{ - NSData *content; - NSDate *lastLMTime; - NSUInteger lastInode; - - [container ensureDirectory]; - - // [self logWithFormat: @"%d props in whole dict", [properties count]]; - - content = [NSPropertyListSerialization - dataFromPropertyList: [self properties] - format: NSPropertyListBinaryFormat_v1_0 - errorDescription: NULL]; - if (![content writeToFile: [self completeFilename] atomically: YES]) - [NSException raise: @"MAPIStoreIOException" - format: @"could not save message"]; - - [self _readFileChangesDataWithDate: &lastLMTime andInode: &lastInode]; - ASSIGN (lastModificationTime, lastLMTime); - inode = lastInode; - // [self logWithFormat: @"fs message written to '%@'", [self completeFilename]]; -} - -- (NSString *) davEntityTag -{ - NSDate *lm; - - lm = [self lastModificationTime]; - - return [NSString stringWithFormat: @"%d", (int) [lm timeIntervalSince1970]]; -} - -- (NSException *) delete -{ - NSFileManager *fm; - NSException *error; - - fm = [NSFileManager defaultManager]; - - if (![fm removeFileAtPath: [self completeFilename] handler: NULL]) - error = [NSException exceptionWithName: @"MAPIStoreIOException" - reason: @"could not delete message" - userInfo: nil]; - else - error = nil; - - return error; -} - -- (id) _fileAttributeForKey: (NSString *) key -{ - NSDictionary *attributes; - - attributes = [[NSFileManager defaultManager] - fileAttributesAtPath: [self completeFilename] - traverseLink: NO]; - - return [attributes objectForKey: key]; -} - -- (NSDate *) creationTime -{ - return [self _fileAttributeForKey: NSFileCreationDate]; -} - -- (NSDate *) lastModificationTime -{ - return [self _fileAttributeForKey: NSFileModificationDate]; -} - -@end diff --git a/OpenChange/SOGoMAPIVolatileMessage.h b/OpenChange/SOGoMAPIObject.h similarity index 63% rename from OpenChange/SOGoMAPIVolatileMessage.h rename to OpenChange/SOGoMAPIObject.h index 00596fef6..fbebaf641 100644 --- a/OpenChange/SOGoMAPIVolatileMessage.h +++ b/OpenChange/SOGoMAPIObject.h @@ -1,12 +1,12 @@ -/* SOGoMAPIVolatileMessage.h - this file is part of SOGo +/* SOGoMAPIObject.h - this file is part of SOGo * - * Copyright (C) 2011 Inverse inc + * Copyright (C) 2012 Inverse inc * * Author: Wolfgang Sourdeau * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3, or (at your option) + * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This file is distributed in the hope that it will be useful, @@ -20,22 +20,30 @@ * Boston, MA 02111-1307, USA. */ -#ifndef SOGOMAPIVOLATILEMESSAGE_H -#define SOGOMAPIVOLATILEMESSAGE_H +#ifndef SOGOMAPIOBJECT_H +#define SOGOMAPIOBJECT_H #import -@class NSDictionary; @class NSMutableDictionary; -@interface SOGoMAPIVolatileMessage : SOGoObject +@interface SOGoMAPIObject : SOGoObject { + BOOL isNew; NSMutableDictionary *properties; + NSCalendarDate *creationDate; + NSCalendarDate *lastModified; } +- (void) setIsNew: (BOOL) newIsNew; +- (BOOL) isNew; + +- (void) adjustLastModified; + - (NSMutableDictionary *) properties; -- (void) appendProperties: (NSDictionary *) newProperties; +- (NSCalendarDate *) creationDate; +- (NSCalendarDate *) lastModified; @end -#endif /* SOGOMAPIVOLATILEMESSAGE_H */ +#endif /* SOGOMAPIOBJECT_H */ diff --git a/OpenChange/SOGoMAPIVolatileMessage.m b/OpenChange/SOGoMAPIObject.m similarity index 58% rename from OpenChange/SOGoMAPIVolatileMessage.m rename to OpenChange/SOGoMAPIObject.m index b46a81c61..ec8a9ae52 100644 --- a/OpenChange/SOGoMAPIVolatileMessage.m +++ b/OpenChange/SOGoMAPIObject.m @@ -1,6 +1,6 @@ -/* SOGoMAPIVolatileMessage.m - this file is part of SOGo +/* SOGoMAPIObject.m - this file is part of SOGo * - * Copyright (C) 2011 Inverse inc + * Copyright (C) 2012 Inverse inc * * Author: Wolfgang Sourdeau * @@ -20,17 +20,19 @@ * Boston, MA 02111-1307, USA. */ -#import +#import "SOGoMAPIObject.h" -#import "SOGoMAPIVolatileMessage.h" - -@implementation SOGoMAPIVolatileMessage +@implementation SOGoMAPIObject - (id) init { if ((self = [super init])) { - properties = nil; + isNew = NO; + creationDate = [NSCalendarDate date]; + [creationDate retain]; + lastModified = [creationDate copy]; + properties = [NSMutableDictionary new]; } return self; @@ -38,21 +40,45 @@ - (void) dealloc { + [creationDate release]; + [lastModified release]; [properties release]; [super dealloc]; } +- (void) setIsNew: (BOOL) newIsNew +{ + isNew = newIsNew; +} + +- (BOOL) isNew +{ + return isNew; +} + +- (void) adjustLastModified +{ + ASSIGN (lastModified, [NSCalendarDate date]); +} + +- (BOOL) isFolderish +{ + return NO; +} + - (NSMutableDictionary *) properties { - if (!properties) - properties = [NSMutableDictionary new]; - return properties; } -- (void) appendProperties: (NSDictionary *) newProperties +- (NSCalendarDate *) creationDate { - [[self properties] addEntriesFromDictionary: newProperties]; + return creationDate; +} + +- (NSCalendarDate *) lastModified +{ + return lastModified; } @end diff --git a/OpenChange/plreader.m b/OpenChange/plreader.m index 68c7a81e5..b027a987c 100644 --- a/OpenChange/plreader.m +++ b/OpenChange/plreader.m @@ -23,164 +23,19 @@ /* A format-agnostic property list dumper. Usage: plreader [filename] */ -#import #import -#import -#import +#import #import -#import -#import -#import -#import -const char *indentationStep = " "; - -@interface NSObject (plext) - -- (void) displayWithIndentation: (NSInteger) anInt; - -@end - -@implementation NSObject (plext) - -- (void) _outputIndentation: (NSInteger) anInt -{ - NSInteger i; - - for (i = 0; i < anInt; i++) - printf ("%s", indentationStep); -} - -- (void) displayWithIndentation: (NSInteger) anInt -{ - printf ("(%s) %s", - [NSStringFromClass (isa) UTF8String], - [[self description] UTF8String]); -} - -@end - -@implementation NSDictionary (plext) - -- (void) displayKey: (NSString *) key - withIndentation: (NSInteger) anInt -{ - [self _outputIndentation: anInt]; - - printf ("%s ", [[key description] UTF8String]); - if ([key isKindOfClass: [NSValue class]]) - printf ("(%s: 0x%.8x) ", [(NSValue *) key objCType], [key intValue]); - - printf ("= "); -} - -- (void) displayWithIndentation: (NSInteger) anInt -{ - NSUInteger i, max; - NSArray *keys; - NSInteger subIndent; - NSString *key; - - keys = [self allKeys]; - max = [keys count]; - - printf ("{ (%ld) items\n", (long) max); - - subIndent = anInt + 1; - - for (i = 0; i < max; i++) - { - key = [keys objectAtIndex: i]; - [self displayKey: key withIndentation: subIndent]; - [[self objectForKey: key] displayWithIndentation: subIndent]; - if (i < (max - 1)) - printf (","); - printf ("\n"); - } - - [self _outputIndentation: anInt]; - printf ("}"); -} - -@end - -@implementation NSArray (plext) - -- (void) displayCount: (NSUInteger) count - withIndentation: (NSInteger) anInt -{ - [self _outputIndentation: anInt]; - printf ("%lu = ", (unsigned long) count); -} - -- (void) displayWithIndentation: (NSInteger) anInt -{ - NSUInteger i, max; - NSInteger subIndent; - - max = [self count]; - - printf ("[ (%ld) items\n", (long) max); - - subIndent = anInt + 1; - - for (i = 0; i < max; i++) - { - [self displayCount: i withIndentation: subIndent]; - [[self objectAtIndex: i] displayWithIndentation: subIndent]; - if (i < (max - 1)) - printf (","); - printf ("\n"); - } - - [self _outputIndentation: anInt]; - printf ("]"); -} - -@end +#import "NSObject+PropertyList.m" static void PLReaderDumpPListFile (NSString *filename) { NSData *content; - NSDictionary *d; - NSPropertyListFormat format; - NSString *error = nil; - const char *formatName; content = [NSData dataWithContentsOfFile: filename]; - d = [NSPropertyListSerialization propertyListFromData: content - mutabilityOption: NSPropertyListImmutable - format: &format - errorDescription: &error]; - if (d) - { - switch (format) - { - case NSPropertyListOpenStepFormat: - formatName = "OpenStep"; - break; - case NSPropertyListXMLFormat_v1_0: - formatName = "XML"; - break; - case NSPropertyListBinaryFormat_v1_0: - formatName = "Binary"; - break; - case NSPropertyListGNUstepFormat: - formatName = "GNUstep"; - break; - case NSPropertyListGNUstepBinaryFormat: - formatName = "GNUstep binary"; - break; - default: formatName = "unknown"; - } - - printf ("File format is: %s\n", formatName); - [d displayWithIndentation: 0]; - printf ("\n"); - } - else - printf ("an error occurred: %s\n", [error UTF8String]); + OCDumpPListData (content); } int main() From a783d81528b67a1dcbdc2f19ed065684308b7a6d Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 29 Jun 2012 19:09:02 +0000 Subject: [PATCH 08/21] Monotone-Parent: 905276f295d6f28a6946297f6a7af9ad60f71842 Monotone-Revision: 4ecb95aab8f686702ff3eb186a97b1bf1f3b5531 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-06-29T19:09:02 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 4 ++++ OpenChange/MAPIStoreAttachment.h | 1 + OpenChange/MAPIStoreAttachment.m | 23 ++++++++++++++--------- OpenChange/MAPIStoreSOGo.m | 6 +++--- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/ChangeLog b/ChangeLog index a2d6e0ffe..440ab3de1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2012-06-29 Wolfgang Sourdeau + * OpenChange/MAPIStoreSOGo.m + (sogo_message_attachment_open_embedded_message): added the "mode" + parameter. + * OpenChange/SOGoMAPIDBObject.m: new class module that replaced SOGoMAPIFSMessage. diff --git a/OpenChange/MAPIStoreAttachment.h b/OpenChange/MAPIStoreAttachment.h index f9716f7e7..1d4576572 100644 --- a/OpenChange/MAPIStoreAttachment.h +++ b/OpenChange/MAPIStoreAttachment.h @@ -37,6 +37,7 @@ - (uint32_t) AID; - (int) openEmbeddedMessage: (MAPIStoreEmbeddedMessage **) messagePtr + inMode: (enum OpenEmbeddedMessage_OpenModeFlags) mode withMID: (uint64_t *) mid withMAPIStoreMsg: (struct mapistore_message **) mapistoreMsgPtr inMemCtx: (TALLOC_CTX *) memCtx; diff --git a/OpenChange/MAPIStoreAttachment.m b/OpenChange/MAPIStoreAttachment.m index 59c8832df..86944436b 100644 --- a/OpenChange/MAPIStoreAttachment.m +++ b/OpenChange/MAPIStoreAttachment.m @@ -91,6 +91,7 @@ } - (int) openEmbeddedMessage: (MAPIStoreEmbeddedMessage **) messagePtr + inMode: (enum OpenEmbeddedMessage_OpenModeFlags) mode withMID: (uint64_t *) mid withMAPIStoreMsg: (struct mapistore_message **) mapistoreMsgPtr inMemCtx: (TALLOC_CTX *) memCtx @@ -103,19 +104,23 @@ mapping = [self mapping]; - attMessage = [self openEmbeddedMessage]; - if (attMessage) + if (mode == MAPI_CREATE) + attMessage = [self createEmbeddedMessage]; + else { - *mid = [mapping idFromURL: [attMessage url]]; - *messagePtr = attMessage; - *mapistoreMsgPtr = mapistoreMsg; + // if (attMessage) + // [mapping registerURL: [attMessage url] + // withID: *mid]; + attMessage = [self openEmbeddedMessage]; + if (attMessage) + { + *mid = [mapping idFromURL: [attMessage url]]; + *messagePtr = attMessage; + *mapistoreMsgPtr = mapistoreMsg; + } } // else if (flags == MAPI_CREATE) // { - // attMessage = [self createEmbeddedMessage]; - // if (attMessage) - // [mapping registerURL: [attMessage url] - // withID: *mid]; // } return (attMessage ? MAPISTORE_SUCCESS : MAPISTORE_ERROR); diff --git a/OpenChange/MAPIStoreSOGo.m b/OpenChange/MAPIStoreSOGo.m index 1a5fc64d0..0a474aba7 100644 --- a/OpenChange/MAPIStoreSOGo.m +++ b/OpenChange/MAPIStoreSOGo.m @@ -963,9 +963,8 @@ sogo_message_submit (void *message_object, enum SubmitFlags flags) static enum mapistore_error sogo_message_attachment_open_embedded_message -(void *attachment_object, - TALLOC_CTX *mem_ctx, void **message_object, - uint64_t *midP, +(void *attachment_object, enum OpenEmbeddedMessage_OpenModeFlags mode, + TALLOC_CTX *mem_ctx, void **message_object, uint64_t *midP, struct mapistore_message **msg) { struct MAPIStoreTallocWrapper *wrapper; @@ -983,6 +982,7 @@ sogo_message_attachment_open_embedded_message GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [attachment openEmbeddedMessage: &message + inMode: mode withMID: midP withMAPIStoreMsg: msg inMemCtx: mem_ctx]; From 3c6ed20f53cca33227007fd32b724f00a571cc5d Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 29 Jun 2012 19:25:31 +0000 Subject: [PATCH 09/21] Missing files Monotone-Parent: 0c4f43a27b60ff9951c52b8ab1d9309d5fc9f209 Monotone-Revision: 2eae9f47dfec8f0fc9908ebce5e7d249f21120bc Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-06-29T19:25:31 Monotone-Branch: ca.inverse.sogo --- OpenChange/SOGoMAPIDBMessage.h | 34 +++++++++++++++++++ OpenChange/SOGoMAPIDBMessage.m | 61 ++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 OpenChange/SOGoMAPIDBMessage.h create mode 100644 OpenChange/SOGoMAPIDBMessage.m diff --git a/OpenChange/SOGoMAPIDBMessage.h b/OpenChange/SOGoMAPIDBMessage.h new file mode 100644 index 000000000..29909a60a --- /dev/null +++ b/OpenChange/SOGoMAPIDBMessage.h @@ -0,0 +1,34 @@ +/* SOGoMAPIDBMessage.h - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef SOGOMAPIDBMESSAGE_H +#define SOGOMAPIDBMESSAGE_H + +#import "SOGoMAPIDBObject.h" + +@class NSDate; +@class NSString; + +@interface SOGoMAPIDBMessage : SOGoMAPIDBObject +@end + +#endif /* SOGOMAPIDBMESSAGE_H */ diff --git a/OpenChange/SOGoMAPIDBMessage.m b/OpenChange/SOGoMAPIDBMessage.m new file mode 100644 index 000000000..f17093be1 --- /dev/null +++ b/OpenChange/SOGoMAPIDBMessage.m @@ -0,0 +1,61 @@ +/* SOGoMAPIDBMessage.m - this file is part of SOGo + * + * Copyright (C) 2012 Inverse inc. + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import +#import +#import +#import + +#import + +#import "SOGoMAPIDBFolder.h" + +#import "SOGoMAPIDBMessage.h" + +@implementation SOGoMAPIDBMessage + +- (Class) mapistoreMessageClass +{ + // NSArray *dirMembers; + NSString *className; + + [NSException raise: @"whereisthisusedexception" + format: @"this exception should be triggered only for tracing"]; + // /* FIXME: this method is a bit dirty */ + // dirMembers = [[container directory] componentsSeparatedByString: @"/"]; + // if ([dirMembers containsObject: @"fai"]) /* should not occur as FAI message + // are instantiated directly in + // MAPIStoreFolder */ + // className = @"MAPIStoreFAIMessage"; + // else if ([dirMembers containsObject: @"notes"]) + // className = @"MAPIStoreNotesMessage"; + // else + // className = @"MAPIStoreDBMessage"; + + className = @"nimportequoi"; + return NSClassFromString (className); +} + +@end From 6987cba3c251db3ca2456488e95652d2f93d84bd Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Fri, 29 Jun 2012 19:37:15 +0000 Subject: [PATCH 10/21] Missing files Monotone-Parent: 2eae9f47dfec8f0fc9908ebce5e7d249f21120bc Monotone-Revision: 53c7bfdc2c357c162e23401ab5600d2cb4a1483e Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-06-29T19:37:15 Monotone-Branch: ca.inverse.sogo --- OpenChange/dbmsgreader.m | 109 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 OpenChange/dbmsgreader.m diff --git a/OpenChange/dbmsgreader.m b/OpenChange/dbmsgreader.m new file mode 100644 index 000000000..b4b4332b3 --- /dev/null +++ b/OpenChange/dbmsgreader.m @@ -0,0 +1,109 @@ +/* dbmsgreader.m - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* A format-agnostic property list readerer. + Usage: dbmsgreader [username] [filename] */ + +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import "MAPIStoreUserContext.h" +#import "SOGoMAPIDBObject.h" + +#import "NSObject+PropertyList.m" + +Class MAPIStoreUserContextK, SOGoMAPIDBObjectK; + +static void +DbDumpObject (NSString *username, NSString *path) +{ + id ctx; + NSData *content; + id dbobject; + NSDictionary *record; + + ctx = [MAPIStoreUserContextK userContextWithUsername: username + andTDBIndexing: NULL]; + dbobject = [SOGoMAPIDBObjectK new]; + [dbobject setTableUrl: [ctx folderTableURL]]; + record = [dbobject lookupRecord: path newerThanVersion: -1]; + if (record) + { + content = [[record objectForKey: @"c_content"] dataByDecodingBase64]; + OCDumpPListData (content); + } + else + NSLog (@"record not found"); + + [dbobject release]; +} + +int main (int argc, char *argv[], char *envp[]) +{ + NSAutoreleasePool *pool; + SOGoProductLoader *loader; + NSUserDefaults *ud; + SoProductRegistry *registry; + NSArray *arguments; + + /* Here we work around a bug in GNUstep which decodes XML user + defaults using the system encoding rather than honouring + the encoding specified in the file. */ + putenv ("GNUSTEP_STRING_ENCODING=NSUTF8StringEncoding"); + + pool = [NSAutoreleasePool new]; + + [SOGoSystemDefaults sharedSystemDefaults]; + + /* We force the plugin to base its configuration on the SOGo tree. */ + ud = [NSUserDefaults standardUserDefaults]; + [ud registerDefaults: [ud persistentDomainForName: @"sogod"]]; + + [NSProcessInfo initializeWithArguments: argv + count: argc + environment: envp]; + + registry = [SoProductRegistry sharedProductRegistry]; + [registry scanForProductsInDirectory: SOGO_BUNDLES_DIR]; + + loader = [SOGoProductLoader productLoader]; + [loader loadProducts: [NSArray arrayWithObject: BACKEND_BUNDLE_NAME]]; + + MAPIStoreUserContextK = NSClassFromString (@"MAPIStoreUserContext"); + SOGoMAPIDBObjectK = NSClassFromString (@"SOGoMAPIDBObject"); + + arguments = [[NSProcessInfo processInfo] arguments]; + if ([arguments count] > 2) + DbDumpObject ([arguments objectAtIndex: 1], + [arguments objectAtIndex: 2]); + + [pool release]; + + return 0; +} From 13bad20737c7ac33a8092610555df01e093a392b Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Sat, 30 Jun 2012 07:30:44 +0000 Subject: [PATCH 11/21] Monotone-Parent: 53c7bfdc2c357c162e23401ab5600d2cb4a1483e Monotone-Revision: fc5a6818c0ee3f04b21668c4b2008a9549fb34f7 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-06-30T07:30:44 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 6 +++ OpenChange/MAPIStoreSOGo.m | 66 ++++++++++++++++----------------- OpenChange/NSObject+MAPIStore.h | 2 +- OpenChange/NSObject+MAPIStore.m | 4 +- 4 files changed, 42 insertions(+), 36 deletions(-) diff --git a/ChangeLog b/ChangeLog index 884a80698..089d771c0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2012-06-30 Wolfgang Sourdeau + + * OpenChange/NSObject+MAPIStore.h: renamed + MAPIStoreTallocWrapper.MAPIStoreSOGoObject to .instance, to avoid + confusion in certain versions of GCC with our new class type. + 2012-06-29 Wolfgang Sourdeau * OpenChange/MAPIStoreSOGo.m diff --git a/OpenChange/MAPIStoreSOGo.m b/OpenChange/MAPIStoreSOGo.m index 0a474aba7..d7ba5c387 100644 --- a/OpenChange/MAPIStoreSOGo.m +++ b/OpenChange/MAPIStoreSOGo.m @@ -273,7 +273,7 @@ sogo_context_get_path(void *backend_object, TALLOC_CTX *mem_ctx, if (backend_object) { wrapper = backend_object; - context = wrapper->MAPIStoreSOGoObject; + context = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [context getPath: path ofFMID: fmid inMemCtx: mem_ctx]; @@ -303,7 +303,7 @@ sogo_context_get_root_folder(void *backend_object, TALLOC_CTX *mem_ctx, if (backend_object) { wrapper = backend_object; - context = wrapper->MAPIStoreSOGoObject; + context = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [context getRootFolder: &folder withFID: fid]; @@ -342,7 +342,7 @@ sogo_folder_open_folder(void *folder_object, TALLOC_CTX *mem_ctx, uint64_t fid, if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder openFolder: &childFolder withFID: fid]; @@ -382,7 +382,7 @@ sogo_folder_create_folder(void *folder_object, TALLOC_CTX *mem_ctx, if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder createFolder: &childFolder withRow: aRow andFID: fid]; @@ -421,7 +421,7 @@ sogo_folder_delete(void *folder_object) if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder deleteFolder]; @@ -449,7 +449,7 @@ sogo_folder_get_child_count(void *folder_object, enum mapistore_table_type table if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder getChildCount: child_count ofTableType: table_type]; @@ -481,7 +481,7 @@ sogo_folder_open_message(void *folder_object, if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder openMessage: &message @@ -519,7 +519,7 @@ sogo_folder_create_message(void *folder_object, if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder createMessage: &message @@ -551,7 +551,7 @@ sogo_folder_delete_message(void *folder_object, uint64_t mid, uint8_t flags) if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder deleteMessageWithMID: mid andFlags: flags]; @@ -584,10 +584,10 @@ sogo_folder_move_copy_messages(void *folder_object, if (folder_object) { wrapper = folder_object; - targetFolder = wrapper->MAPIStoreSOGoObject; + targetFolder = wrapper->instance; wrapper = source_folder_object; - sourceFolder = wrapper->MAPIStoreSOGoObject; + sourceFolder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; @@ -623,7 +623,7 @@ sogo_folder_get_deleted_fmids(void *folder_object, TALLOC_CTX *mem_ctx, if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder getDeletedFMIDs: fmidsp @@ -658,7 +658,7 @@ sogo_folder_open_table(void *folder_object, TALLOC_CTX *mem_ctx, if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder getTable: &table @@ -693,7 +693,7 @@ sogo_folder_modify_permissions(void *folder_object, uint8_t flags, if (folder_object) { wrapper = folder_object; - folder = wrapper->MAPIStoreSOGoObject; + folder = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [folder modifyPermissions: permissions @@ -725,7 +725,7 @@ sogo_message_get_message_data(void *message_object, if (message_object) { wrapper = message_object; - message = wrapper->MAPIStoreSOGoObject; + message = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; [message getMessageData: msg_dataP @@ -756,7 +756,7 @@ sogo_message_create_attachment (void *message_object, TALLOC_CTX *mem_ctx, void if (message_object) { wrapper = message_object; - message = wrapper->MAPIStoreSOGoObject; + message = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [message createAttachment: &attachment inAID: aidp]; @@ -789,7 +789,7 @@ sogo_message_open_attachment (void *message_object, TALLOC_CTX *mem_ctx, if (message_object) { wrapper = message_object; - message = wrapper->MAPIStoreSOGoObject; + message = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [message getAttachment: &attachment withAID: aid]; @@ -821,7 +821,7 @@ sogo_message_get_attachment_table (void *message_object, TALLOC_CTX *mem_ctx, vo if (message_object) { wrapper = message_object; - message = wrapper->MAPIStoreSOGoObject; + message = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [message getAttachmentTable: &table @@ -856,7 +856,7 @@ sogo_message_modify_recipients (void *message_object, if (message_object) { wrapper = message_object; - message = wrapper->MAPIStoreSOGoObject; + message = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [message modifyRecipientsWithRecipients: recipients @@ -887,7 +887,7 @@ sogo_message_set_read_flag (void *message_object, uint8_t flag) if (message_object) { wrapper = message_object; - message = wrapper->MAPIStoreSOGoObject; + message = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [message setReadFlag: flag]; @@ -916,7 +916,7 @@ sogo_message_save (void *message_object) if (message_object) { wrapper = message_object; - message = wrapper->MAPIStoreSOGoObject; + message = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [message saveMessage]; @@ -945,7 +945,7 @@ sogo_message_submit (void *message_object, enum SubmitFlags flags) if (message_object) { wrapper = message_object; - message = wrapper->MAPIStoreSOGoObject; + message = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [message submitWithFlags: flags]; @@ -978,7 +978,7 @@ sogo_message_attachment_open_embedded_message if (attachment_object) { wrapper = attachment_object; - attachment = wrapper->MAPIStoreSOGoObject; + attachment = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [attachment openEmbeddedMessage: &message @@ -1012,7 +1012,7 @@ static enum mapistore_error sogo_table_get_available_properties(void *table_obje if (table_object) { wrapper = table_object; - table = wrapper->MAPIStoreSOGoObject; + table = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [table getAvailableProperties: propertiesP inMemCtx: mem_ctx]; @@ -1040,7 +1040,7 @@ sogo_table_set_columns (void *table_object, uint16_t count, enum MAPITAGS *prope if (table_object) { wrapper = table_object; - table = wrapper->MAPIStoreSOGoObject; + table = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [table setColumns: properties @@ -1069,7 +1069,7 @@ sogo_table_set_restrictions (void *table_object, struct mapi_SRestriction *restr if (table_object) { wrapper = table_object; - table = wrapper->MAPIStoreSOGoObject; + table = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; [table setRestrictions: restrictions]; @@ -1100,7 +1100,7 @@ sogo_table_set_sort_order (void *table_object, struct SSortOrderSet *sort_order, if (table_object) { wrapper = table_object; - table = wrapper->MAPIStoreSOGoObject; + table = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; [table setSortOrder: sort_order]; @@ -1133,7 +1133,7 @@ sogo_table_get_row (void *table_object, TALLOC_CTX *mem_ctx, if (table_object) { wrapper = table_object; - table = wrapper->MAPIStoreSOGoObject; + table = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [table getRow: data withRowID: row_id andQueryType: query_type @@ -1164,7 +1164,7 @@ sogo_table_get_row_count (void *table_object, if (table_object) { wrapper = table_object; - table = wrapper->MAPIStoreSOGoObject; + table = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [table getRowCount: row_countp @@ -1193,7 +1193,7 @@ sogo_table_handle_destructor (void *table_object, uint32_t handle_id) if (table_object) { wrapper = table_object; - table = wrapper->MAPIStoreSOGoObject; + table = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; [table destroyHandle: handle_id]; @@ -1223,7 +1223,7 @@ static enum mapistore_error sogo_properties_get_available_properties(void *objec if (object) { wrapper = object; - propObject = wrapper->MAPIStoreSOGoObject; + propObject = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [propObject getAvailableProperties: propertiesP inMemCtx: mem_ctx]; @@ -1254,7 +1254,7 @@ sogo_properties_get_properties (void *object, if (object) { wrapper = object; - propObject = wrapper->MAPIStoreSOGoObject; + propObject = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [propObject getProperties: data withTags: properties @@ -1284,7 +1284,7 @@ sogo_properties_set_properties (void *object, struct SRow *aRow) if (object) { wrapper = object; - propObject = wrapper->MAPIStoreSOGoObject; + propObject = wrapper->instance; GSRegisterCurrentThread (); pool = [NSAutoreleasePool new]; rc = [propObject addPropertiesFromRow: aRow]; diff --git a/OpenChange/NSObject+MAPIStore.h b/OpenChange/NSObject+MAPIStore.h index 19254456f..2e532eb8d 100644 --- a/OpenChange/NSObject+MAPIStore.h +++ b/OpenChange/NSObject+MAPIStore.h @@ -29,7 +29,7 @@ struct MAPIStoreTallocWrapper { - id MAPIStoreSOGoObject; + id instance; }; @interface NSObject (MAPIStoreTallocHelpers) diff --git a/OpenChange/NSObject+MAPIStore.m b/OpenChange/NSObject+MAPIStore.m index 475395755..c291990f4 100644 --- a/OpenChange/NSObject+MAPIStore.m +++ b/OpenChange/NSObject+MAPIStore.m @@ -50,7 +50,7 @@ MAPIStoreTallocWrapperDestroy (void *data) pool = [NSAutoreleasePool new]; wrapper = data; // NSLog (@"destroying wrapped object (wrapper: %p; object: %p)...\n", wrapper, wrapper->MAPIStoreSOGoObject); - [wrapper->MAPIStoreSOGoObject release]; + [wrapper->instance release]; [pool release]; GSUnregisterCurrentThread (); @@ -63,7 +63,7 @@ MAPIStoreTallocWrapperDestroy (void *data) wrapper = talloc_zero (tallocCtx, struct MAPIStoreTallocWrapper); talloc_set_destructor ((void *) wrapper, MAPIStoreTallocWrapperDestroy); - wrapper->MAPIStoreSOGoObject = self; + wrapper->instance = self; [self retain]; // NSLog (@"returning wrapper: %p; object: %p", wrapper, self); From 674fb1081cc25690609752aa9d11757e9e137cae Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Sun, 1 Jul 2012 20:55:54 +0000 Subject: [PATCH 12/21] Monotone-Parent: b4eb1c4ecb6343d34ed9700a15e35cd1c9dc33af Monotone-Revision: 0ad1e10fb7587b8444bd502de4908d1aabf70c96 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-01T20:55:54 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 5 +++++ OpenChange/NSDate+MAPIStore.h | 6 +++++- OpenChange/NSDate+MAPIStore.m | 21 ++++++++++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 69098850d..0787be8c6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2012-07-01 Wolfgang Sourdeau + + * OpenChange/NSDate+MAPIStore.m (NSDateCompare): new comparison + function for sorting array of NSDate instances. + 2012-06-30 Wolfgang Sourdeau * OpenChange/NSObject+MAPIStore.h: renamed diff --git a/OpenChange/NSDate+MAPIStore.h b/OpenChange/NSDate+MAPIStore.h index 3c7809f79..0a01a2ddc 100644 --- a/OpenChange/NSDate+MAPIStore.h +++ b/OpenChange/NSDate+MAPIStore.h @@ -25,9 +25,11 @@ #import +@class NSCalendarDate; + @interface NSDate (MAPIStoreDataTypes) -+ (id) dateFromMinutesSince1601: (uint32_t) minutes; ++ (NSCalendarDate *) dateFromMinutesSince1601: (uint32_t) minutes; - (uint32_t) asMinutesSince1601; + (id) dateFromFileTime: (const struct FILETIME *) timeValue; @@ -37,4 +39,6 @@ @end +NSComparisonResult NSDateCompare (NSDate *date1, NSDate *date2, void *); + #endif /* NSCALENDARDATE+MAPISTORE_H */ diff --git a/OpenChange/NSDate+MAPIStore.m b/OpenChange/NSDate+MAPIStore.m index eb873978b..1e631b82b 100644 --- a/OpenChange/NSDate+MAPIStore.m +++ b/OpenChange/NSDate+MAPIStore.m @@ -51,7 +51,7 @@ _setupRefDate () timeZone: [NSTimeZone timeZoneWithName: @"UTC"]]; } -+ (id) dateFromMinutesSince1601: (uint32_t) minutes ++ (NSCalendarDate *) dateFromMinutesSince1601: (uint32_t) minutes { NSCalendarDate *result; @@ -129,3 +129,22 @@ _setupRefDate () } @end + +NSComparisonResult +NSDateCompare (NSDate *date1, NSDate *date2, void *ctx) +{ + NSTimeInterval secs1, secs2; + NSComparisonResult result; + + secs1 = [date1 timeIntervalSince1970]; + secs2 = [date2 timeIntervalSince1970]; + if (secs1 == secs2) + result = NSOrderedSame; + else if (secs1 < secs2) + result = NSOrderedAscending; + else + result = NSOrderedDescending; + + return result; +} + From b3acd2e2add97681606d1ee811282adf38c3bcdc Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Sun, 1 Jul 2012 20:58:46 +0000 Subject: [PATCH 13/21] Monotone-Parent: 0ad1e10fb7587b8444bd502de4908d1aabf70c96 Monotone-Revision: a6b977673a1cc94b1269e19c469101dca9fd17bc Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-01T20:58:46 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 8 ++ ...Native Microsoft Outlook Configuration.odt | Bin 27917 -> 27917 bytes OpenChange/MAPIStoreAppointmentWrapper.m | 86 ++++++++---------- OpenChange/MAPIStoreRecurrenceUtils.h | 11 ++- OpenChange/MAPIStoreRecurrenceUtils.m | 48 ++++++++-- 5 files changed, 98 insertions(+), 55 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0787be8c6..43598829b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2012-07-01 Wolfgang Sourdeau + * OpenChange/MAPIStoreRecurrenceUtils.m + (-setupRecurrenceWithMasterEntity:fromRecurrencePattern:): add + exception dates to master entity based on the + "DeletedInstanceDates" member of the struct. + (-fillRecurrencePattern:withEvent:inTimeZone:inMemCtx:): new name + for fillRecurrencePattern:withStartDate:andEndDate:, add exception + dates to struct. + * OpenChange/NSDate+MAPIStore.m (NSDateCompare): new comparison function for sorting array of NSDate instances. diff --git a/Documentation/SOGo Native Microsoft Outlook Configuration.odt b/Documentation/SOGo Native Microsoft Outlook Configuration.odt index a350de392a22f7c89e179aeacef624755b9329af..3761cc8329c4bc0bb68370d8af7f6d7cb5d83a0a 100644 GIT binary patch delta 19554 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 delta 19545 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 diff --git a/OpenChange/MAPIStoreAppointmentWrapper.m b/OpenChange/MAPIStoreAppointmentWrapper.m index 7862980aa..d306e3c8f 100644 --- a/OpenChange/MAPIStoreAppointmentWrapper.m +++ b/OpenChange/MAPIStoreAppointmentWrapper.m @@ -28,11 +28,8 @@ #import #import #import -#import #import #import -#import -#import #import #import #import @@ -580,7 +577,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagMessageClass: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { const char *className; @@ -633,7 +630,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagStartDate: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { NSCalendarDate *dateValue; NSInteger offset; @@ -827,7 +824,7 @@ static NSCharacterSet *hexCharacterSet = nil; /* sender (organizer) */ - (int) getPidTagSenderEmailAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getEmailAddress: data forICalPerson: [event organizer] @@ -835,7 +832,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagSenderAddressType: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getAddrType: data forICalPerson: [event organizer] @@ -843,7 +840,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagSenderName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getName: data forICalPerson: [event organizer] @@ -851,7 +848,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagSenderEntryId: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getEntryId: data forICalPerson: [event organizer] @@ -860,7 +857,7 @@ static NSCharacterSet *hexCharacterSet = nil; /* attendee */ - (int) getPidTagReceivedByEmailAddress: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getEmailAddress: data forICalPerson: [event userAsAttendee: user] @@ -868,7 +865,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagReceivedByAddressType: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getAddrType: data forICalPerson: [event userAsAttendee: user] @@ -876,7 +873,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagReceivedByName: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getName: data forICalPerson: [event userAsAttendee: user] @@ -884,7 +881,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagReceivedByEntryId: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { return [self _getEntryId: data forICalPerson: [event userAsAttendee: user] @@ -893,7 +890,7 @@ static NSCharacterSet *hexCharacterSet = nil; /* /attendee */ - (int) getPidTagEndDate: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { NSCalendarDate *dateValue; NSInteger offset; @@ -995,7 +992,8 @@ static NSCharacterSet *hexCharacterSet = nil; return [self getPidLidLocation: data inMemCtx: memCtx]; } -- (int) getPidLidServerProcessed: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +- (int) getPidLidServerProcessed: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx { /* TODO: we need to check whether the event has been processed internally by SOGo or if it was received only by mail. We only assume the SOGo case @@ -1003,7 +1001,8 @@ static NSCharacterSet *hexCharacterSet = nil; return [self getYes: data inMemCtx: memCtx]; } -- (int) getPidLidServerProcessingActions: (void **) data inMemCtx: (TALLOC_CTX *) memCtx +- (int) getPidLidServerProcessingActions: (void **) data + inMemCtx: (TALLOC_CTX *) memCtx { *data = MAPILongValue (memCtx, 0x00000010 /* cpsCreatedOnPrincipal */ @@ -1020,14 +1019,14 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagSensitivity: (void **) data // not implemented, depends on CLASS - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { // normal = 0, personal?? = 1, private = 2, confidential = 3 return [self getLongZero: data inMemCtx: memCtx]; } - (int) getPidTagImportance: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { uint32_t v; if ([[event priority] isEqualToString: @"9"]) @@ -1043,7 +1042,7 @@ static NSCharacterSet *hexCharacterSet = nil; } - (int) getPidTagBody: (void **) data - inMemCtx: (TALLOC_CTX *) memCtx + inMemCtx: (TALLOC_CTX *) memCtx { int rc = MAPISTORE_SUCCESS; NSString *stringValue; @@ -1074,29 +1073,6 @@ static NSCharacterSet *hexCharacterSet = nil; return MAPISTORE_SUCCESS; } -static void -_fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp, - NSCalendarDate *startDate, NSTimeInterval duration, - NSCalendarDate * endDate, iCalRecurrenceRule *rule) -{ - uint32_t startMinutes; - - [rule fillRecurrencePattern: &arp->RecurrencePattern - withStartDate: startDate andEndDate: endDate]; - arp->ReaderVersion2 = 0x00003006; - arp->WriterVersion2 = 0x00003009; - - startMinutes = ([startDate hourOfDay] * 60 + [startDate minuteOfHour]); - arp->StartTimeOffset = startMinutes; - arp->EndTimeOffset = startMinutes + (uint32_t) (duration / 60); - - arp->ExceptionCount = 0; - arp->ReservedBlock1Size = 0; - - /* Currently ignored in property.idl: - arp->ReservedBlock2Size = 0; */ -} - - (struct SBinary_short *) _computeAppointmentRecurInMemCtx: (TALLOC_CTX *) memCtx { @@ -1105,6 +1081,7 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp, struct SBinary_short *sBin; NSCalendarDate *firstStartDate; iCalRecurrenceRule *rule; + NSUInteger startMinutes; rule = [[event recurrenceRules] objectAtIndex: 0]; @@ -1114,10 +1091,27 @@ _fillAppointmentRecurrencePattern (struct AppointmentRecurrencePattern *arp, [firstStartDate setTimeZone: timeZone]; arp = talloc_zero (memCtx, struct AppointmentRecurrencePattern); - _fillAppointmentRecurrencePattern (arp, firstStartDate, - [event durationAsTimeInterval], - [event lastPossibleRecurrenceStartDate], - rule); + [rule fillRecurrencePattern: &arp->RecurrencePattern + withEvent: event + inTimeZone: timeZone + inMemCtx: arp]; + arp->ReaderVersion2 = 0x00003006; + arp->WriterVersion2 = 0x00003009; + + startMinutes = ([firstStartDate hourOfDay] * 60 + + [firstStartDate minuteOfHour]); + arp->StartTimeOffset = startMinutes; + arp->EndTimeOffset = (startMinutes + + (NSUInteger) ([event durationAsTimeInterval] + / 60)); + + arp->ExceptionCount = 0; + arp->ReservedBlock1Size = 0; + + /* Currently ignored in property.idl: arp->ReservedBlock2Size = 0; */ + + + /* convert struct to blob */ sBin = talloc_zero (memCtx, struct SBinary_short); bin = set_AppointmentRecurrencePattern (sBin, arp); sBin->cb = bin->cb; diff --git a/OpenChange/MAPIStoreRecurrenceUtils.h b/OpenChange/MAPIStoreRecurrenceUtils.h index 4fdf8c54e..a72a23af8 100644 --- a/OpenChange/MAPIStoreRecurrenceUtils.h +++ b/OpenChange/MAPIStoreRecurrenceUtils.h @@ -23,10 +23,14 @@ #ifndef MAPISTORERECURRENCEUTILS_H #define MAPISTORERECURRENCEUTILS_H +#include + #import #import -@class NSCalendarDate; +@class NSTimeZone; + +@class iCalEvent; @class iCalRepeatableEntityObject; @class iCalRecurrenceRule; @@ -44,8 +48,9 @@ @interface iCalRecurrenceRule (MAPIStoreRecurrence) - (void) fillRecurrencePattern: (struct RecurrencePattern *) rp - withStartDate: (NSCalendarDate *) startDate - andEndDate: (NSCalendarDate *) endDate; + withEvent: (iCalEvent *) event + inTimeZone: (NSTimeZone *) timeZone + inMemCtx: (TALLOC_CTX *) memCtx; @end diff --git a/OpenChange/MAPIStoreRecurrenceUtils.m b/OpenChange/MAPIStoreRecurrenceUtils.m index 09d6ce793..00ddf437a 100644 --- a/OpenChange/MAPIStoreRecurrenceUtils.m +++ b/OpenChange/MAPIStoreRecurrenceUtils.m @@ -21,17 +21,22 @@ */ #import +#import #import #import #import +#import +#import +#import #import #import -#import +#import #import "NSDate+MAPIStore.h" #import "MAPIStoreRecurrenceUtils.h" +#import "MAPIStoreTypes.h" #include #include @@ -43,7 +48,7 @@ - (void) setupRecurrenceWithMasterEntity: (iCalRepeatableEntityObject *) entity fromRecurrencePattern: (struct RecurrencePattern *) rp { - NSCalendarDate *startDate, *olEndDate, *untilDate; + NSCalendarDate *startDate, *olEndDate, *untilDate, *exDate; NSString *monthDay, *month; iCalRecurrenceRule *rule; iCalByDayMask *byDayMask; @@ -203,6 +208,17 @@ [self errorWithFormat: @"invalid value for EndType: %.4x", rp->EndType]; } + + /* exception dates */ + for (count = 0; count < rp->DeletedInstanceCount; count++) + { + exDate + = [NSDate dateFromMinutesSince1601: rp->DeletedInstanceDates[count]]; + exDate = [exDate hour: [startDate hourOfDay] + minute: [startDate minuteOfHour] + second: [startDate secondOfMinute]]; + [entity addToExceptionDates: exDate]; + } } @end @@ -210,17 +226,24 @@ @implementation iCalRecurrenceRule (MAPIStoreRecurrence) - (void) fillRecurrencePattern: (struct RecurrencePattern *) rp - withStartDate: (NSCalendarDate *) startDate - andEndDate: (NSCalendarDate *) endDate + withEvent: (iCalEvent *) event + inTimeZone: (NSTimeZone *) timeZone + inMemCtx: (TALLOC_CTX *) memCtx { iCalRecurrenceFrequency freq; iCalByDayMask *byDayMask; NSString *byMonthDay, *bySetPos; - NSCalendarDate *untilDate, *beginOfWeek, *minimumDate, *moduloDate, *midnight; + NSCalendarDate *startDate, *endDate, *untilDate, *beginOfWeek, *minimumDate, *moduloDate, *midnight; iCalWeekOccurrences *days; - NSInteger dayOfWeek, repeatInterval, repeatCount, count, firstOccurrence; + NSInteger dayOfWeek, repeatInterval, repeatCount, count, firstOccurrence, max; uint32_t nbrMonths, mask; + NSArray *exDates; + startDate = [event firstRecurrenceStartDate]; + [startDate setTimeZone: timeZone]; + endDate = [event lastPossibleRecurrenceStartDate]; + [endDate setTimeZone: timeZone]; + rp->ReaderVersion = 0x3004; rp->WriterVersion = 0x3004; @@ -370,6 +393,19 @@ [self errorWithFormat: @"rule for an event that never occurs"]; } } + + + exDates = [[event exceptionDatesWithTimeZone: utcTZ] + sortedArrayUsingFunction: NSDateCompare + context: NULL]; + max = [exDates count]; + rp->DeletedInstanceCount = max; + rp->DeletedInstanceDates = talloc_array (memCtx, uint32_t, max); + for (count = 0; count < max; count++) + { + startDate = [[exDates objectAtIndex: count] hour: 0 minute: 0 second: 0]; + *(rp->DeletedInstanceDates + count) = [startDate asMinutesSince1601]; + } } @end From 79c3eb33dcfcf029f877231de49c364dc2e1644e Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Sun, 1 Jul 2012 22:31:05 +0000 Subject: [PATCH 14/21] Monotone-Parent: a6b977673a1cc94b1269e19c469101dca9fd17bc Monotone-Revision: ebf7c7e799b00cc44e631f47d644d492cac8a9aa Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-01T22:31:05 Monotone-Branch: ca.inverse.sogo --- OpenChange/NSDate+MAPIStore.h | 2 +- OpenChange/NSDate+MAPIStore.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenChange/NSDate+MAPIStore.h b/OpenChange/NSDate+MAPIStore.h index 0a01a2ddc..93f09be37 100644 --- a/OpenChange/NSDate+MAPIStore.h +++ b/OpenChange/NSDate+MAPIStore.h @@ -39,6 +39,6 @@ @end -NSComparisonResult NSDateCompare (NSDate *date1, NSDate *date2, void *); +NSComparisonResult NSDateCompare (id date1, id date2, void *); #endif /* NSCALENDARDATE+MAPISTORE_H */ diff --git a/OpenChange/NSDate+MAPIStore.m b/OpenChange/NSDate+MAPIStore.m index 1e631b82b..06e55f1d7 100644 --- a/OpenChange/NSDate+MAPIStore.m +++ b/OpenChange/NSDate+MAPIStore.m @@ -131,7 +131,7 @@ _setupRefDate () @end NSComparisonResult -NSDateCompare (NSDate *date1, NSDate *date2, void *ctx) +NSDateCompare (id date1, id date2, void *ctx) { NSTimeInterval secs1, secs2; NSComparisonResult result; From 8bf73b73b682ccbf2c588793e00133485a86bdf4 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 2 Jul 2012 19:49:05 +0000 Subject: [PATCH 15/21] Monotone-Parent: 47874ea582a8f97d988110f4d4174d2668de0fb0 Monotone-Revision: 32541b6f161d32f977e2eaff040e594f4ef885b2 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-02T19:49:05 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 1 - 1 file changed, 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 46995e658..43598829b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,3 @@ -<<<<<<< variant A 2012-07-01 Wolfgang Sourdeau * OpenChange/MAPIStoreRecurrenceUtils.m From 19adabfa92267b39ace1f4ceb59f3d2584d5899a Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Mon, 2 Jul 2012 19:57:29 +0000 Subject: [PATCH 16/21] Monotone-Parent: 32541b6f161d32f977e2eaff040e594f4ef885b2 Monotone-Revision: f25607e5762568e3c223b1868307c8cc92bd66da Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-02T19:57:29 Monotone-Branch: ca.inverse.sogo --- SOPE/GDLContentStore/GCSSpecialQueries.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SOPE/GDLContentStore/GCSSpecialQueries.m b/SOPE/GDLContentStore/GCSSpecialQueries.m index d3c916864..e99afbaf5 100644 --- a/SOPE/GDLContentStore/GCSSpecialQueries.m +++ b/SOPE/GDLContentStore/GCSSpecialQueries.m @@ -181,7 +181,7 @@ { static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (" - @" c_id VARCHAR(255) NOT NULL PRIMARY KEY," + @" c_id VARCHAR(255) PRIMARY KEY," @" c_value VARCHAR(255) NOT NULL," @" c_creationdate INT4 NOT NULL," @" c_lastseen INT4 NOT NULL)"); From 934e15d2d0df5063f1da21da7e92aa936d71f1bd Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Tue, 10 Jul 2012 15:49:05 +0000 Subject: [PATCH 17/21] Monotone-Parent: c6b25920d896b2d19d0ef40a4cadee46c87bba2d Monotone-Revision: 1c3bb3511167199e0dc7bafa4db7fc5540648666 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-10T15:49:05 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 9 ++++++ .../Appointments/SOGoAppointmentFolder.m | 22 ++++---------- SoObjects/SOGo/SOGoObject.h | 4 +++ SoObjects/SOGo/SOGoObject.m | 30 +++++++++++++++++++ 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index f5fdab097..e1747cb15 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2012-07-10 Wolfgang Sourdeau + + * SoObjects/SOGo/SOGoObject.m (-davBooleanForResult:): new method + that returns a valid DAV boolean from a BOOL. + (-isValidDAVBoolean:): new method that validates the value as a + DAV boolean. + (-resultForDAVBoolean:): new method that returns a BOOL from a DAV + boolean. + 2012-07-09 Ludovic Marcotte * Dropped old templates (SOGoAptMailDeletionReceipt.wox diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 864d0ec26..6a79582d8 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -2250,28 +2250,18 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir - (NSString *) davCalendarShowAlarms { - NSString *boolean; - - if ([self showCalendarAlarms]) - boolean = @"true"; - else - boolean = @"false"; - - return boolean; + return [self davBooleanForResult: [self showCalendarAlarms]]; } - (NSException *) setDavCalendarShowAlarms: (id) newBoolean { NSException *error; - error = nil; - - if ([newBoolean isEqualToString: @"true"] - || [newBoolean isEqualToString: @"1"]) - [self setShowCalendarAlarms: YES]; - else if ([newBoolean isEqualToString: @"false"] - || [newBoolean isEqualToString: @"0"]) - [self setShowCalendarAlarms: NO]; + if ([self isValidDAVBoolean: newBoolean]) + { + [self setShowCalendarAlarms: [self resultForDAVBoolean: newBoolean]]; + error = nil; + } else error = [NSException exceptionWithHTTPStatus: 400 reason: @"Bad boolean value."]; diff --git a/SoObjects/SOGo/SOGoObject.h b/SoObjects/SOGo/SOGoObject.h index dc26fd78e..172f45a60 100644 --- a/SoObjects/SOGo/SOGoObject.h +++ b/SoObjects/SOGo/SOGoObject.h @@ -166,6 +166,10 @@ parameters: (NSArray *) params; /* utilities */ +- (NSString *) davBooleanForResult: (BOOL) result; +- (BOOL) isValidDAVBoolean: (NSString *) davBoolean; +- (BOOL) resultForDAVBoolean: (NSString *) davBoolean; + - (NSString *) labelForKey: (NSString *) key; /* description */ diff --git a/SoObjects/SOGo/SOGoObject.m b/SoObjects/SOGo/SOGoObject.m index b7b42ba9f..8a0f0e24d 100644 --- a/SoObjects/SOGo/SOGoObject.m +++ b/SoObjects/SOGo/SOGoObject.m @@ -27,6 +27,7 @@ #import #import #import +#import #import #import #import @@ -1585,6 +1586,35 @@ return exception; } +- (NSString *) davBooleanForResult: (BOOL) result +{ + return (result ? @"true" : @"false"); +} + +- (BOOL) isValidDAVBoolean: (NSString *) davBoolean +{ + static NSSet *validBooleans = nil; + + if (!validBooleans) + { + validBooleans = [NSSet setWithObjects: @"true", @"false", @"1", @"0", + nil]; + [validBooleans retain]; + } + + return [validBooleans containsObject: davBoolean]; +} + +- (BOOL) resultForDAVBoolean: (NSString *) davBoolean +{ + BOOL result; + + result = ([davBoolean isEqualToString: @"true"] + || [davBoolean isEqualToString: @"1"]); + + return result; +} + - (NSString *) labelForKey: (NSString *) key { return [self labelForKey: key inContext: context]; From a469952773f3c3bbec7cdbee0ab2c301a2f9b57e Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Tue, 10 Jul 2012 15:49:24 +0000 Subject: [PATCH 18/21] Monotone-Parent: 1c3bb3511167199e0dc7bafa4db7fc5540648666 Monotone-Revision: eb2ae1364da011cec8f9e025b9b53785ba2647d6 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-10T15:49:24 Monotone-Branch: ca.inverse.sogo --- SoObjects/Appointments/SOGoAppointmentFolder.m | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 6a79582d8..a271c7b22 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -455,7 +455,7 @@ static NSNumber *sharedYes = nil; // We MUST keep the 'NO' value here, because we will always // fallback to the domain defaults otherwise. // -- (BOOL) _setNotificationValue: (BOOL) b +- (void) _setNotificationValue: (BOOL) b forKey: (NSString *) theKey { [self setFolderPropertyValue: [NSNumber numberWithBool: b] @@ -1005,7 +1005,9 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir rules = [cycleinfo objectForKey: @"rules"]; exRules = [cycleinfo objectForKey: @"exRules"]; exDates = [cycleinfo objectForKey: @"exDates"]; - eventTimeZone = allDayTimeZone = tz = nil; + eventTimeZone = nil; + allDayTimeZone = nil; + tz = nil; row = [self fixupRecord: theRecord]; [row removeObjectForKey: @"c_cycleinfo"]; @@ -1062,7 +1064,9 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir } } - tz = eventTimeZone? eventTimeZone : allDayTimeZone; +#warning this code is ugly: we should not mix objects with different types as\ + it reduces readability + tz = eventTimeZone ? eventTimeZone : allDayTimeZone; if (tz) { // Adjust the exception dates From 961302407db37073606f9be1bb2507f3f7634b5e Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Tue, 10 Jul 2012 16:02:16 +0000 Subject: [PATCH 19/21] Monotone-Parent: eb2ae1364da011cec8f9e025b9b53785ba2647d6 Monotone-Revision: fcec9822c0aaee8cefba77d0f50a8582b33fa3f0 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-10T16:02:16 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 11 +++ .../Appointments/SOGoAppointmentFolder.m | 78 +++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/ChangeLog b/ChangeLog index e1747cb15..8e3df2cf8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,16 @@ 2012-07-10 Wolfgang Sourdeau + * SoObjects/Appointments/SOGoAppointmentFolder.m + (-davNotifyOnPersonalModifications) + (-setDavNotifyOnPersonalModifications:) + (-davNotifyOnExternalModifications) + (-setDavNotifyOnExternalModifications:) + (-davNotifyUserOnPersonalModifications) + (-setDavNotifyUserOnPersonalModifications:) + (-davNotifiedUserOnPersonalModifications) + (-setDavNotifiedUserOnPersonalModifications:): new dav accessors. + + * SoObjects/SOGo/SOGoObject.m (-davBooleanForResult:): new method that returns a valid DAV boolean from a BOOL. (-isValidDAVBoolean:): new method that validates the value as a diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index a271c7b22..29a64b77b 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -2273,6 +2273,84 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir return error; } +- (NSString *) davNotifyOnPersonalModifications +{ + return [self davBooleanForResult: [self notifyOnPersonalModifications]]; +} + +- (NSException *) setDavNotifyOnPersonalModifications: (NSString *) newBoolean +{ + NSException *error; + + if ([self isValidDAVBoolean: newBoolean]) + { + [self setNotifyOnPersonalModifications: + [self resultForDAVBoolean: newBoolean]]; + error = nil; + } + else + error = [NSException exceptionWithHTTPStatus: 400 + reason: @"Bad boolean value."]; + + return error; +} + +- (NSString *) davNotifyOnExternalModifications +{ + return [self davBooleanForResult: [self notifyOnExternalModifications]]; +} + +- (NSException *) setDavNotifyOnExternalModifications: (NSString *) newBoolean +{ + NSException *error; + + if ([self isValidDAVBoolean: newBoolean]) + { + [self setNotifyOnExternalModifications: + [self resultForDAVBoolean: newBoolean]]; + error = nil; + } + else + error = [NSException exceptionWithHTTPStatus: 400 + reason: @"Bad boolean value."]; + + return error; +} + +- (NSString *) davNotifyUserOnPersonalModifications +{ + return [self davBooleanForResult: [self notifyUserOnPersonalModifications]]; +} + +- (NSException *) setDavNotifyUserOnPersonalModifications: (NSString *) newBoolean +{ + NSException *error; + + if ([self isValidDAVBoolean: newBoolean]) + { + [self setNotifyUserOnPersonalModifications: + [self resultForDAVBoolean: newBoolean]]; + error = nil; + } + else + error = [NSException exceptionWithHTTPStatus: 400 + reason: @"Bad boolean value."]; + + return error; +} + +- (NSString *) davNotifiedUserOnPersonalModifications +{ + return [self notifiedUserOnPersonalModifications]; +} + +- (NSException *) setDavNotifiedUserOnPersonalModifications: (NSString *) theUser +{ + [self setNotifiedUserOnPersonalModifications: theUser]; + + return nil; +} + /* vevent UID handling */ - (NSString *) resourceNameForEventUID: (NSString *) uid From d932f1fd475d5c482742fa8a794261ff80b06d5f Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 11 Jul 2012 14:09:37 +0000 Subject: [PATCH 20/21] Fixed merge Monotone-Parent: 6eabcf880fa6d4620fb9a1ed111b15bef9bd9877 Monotone-Revision: 2252f3047b058ea629e568aec7efb2b0432e3c0c Monotone-Author: ludovic@Sophos.ca Monotone-Date: 2012-07-11T14:09:37 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1c4d2d23a..b5df6d237 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,11 +1,9 @@ -<<<<<<< variant A 2012-07-11 Ludovic Marcotte * Finished the implementation and testing of the two new defaults. Also added documentation in the SOGo Installation and Configuration Guide. ->>>>>>> variant B 2012-07-10 Wolfgang Sourdeau * SoObjects/Appointments/SOGoAppointmentFolder.m @@ -26,8 +24,6 @@ (-resultForDAVBoolean:): new method that returns a BOOL from a DAV boolean. -####### Ancestor -======= end 2012-07-09 Ludovic Marcotte * Dropped old templates (SOGoAptMailDeletionReceipt.wox From c342d3defe58629f1b365a06ba5c0e38df77870a Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Thu, 12 Jul 2012 15:05:33 +0000 Subject: [PATCH 21/21] Monotone-Parent: f48e9e2abfb00e03cccd2aa8fd8998a415bb5ce1 Monotone-Revision: 3dfe653dfcaeadc5fe93ff1f2c0a1f9d049c1836 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2012-07-12T15:05:33 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3381a6eb5..a3ba33ddf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,7 +4,6 @@ new defaults. Also added documentation in the SOGo Installation and Configuration Guide. -<<<<<<< variant A * Updated UI/Templates/SchedulerUI/UIxCalendarProperties.wox to not show the notification options if it's a web calendar or if the active user isn't the owner of the calendar. @@ -32,29 +31,6 @@ (-resultForDAVBoolean:): new method that returns a BOOL from a DAV boolean. ->>>>>>> variant B -2012-07-10 Wolfgang Sourdeau - - * SoObjects/Appointments/SOGoAppointmentFolder.m - (-davNotifyOnPersonalModifications) - (-setDavNotifyOnPersonalModifications:) - (-davNotifyOnExternalModifications) - (-setDavNotifyOnExternalModifications:) - (-davNotifyUserOnPersonalModifications) - (-setDavNotifyUserOnPersonalModifications:) - (-davNotifiedUserOnPersonalModifications) - (-setDavNotifiedUserOnPersonalModifications:): new dav accessors. - - - * SoObjects/SOGo/SOGoObject.m (-davBooleanForResult:): new method - that returns a valid DAV boolean from a BOOL. - (-isValidDAVBoolean:): new method that validates the value as a - DAV boolean. - (-resultForDAVBoolean:): new method that returns a BOOL from a DAV - boolean. - -####### Ancestor -======= end 2012-07-09 Ludovic Marcotte * Dropped old templates (SOGoAptMailDeletionReceipt.wox