IMAPClient.Mod 82 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774
  1. MODULE IMAPClient; (** AUTHOR "retmeier"; PURPOSE "An IMAP Client and its data structures"; *)
  2. IMPORT
  3. Configuration, Streams, Strings, KernelLog, Classes := TFClasses, Kernel, IMAP, IMAPUtilities, XML, XMLObjects;
  4. CONST
  5. DEBUG = FALSE;
  6. KEEPALIVE = 20 * 1000 * 1; (* ms *)
  7. Port = 143;
  8. (* Return value of Procecures *)
  9. OK* = 0;
  10. ERROR* = 1;
  11. (* status *)
  12. DEAD* = -1;
  13. ONLINE* = 0;
  14. OFFLINE * = 1;
  15. DISCONNECTED *= 2;
  16. CONNECTIONERROR* = 3;
  17. AUTHENTICATIONERROR* = 4;
  18. (* constans for the current work the client is doing. Can be used to output a corresponding status string *)
  19. CWFINISHED* = 0;
  20. CWCONNECTING *= 1;
  21. CWLOADING *= 2;
  22. CWCREATING *= 3;
  23. CWRENAMING *= 4;
  24. CWDELETINGFOLDER *= 5;
  25. CWSEARCHING *= 6;
  26. CWCOPYING *= 7;
  27. CWDELETINGMESSAGE *= 8;
  28. CWAPPENDING *= 9;
  29. CWCLOSING *= 10;
  30. CWSAVINGACCOUNT *= 11;
  31. CWLOADINGACCOUNT *= 12;
  32. CWPOLLING *= 13;
  33. CWEXPUNGING *= 14;
  34. CWRESTORING *= 15;
  35. (* Tasks *)
  36. TNothing *= 0;
  37. TLoadAllMessages *= 1;
  38. VAR
  39. globalR: LONGINT;
  40. TYPE
  41. String = Strings.String;
  42. EventListener* = PROCEDURE { DELEGATE };
  43. ErrorListener* = PROCEDURE{ DELEGATE} (CONST s:ARRAY OF CHAR);
  44. Message* = OBJECT
  45. VAR
  46. header*: HeaderElement;
  47. message*: String;
  48. bodystructure*: Bodystructure;
  49. internalDate*: String;
  50. size*: LONGINT;
  51. flags*: Flags;
  52. uID*: LONGINT;
  53. PROCEDURE ToString*():String;
  54. VAR
  55. buffer: Strings.Buffer;
  56. w: Streams.Writer;
  57. s: String;
  58. result: String;
  59. BEGIN
  60. NEW(buffer, 16);
  61. w := buffer.GetWriter();
  62. IF header.date # NIL THEN
  63. w.String("Date: "); w.String(header.date^); w.Ln();
  64. END;
  65. IF header.subject # NIL THEN
  66. w.String("Subject: "); w.String(header.subject^); w.Ln();
  67. END;
  68. IF header.from # NIL THEN
  69. IMAPUtilities.AddressesToString(header.from, s); w.String("From: "); w.String(s^); w.Ln();
  70. END;
  71. IF header.sender # NIL THEN
  72. IMAPUtilities.AddressesToString(header.sender, s); w.String("Sender: "); w.String(s^); w.Ln();
  73. END;
  74. IF header.replyTo # NIL THEN
  75. IMAPUtilities.AddressesToString(header.replyTo, s); w.String("Reply-To: "); w.String(s^); w.Ln();
  76. END;
  77. IF header.to # NIL THEN
  78. IMAPUtilities.AddressesToString(header.to, s); w.String("To: "); w.String(s^); w.Ln();
  79. END;
  80. IF header.cc # NIL THEN
  81. IMAPUtilities.AddressesToString(header.cc, s); w.String("Cc: "); w.String(s^); w.Ln();
  82. END;
  83. IF header.bcc # NIL THEN
  84. IMAPUtilities.AddressesToString(header.from, s); w.String("Bcc: "); w.String(s^); w.Ln();
  85. END;
  86. w.String("Content-type: text/plain; charset="); w.Char(CHR(34)); w.String("utf-8"); w.Char(CHR(34)); w.Ln();
  87. w.String("Content-Transfer-Encoding: quoted-printable"); w.Ln();
  88. w.Ln();
  89. s := IMAPUtilities.NewString(message^);
  90. IMAPUtilities.encodeQuotedPrintable(s);
  91. w.String(s^);
  92. result := buffer.GetString();
  93. RETURN result;
  94. END ToString;
  95. END Message;
  96. Client* = OBJECT
  97. VAR
  98. status-: LONGINT;
  99. currentWork-: LONGINT;
  100. abort*, userAbort*: BOOLEAN;
  101. c: IMAP.Connection;
  102. currentFolder-: Folder;
  103. mailboxContent-: Folder;
  104. getSubFoldersContext: Folder; (* shared Variable, that is used to pass the Folder from GetSubFolders() to CheckAnswer() *)
  105. FolderIsSynchronized: BOOLEAN; (* is set to FALSE by SelectFolder and tells the client to synchronize itself. Synchronization happens on timer wakeup *)
  106. FolderComplete: BOOLEAN; (* is set to FALSE by SelectFolder an means that Synchronize has not yet been executed completly *)
  107. Task*: LONGINT;
  108. searchResult-: POINTER TO ARRAY OF LONGINT;
  109. timer*: Kernel.Timer;
  110. observer: EventListener;
  111. errorHandler: ErrorListener;
  112. applySearchFilter*: BOOLEAN;
  113. ret: Classes.List;
  114. numberOfMessages: LONGINT;
  115. preferences*: AccountPreferences;
  116. PROCEDURE &Init*(obs: EventListener; error: ErrorListener);
  117. BEGIN
  118. NEW(preferences);
  119. preferences.LoadStandardConfig();
  120. abort := FALSE; userAbort := FALSE;
  121. observer := obs;
  122. errorHandler := error;
  123. applySearchFilter := FALSE;
  124. FolderIsSynchronized := TRUE;
  125. Task := TNothing;
  126. NEW(timer);
  127. NEW(mailboxContent,"Folders");
  128. mailboxContent.Noselect := TRUE;
  129. currentFolder := mailboxContent;
  130. status := DISCONNECTED;
  131. currentWork := CWFINISHED;
  132. c := NIL;
  133. END Init;
  134. PROCEDURE SetObserverMethod*(m: EventListener);
  135. BEGIN
  136. observer := m;
  137. END SetObserverMethod;
  138. PROCEDURE CallObserverMethod;
  139. BEGIN
  140. IF observer # NIL THEN
  141. observer();
  142. END;
  143. END CallObserverMethod;
  144. PROCEDURE SetErrorHandler*(m: ErrorListener);
  145. BEGIN
  146. errorHandler:= m;
  147. END SetErrorHandler;
  148. PROCEDURE CallErrorHandler(CONST string: ARRAY OF CHAR);
  149. BEGIN
  150. IF errorHandler # NIL THEN
  151. IF DEBUG THEN KernelLog.String(string); KernelLog.Ln(); END;
  152. errorHandler(string);
  153. END;
  154. END CallErrorHandler;
  155. PROCEDURE Connect*(CONST host, user, pass: ARRAY OF CHAR): LONGINT;
  156. BEGIN {EXCLUSIVE}
  157. RETURN ConnectUnlocked(host, user, pass);
  158. END Connect;
  159. PROCEDURE ConnectUnlocked(host, user, pass: ARRAY OF CHAR):LONGINT;
  160. VAR
  161. r: LONGINT;
  162. buffer: Strings.Buffer;
  163. w: Streams.Writer;
  164. errorString: String;
  165. inbox: Folder;
  166. BEGIN
  167. applySearchFilter := FALSE;
  168. userAbort := FALSE;
  169. abort := FALSE;
  170. preferences.IMAPServer := IMAPUtilities.NewString(host);
  171. preferences.UserName := IMAPUtilities.NewString(user);
  172. r := 0;
  173. NEW(c, host, Port, r);
  174. IF r # IMAP.OK THEN
  175. NEW(buffer, 16);
  176. w := buffer.GetWriter();
  177. w.String("Connection to host: ");
  178. w.String(host);
  179. w.String(" could not be estabilshed.");
  180. errorString := buffer.GetString();
  181. CallErrorHandler(errorString^);
  182. status := CONNECTIONERROR;
  183. c := NIL;
  184. RETURN ERROR;
  185. END;
  186. IF c.GetCurrentState() = IMAP.NOAUTH THEN
  187. r := c.Login(user, pass);
  188. IF r # IMAP.OK THEN
  189. CallErrorHandler("Username or Password wrong!");
  190. r := c.Logout();
  191. c := NIL;
  192. status := AUTHENTICATIONERROR;
  193. RETURN ERROR;
  194. END;
  195. END;
  196. status := ONLINE;
  197. currentWork := CWLOADING;
  198. currentFolder := mailboxContent;
  199. r := GetSubFolders(currentFolder);
  200. IF r # OK THEN
  201. currentWork := CWFINISHED;
  202. RETURN r;
  203. END;
  204. inbox := mailboxContent.FindSubFolder("INBOX");
  205. IF inbox # NIL THEN
  206. r := SelectFolderUnlocked(inbox);
  207. ELSE
  208. r := SelectFolderUnlocked(currentFolder);
  209. END;
  210. currentWork := CWFINISHED;
  211. IF r # OK THEN RETURN r; END;
  212. CallObserverMethod();
  213. RETURN OK;
  214. END ConnectUnlocked;
  215. PROCEDURE Disconnect*;
  216. VAR
  217. r: LONGINT;
  218. BEGIN {EXCLUSIVE}
  219. IF status = ONLINE THEN
  220. r := c.Logout();
  221. c := NIL;
  222. END;
  223. NEW(mailboxContent,"Folders");
  224. mailboxContent.Noselect := TRUE;
  225. currentFolder := mailboxContent;
  226. status := DISCONNECTED;
  227. CallObserverMethod();
  228. END Disconnect;
  229. PROCEDURE SwitchToOffline*;
  230. VAR
  231. r: LONGINT;
  232. BEGIN {EXCLUSIVE}
  233. IF status = ONLINE THEN
  234. r := c.Logout();
  235. status := OFFLINE;
  236. CallObserverMethod();
  237. END;
  238. END SwitchToOffline;
  239. PROCEDURE SwitchToOnline*(CONST password: ARRAY OF CHAR);
  240. VAR
  241. r: LONGINT;
  242. BEGIN {EXCLUSIVE}
  243. IF status = OFFLINE THEN
  244. (* authenticate to the server *)
  245. r := ConnectUnlocked(preferences.IMAPServer^, preferences.UserName^, password);
  246. IF r = OK THEN
  247. (* do local change *)
  248. status := ONLINE;
  249. ELSE
  250. status := OFFLINE;
  251. END;
  252. CallObserverMethod();
  253. END;
  254. END SwitchToOnline;
  255. PROCEDURE CheckAnswer(ret: Classes.List);
  256. VAR
  257. i: LONGINT;
  258. answerP: ANY;
  259. answer: IMAP.Entry;
  260. BEGIN
  261. i := 0;
  262. WHILE i < ret.GetCount() DO
  263. answerP := ret.GetItem(i);
  264. answer := answerP(IMAP.Entry);
  265. IF (answer.command = "EXISTS") THEN
  266. CheckExists(answer);
  267. ELSIF (answer.command = "RECENT") THEN
  268. CheckRecent(answer);
  269. ELSIF (answer.command = "EXPUNGE") THEN
  270. CheckExpunge(answer);
  271. ELSIF answer.command = "SEARCH" THEN
  272. CheckSearch(answer);
  273. ELSIF answer.command = "STATUS" THEN
  274. CheckStatus(answer);
  275. ELSIF answer.command = "LIST" THEN
  276. CheckList(answer);
  277. ELSIF answer.command = "FETCH" THEN
  278. CheckFetch(answer);
  279. ELSIF answer.command = "BYE" THEN
  280. CallErrorHandler("The server kicked us out by sending the BYE command. The client is disconnected.");
  281. c := NIL;
  282. NEW(mailboxContent,"Folders");
  283. mailboxContent.Noselect := TRUE;
  284. currentFolder := mailboxContent;
  285. status := DISCONNECTED;
  286. CallObserverMethod();
  287. END;
  288. INC(i);
  289. END;
  290. CallObserverMethod();
  291. END CheckAnswer;
  292. PROCEDURE CheckExists(answer: IMAP.Entry);
  293. BEGIN
  294. numberOfMessages := answer.number;
  295. FolderIsSynchronized := FALSE;
  296. timer.Wakeup();
  297. END CheckExists;
  298. PROCEDURE CheckRecent(answer: IMAP.Entry);
  299. BEGIN
  300. FolderIsSynchronized := FALSE;
  301. timer.Wakeup();
  302. END CheckRecent;
  303. PROCEDURE CheckExpunge(answer: IMAP.Entry);
  304. VAR
  305. messageP: ANY;
  306. BEGIN
  307. messageP := currentFolder.messages.GetItem(answer.number - 1);
  308. currentFolder.messages.Remove(messageP);
  309. DEC(numberOfMessages);
  310. END CheckExpunge;
  311. PROCEDURE CheckSearch(answer: IMAP.Entry);
  312. VAR
  313. list: Classes.List;
  314. j, count, number: LONGINT;
  315. entP: ANY;
  316. ent: IMAP.Entry;
  317. BEGIN
  318. list := answer.list;
  319. j := 0;
  320. count := list.GetCount();
  321. NEW(searchResult, count);
  322. WHILE j < count DO
  323. entP := list.GetItem(j);
  324. ent := entP(IMAP.Entry);
  325. Strings.StrToInt(ent.data^, number);
  326. searchResult[j] := number-1;
  327. INC(j);
  328. END;
  329. END CheckSearch;
  330. PROCEDURE CheckStatus(answer: IMAP.Entry);
  331. VAR
  332. list: Classes.List;
  333. j: LONGINT;
  334. entP: ANY;
  335. ent: IMAP.Entry;
  336. BEGIN
  337. list := answer.list;
  338. FOR j := 0 TO list.GetCount()-1 BY 2 DO
  339. entP := list.GetItem(j);
  340. ent := entP(IMAP.Entry);
  341. IF ent.data^ = "MESSAGES" THEN
  342. entP := list.GetItem(j+1);
  343. ent := entP(IMAP.Entry);
  344. Strings.StrToInt(ent.data^, numberOfMessages);
  345. END;
  346. END;
  347. END CheckStatus;
  348. PROCEDURE CheckList(answer: IMAP.Entry);
  349. VAR
  350. j: LONGINT;
  351. list, flags: Classes.List;
  352. entP, flagP: ANY;
  353. ent, flag: IMAP.Entry;
  354. path, name: String;
  355. folder, temp: Folder;
  356. BEGIN
  357. folder := getSubFoldersContext;
  358. list := answer.list;
  359. entP := list.GetItem(2);
  360. ent := entP(IMAP.Entry);
  361. IF getSubFoldersContext # mailboxContent THEN
  362. NEW(path, IMAPUtilities.StringLength(folder.path^)+IMAPUtilities.StringLength(folder.name^)+2);
  363. IF folder.parent = mailboxContent THEN
  364. IMAPUtilities.StringCopy(folder.name^, 0, IMAPUtilities.StringLength(folder.name^), path^);
  365. ELSE
  366. IMAPUtilities.StringCopy(folder.path^, 0, IMAPUtilities.StringLength(folder.path^), path^);
  367. path^[IMAPUtilities.StringLength(folder.path^)] := folder.hierarchyDelimiter;
  368. Strings.Append(path^, folder.name^);
  369. END;
  370. name := Strings.Substring2(IMAPUtilities.StringLength(path^) + 1, ent.data^);
  371. ELSE
  372. NEW(path, 1);
  373. path^[0] := 0X;
  374. name := IMAPUtilities.NewString(ent.data^);
  375. END;
  376. temp := folder.FindSubFolder(name^);
  377. IF temp = NIL THEN
  378. NEW(temp, name^);
  379. temp.path := path;
  380. temp.parent := folder;
  381. folder.children.Add(temp);
  382. END;
  383. temp.alive := TRUE;
  384. entP := list.GetItem(0);
  385. ent := entP(IMAP.Entry);
  386. flags := ent.list;
  387. j := 0;
  388. WHILE j < flags.GetCount() DO
  389. flagP := flags.GetItem(j);
  390. flag := flagP(IMAP.Entry);
  391. IF flag.data^ = "Noselect" THEN
  392. temp.Noselect := TRUE;
  393. ELSIF flag.data^ = "Noinferiors" THEN
  394. temp.Noinferiors := TRUE;
  395. ELSIF flag.data^ = "Marked" THEN
  396. temp.Marked := TRUE;
  397. ELSIF flag.data^ = "Unmarked" THEN
  398. temp.Unmarked := TRUE;
  399. END;
  400. INC(j);
  401. END;
  402. entP := list.GetItem(1);
  403. ent := entP(IMAP.Entry);
  404. temp.hierarchyDelimiter := ent.data^[0];
  405. END CheckList;
  406. PROCEDURE CheckFetch(answer: IMAP.Entry);
  407. VAR
  408. list, envList, structureList, subStructureList: Classes.List;
  409. entP, envEntP, structureP: ANY;
  410. ent, envEnt, structure: IMAP.Entry;
  411. j, l: LONGINT;
  412. message: Message;
  413. header: HeaderElement;
  414. bodystructure: Bodystructure;
  415. messageP: ANY;
  416. (* translate the internal IMAP representation [Realname] [namePart] [domainPart] to a list of Address objects *)
  417. PROCEDURE Imap2AdrList(entry:IMAP.Entry):Classes.List;
  418. VAR
  419. k: LONGINT;
  420. ent,temp: IMAP.Entry;
  421. entP, tempP:ANY;
  422. inlist, outlist: Classes.List;
  423. address: IMAPUtilities.Address;
  424. BEGIN
  425. NEW(outlist);
  426. IF entry.type # IMAP.LIST THEN RETURN outlist; END;
  427. inlist := entry.list;
  428. FOR k := 0 TO inlist.GetCount()-1 DO
  429. NEW(address);
  430. entP := inlist.GetItem(k);ent := entP(IMAP.Entry);
  431. ASSERT(ent.type = IMAP.LIST,1001);
  432. tempP := ent.list.GetItem(0); temp := tempP(IMAP.Entry);
  433. IF temp.data^ = "NIL" THEN
  434. NEW(address.realName, 1);
  435. COPY("",address.realName^);
  436. ELSE
  437. address.realName := temp.data;
  438. END;
  439. tempP := ent.list.GetItem(2);
  440. temp := tempP(IMAP.Entry);
  441. address.namePart := temp.data;
  442. tempP := ent.list.GetItem(3);
  443. temp := tempP(IMAP.Entry);
  444. address.domainPart := temp.data;
  445. outlist.Add(address);
  446. END;
  447. RETURN outlist;
  448. END Imap2AdrList;
  449. BEGIN
  450. messageP := currentFolder.messages.GetItem(answer.number - 1);
  451. message := messageP(Message);
  452. list := answer.list;
  453. FOR j := 0 TO list.GetCount()-1 BY 2 DO
  454. entP := list.GetItem(j);
  455. ent := entP(IMAP.Entry);
  456. Strings.UpperCase(ent.data^);
  457. IF ent.data^ = "FLAGS" THEN
  458. entP := list.GetItem(j+1);
  459. ent := entP(IMAP.Entry); (* list of flags *)
  460. NEW(message.flags);
  461. message.flags.ParseList(ent.list);
  462. ELSIF ent.data^ = "INTERNALDATE" THEN
  463. entP := list.GetItem(j+1);
  464. ent := entP(IMAP.Entry);
  465. message.internalDate := ent.data;
  466. ELSIF ent.data^ = "RFC822.SIZE" THEN
  467. entP := list.GetItem(j+1);
  468. ent := entP(IMAP.Entry);
  469. Strings.StrToInt(ent.data^,message.size);
  470. ELSIF ent.data^ = "UID" THEN
  471. entP := list.GetItem(j+1);
  472. ent := entP(IMAP.Entry);
  473. Strings.StrToInt(ent.data^,message.uID);
  474. ELSIF ent.data^ = "ENVELOPE" THEN
  475. NEW(header);
  476. message.header := header;
  477. entP := list.GetItem(j+1);
  478. ent := entP(IMAP.Entry);
  479. envList := ent.list;
  480. envEntP := envList.GetItem(0); envEnt := envEntP(IMAP.Entry); header.date := envEnt.data;
  481. envEntP := envList.GetItem(1); envEnt := envEntP(IMAP.Entry); header.subject := envEnt.data;
  482. envEntP := envList.GetItem(8); envEnt := envEntP(IMAP.Entry); header.inReplyTo := envEnt.data;
  483. envEntP := envList.GetItem(9); envEnt := envEntP(IMAP.Entry); header.messageID := envEnt.data;
  484. envEntP := envList.GetItem(2); envEnt := envEntP(IMAP.Entry); header.from := Imap2AdrList(envEnt);
  485. envEntP := envList.GetItem(3); envEnt := envEntP(IMAP.Entry); header.sender := Imap2AdrList(envEnt);
  486. envEntP := envList.GetItem(4); envEnt := envEntP(IMAP.Entry); header.replyTo := Imap2AdrList(envEnt);
  487. envEntP := envList.GetItem(5); envEnt := envEntP(IMAP.Entry); header.to := Imap2AdrList(envEnt);
  488. envEntP := envList.GetItem(6); envEnt := envEntP(IMAP.Entry); header.cc := Imap2AdrList(envEnt);
  489. envEntP := envList.GetItem(7); envEnt := envEntP(IMAP.Entry); header.bcc := Imap2AdrList(envEnt);
  490. ELSIF ent.data^ = "RFC822.TEXT" THEN
  491. entP := list.GetItem(j+1);
  492. ent := entP(IMAP.Entry);
  493. message.message := IMAPUtilities.NewString(ent.data^);
  494. ELSIF ent.data^ = "BODYSTRUCTURE" THEN
  495. entP := list.GetItem(j+1);
  496. ent := entP(IMAP.Entry);
  497. structureList := ent.list;
  498. structureP := structureList.GetItem(0);
  499. structure := structureP(IMAP.Entry);
  500. NEW(bodystructure);
  501. IF structure.type = IMAP.LIST THEN
  502. Strings.Copy("MULTIPART", 0, 9, bodystructure.type);
  503. bodystructure.subpart := NIL;
  504. ELSE
  505. structureP := structureList.GetItem(0);
  506. structure := structureP(IMAP.Entry);
  507. IMAPUtilities.StringCopy(structure.data^, 0, IMAPUtilities.StringLength(structure.data^), bodystructure.type);
  508. structureP := structureList.GetItem(1);
  509. structure := structureP(IMAP.Entry);
  510. IMAPUtilities.StringCopy(structure.data^, 0, IMAPUtilities.StringLength(structure.data^), bodystructure.subtype);
  511. structureP := structureList.GetItem(5);
  512. structure := structureP(IMAP.Entry);
  513. IMAPUtilities.StringCopy(structure.data^, 0, IMAPUtilities.StringLength(structure.data^), bodystructure.encoding);
  514. structureP := structureList.GetItem(2);
  515. structure := structureP(IMAP.Entry);
  516. subStructureList := structure.list;
  517. IF subStructureList # NIL THEN
  518. FOR l := 0 TO subStructureList.GetCount()-1 BY 2 DO
  519. structureP := subStructureList.GetItem(l);
  520. structure := structureP(IMAP.Entry);
  521. Strings.UpperCase(structure.data^);
  522. IF structure.data^ = "CHARSET" THEN
  523. structureP := subStructureList.GetItem(l+1);
  524. structure := structureP(IMAP.Entry);
  525. IMAPUtilities.StringCopy(structure.data^, 0, IMAPUtilities.StringLength(structure.data^), bodystructure.charset);
  526. END;
  527. END;
  528. END;
  529. bodystructure.subpart := NIL;
  530. END;
  531. message.bodystructure := bodystructure;
  532. END;
  533. END;
  534. END CheckFetch;
  535. PROCEDURE Synchronize(): LONGINT;
  536. VAR
  537. path, items: String;
  538. r, i: LONGINT;
  539. count, step, start, stop, single, fetchStart, fetchStop: LONGINT;
  540. oldMessages, newMessages: Classes.List;
  541. p, pOld: ANY;
  542. message, oldMsg: Message;
  543. found, found2, findable: BOOLEAN;
  544. sortedList: Classes.SortedList;
  545. BEGIN
  546. (* check status *)
  547. path := currentFolder.GetPath();
  548. items := Strings.NewString("(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)");
  549. r := c.Status(path^, items^, ret);
  550. IF r # IMAP.OK THEN
  551. CallErrorHandler("An error happend while trying to get the status from the server");
  552. abort := TRUE;
  553. RETURN ERROR;
  554. END;
  555. CheckAnswer(ret);
  556. (* build the new list *)
  557. oldMessages := currentFolder.messages;
  558. NEW(newMessages);
  559. i := 0;
  560. WHILE i < numberOfMessages DO
  561. NEW(message);
  562. newMessages.Add(message);
  563. INC(i);
  564. END;
  565. currentFolder.messages := newMessages;
  566. (* delete NIL entries in the old list *)
  567. i := 0;
  568. WHILE i < oldMessages.GetCount() DO
  569. p := oldMessages.GetItem(i);
  570. message := p(Message);
  571. IF message.header = NIL THEN
  572. oldMessages.Remove(p);
  573. ELSE
  574. INC(i);
  575. END;
  576. END;
  577. (* create a sorted list with the old messages, sorted by UIDs *)
  578. NEW(sortedList, BiggestUIDFirst);
  579. FOR i := 0 TO oldMessages.GetCount()-1 DO
  580. p := oldMessages.GetItem(i);
  581. message := p(Message);
  582. sortedList.Add(message);
  583. END;
  584. (* load the messages in intervalls *)
  585. count := numberOfMessages - 1;
  586. step := (numberOfMessages DIV 20) + 1;
  587. WHILE (count >= 0) & (~abort) & (~userAbort) DO
  588. (* determine the intervall *)
  589. stop := count;
  590. start := count - step + 1;
  591. IF start < 0 THEN
  592. start := 0;
  593. END;
  594. (* load the UIDs and Flags if necessary *)
  595. IF ~FolderComplete THEN
  596. r := FetchSomeUIDs(start, stop-start+1);
  597. IF r # OK THEN
  598. abort := TRUE;
  599. RETURN r;
  600. END;
  601. END;
  602. (* use oldMessage in case of a UID-match for all the messages in the intervall *)
  603. single := stop;
  604. WHILE (single >= start) DO
  605. p := newMessages.GetItem(single);
  606. message := p(Message);
  607. i := 0;
  608. found := FALSE;
  609. findable := TRUE; (* as long as the UID is bigger then the UID of the current message in sortedList *)
  610. WHILE (i < sortedList.GetCount()) & (~found) & (findable) DO
  611. pOld := sortedList.GetItem(i);
  612. oldMsg := pOld(Message);
  613. IF oldMsg.uID = message.uID THEN
  614. found := TRUE;
  615. ELSIF oldMsg.uID < message.uID THEN
  616. findable := FALSE;
  617. ELSE
  618. INC(i);
  619. END;
  620. END;
  621. IF found THEN
  622. oldMsg.flags := message.flags;
  623. newMessages.Replace(p, pOld);
  624. sortedList.Remove(pOld);
  625. END;
  626. DEC(single);
  627. END;
  628. (* fetch all the messages in the intervall that have header = NIL *)
  629. single := stop;
  630. WHILE (single >= start) DO
  631. (* find the first message with header = NIL *)
  632. found := FALSE;
  633. WHILE ((single >= start) & (~found)) DO
  634. p := newMessages.GetItem(single);
  635. message := p(Message);
  636. IF message.header = NIL THEN
  637. found := TRUE;
  638. fetchStop := single;
  639. fetchStart := single;
  640. END;
  641. DEC(single);
  642. END;
  643. (* look for more messages with header = NIL *)
  644. found2 := FALSE;
  645. WHILE ((single >= start) & (~found2)) DO
  646. p := newMessages.GetItem(single);
  647. message := p(Message);
  648. IF message.header = NIL THEN
  649. fetchStart := single;
  650. ELSE
  651. found2 := TRUE;
  652. END;
  653. DEC(single);
  654. END;
  655. IF found THEN
  656. r := FetchSomeHeaders(fetchStart, fetchStop-fetchStart+1);
  657. IF r # OK THEN
  658. abort := TRUE;
  659. RETURN r;
  660. END;
  661. END;
  662. END;
  663. count := count - step;
  664. END;
  665. FolderComplete := TRUE;
  666. FolderIsSynchronized := TRUE;
  667. RETURN OK;
  668. END Synchronize;
  669. PROCEDURE DownloadAllMessages(): LONGINT;
  670. VAR
  671. r, count, step: LONGINT;
  672. start, end: LONGINT;
  673. message: Message;
  674. p: ANY;
  675. BEGIN
  676. Task := TNothing;
  677. count := currentFolder.messages.GetCount() - 1;
  678. step := (count DIV 20) + 1;
  679. WHILE (count >= 0) & (~abort) & (~userAbort) DO
  680. p := currentFolder.messages.GetItem(count);
  681. message := p(Message);
  682. WHILE (message.message # NIL) & (message.header # NIL) & (count >= 0) DO
  683. DEC(count);
  684. IF count >= 0 THEN
  685. p := currentFolder.messages.GetItem(count);
  686. message := p(Message);
  687. END;
  688. END;
  689. end := count;
  690. start := count - step + 1;
  691. IF start < 0 THEN
  692. start := 0;
  693. END;
  694. IF count < 0 THEN
  695. RETURN OK;
  696. END;
  697. p := currentFolder.messages.GetItem(count);
  698. message := p(Message);
  699. WHILE ((message.message = NIL) OR (message.header = NIL)) & (count >= start) DO
  700. DEC(count);
  701. IF count >= 0 THEN
  702. p := currentFolder.messages.GetItem(count);
  703. message := p(Message);
  704. END;
  705. END;
  706. start := count;
  707. IF start < 0 THEN
  708. start := 0;
  709. END;
  710. r := FetchSomeMessages(start, end-start+1);
  711. END;
  712. RETURN OK;
  713. END DownloadAllMessages;
  714. (* fetches starting from idx the following len Messages *)
  715. PROCEDURE FetchSomeHeaders(idx, len: LONGINT): LONGINT;
  716. VAR
  717. ret: Classes.List;
  718. r: LONGINT;
  719. start, end, set: ARRAY 64 OF CHAR;
  720. BEGIN
  721. Strings.IntToStr(idx+1, start);
  722. Strings.IntToStr(idx+len, end);
  723. IMAPUtilities.StringCopy(start, 0, IMAPUtilities.StringLength(start), set);
  724. Strings.Append(set, ":");
  725. Strings.Append(set, end);
  726. r := c.Fetch(set, "(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE UID)", ret);
  727. IF r # IMAP.OK THEN
  728. CallErrorHandler("An error happend while trying to fetch some headers.");
  729. RETURN ERROR;
  730. END;
  731. CheckAnswer(ret);
  732. RETURN OK;
  733. END FetchSomeHeaders;
  734. (* fetches starting from idx the following len Messages *)
  735. PROCEDURE FetchSomeUIDs(idx, len: LONGINT): LONGINT;
  736. VAR
  737. ret: Classes.List;
  738. r: LONGINT;
  739. start, end, set: ARRAY 64 OF CHAR;
  740. BEGIN
  741. Strings.IntToStr(idx+1, start);
  742. Strings.IntToStr(idx+len, end);
  743. IMAPUtilities.StringCopy(start, 0, IMAPUtilities.StringLength(start), set);
  744. Strings.Append(set, ":");
  745. Strings.Append(set, end);
  746. r := c.Fetch(set, "(FLAGS UID)", ret);
  747. IF r # IMAP.OK THEN
  748. CallErrorHandler("An error happend while trying to fetch some UIDs.");
  749. RETURN ERROR;
  750. END;
  751. CheckAnswer(ret);
  752. RETURN OK;
  753. END FetchSomeUIDs;
  754. PROCEDURE FetchSomeMessages(idx, len: LONGINT): LONGINT;
  755. VAR
  756. ret: Classes.List;
  757. r: LONGINT;
  758. start, end, set: ARRAY 64 OF CHAR;
  759. BEGIN
  760. Strings.IntToStr(idx+1, start);
  761. Strings.IntToStr(idx+len, end);
  762. IMAPUtilities.StringCopy(start, 0, IMAPUtilities.StringLength(start), set);
  763. Strings.Append(set, ":");
  764. Strings.Append(set, end);
  765. r := c.Fetch(set, "(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE UID RFC822.TEXT BODYSTRUCTURE)", ret);
  766. IF r # IMAP.OK THEN
  767. CallErrorHandler("An error happend while trying to fetch some messages.");
  768. RETURN ERROR;
  769. END;
  770. CheckAnswer(ret);
  771. RETURN OK;
  772. END FetchSomeMessages;
  773. PROCEDURE FetchMessage*(message: Message): LONGINT;
  774. VAR
  775. i: LONGINT;
  776. number: ARRAY 20 OF CHAR;
  777. ret: Classes.List;
  778. BEGIN {EXCLUSIVE}
  779. IF status # ONLINE THEN
  780. CallErrorHandler("An error happend while trying to fetch a message. The Client is not online.");
  781. RETURN ERROR;
  782. END;
  783. currentWork := CWLOADING;
  784. Strings.IntToStr(message.uID, number);
  785. i := c.UIDFetch(number, "(RFC822.TEXT BODYSTRUCTURE)", ret);
  786. IF i # IMAP.OK THEN
  787. CallErrorHandler("An error happend while trying to fetch a message.");
  788. currentWork := CWFINISHED;
  789. RETURN ERROR;
  790. END;
  791. CheckAnswer(ret);
  792. currentWork := CWFINISHED;
  793. CallObserverMethod();
  794. RETURN OK;
  795. END FetchMessage;
  796. PROCEDURE DeleteMessage*(message: Message; expunge: BOOLEAN): LONGINT;
  797. VAR
  798. set: ARRAY 20 OF CHAR;
  799. ret: Classes.List;
  800. r: LONGINT;
  801. BEGIN {EXCLUSIVE}
  802. IF status # ONLINE THEN
  803. CallErrorHandler("An error happend while trying to delete a message. The Client is not online.");
  804. RETURN ERROR;
  805. END;
  806. currentWork := CWDELETINGMESSAGE;
  807. Strings.IntToStr(message.uID, set);
  808. r := c.UIDStore(set, "\Deleted", TRUE, ret);
  809. IF r # IMAP.OK THEN
  810. CallErrorHandler("An error happend while trying to delete a message.");
  811. currentWork := CWFINISHED;
  812. RETURN ERROR;
  813. END;
  814. CheckAnswer(ret);
  815. IF expunge THEN
  816. r := ExpungeUnlocked();
  817. IF r # IMAP.OK THEN
  818. currentWork := CWFINISHED;
  819. RETURN ERROR;
  820. END;
  821. END;
  822. currentWork := CWFINISHED;
  823. CallObserverMethod();
  824. RETURN OK;
  825. END DeleteMessage;
  826. PROCEDURE MoveMessageToTrashBin*(message: Message): LONGINT;
  827. VAR
  828. set: ARRAY 20 OF CHAR;
  829. ret: Classes.List;
  830. r: LONGINT;
  831. folder: String;
  832. BEGIN {EXCLUSIVE}
  833. IF preferences.TrashBin^ = "" THEN
  834. CallErrorHandler("Trash bin is not specified in Preferences.");
  835. RETURN ERROR;
  836. END;
  837. currentWork := CWDELETINGMESSAGE;
  838. Strings.IntToStr(message.uID, set);
  839. folder := currentFolder.GetPath();
  840. IF folder^ # preferences.TrashBin^ THEN (* for the case of deleting the trash bin itself, we continue after the if branche as if we were not using a trash bin *)
  841. r := CopyMessageUnlocked(message, preferences.TrashBin);
  842. IF r # OK THEN
  843. CallErrorHandler("An error happend while trying to move a message to the trash bin.");
  844. currentWork := CWFINISHED;
  845. RETURN ERROR;
  846. END;
  847. r := c.UIDStore(set, "\Deleted", TRUE, ret);
  848. IF r # IMAP.OK THEN
  849. CallErrorHandler("An error happend while trying to delete a message.");
  850. currentWork := CWFINISHED;
  851. RETURN ERROR;
  852. END;
  853. CheckAnswer(ret);
  854. r := ExpungeUnlocked();
  855. IF r # OK THEN
  856. currentWork := CWFINISHED;
  857. RETURN ERROR;
  858. END;
  859. CheckAnswer(ret);
  860. currentWork := CWFINISHED;
  861. CallObserverMethod();
  862. RETURN OK;
  863. END;
  864. (* if we get here we are deleting a message from the trash bin *)
  865. r := c.UIDStore(set, "\Deleted", TRUE, ret);
  866. IF r # IMAP.OK THEN
  867. CallErrorHandler("An error happend while trying to delete a message.");
  868. currentWork := CWFINISHED;
  869. RETURN ERROR;
  870. END;
  871. CheckAnswer(ret);
  872. IF (preferences.ExpungeOnDelete) THEN
  873. r := ExpungeUnlocked();
  874. IF r # OK THEN
  875. currentWork := CWFINISHED;
  876. RETURN ERROR;
  877. END;
  878. END;
  879. currentWork := CWFINISHED;
  880. CallObserverMethod();
  881. RETURN OK;
  882. END MoveMessageToTrashBin;
  883. PROCEDURE RestoreMessage*(message: Message): LONGINT;
  884. VAR
  885. set: ARRAY 20 OF CHAR;
  886. ret: Classes.List;
  887. r: LONGINT;
  888. BEGIN {EXCLUSIVE}
  889. IF status # ONLINE THEN
  890. CallErrorHandler("An error happend while trying to restore a message. The Client is not online.");
  891. RETURN ERROR;
  892. END;
  893. currentWork := CWRESTORING;
  894. Strings.IntToStr(message.uID, set);
  895. r := c.UIDStore(set, "\Deleted", FALSE, ret);
  896. IF r # IMAP.OK THEN
  897. CallErrorHandler("An error happend while trying to restore a message.");
  898. currentWork := CWFINISHED;
  899. RETURN ERROR;
  900. END;
  901. CheckAnswer(ret);
  902. currentWork := CWFINISHED;
  903. CallObserverMethod();
  904. RETURN OK;
  905. END RestoreMessage;
  906. PROCEDURE CopyMessage*(message: Message; path: String): LONGINT;
  907. BEGIN {EXCLUSIVE}
  908. RETURN CopyMessageUnlocked(message, path);
  909. END CopyMessage;
  910. (* copy the Message message to the Folder target *)
  911. PROCEDURE CopyMessageUnlocked*(message: Message; path: String): LONGINT;
  912. VAR
  913. r: LONGINT;
  914. set: ARRAY 20 OF CHAR;
  915. ret: Classes.List;
  916. BEGIN
  917. IF path^ = "" THEN
  918. CallErrorHandler("The Target Folder is not specified. Select a Target Folder before trying to copy!");
  919. RETURN ERROR;
  920. END;
  921. IF status # ONLINE THEN
  922. CallErrorHandler("An error happend while trying to copy a message. The Client is not online.");
  923. RETURN ERROR;
  924. END;
  925. currentWork := CWCOPYING;
  926. Strings.IntToStr(message.uID, set);
  927. r := c.UIDCopy(set, path^, ret);
  928. IF r # IMAP.OK THEN
  929. CallErrorHandler("An error happend while trying to copy a message.");
  930. currentWork := CWFINISHED;
  931. RETURN ERROR;
  932. END;
  933. CheckAnswer(ret);
  934. currentWork := CWFINISHED;
  935. CallObserverMethod();
  936. RETURN OK;
  937. END CopyMessageUnlocked;
  938. PROCEDURE AppendMessage*(message: Message; path: String): LONGINT;
  939. VAR
  940. string: String;
  941. r: LONGINT;
  942. ret: Classes.List;
  943. BEGIN {EXCLUSIVE}
  944. IF status # ONLINE THEN
  945. CallErrorHandler("An error happend while trying to append a message. The Client is not online.");
  946. RETURN ERROR;
  947. END;
  948. currentWork := CWAPPENDING;
  949. string := message.ToString();
  950. r := c.Append(path^, string^, ret);
  951. IF r # IMAP.OK THEN
  952. CallErrorHandler("An error happend while trying to append a message.");
  953. currentWork := CWFINISHED;
  954. RETURN ERROR;
  955. END;
  956. CheckAnswer(ret);
  957. currentWork := CWFINISHED;
  958. CallObserverMethod();
  959. RETURN OK;
  960. END AppendMessage;
  961. PROCEDURE SetAnsweredFlag*(message: Message): LONGINT;
  962. VAR
  963. set: ARRAY 20 OF CHAR;
  964. ret: Classes.List;
  965. r: LONGINT;
  966. BEGIN {EXCLUSIVE}
  967. IF status # ONLINE THEN
  968. CallErrorHandler("An error happend while trying to set the answered flag of a message. The Client is not online.");
  969. RETURN ERROR;
  970. END;
  971. Strings.IntToStr(message.uID, set);
  972. r := c.UIDStore(set, "\Answered", TRUE, ret);
  973. IF r # IMAP.OK THEN
  974. CallErrorHandler("An error happend while trying to set the answered flag of a message.");
  975. RETURN ERROR;
  976. END;
  977. CheckAnswer(ret);
  978. currentWork := CWFINISHED;
  979. CallObserverMethod();
  980. RETURN OK;
  981. END SetAnsweredFlag;
  982. PROCEDURE SaveSentMessage*(message: Message):LONGINT;
  983. VAR
  984. r: LONGINT;
  985. string: String;
  986. ret: Classes.List;
  987. BEGIN {EXCLUSIVE}
  988. IF status # ONLINE THEN
  989. CallErrorHandler("An error happend while trying to save the message. The Client is not online.");
  990. RETURN ERROR;
  991. END;
  992. IF preferences.SentFolder^ = "" THEN
  993. CallErrorHandler("You didn't specify in your Preferences where to store a sent Message.");
  994. RETURN ERROR;
  995. END;
  996. currentWork := CWAPPENDING;
  997. string := message.ToString();
  998. r := c.Append(preferences.SentFolder^, string^, ret);
  999. IF r # IMAP.OK THEN
  1000. CallErrorHandler("An error happend while trying to save the message.");
  1001. currentWork := CWFINISHED;
  1002. RETURN ERROR;
  1003. END;
  1004. CheckAnswer(ret);
  1005. currentWork := CWFINISHED;
  1006. CallObserverMethod();
  1007. RETURN OK;
  1008. END SaveSentMessage;
  1009. PROCEDURE Expunge*(): LONGINT;
  1010. BEGIN {EXCLUSIVE}
  1011. RETURN ExpungeUnlocked();
  1012. END Expunge;
  1013. PROCEDURE ExpungeUnlocked(): LONGINT;
  1014. VAR
  1015. r: LONGINT;
  1016. ret: Classes.List;
  1017. BEGIN
  1018. IF status # ONLINE THEN
  1019. CallErrorHandler("An error happend while trying to expunge. The Client is not online.");
  1020. RETURN ERROR;
  1021. END;
  1022. currentWork := CWEXPUNGING;
  1023. r := c.Expunge(ret);
  1024. IF r # IMAP.OK THEN
  1025. CallErrorHandler("An error happend while trying to expunge.");
  1026. currentWork := CWFINISHED;
  1027. RETURN ERROR;
  1028. END;
  1029. CheckAnswer(ret);
  1030. currentWork := CWFINISHED;
  1031. CallObserverMethod();
  1032. RETURN OK;
  1033. END ExpungeUnlocked;
  1034. PROCEDURE SelectFolder*(folder: Folder): LONGINT;
  1035. BEGIN {EXCLUSIVE}
  1036. RETURN SelectFolderUnlocked(folder);
  1037. END SelectFolder;
  1038. PROCEDURE SelectFolderUnlocked(folder: Folder): LONGINT;
  1039. VAR
  1040. ret: Classes.List;
  1041. i: LONGINT;
  1042. path: String;
  1043. BEGIN
  1044. currentWork := CWLOADING;
  1045. IF status = OFFLINE THEN
  1046. currentFolder := folder;
  1047. numberOfMessages := currentFolder.messages.GetCount();
  1048. currentWork := CWFINISHED;
  1049. ELSIF status = ONLINE THEN
  1050. IF currentFolder = folder THEN
  1051. currentWork := CWFINISHED;
  1052. ELSE
  1053. IF (c.GetCurrentState() = IMAP.SELECT) & preferences.ExpungeOnFolderChange THEN
  1054. i := c.Close();
  1055. END;
  1056. i := GetSubFolders(folder);
  1057. IF i # OK THEN
  1058. CallErrorHandler("An error happend while trying to the subfolders of the Folder.");
  1059. currentWork := CWFINISHED;
  1060. RETURN i;
  1061. END;
  1062. IF folder.Noselect = FALSE THEN
  1063. path := folder.GetPath();
  1064. i := c.Select(path^, ret);
  1065. IF i # IMAP.OK THEN
  1066. CallErrorHandler("An error happend while trying to select a Folder.");
  1067. currentWork := CWFINISHED;
  1068. CallObserverMethod();
  1069. RETURN ERROR;
  1070. END;
  1071. currentFolder := folder;
  1072. FolderIsSynchronized := FALSE;
  1073. FolderComplete := FALSE;
  1074. currentWork := CWFINISHED;
  1075. timer.Wakeup();
  1076. ELSE
  1077. currentFolder := folder;
  1078. FolderIsSynchronized := TRUE;
  1079. currentWork := CWFINISHED;
  1080. END;
  1081. END;
  1082. END;
  1083. CallObserverMethod();
  1084. RETURN OK;
  1085. END SelectFolderUnlocked;
  1086. PROCEDURE GetSubFolders(VAR folder: Folder): LONGINT;
  1087. VAR
  1088. i: LONGINT;
  1089. p: ANY;
  1090. r: LONGINT;
  1091. temp: Folder;
  1092. path: String;
  1093. ret: Classes.List;
  1094. nameLen, pathLen: LONGINT;
  1095. BEGIN
  1096. (* set the alive flag of all the current subfolders to FALSE to elimitate dead subfolders later *)
  1097. i := 0;
  1098. WHILE i < folder.children.GetCount() DO
  1099. p := folder.children.GetItem(i);
  1100. temp := p(Folder);
  1101. temp.alive := FALSE;
  1102. INC(i);
  1103. END;
  1104. (* check that folder is a sub folder of mailboxContent *)
  1105. temp := folder;
  1106. path := Strings.NewString("");
  1107. WHILE(temp # mailboxContent) & (temp # NIL) DO
  1108. temp := temp.parent;
  1109. END;
  1110. IF (temp = NIL) THEN
  1111. CallErrorHandler("An error happend while trying to get the subfolders of a folder which does not belong to the client's folder structure.");
  1112. RETURN ERROR;
  1113. END;
  1114. IF folder = mailboxContent THEN
  1115. path := Strings.NewString("%");
  1116. ELSE
  1117. pathLen := IMAPUtilities.StringLength(folder.path^);
  1118. nameLen := IMAPUtilities.StringLength(folder.name^);
  1119. IF pathLen = 0 THEN
  1120. NEW(path, nameLen + 3);
  1121. IMAPUtilities.StringCopy(folder.name^, 0, nameLen, path^);
  1122. path[nameLen] := folder.hierarchyDelimiter;
  1123. path[nameLen + 1] := "%";
  1124. path[nameLen + 2] := 0X;
  1125. ELSE
  1126. NEW(path, nameLen+pathLen+4);
  1127. IMAPUtilities.StringCopy(folder.path^, 0, pathLen, path^);
  1128. path[pathLen] := folder.hierarchyDelimiter;
  1129. Strings.Append(path^, folder.name^);
  1130. path[nameLen + pathLen + 1] := folder.hierarchyDelimiter;
  1131. path[nameLen + pathLen + 2] := "%";
  1132. path[nameLen + pathLen + 3] := 0X;
  1133. END;
  1134. END;
  1135. IF DEBUG THEN KernelLog.String("Before c.List"); KernelLog.Ln(); END;
  1136. r := c.List("", path^, ret);
  1137. IF DEBUG THEN KernelLog.String("After c.List r= "); KernelLog.Int(r,0); KernelLog.String(" state= "); KernelLog.Int(c.GetCurrentState(),0); KernelLog.Ln(); END;
  1138. IF r # IMAP.OK THEN
  1139. CallErrorHandler("An error happend while trying to get the sub folders of a Folder.");
  1140. RETURN ERROR;
  1141. END;
  1142. getSubFoldersContext := folder;
  1143. CheckAnswer(ret);
  1144. (* eliminate those folders with the alive flag equal to FALSE *)
  1145. i := 0;
  1146. WHILE i < folder.children.GetCount() DO
  1147. p := folder.children.GetItem(i);
  1148. temp := p(Folder);
  1149. IF temp.alive = FALSE THEN
  1150. folder.children.Remove(p);
  1151. ELSE
  1152. INC(i);
  1153. END;
  1154. END;
  1155. CallObserverMethod();
  1156. RETURN OK;
  1157. END GetSubFolders;
  1158. PROCEDURE Close*;
  1159. VAR
  1160. r: LONGINT;
  1161. BEGIN {EXCLUSIVE}
  1162. IF DEBUG THEN KernelLog.String("Client is closing..."); KernelLog.Ln(); END;
  1163. currentWork := CWCLOSING;
  1164. IF status = ONLINE THEN
  1165. r := c.Logout();
  1166. CheckAnswer(ret);
  1167. c := NIL;
  1168. END;
  1169. status := DEAD;
  1170. timer.Wakeup();
  1171. END Close;
  1172. PROCEDURE Update(): LONGINT;
  1173. VAR
  1174. i, count: LONGINT;
  1175. p: ANY;
  1176. message: Message;
  1177. ret: Classes.List;
  1178. BEGIN
  1179. i := c.Noop(ret);
  1180. IF i # IMAP.OK THEN
  1181. CallErrorHandler("An error happend while trying to get update information from the server.");
  1182. RETURN ERROR;
  1183. END;
  1184. CheckAnswer(ret);
  1185. count := 0;
  1186. WHILE count < currentFolder.messages.GetCount() DO
  1187. p := currentFolder.messages.GetItem(count);
  1188. message := p(Message);
  1189. IF message.header = NIL THEN
  1190. IF DEBUG THEN KernelLog.String("In Update. Message header is NIL"); KernelLog.Ln(); END;
  1191. FolderIsSynchronized := FALSE;
  1192. END;
  1193. INC(count);
  1194. END;
  1195. CallObserverMethod();
  1196. RETURN OK;
  1197. END Update;
  1198. (* tries to rename the folder *)
  1199. PROCEDURE Rename*(folder: Folder; VAR name: ARRAY OF CHAR): LONGINT;
  1200. VAR
  1201. newName: String;
  1202. oldName: String;
  1203. r: LONGINT;
  1204. pathLen: LONGINT;
  1205. ret: Classes.List;
  1206. parent: Folder;
  1207. BEGIN {EXCLUSIVE}
  1208. IF status # ONLINE THEN
  1209. CallErrorHandler("An error happend while trying to rename a Folder. The Client is not online.");
  1210. RETURN ERROR;
  1211. END;
  1212. currentWork := CWRENAMING;
  1213. parent := folder.parent;
  1214. oldName := folder.GetPath();
  1215. pathLen := IMAPUtilities.StringLength(folder.path^);
  1216. IF pathLen = 0 THEN
  1217. newName := IMAPUtilities.NewString(name);
  1218. ELSE
  1219. NEW(newName, pathLen + IMAPUtilities.StringLength(name) + 2);
  1220. IMAPUtilities.StringCopy(folder.path^, 0, pathLen, newName^);
  1221. newName^[pathLen] := folder.hierarchyDelimiter;
  1222. Strings.Append(newName^, name);
  1223. END;
  1224. IF DEBUG THEN
  1225. KernelLog.String("Renaming folder"); KernelLog.Ln();
  1226. KernelLog.String("old Name: "); KernelLog.String(oldName^); KernelLog.Ln();
  1227. KernelLog.String("new Name: "); KernelLog.String(newName^); KernelLog.Ln();
  1228. END;
  1229. r := c.Rename(oldName^, newName^, ret);
  1230. IF r # IMAP.OK THEN
  1231. CallErrorHandler("An error happend while trying to rename a Folder.");
  1232. currentWork := CWFINISHED;
  1233. RETURN ERROR;
  1234. END;
  1235. CheckAnswer(ret);
  1236. r := OK;
  1237. IF parent # NIL THEN
  1238. r := GetSubFolders(parent);
  1239. ELSE
  1240. r := GetSubFolders(currentFolder);
  1241. END;
  1242. currentWork := CWFINISHED;
  1243. CallObserverMethod();
  1244. RETURN r;
  1245. END Rename;
  1246. (* tries to delete the folder *)
  1247. PROCEDURE Delete*(folder: Folder): LONGINT;
  1248. VAR
  1249. r: LONGINT;
  1250. path: String;
  1251. ret: Classes.List;
  1252. parent: Folder;
  1253. BEGIN {EXCLUSIVE}
  1254. IF status # ONLINE THEN
  1255. CallErrorHandler("An error happend while trying to delete a Folder. The Client is not online.");
  1256. RETURN ERROR;
  1257. END;
  1258. currentWork := CWDELETINGFOLDER;
  1259. parent := folder.parent;
  1260. path := folder.GetPath();
  1261. r := c.Delete(path^, ret);
  1262. IF r # IMAP.OK THEN
  1263. CallErrorHandler("An error happend while trying to delete a Folder.");
  1264. currentWork := CWFINISHED;
  1265. RETURN ERROR;
  1266. END;
  1267. CheckAnswer(ret);
  1268. r := OK;
  1269. IF parent # NIL THEN
  1270. r := GetSubFolders(parent);
  1271. ELSE
  1272. r := GetSubFolders(currentFolder);
  1273. END;
  1274. currentWork := CWFINISHED;
  1275. CallObserverMethod();
  1276. RETURN r;
  1277. END Delete;
  1278. PROCEDURE Create*(folder: Folder; name: ARRAY OF CHAR): LONGINT;
  1279. VAR
  1280. r: LONGINT;
  1281. string: String;
  1282. newName: String;
  1283. len, pos: LONGINT;
  1284. ret: Classes.List;
  1285. BEGIN {EXCLUSIVE}
  1286. IF status # ONLINE THEN
  1287. CallErrorHandler("An error happend while trying to create a Folder. The Client is not online.");
  1288. RETURN ERROR;
  1289. END;
  1290. currentWork := CWCREATING;
  1291. string := folder.GetPath();
  1292. pos := IMAPUtilities.StringLength(string^);
  1293. len := pos + IMAPUtilities.StringLength(name) + 2;
  1294. NEW(newName, len);
  1295. IMAPUtilities.StringCopy(string^, 0, pos, newName^);
  1296. newName^[pos] := folder.hierarchyDelimiter;
  1297. newName^[pos+1] := 0X;
  1298. Strings.Append(newName^, name);
  1299. r := c.Create(newName^, ret);
  1300. IF r # IMAP.OK THEN
  1301. CallErrorHandler("An error happend while trying to create a Folder.");
  1302. currentWork := CWFINISHED;
  1303. RETURN ERROR;
  1304. END;
  1305. CheckAnswer(ret);
  1306. r := OK;
  1307. r := GetSubFolders(folder);
  1308. currentWork := CWFINISHED;
  1309. CallObserverMethod();
  1310. RETURN r;
  1311. END Create;
  1312. PROCEDURE Search*(string: ARRAY OF CHAR): LONGINT;
  1313. VAR
  1314. r: LONGINT;
  1315. ret: Classes.List;
  1316. BEGIN {EXCLUSIVE}
  1317. IF status = OFFLINE THEN
  1318. RETURN OfflineSearch(string);
  1319. END;
  1320. IF status # ONLINE THEN
  1321. CallErrorHandler("An error happend while trying to search. The Client is not online.");
  1322. RETURN ERROR;
  1323. END;
  1324. currentWork := CWSEARCHING;
  1325. r := c.Search(string, ret);
  1326. IF r # IMAP.OK THEN
  1327. CallErrorHandler("An error happend while trying to search.");
  1328. currentWork := CWFINISHED;
  1329. CallObserverMethod();
  1330. RETURN -1; (* ERROR *)
  1331. END;
  1332. CheckAnswer(ret);
  1333. applySearchFilter := TRUE;
  1334. currentWork := CWFINISHED;
  1335. CallObserverMethod();
  1336. RETURN LEN(searchResult);
  1337. END Search;
  1338. PROCEDURE OfflineSearch(string: ARRAY OF CHAR): LONGINT;
  1339. VAR
  1340. i, count: LONGINT;
  1341. reader: Streams.StringReader;
  1342. command: String;
  1343. Result: POINTER TO ARRAY OF BOOLEAN;
  1344. PROCEDURE CheckCommand(CONST command: ARRAY OF CHAR);
  1345. VAR
  1346. p: ANY;
  1347. message: Message;
  1348. string, string2: String;
  1349. value: LONGINT;
  1350. date, internalDate: Date;
  1351. temp1, temp2: POINTER TO ARRAY OF BOOLEAN;
  1352. BEGIN
  1353. NEW(date); NEW(internalDate);
  1354. IF DEBUG THEN KernelLog.String("Checking Command: "); KernelLog.String(command); KernelLog.Ln(); END;
  1355. IF command = "ANSWERED" THEN
  1356. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1357. p := currentFolder.messages.GetItem(i);
  1358. message := p(Message);
  1359. IF Result[i] & (~message.flags.answered) THEN
  1360. Result[i] := FALSE;
  1361. END;
  1362. END;
  1363. ELSIF command= "UNANSWERED" THEN
  1364. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1365. p := currentFolder.messages.GetItem(i);
  1366. message := p(Message);
  1367. IF Result[i] & (message.flags.answered) THEN
  1368. Result[i] := FALSE;
  1369. END;
  1370. END;
  1371. ELSIF command= "DELETED" THEN
  1372. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1373. p := currentFolder.messages.GetItem(i);
  1374. message := p(Message);
  1375. IF Result[i] & (~message.flags.deleted) THEN
  1376. Result[i] := FALSE;
  1377. END;
  1378. END;
  1379. ELSIF command= "UNDELETED" THEN
  1380. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1381. p := currentFolder.messages.GetItem(i);
  1382. message := p(Message);
  1383. IF Result[i] & (message.flags.deleted) THEN
  1384. Result[i] := FALSE;
  1385. END;
  1386. END;
  1387. ELSIF command= "DRAFT" THEN
  1388. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1389. p := currentFolder.messages.GetItem(i);
  1390. message := p(Message);
  1391. IF Result[i] & (~message.flags.draft) THEN
  1392. Result[i] := FALSE;
  1393. END;
  1394. END;
  1395. ELSIF command= "UNDRAFT" THEN
  1396. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1397. p := currentFolder.messages.GetItem(i);
  1398. message := p(Message);
  1399. IF Result[i] & (message.flags.draft) THEN
  1400. Result[i] := FALSE;
  1401. END;
  1402. END;
  1403. ELSIF command= "FLAGGED" THEN
  1404. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1405. p := currentFolder.messages.GetItem(i);
  1406. message := p(Message);
  1407. IF Result[i] & (~message.flags.flagged) THEN
  1408. Result[i] := FALSE;
  1409. END;
  1410. END;
  1411. ELSIF command= "UNFLAGGED" THEN
  1412. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1413. p := currentFolder.messages.GetItem(i);
  1414. message := p(Message);
  1415. IF Result[i] & (message.flags.flagged) THEN
  1416. Result[i] := FALSE;
  1417. END;
  1418. END;
  1419. ELSIF command= "SEEN" THEN
  1420. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1421. p := currentFolder.messages.GetItem(i);
  1422. message := p(Message);
  1423. IF Result[i] & (~message.flags.seen) THEN
  1424. Result[i] := FALSE;
  1425. END;
  1426. END;
  1427. ELSIF command= "UNSEEN" THEN
  1428. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1429. p := currentFolder.messages.GetItem(i);
  1430. message := p(Message);
  1431. IF Result[i] & (message.flags.seen) THEN
  1432. Result[i] := FALSE;
  1433. END;
  1434. END;
  1435. ELSIF command= "RECENT" THEN
  1436. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1437. p := currentFolder.messages.GetItem(i);
  1438. message := p(Message);
  1439. IF Result[i] & (~message.flags.recent) THEN
  1440. Result[i] := FALSE;
  1441. END;
  1442. END;
  1443. ELSIF command= "OLD" THEN
  1444. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1445. p := currentFolder.messages.GetItem(i);
  1446. message := p(Message);
  1447. IF Result[i] & (message.flags.recent) THEN
  1448. Result[i] := FALSE;
  1449. END;
  1450. END;
  1451. ELSIF command= "SUBJECT" THEN
  1452. GetString(string);
  1453. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1454. p := currentFolder.messages.GetItem(i);
  1455. message := p(Message);
  1456. IF Result[i] & (~IMAPUtilities.StringContains(message.header.subject, string)) THEN
  1457. Result[i] := FALSE;
  1458. END;
  1459. END;
  1460. ELSIF command= "FROM" THEN
  1461. GetString(string);
  1462. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1463. p := currentFolder.messages.GetItem(i);
  1464. message := p(Message);
  1465. IMAPUtilities.AddressesToString(message.header.from, string2);
  1466. IF Result[i] & (~IMAPUtilities.StringContains(string2, string)) THEN
  1467. Result[i] := FALSE;
  1468. END;
  1469. END;
  1470. ELSIF command= "BODY" THEN
  1471. GetString(string);
  1472. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1473. p := currentFolder.messages.GetItem(i);
  1474. message := p(Message);
  1475. IF Result[i] & (~IMAPUtilities.StringContains(message.message, string)) THEN
  1476. Result[i] := FALSE;
  1477. END;
  1478. END;
  1479. ELSIF command= "LARGER" THEN
  1480. GetString(string);
  1481. Strings.StrToInt(string^, value);
  1482. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1483. p := currentFolder.messages.GetItem(i);
  1484. message := p(Message);
  1485. IF Result[i] & (~(message.size > value)) THEN
  1486. Result[i] := FALSE;
  1487. END;
  1488. END;
  1489. ELSIF command= "SMALLER" THEN
  1490. GetString(string);
  1491. Strings.StrToInt(string^, value);
  1492. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1493. p := currentFolder.messages.GetItem(i);
  1494. message := p(Message);
  1495. IF Result[i] & (~(message.size < value)) THEN
  1496. Result[i] := FALSE;
  1497. END;
  1498. END;
  1499. ELSIF command= "BEFORE" THEN
  1500. GetString(string);
  1501. date.FromInternalDate(string);
  1502. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1503. p := currentFolder.messages.GetItem(i);
  1504. message := p(Message);
  1505. internalDate.FromInternalDate(message.internalDate);
  1506. IF Result[i] & (~(internalDate.Before(date))) THEN
  1507. Result[i] := FALSE;
  1508. END;
  1509. END;
  1510. ELSIF command= "ON" THEN
  1511. GetString(string);
  1512. date.FromInternalDate(string);
  1513. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1514. p := currentFolder.messages.GetItem(i);
  1515. message := p(Message);
  1516. internalDate.FromInternalDate(message.internalDate);
  1517. IF Result[i] & (~(internalDate.Equal(date))) THEN
  1518. Result[i] := FALSE;
  1519. END;
  1520. END;
  1521. ELSIF command= "SINCE" THEN
  1522. GetString(string);
  1523. date.FromInternalDate(string);
  1524. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1525. p := currentFolder.messages.GetItem(i);
  1526. message := p(Message);
  1527. internalDate.FromInternalDate(message.internalDate);
  1528. IF Result[i] & (~(date.Before(internalDate))) THEN
  1529. Result[i] := FALSE;
  1530. END;
  1531. END;
  1532. ELSIF command= "OR" THEN
  1533. reader.SkipWhitespace();
  1534. NEW(string, reader.Available() + 1);
  1535. reader.Token(string^);
  1536. NEW(temp1, currentFolder.messages.GetCount());
  1537. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1538. temp1[i] := Result[i];
  1539. Result[i] := TRUE;
  1540. END;
  1541. CheckCommand(string^);
  1542. reader.SkipWhitespace();
  1543. reader.Token(string^);
  1544. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1545. temp2[i] := Result[i];
  1546. Result[i] := TRUE;
  1547. END;
  1548. CheckCommand(string^);
  1549. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1550. IF (~Result[i]) & (~temp2[i]) THEN
  1551. Result[i] := FALSE;
  1552. ELSE
  1553. Result[i] := temp1[i];
  1554. END;
  1555. END;
  1556. ELSE
  1557. CallErrorHandler("Unknown Search command");
  1558. END;
  1559. END CheckCommand;
  1560. PROCEDURE GetString(VAR string: String);
  1561. VAR
  1562. s: String;
  1563. buffer: Strings.Buffer;
  1564. w: Streams.Writer;
  1565. c: CHAR;
  1566. BEGIN
  1567. NEW(buffer, 16);
  1568. w := buffer.GetWriter();
  1569. reader.SkipWhitespace();
  1570. reader.Char(c);
  1571. IF c = '"' THEN
  1572. reader.Char(c);
  1573. WHILE (ORD(c) # 34) DO
  1574. w.Char(c);
  1575. reader.Char(c);
  1576. END;
  1577. ELSE
  1578. w.Char(c);
  1579. NEW(s, reader.Available()+1);
  1580. reader.Token(s^);
  1581. w.String(s^);
  1582. END;
  1583. string := buffer.GetString();
  1584. END GetString;
  1585. BEGIN
  1586. currentWork := CWSEARCHING;
  1587. IF DEBUG THEN KernelLog.String("Performing offline search. Search string: "); KernelLog.String(string); KernelLog.Ln(); END;
  1588. NEW(Result, currentFolder.messages.GetCount());
  1589. FOR i := 0 TO currentFolder.messages.GetCount()-1 BY 1 DO
  1590. Result[i] := TRUE;
  1591. END;
  1592. NEW(reader, IMAPUtilities.StringLength(string)+1);
  1593. reader.SetRaw(string, 0, IMAPUtilities.StringLength(string));
  1594. NEW(command, IMAPUtilities.StringLength(string)+1);
  1595. reader.SkipWhitespace();
  1596. WHILE reader.Available() > 0 DO
  1597. reader.Token(command^);
  1598. CheckCommand(command^);
  1599. reader.SkipWhitespace();
  1600. END;
  1601. count := 0;
  1602. FOR i := 0 TO LEN(Result)-1 BY 1 DO
  1603. IF Result[i] THEN
  1604. INC(count);
  1605. END;
  1606. END;
  1607. NEW(searchResult, count);
  1608. count := 0;
  1609. FOR i := 0 TO LEN(Result) - 1 BY 1 DO
  1610. IF Result[i] THEN
  1611. searchResult[count] := i;
  1612. INC(count);
  1613. END;
  1614. END;
  1615. applySearchFilter := TRUE;
  1616. currentWork := CWFINISHED;
  1617. CallObserverMethod();
  1618. RETURN LEN(searchResult);
  1619. END OfflineSearch;
  1620. PROCEDURE Save*(VAR doc: XML.Document): LONGINT;
  1621. VAR
  1622. element, sub: XML.Element;
  1623. buf: Strings.Buffer;
  1624. w: Streams.Writer;
  1625. BEGIN {EXCLUSIVE}
  1626. IF DEBUG THEN KernelLog.String("Starting Save"); KernelLog.Ln(); END;
  1627. IF (status # ONLINE) & (status # OFFLINE) THEN
  1628. CallErrorHandler("An error happend while trying to save the account. The Client is disconnected.");
  1629. RETURN ERROR;
  1630. END;
  1631. currentWork := CWSAVINGACCOUNT;
  1632. NEW(doc);
  1633. NEW(element);
  1634. NEW(sub);
  1635. NEW(buf, 16);
  1636. w := buf.GetWriter();
  1637. element.SetName("account");
  1638. doc.AddContent(element);
  1639. SavePreferences(element);
  1640. ExtractMailboxContent(mailboxContent, element);
  1641. currentWork := CWFINISHED;
  1642. CallObserverMethod();
  1643. RETURN OK;
  1644. END Save;
  1645. PROCEDURE SavePreferences(element: XML.Element);
  1646. VAR
  1647. pref, sub: XML.Element;
  1648. cdata: XML.CDataSect;
  1649. value: String;
  1650. PROCEDURE GetBoolean(b: BOOLEAN);
  1651. BEGIN
  1652. IF b THEN value := Strings.NewString("TRUE"); ELSE value := Strings.NewString("FALSE"); END;
  1653. END GetBoolean;
  1654. BEGIN
  1655. NEW(pref); pref.SetName("preferences");
  1656. NEW(sub); sub.SetName("IMAPServer");
  1657. NEW(cdata); cdata.SetStr(preferences.IMAPServer^);
  1658. sub.AddContent(cdata); pref.AddContent(sub);
  1659. NEW(sub); sub.SetName("UserName");
  1660. NEW(cdata); cdata.SetStr(preferences.UserName^);
  1661. sub.AddContent(cdata); pref.AddContent(sub);
  1662. NEW(sub); sub.SetName("SMTPServer");
  1663. NEW(cdata); cdata.SetStr(preferences.SMTPServer^);
  1664. sub.AddContent(cdata); pref.AddContent(sub);
  1665. NEW(sub); sub.SetName("SMTPThisHost");
  1666. NEW(cdata); cdata.SetStr(preferences.SMTPThisHost^);
  1667. sub.AddContent(cdata); pref.AddContent(sub);
  1668. NEW(sub); sub.SetName("SentFolder");
  1669. NEW(cdata); cdata.SetStr(preferences.SentFolder^);
  1670. sub.AddContent(cdata); pref.AddContent(sub);
  1671. NEW(sub); sub.SetName("DraftFolder");
  1672. NEW(cdata); cdata.SetStr(preferences.DraftFolder^);
  1673. sub.AddContent(cdata); pref.AddContent(sub);
  1674. NEW(sub); sub.SetName("TrashBin");
  1675. NEW(cdata); cdata.SetStr(preferences.TrashBin^);
  1676. sub.AddContent(cdata); pref.AddContent(sub);
  1677. NEW(sub); sub.SetName("From");
  1678. NEW(cdata); cdata.SetStr(preferences.From^);
  1679. sub.AddContent(cdata); pref.AddContent(sub);
  1680. NEW(sub); sub.SetName("ExpungeOnFolderChange");
  1681. GetBoolean(preferences.ExpungeOnFolderChange);
  1682. NEW(cdata); cdata.SetStr(value^);
  1683. sub.AddContent(cdata); pref.AddContent(sub);
  1684. NEW(sub); sub.SetName("ExpungeOnDelete");
  1685. GetBoolean(preferences.ExpungeOnDelete);
  1686. NEW(cdata); cdata.SetStr(value^);
  1687. sub.AddContent(cdata); pref.AddContent(sub);
  1688. NEW(sub); sub.SetName("UseDragNDropAsMove");
  1689. GetBoolean(preferences.UseDragNDropAsMove);
  1690. NEW(cdata); cdata.SetStr(value^);
  1691. sub.AddContent(cdata); pref.AddContent(sub);
  1692. NEW(sub); sub.SetName("ExpungeOnMove");
  1693. GetBoolean(preferences.ExpungeOnMove);
  1694. NEW(cdata); cdata.SetStr(value^);
  1695. sub.AddContent(cdata); pref.AddContent(sub);
  1696. NEW(sub); sub.SetName("UseATrashBin");
  1697. GetBoolean(preferences.UseATrashBin);
  1698. NEW(cdata); cdata.SetStr(value^);
  1699. sub.AddContent(cdata); pref.AddContent(sub);
  1700. element.AddContent(pref)
  1701. END SavePreferences;
  1702. PROCEDURE ExtractMailboxContent(folder: Folder; element: XML.Element);
  1703. VAR
  1704. att: XML.Attribute;
  1705. string: ARRAY 30 OF CHAR;
  1706. sub, subSub: XML.Element;
  1707. subFolders: Classes.List;
  1708. subFolderP, messageP, addressP: ANY;
  1709. subFolder: Folder;
  1710. address: IMAPUtilities.Address;
  1711. messages: Classes.List;
  1712. message: Message;
  1713. cdata: XML.CDataSect;
  1714. i: LONGINT;
  1715. PROCEDURE ExtractAddresses(addresses: Classes.List; CONST tag: ARRAY OF CHAR);
  1716. VAR
  1717. i: LONGINT;
  1718. part: XML.Element;
  1719. BEGIN
  1720. i := 0;
  1721. IF addresses # NIL THEN
  1722. WHILE i < addresses.GetCount() DO
  1723. addressP := addresses.GetItem(i);
  1724. address := addressP(IMAPUtilities.Address);
  1725. NEW(subSub);
  1726. subSub.SetName(tag);
  1727. NEW(part); part.SetName("realName");
  1728. NEW(cdata); cdata.SetStr(address.realName^); subSub.AddContent(part); part.AddContent(cdata);
  1729. NEW(part); part.SetName("namePart");
  1730. NEW(cdata); cdata.SetStr(address.namePart^); subSub.AddContent(part); part.AddContent(cdata);
  1731. NEW(part); part.SetName("domainPart");
  1732. NEW(cdata); cdata.SetStr(address.domainPart^); subSub.AddContent(part); part.AddContent(cdata);
  1733. sub.AddContent(subSub);
  1734. INC(i);
  1735. END;
  1736. END;
  1737. END ExtractAddresses;
  1738. BEGIN
  1739. subFolders := folder.children;
  1740. i := 0;
  1741. WHILE i < subFolders.GetCount() DO
  1742. subFolderP := subFolders.GetItem(i);
  1743. subFolder := subFolderP(Folder);
  1744. IF DEBUG THEN KernelLog.String("In ExtractMailboxContent: subfolder: "); KernelLog.String(subFolder.name^); KernelLog.Ln(); END;
  1745. NEW(sub);
  1746. sub.SetName("folder");
  1747. NEW(subSub); subSub.SetName("name");
  1748. NEW(cdata); cdata.SetStr(subFolder.name^); sub.AddContent(subSub); subSub.AddContent(cdata);
  1749. NEW(subSub); subSub.SetName("hierarchyDelimiter");
  1750. string[0] := subFolder.hierarchyDelimiter; string[1] := 0X;
  1751. NEW(cdata); cdata.SetStr(string); sub.AddContent(subSub); subSub.AddContent(cdata);
  1752. ExtractMailboxContent(subFolder, sub);
  1753. element.AddContent(sub);
  1754. INC(i);
  1755. END;
  1756. messages := folder.messages;
  1757. i := 0;
  1758. WHILE i < messages.GetCount() DO
  1759. IF DEBUG THEN KernelLog.String("In ExtractMailboxContent: message "); KernelLog.Ln(); END;
  1760. messageP := messages.GetItem(i);
  1761. message := messageP(Message);
  1762. NEW(sub);
  1763. sub.SetName("message");
  1764. IF message.header # NIL THEN
  1765. NEW(subSub); subSub.SetName("date"); sub.AddContent(subSub);
  1766. NEW(cdata); cdata.SetStr(message.header.date^); subSub.AddContent(cdata);
  1767. NEW(subSub); subSub.SetName("subject"); sub.AddContent(subSub);
  1768. NEW(cdata); cdata.SetStr(message.header.subject^); subSub.AddContent(cdata);
  1769. NEW(subSub); subSub.SetName("inReplyTo"); sub.AddContent(subSub);
  1770. NEW(cdata); cdata.SetStr(message.header.inReplyTo^); subSub.AddContent(cdata);
  1771. NEW(subSub); subSub.SetName("messageID"); sub.AddContent(subSub);
  1772. NEW(cdata); cdata.SetStr(message.header.messageID^); subSub.AddContent(cdata);
  1773. NEW(subSub); subSub.SetName("internalDate"); sub.AddContent(subSub);
  1774. NEW(cdata); cdata.SetStr(message.internalDate^); subSub.AddContent(cdata);
  1775. NEW(att); string := "size"; att.SetName(string); Strings.IntToStr(message.size, string); att.SetValue(string); sub.AddAttribute(att);
  1776. NEW(att); string := "uid"; att.SetName(string); Strings.IntToStr(message.uID, string); att.SetValue(string); sub.AddAttribute(att);
  1777. NEW(att); string := "Answered"; att.SetName(string); IF message.flags.answered THEN string := "TRUE" ELSE string := "FALSE" END; att.SetValue(string); sub.AddAttribute(att);
  1778. NEW(att); string := "Flagged"; att.SetName(string); IF message.flags.flagged THEN string := "TRUE" ELSE string := "FALSE" END; att.SetValue(string); sub.AddAttribute(att);
  1779. NEW(att); string := "Deleted"; att.SetName(string); IF message.flags.deleted THEN string := "TRUE" ELSE string := "FALSE" END; att.SetValue(string); sub.AddAttribute(att);
  1780. NEW(att); string := "Seen"; att.SetName(string); IF message.flags.seen THEN string := "TRUE" ELSE string := "FALSE" END; att.SetValue(string); sub.AddAttribute(att);
  1781. NEW(att); string := "Recent"; att.SetName(string); IF message.flags.recent THEN string := "TRUE" ELSE string := "FALSE" END; att.SetValue(string); sub.AddAttribute(att);
  1782. NEW(att); string := "Draft"; att.SetName(string); IF message.flags.draft THEN string := "TRUE" ELSE string := "FALSE" END; att.SetValue(string); sub.AddAttribute(att);
  1783. ExtractAddresses(message.header.from, "from");
  1784. ExtractAddresses(message.header.sender, "sender");
  1785. ExtractAddresses(message.header.replyTo, "replyTo");
  1786. ExtractAddresses(message.header.to, "to");
  1787. ExtractAddresses(message.header.cc, "cc");
  1788. ExtractAddresses(message.header.bcc, "bcc");
  1789. IF message.message # NIL THEN
  1790. NEW(subSub); subSub.SetName("text"); sub.AddContent(subSub);
  1791. NEW(cdata); cdata.SetStr(message.message^); subSub.AddContent(cdata);
  1792. END;
  1793. IF message.bodystructure # NIL THEN
  1794. NEW(subSub); subSub.SetName("bodystructureType"); sub.AddContent(subSub);
  1795. NEW(cdata); cdata.SetStr(message.bodystructure.type); subSub.AddContent(cdata);
  1796. NEW(subSub); subSub.SetName("bodystructureSubType"); sub.AddContent(subSub);
  1797. NEW(cdata); cdata.SetStr(message.bodystructure.subtype); subSub.AddContent(cdata);
  1798. NEW(subSub); subSub.SetName("bodystructureEncoding"); sub.AddContent(subSub);
  1799. NEW(cdata); cdata.SetStr(message.bodystructure.encoding); subSub.AddContent(cdata);
  1800. NEW(subSub); subSub.SetName("bodystructureCharset"); sub.AddContent(subSub);
  1801. NEW(cdata); cdata.SetStr(message.bodystructure.charset); subSub.AddContent(cdata);
  1802. END;
  1803. element.AddContent(sub);
  1804. END;
  1805. INC(i);
  1806. END;
  1807. END ExtractMailboxContent;
  1808. PROCEDURE Load*(document:XML.Document): LONGINT;
  1809. VAR
  1810. buffer: Strings.Buffer;
  1811. writer: Streams.Writer;
  1812. string: String;
  1813. i, r: LONGINT;
  1814. element, subElement: XML.Element;
  1815. subElements, subSubElements, data: XMLObjects.Enumerator;
  1816. cdata: XML.CDataSect;
  1817. elementP: ANY;
  1818. folder: Folder;
  1819. BEGIN {EXCLUSIVE}
  1820. currentWork := CWLOADINGACCOUNT;
  1821. status := OFFLINE;
  1822. NEW(buffer,16);
  1823. writer := buffer.GetWriter();
  1824. IF document # NIL THEN
  1825. element := document.GetRoot();
  1826. string := element.GetName();
  1827. IF ~Strings.Equal(string, Strings.NewString("account")) THEN
  1828. CallErrorHandler("An error happend while trying to load an Account. The file is not compatible");
  1829. currentWork := CWFINISHED;
  1830. CallObserverMethod();
  1831. RETURN ERROR;
  1832. END;
  1833. subElements := element.GetContents();
  1834. WHILE subElements.HasMoreElements() DO
  1835. elementP := subElements.GetNext();
  1836. subElement := elementP(XML.Element);
  1837. string := subElement.GetName();
  1838. IF string^ = "preferences" THEN
  1839. r := LoadPreferences(subElement);
  1840. IF r # OK THEN
  1841. CallErrorHandler("An error happend while trying to load the Preferences");
  1842. currentWork := CWFINISHED;
  1843. RETURN r;
  1844. END;
  1845. ELSIF string^ = "folder" THEN
  1846. subSubElements := subElement.GetContents();
  1847. (* get name *)
  1848. elementP := subSubElements.GetNext();
  1849. element := elementP(XML.Element);
  1850. data := element.GetContents();
  1851. elementP := data.GetNext();
  1852. cdata := elementP(XML.CDataSect);
  1853. string := cdata.GetStr();
  1854. NEW(folder, string^);
  1855. (* get hierarchyDelimiter *)
  1856. elementP := subSubElements.GetNext();
  1857. element := elementP(XML.Element);
  1858. data := element.GetContents();
  1859. elementP := data.GetNext();
  1860. cdata := elementP(XML.CDataSect);
  1861. string := cdata.GetStr();
  1862. folder.hierarchyDelimiter := string^[0];
  1863. folder.parent := mailboxContent;
  1864. mailboxContent.children.Add(folder);
  1865. r := InsertMailboxContent(folder, subElement);
  1866. IF r # OK THEN
  1867. currentWork := CWFINISHED;
  1868. RETURN r;
  1869. END;
  1870. currentFolder := mailboxContent;
  1871. END;
  1872. END;
  1873. i := mailboxContent.children.GetCount();
  1874. IF DEBUG THEN KernelLog.String("Reading File successful"); KernelLog.Ln(); END;
  1875. ELSE
  1876. CallErrorHandler("Reading failed");
  1877. currentWork := CWFINISHED;
  1878. RETURN ERROR;
  1879. END;
  1880. currentWork := CWFINISHED;
  1881. CallObserverMethod();
  1882. RETURN OK;
  1883. END Load;
  1884. PROCEDURE LoadPreferences(element: XML.Element): LONGINT;
  1885. VAR
  1886. subElements, data: XMLObjects.Enumerator;
  1887. subElement: XML.Element;
  1888. cdata: XML.CDataSect;
  1889. p: ANY;
  1890. string, value: String;
  1891. PROCEDURE GetBoolean(): BOOLEAN;
  1892. BEGIN
  1893. value := cdata.GetStr();
  1894. IF value^ = "TRUE" THEN RETURN TRUE; ELSE RETURN FALSE; END;
  1895. END GetBoolean;
  1896. BEGIN
  1897. IF DEBUG THEN KernelLog.String("In LoadPreferences"); KernelLog.Ln(); END;
  1898. subElements := element.GetContents();
  1899. WHILE subElements.HasMoreElements() DO
  1900. p := subElements.GetNext();
  1901. subElement := p(XML.Element);
  1902. string := subElement.GetName();
  1903. data := subElement.GetContents();
  1904. p := data.GetNext();
  1905. cdata := p(XML.CDataSect);
  1906. IF string^ = "IMAPServer" THEN
  1907. preferences.IMAPServer := cdata.GetStr();
  1908. ELSIF string^ = "UserName" THEN
  1909. preferences.UserName := cdata.GetStr();
  1910. ELSIF string^ = "SMTPServer" THEN
  1911. preferences.SMTPServer := cdata.GetStr();
  1912. ELSIF string^ = "SMTPThisHost" THEN
  1913. preferences.SMTPThisHost := cdata.GetStr();
  1914. ELSIF string^ = "ExpungeOnFolderChange" THEN
  1915. preferences.ExpungeOnFolderChange := GetBoolean();
  1916. ELSIF string^ = "ExpungeOnDelete" THEN
  1917. preferences.ExpungeOnDelete := GetBoolean();
  1918. ELSIF string^ = "UseDragNDropAsMove" THEN
  1919. preferences.UseDragNDropAsMove := GetBoolean();
  1920. ELSIF string^ = "ExpungeOnMove" THEN
  1921. preferences.ExpungeOnMove := GetBoolean();
  1922. ELSIF string^ = "UseATrashBin" THEN
  1923. preferences.UseATrashBin := GetBoolean();
  1924. ELSIF string^ = "SentFolder" THEN
  1925. preferences.SentFolder := cdata.GetStr();
  1926. ELSIF string^ = "DraftFolder" THEN
  1927. preferences.DraftFolder := cdata.GetStr();
  1928. ELSIF string^ = "TrashBin" THEN
  1929. preferences.TrashBin := cdata.GetStr();
  1930. ELSIF string^ = "From" THEN
  1931. preferences.From := cdata.GetStr();
  1932. ELSE
  1933. CallErrorHandler("Invalid name for an XML Element detected");
  1934. CallErrorHandler(string^);
  1935. RETURN ERROR;
  1936. END;
  1937. END;
  1938. RETURN OK;
  1939. END LoadPreferences;
  1940. PROCEDURE InsertMailboxContent(folder: Folder; element: XML.Element): LONGINT;
  1941. VAR
  1942. subElements, messageElements, data: XMLObjects.Enumerator;
  1943. elementP: ANY;
  1944. subElem, messageElement: XML.Element;
  1945. cdata: XML.CDataSect;
  1946. address: IMAPUtilities.Address;
  1947. elementName, string: String;
  1948. subFolder: Folder;
  1949. message: Message;
  1950. header: HeaderElement;
  1951. flag : Flags;
  1952. i, r: LONGINT;
  1953. PROCEDURE GetAddress(element: XML.Element): IMAPUtilities.Address;
  1954. VAR
  1955. addressParts: XMLObjects.Enumerator;
  1956. part: XML.Element;
  1957. address: IMAPUtilities.Address;
  1958. i: LONGINT;
  1959. BEGIN
  1960. NEW(address);
  1961. addressParts := element.GetContents();
  1962. FOR i := 0 TO 2 DO
  1963. elementP := addressParts.GetNext();
  1964. part := elementP(XML.Element);
  1965. data := part.GetContents();
  1966. elementP := data.GetNext();
  1967. cdata := elementP(XML.CDataSect);
  1968. IF i = 0 THEN address.realName := cdata.GetStr();
  1969. ELSIF i = 1 THEN address.namePart := cdata.GetStr();
  1970. ELSIF i = 2 THEN address.domainPart := cdata.GetStr();
  1971. END;
  1972. END;
  1973. RETURN address;
  1974. END GetAddress;
  1975. BEGIN
  1976. subElements := element.GetContents();
  1977. WHILE subElements.HasMoreElements() DO
  1978. elementP := subElements.GetNext();
  1979. subElem := elementP(XML.Element);
  1980. elementName := subElem.GetName();
  1981. IF elementName^ = "name" THEN
  1982. data := subElem.GetContents();
  1983. elementP := data.GetNext();
  1984. cdata := elementP(XML.CDataSect);
  1985. folder.name := cdata.GetStr();
  1986. ELSIF elementName^ = "hierarchyDelimiter" THEN
  1987. data := subElem.GetContents();
  1988. elementP := data.GetNext();
  1989. cdata := elementP(XML.CDataSect);
  1990. string := cdata.GetStr();
  1991. folder.hierarchyDelimiter := string^[0];
  1992. ELSIF elementName^ = "folder" THEN
  1993. (* create the subFolder *)
  1994. NEW(subFolder, "temp"); (* will be overwritten when calling InsertMailboxContent recursivly *)
  1995. subFolder.parent := folder;
  1996. folder.children.Add(subFolder);
  1997. r := InsertMailboxContent(subFolder, subElem);
  1998. IF r # OK THEN
  1999. currentWork := CWFINISHED;
  2000. RETURN ERROR;
  2001. END;
  2002. ELSIF elementName^ = "message" THEN
  2003. NEW(message);
  2004. NEW(header);
  2005. message.header := header;
  2006. NEW(message.header.from);
  2007. NEW(message.header.sender);
  2008. NEW(message.header.replyTo);
  2009. NEW(message.header.to);
  2010. NEW(message.header.cc);
  2011. NEW(message.header.bcc);
  2012. messageElements := subElem.GetContents();
  2013. WHILE messageElements.HasMoreElements() DO
  2014. elementP := messageElements.GetNext();
  2015. messageElement := elementP(XML.Element);
  2016. elementName := messageElement.GetName();
  2017. IF (elementName^ = "date") OR (elementName^ = "subject") OR (elementName^ = "inReplyTo") OR (elementName^ = "messageID") OR (elementName^ = "internalDate") OR (elementName^ = "text") OR (elementName^ = "bodystructureType") OR (elementName^ = "bodystructureSubType") OR (elementName^ = "bodystructureEncoding") OR (elementName^ = "bodystructureCharset") THEN
  2018. data := messageElement.GetContents();
  2019. elementP := data.GetNext();
  2020. cdata := elementP(XML.CDataSect);
  2021. IF elementName^ = "date" THEN
  2022. message.header.date := cdata.GetStr();
  2023. ELSIF elementName^ = "subject" THEN
  2024. message.header.subject := cdata.GetStr();
  2025. ELSIF elementName^ = "inReplyTo" THEN
  2026. message.header.inReplyTo := cdata.GetStr();
  2027. ELSIF elementName^ = "messageID" THEN
  2028. message.header.messageID := cdata.GetStr();
  2029. ELSIF elementName^ = "internalDate" THEN
  2030. message.internalDate := cdata.GetStr();
  2031. ELSIF elementName^ = "size" THEN
  2032. string := cdata.GetStr();
  2033. Strings.StrToInt(string^, message.size);
  2034. ELSIF elementName^ = "uid" THEN
  2035. string := cdata.GetStr();
  2036. Strings.StrToInt(string^, message.uID);
  2037. ELSIF elementName^ = "text" THEN
  2038. message.message := cdata.GetStr();
  2039. ELSIF elementName^ = "bodystructureType" THEN
  2040. IF message.bodystructure = NIL THEN
  2041. NEW(message.bodystructure);
  2042. END;
  2043. string := cdata.GetStr();
  2044. IMAPUtilities.StringCopy(string^, 0, IMAPUtilities.StringLength(string^), message.bodystructure.type);
  2045. ELSIF elementName^ = "bodystructureSubType" THEN
  2046. IF message.bodystructure = NIL THEN
  2047. NEW(message.bodystructure);
  2048. END;
  2049. string := cdata.GetStr();
  2050. IMAPUtilities.StringCopy(string^, 0, IMAPUtilities.StringLength(string^), message.bodystructure.subtype);
  2051. ELSIF elementName^ = "bodystructureEncoding" THEN
  2052. IF message.bodystructure = NIL THEN
  2053. NEW(message.bodystructure);
  2054. END;
  2055. string := cdata.GetStr();
  2056. IMAPUtilities.StringCopy(string^, 0, IMAPUtilities.StringLength(string^), message.bodystructure.encoding);
  2057. ELSIF elementName^ = "bodystructureCharset" THEN
  2058. IF message.bodystructure = NIL THEN
  2059. NEW(message.bodystructure);
  2060. END;
  2061. string := cdata.GetStr();
  2062. IMAPUtilities.StringCopy(string^, 0, IMAPUtilities.StringLength(string^), message.bodystructure.charset);
  2063. END;
  2064. ELSIF (elementName^ = "from") OR (elementName^ = "sender") OR (elementName^ = "replyTo") OR (elementName^ = "to") OR (elementName^ = "cc") OR (elementName^ = "bcc") THEN
  2065. address := GetAddress(messageElement);
  2066. IF elementName^ = "from" THEN
  2067. message.header.from.Add(address);
  2068. ELSIF elementName^ = "sender" THEN
  2069. message.header.sender.Add(address);
  2070. ELSIF elementName^ = "replyTo" THEN
  2071. message.header.replyTo.Add(address);
  2072. ELSIF elementName^ = "to" THEN
  2073. message.header.to.Add(address);
  2074. ELSIF elementName^ = "cc" THEN
  2075. message.header.cc.Add(address);
  2076. ELSIF elementName^ = "bcc" THEN
  2077. message.header.bcc.Add(address);
  2078. END;
  2079. ELSE
  2080. CallErrorHandler("Invalid XML element name");
  2081. RETURN ERROR;
  2082. END;
  2083. END;
  2084. string := subElem.GetAttributeValue("size"); Strings.StrToInt(string^, i); message.size := i;
  2085. string := subElem.GetAttributeValue("uid"); Strings.StrToInt(string^, i); message.uID := i;
  2086. NEW(flag);
  2087. string := subElem.GetAttributeValue("Answered"); IF string^ = "TRUE" THEN flag.answered := TRUE; ELSE flag.answered := FALSE; END;
  2088. string := subElem.GetAttributeValue("Flagged"); IF string^ = "TRUE" THEN flag.flagged := TRUE; ELSE flag.flagged := FALSE; END;
  2089. string := subElem.GetAttributeValue("Deleted"); IF string^ = "TRUE" THEN flag.deleted := TRUE; ELSE flag.deleted := FALSE; END;
  2090. string := subElem.GetAttributeValue("Seen"); IF string^ = "TRUE" THEN flag.seen := TRUE; ELSE flag.seen := FALSE; END;
  2091. string := subElem.GetAttributeValue("Recent"); IF string^ = "TRUE" THEN flag.recent := TRUE; ELSE flag.recent := FALSE; END;
  2092. string := subElem.GetAttributeValue("Draft"); IF string^ = "TRUE" THEN flag.draft := TRUE; ELSE flag.draft := FALSE; END;
  2093. message.flags := flag;
  2094. folder.messages.Add(message);
  2095. END;
  2096. END;
  2097. RETURN OK;
  2098. END InsertMailboxContent;
  2099. BEGIN {ACTIVE} (* keepalive *)
  2100. NEW(timer);
  2101. WHILE status # DEAD DO
  2102. timer.Sleep(KEEPALIVE);
  2103. BEGIN {EXCLUSIVE}
  2104. IF status = ONLINE THEN
  2105. IF Task = TLoadAllMessages THEN
  2106. currentWork := CWLOADING;
  2107. globalR := DownloadAllMessages();
  2108. ELSE
  2109. IF FolderIsSynchronized OR abort OR userAbort THEN
  2110. currentWork := CWPOLLING;
  2111. globalR := Update();
  2112. END;
  2113. WHILE (~FolderIsSynchronized) & (~abort) & (~userAbort) DO
  2114. currentWork := CWLOADING;
  2115. globalR := Synchronize();
  2116. END;
  2117. END;
  2118. currentWork := CWFINISHED;
  2119. END; (* IF *)
  2120. CallObserverMethod();
  2121. END (* EXCLUSIVE *);
  2122. END (* WHILE *);
  2123. IF DEBUG THEN KernelLog.String("Client Activitiy finished"); KernelLog.Ln(); END;
  2124. END Client;
  2125. Folder* = OBJECT
  2126. VAR
  2127. name*: String;
  2128. path*: String;
  2129. hierarchyDelimiter*: CHAR;
  2130. parent*: Folder;
  2131. children*: Classes.List;
  2132. Noinferiors*: BOOLEAN;
  2133. Noselect*: BOOLEAN;
  2134. Marked*: BOOLEAN;
  2135. Unmarked*: BOOLEAN;
  2136. messages*: Classes.List;
  2137. alive: BOOLEAN;
  2138. PROCEDURE &Init*(n: ARRAY OF CHAR);
  2139. BEGIN
  2140. NEW(name,IMAPUtilities.StringLength(n)+1 );
  2141. IMAPUtilities.StringCopy(n,0, IMAPUtilities.StringLength(n), name^);
  2142. NEW(path,1);
  2143. path^[0] := 0X;
  2144. hierarchyDelimiter := 0X;
  2145. parent := NIL;
  2146. NEW(children);
  2147. Noinferiors := FALSE;
  2148. Noselect := FALSE;
  2149. Marked := FALSE;
  2150. Unmarked := FALSE;
  2151. NEW(messages);
  2152. alive := TRUE;
  2153. END Init;
  2154. PROCEDURE FindSubFolder(CONST n: ARRAY OF CHAR): Folder;
  2155. VAR
  2156. i: LONGINT;
  2157. sub: Folder;
  2158. p: ANY;
  2159. BEGIN
  2160. i := 0;
  2161. WHILE i < children.GetCount() DO
  2162. p := children.GetItem(i);
  2163. sub := p (Folder);
  2164. IF sub.name^ = n THEN
  2165. RETURN sub;
  2166. END;
  2167. INC(i);
  2168. END;
  2169. RETURN NIL;
  2170. END FindSubFolder;
  2171. (* Returns the Path including the folder name as a String *)
  2172. PROCEDURE GetPath*(): String;
  2173. VAR
  2174. path: String;
  2175. pathLen, nameLen: LONGINT;
  2176. BEGIN
  2177. pathLen := IMAPUtilities.StringLength(SELF.path^);
  2178. nameLen := IMAPUtilities.StringLength(SELF.name^);
  2179. IF pathLen = 0 THEN
  2180. path := IMAPUtilities.NewString(SELF.name^);
  2181. ELSE
  2182. NEW(path, pathLen + nameLen + 2);
  2183. IMAPUtilities.StringCopy(SELF.path^, 0, pathLen, path^);
  2184. path^[pathLen] := SELF.hierarchyDelimiter;
  2185. path^[pathLen+1] := 0X;
  2186. Strings.Append(path^, SELF.name^);
  2187. END;
  2188. RETURN path;
  2189. END GetPath;
  2190. END Folder;
  2191. HeaderElement* = POINTER TO RECORD (** according to RFC 2060 *)
  2192. date*: String;
  2193. subject*: String;
  2194. from*: Classes.List; (** of Address *)
  2195. sender*: Classes.List; (** of Address *)
  2196. replyTo*: Classes.List; (** of Address *)
  2197. to*: Classes.List; (** of Address *)
  2198. cc*: Classes.List; (** of Address *)
  2199. bcc*: Classes.List; (** of Address *)
  2200. inReplyTo*: String;
  2201. messageID*: String;
  2202. END;
  2203. Flags* = OBJECT
  2204. VAR
  2205. answered*: BOOLEAN;
  2206. flagged*: BOOLEAN;
  2207. deleted*: BOOLEAN;
  2208. seen*: BOOLEAN;
  2209. recent*: BOOLEAN;
  2210. draft*: BOOLEAN;
  2211. PROCEDURE Clear*;
  2212. BEGIN
  2213. answered := FALSE;
  2214. flagged := FALSE;
  2215. deleted := FALSE;
  2216. seen := FALSE;
  2217. recent := FALSE;
  2218. draft := FALSE;
  2219. END Clear;
  2220. (* import list of flags *)
  2221. PROCEDURE ParseList*(list: Classes.List);
  2222. VAR
  2223. i: LONGINT;
  2224. ent: IMAP.Entry;
  2225. entP: ANY;
  2226. BEGIN
  2227. Clear; (* reset structure *)
  2228. FOR i := 0 TO list.GetCount() - 1 DO
  2229. entP := list.GetItem(i); ent := entP(IMAP.Entry);
  2230. IMAPUtilities.UpperCase(ent.data^);
  2231. IF ent.data^ = "\ANSWERED" THEN answered := TRUE END;
  2232. IF ent.data^ = "\FLAGGED" THEN flagged := TRUE END;
  2233. IF ent.data^ = "\DELETED" THEN deleted := TRUE END;
  2234. IF ent.data^ = "\SEEN" THEN seen := TRUE END;
  2235. IF ent.data^ = "\RECENT" THEN recent := TRUE END;
  2236. IF ent.data^ = "\DRAFT" THEN draft := TRUE END
  2237. END
  2238. END ParseList;
  2239. PROCEDURE ToString*(VAR string: ARRAY OF CHAR);
  2240. BEGIN
  2241. string[0] := 0X;
  2242. IF answered THEN Strings.Append(string, "A"); ELSE Strings.Append(string, "-") END;
  2243. IF flagged THEN Strings.Append(string, "F"); ELSE Strings.Append(string, "-") END;
  2244. IF deleted THEN Strings.Append(string, "D"); ELSE Strings.Append(string, "-") END;
  2245. IF seen THEN Strings.Append(string, "-"); ELSE Strings.Append(string, "N") END;
  2246. IF recent THEN Strings.Append(string, "R"); ELSE Strings.Append(string, "-") END;
  2247. IF draft THEN Strings.Append(string, "S"); ELSE Strings.Append(string, "-") END
  2248. END ToString;
  2249. END Flags;
  2250. Bodystructure* = POINTER TO RECORD
  2251. type* : ARRAY 32 OF CHAR;
  2252. subtype* : ARRAY 32 OF CHAR;
  2253. encoding* : ARRAY 32 OF CHAR;
  2254. charset*: ARRAY 32 OF CHAR;
  2255. subpart* : Classes.List (* of type Bodystructure *)
  2256. END;
  2257. AccountPreferences* = OBJECT
  2258. VAR
  2259. IMAPServer*: String;
  2260. UserName*: String;
  2261. SMTPServer*: String;
  2262. SMTPThisHost*: String;
  2263. ExpungeOnFolderChange*: BOOLEAN; (* specifies if an expunge or close command is called before another folder is selected *)
  2264. ExpungeOnDelete*: BOOLEAN; (* specifies if a message gets expunged directly when deleting it *)
  2265. UseDragNDropAsMove*: BOOLEAN; (* on drag'n'drop the source is deleted. i.e the Messages are moved. Otherwise they are copied*)
  2266. ExpungeOnMove*: BOOLEAN; (* specifies if an expunge command is called after the Move Operation. *)
  2267. UseATrashBin*: BOOLEAN; (* specifies if deleted Messages are move to a trash bin *)
  2268. SentFolder*: String; (* specifies in which folder to store the sent Messages *)
  2269. DraftFolder*: String; (* specifies in which folder to store the draft Messages *)
  2270. TrashBin*: String; (* specifies in which folder to move the deleted Messages. *)
  2271. From *: String; (* specifies the From Field that is used when sending Messages *)
  2272. PROCEDURE &New*;
  2273. BEGIN
  2274. IMAPServer := Strings.NewString("");
  2275. UserName := Strings.NewString("");
  2276. SMTPServer := Strings.NewString("");
  2277. SMTPThisHost := Strings.NewString("");
  2278. SentFolder := Strings.NewString("");
  2279. DraftFolder := Strings.NewString("");
  2280. TrashBin := Strings.NewString("");
  2281. From := Strings.NewString("");
  2282. END New;
  2283. PROCEDURE LoadStandardConfig;
  2284. VAR
  2285. config : XML.Element;
  2286. enum: XMLObjects.Enumerator;
  2287. p: ANY;
  2288. e: XML.Element;
  2289. name, value: XML.String;
  2290. PROCEDURE GetBoolean(): BOOLEAN;
  2291. BEGIN
  2292. IF value^ = "TRUE" THEN RETURN TRUE; ELSE RETURN FALSE; END;
  2293. END GetBoolean;
  2294. BEGIN
  2295. IF DEBUG THEN KernelLog.String("In LoadStandardConfig"); KernelLog.Ln(); END;
  2296. config := Configuration.GetSection("Applications.MailClient");
  2297. IF config # NIL THEN
  2298. enum := config.GetContents();
  2299. WHILE enum.HasMoreElements() DO
  2300. p := enum.GetNext();
  2301. IF p IS XML.Element THEN
  2302. e := p(XML.Element);
  2303. name := e.GetAttributeValue("name");
  2304. value := e.GetAttributeValue("value");
  2305. IF name^ = "IMAPServer" THEN
  2306. IMAPServer := value;
  2307. ELSIF name^ = "UserName" THEN
  2308. UserName := value;
  2309. ELSIF name^ = "SMTPServer" THEN
  2310. SMTPServer := value;
  2311. ELSIF name^ = "SMTPThisHost" THEN
  2312. SMTPThisHost := value;
  2313. ELSIF name^ = "ExpungeOnFolderChange" THEN
  2314. ExpungeOnFolderChange := GetBoolean();
  2315. ELSIF name^ = "ExpungeOnDelete" THEN
  2316. ExpungeOnDelete := GetBoolean();
  2317. ELSIF name^ = "UseDragNDropAsMove" THEN
  2318. UseDragNDropAsMove := GetBoolean();
  2319. ELSIF name^ = "ExpungeOnMove" THEN
  2320. ExpungeOnMove := GetBoolean();
  2321. ELSIF name^ = "UseATrashBin" THEN
  2322. UseATrashBin := GetBoolean();
  2323. ELSIF name^ = "SentFolder" THEN
  2324. SentFolder := value;
  2325. ELSIF name^ = "DraftFolder" THEN
  2326. DraftFolder := value;
  2327. ELSIF name^ = "TrashBin" THEN
  2328. TrashBin := value;
  2329. ELSIF name^ = "From" THEN
  2330. From := value;
  2331. ELSE
  2332. IF DEBUG THEN KernelLog.String("Unknown Setting in Configuration.XML Section: IMAP Setting: "); KernelLog.String(name^); KernelLog.Ln(); END;
  2333. END;
  2334. END;
  2335. END;
  2336. END;
  2337. END LoadStandardConfig;
  2338. END AccountPreferences;
  2339. Date* = OBJECT
  2340. VAR
  2341. day, month, year: LONGINT;
  2342. (* Returns TRUE if this date is equal to otherDate *)
  2343. PROCEDURE Equal*(otherDate: Date): BOOLEAN;
  2344. BEGIN
  2345. RETURN (otherDate.day = day) & (otherDate.month = month) & (otherDate.year = year);
  2346. END Equal;
  2347. (* Returns TRUE if this date is before otherDate *)
  2348. PROCEDURE Before*(otherDate: Date): BOOLEAN;
  2349. BEGIN
  2350. IF year < otherDate.year THEN
  2351. RETURN TRUE;
  2352. ELSIF otherDate.year < year THEN
  2353. RETURN FALSE;
  2354. END;
  2355. IF month < otherDate.month THEN
  2356. RETURN TRUE;
  2357. ELSIF otherDate.month < month THEN
  2358. RETURN FALSE;
  2359. END;
  2360. IF day < otherDate.day THEN
  2361. RETURN TRUE;
  2362. ELSE
  2363. RETURN FALSE;
  2364. END;
  2365. END Before;
  2366. PROCEDURE FromInternalDate(string: String);
  2367. VAR
  2368. d: ARRAY 3 OF CHAR;
  2369. m: ARRAY 4 OF CHAR;
  2370. y: ARRAY 5 OF CHAR;
  2371. BEGIN
  2372. IF string^[1] = "-" THEN
  2373. Strings.Copy(string^, 0,1, d);
  2374. Strings.Copy(string^, 2, 3, m);
  2375. Strings.Copy(string^, 6, 4, y);
  2376. ELSIF string^[2] = "-" THEN
  2377. IF string^[0] = " " THEN
  2378. Strings.Copy(string^, 1, 1, d);
  2379. ELSE
  2380. Strings.Copy(string^, 0, 2, d);
  2381. END;
  2382. Strings.Copy(string^, 3, 3, m);
  2383. Strings.Copy(string^, 7, 4, y);
  2384. ELSE
  2385. END;
  2386. Strings.StrToInt(d, day);
  2387. IF m = "Jan" THEN
  2388. month := 1;
  2389. ELSIF m = "Feb" THEN
  2390. month := 2;
  2391. ELSIF m = "Mar" THEN
  2392. month := 3;
  2393. ELSIF m = "Apr" THEN
  2394. month := 4;
  2395. ELSIF m = "May" THEN
  2396. month := 5;
  2397. ELSIF m = "Jun" THEN
  2398. month := 6;
  2399. ELSIF m = "Jul" THEN
  2400. month := 7;
  2401. ELSIF m = "Aug" THEN
  2402. month := 8;
  2403. ELSIF m = "Sep" THEN
  2404. month := 9;
  2405. ELSIF m = "Oct" THEN
  2406. month := 10;
  2407. ELSIF m = "Nov" THEN
  2408. month := 11;
  2409. ELSIF m = "Dec" THEN
  2410. month := 12;
  2411. END;
  2412. Strings.StrToInt(y, year);
  2413. END FromInternalDate;
  2414. END Date;
  2415. Time *= OBJECT
  2416. VAR
  2417. hour, minute, second: LONGINT;
  2418. (* Returns TRUE if this time is equal to otherTime *)
  2419. PROCEDURE Equal*(otherTime: Time): BOOLEAN;
  2420. BEGIN
  2421. RETURN (otherTime.hour = hour) & (otherTime.minute = minute) & (otherTime.second = second);
  2422. END Equal;
  2423. (* Returns TRUE if this time is before otherTime *)
  2424. PROCEDURE Before*(otherTime: Time): BOOLEAN;
  2425. BEGIN
  2426. IF hour < otherTime.hour THEN
  2427. RETURN TRUE;
  2428. ELSIF otherTime.hour < hour THEN
  2429. RETURN FALSE;
  2430. END;
  2431. IF minute < otherTime.minute THEN
  2432. RETURN TRUE;
  2433. ELSIF otherTime.minute < minute THEN
  2434. RETURN FALSE;
  2435. END;
  2436. IF second < otherTime.second THEN
  2437. RETURN TRUE;
  2438. ELSE
  2439. RETURN FALSE;
  2440. END;
  2441. END Before;
  2442. PROCEDURE FromInternalDate(string: String);
  2443. VAR
  2444. h, m, s: ARRAY 3 OF CHAR;
  2445. str: String;
  2446. BEGIN
  2447. str := string;
  2448. Strings.Copy(string^, 12, 2, h);
  2449. Strings.Copy(string^, 15, 2, m);
  2450. Strings.Copy(string^, 18, 2, s);
  2451. Strings.StrToInt(h, hour);
  2452. Strings.StrToInt(m, minute);
  2453. Strings.StrToInt(s, second);
  2454. END FromInternalDate;
  2455. END Time;
  2456. DateTime *= OBJECT
  2457. VAR
  2458. time: Time;
  2459. date: Date;
  2460. PROCEDURE &New*;
  2461. BEGIN
  2462. NEW(time);
  2463. NEW(date);
  2464. END New;
  2465. (* Returns TRUE if this DateTime is equal to otherDateTime *)
  2466. PROCEDURE Equal*(otherDateTime: DateTime): BOOLEAN;
  2467. BEGIN
  2468. RETURN date.Equal(otherDateTime.date) & time.Equal(otherDateTime.time);
  2469. END Equal;
  2470. (* Returns TRUE if this DateTime is before otherDateTime *)
  2471. PROCEDURE Before*(otherDateTime:DateTime): BOOLEAN;
  2472. BEGIN
  2473. IF date.Before(otherDateTime.date) THEN
  2474. RETURN TRUE;
  2475. ELSIF otherDateTime.date.Before(date) THEN
  2476. RETURN FALSE;
  2477. ELSE
  2478. IF time.Before(otherDateTime.time) THEN
  2479. RETURN TRUE;
  2480. ELSE
  2481. RETURN FALSE;
  2482. END;
  2483. END;
  2484. END Before;
  2485. PROCEDURE FromInternalDate*(string: String);
  2486. BEGIN
  2487. time.FromInternalDate(string);
  2488. date.FromInternalDate(string);
  2489. END FromInternalDate;
  2490. END DateTime;
  2491. (* defines the ordering of the Messages of a Mailbox as oldest first *)
  2492. PROCEDURE OldestFirst*(x,y: ANY): LONGINT;
  2493. VAR
  2494. m1, m2: Message;
  2495. h1, h2: HeaderElement;
  2496. dt1, dt2: DateTime;
  2497. BEGIN
  2498. m1 := x(Message);
  2499. m2 := y(Message);
  2500. h1 := m1.header;
  2501. h2 := m2.header;
  2502. IF h1 = NIL THEN
  2503. RETURN 1;
  2504. END;
  2505. IF h2 = NIL THEN
  2506. RETURN -1;
  2507. END;
  2508. IF (m1.internalDate = NIL) OR (m1.internalDate^ = "") THEN
  2509. RETURN 1;
  2510. END;
  2511. IF (m2.internalDate = NIL) OR (m2.internalDate^ = "") THEN
  2512. RETURN -1;
  2513. END;
  2514. NEW(dt1); NEW(dt2);
  2515. dt1.FromInternalDate(m1.internalDate);
  2516. dt2.FromInternalDate(m2.internalDate);
  2517. IF dt1.Equal(dt2) THEN RETURN 0; END;
  2518. IF dt1.Before(dt2) THEN
  2519. RETURN 1;
  2520. ELSE
  2521. RETURN -1;
  2522. END;
  2523. END OldestFirst;
  2524. PROCEDURE BiggestUIDFirst*(x,y: ANY): LONGINT;
  2525. VAR
  2526. m1, m2: Message;
  2527. h1, h2: HeaderElement;
  2528. BEGIN
  2529. m1 := x(Message);
  2530. m2 := y(Message);
  2531. h1 := m1.header;
  2532. h2 := m2.header;
  2533. IF h1 = NIL THEN
  2534. RETURN 1;
  2535. END;
  2536. IF h2 = NIL THEN
  2537. RETURN -1;
  2538. END;
  2539. IF m1.uID < m2.uID THEN
  2540. RETURN 1;
  2541. ELSE
  2542. RETURN -1;
  2543. END;
  2544. END BiggestUIDFirst;
  2545. END IMAPClient.