IMAP.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #ifndef IMAP_H
  18. #define IMAP_H
  19. #include <memory>
  20. #include <mutex>
  21. #include <string>
  22. #include <vector>
  23. #include <list>
  24. #include <set>
  25. #include <kopano/zcdefs.h>
  26. #include <kopano/ECIConv.h>
  27. #include <kopano/ECChannel.h>
  28. #include <kopano/memory.hpp>
  29. #include <kopano/hl.hpp>
  30. #include "ClientProto.h"
  31. using namespace std;
  32. using namespace KCHL;
  33. namespace KC {
  34. class ECRestriction;
  35. }
  36. /**
  37. * @defgroup gateway_imap IMAP
  38. * @ingroup gateway
  39. * @{
  40. */
  41. #define ROWS_PER_REQUEST 200
  42. #define IMAP_HIERARCHY_DELIMITER '/'
  43. #define PUBLIC_FOLDERS_NAME L"Public folders"
  44. #define IMAP_RESP_MAX 65536
  45. #define RESP_UNTAGGED "* "
  46. #define RESP_CONTINUE "+ "
  47. #define RESP_TAGGED_OK " OK "
  48. #define RESP_TAGGED_NO " NO "
  49. #define RESP_TAGGED_BAD " BAD "
  50. class BinaryArray _kc_final {
  51. public:
  52. BinaryArray(void) : lpb(NULL), cb(0), bcheap(false) {}
  53. BinaryArray(KEntryId &entry_id) : lpb(reinterpret_cast<BYTE *>(entry_id.lpb())), cb(entry_id.cb()), bcheap(true) {}
  54. BinaryArray(BYTE *lpData, ULONG cbData, bool bcheap = false)
  55. {
  56. this->bcheap = bcheap;
  57. if (cbData == 0) {
  58. cb = 0;
  59. lpb = NULL;
  60. return;
  61. }
  62. if (!bcheap) {
  63. lpb = new BYTE[cbData];
  64. memcpy(lpb, lpData, cbData);
  65. } else {
  66. lpb = lpData;
  67. }
  68. cb = cbData;
  69. }
  70. BinaryArray(const BinaryArray &old) {
  71. bcheap = false;
  72. if (old.cb == 0) {
  73. cb = 0;
  74. lpb = NULL;
  75. return;
  76. }
  77. cb = old.cb;
  78. lpb = new BYTE[cb];
  79. memcpy(lpb, old.lpb, cb);
  80. }
  81. BinaryArray(BinaryArray &&o) :
  82. lpb(o.lpb), cb(o.cb), bcheap(o.bcheap)
  83. {
  84. o.lpb = nullptr;
  85. o.cb = 0;
  86. o.bcheap = false;
  87. }
  88. BinaryArray(const SBinary &bin) {
  89. bcheap = false;
  90. if (bin.cb == 0) {
  91. cb = 0;
  92. lpb = NULL;
  93. return;
  94. }
  95. lpb = new BYTE[bin.cb];
  96. memcpy(lpb, bin.lpb, bin.cb);
  97. cb = bin.cb;
  98. }
  99. BinaryArray(SBinary &&o) :
  100. lpb(o.lpb), cb(o.cb), bcheap(false)
  101. {
  102. o.lpb = nullptr;
  103. o.cb = 0;
  104. }
  105. ~BinaryArray(void)
  106. {
  107. if (!bcheap)
  108. delete[] lpb;
  109. }
  110. bool operator==(const BinaryArray &b) const
  111. {
  112. if (b.cb == 0 && this->cb == 0)
  113. return true;
  114. if (b.cb != this->cb)
  115. return false;
  116. else
  117. return memcmp(lpb, b.lpb, cb) == 0;
  118. }
  119. BinaryArray &operator=(const BinaryArray &b)
  120. {
  121. BYTE *lpbPrev = lpb;
  122. if (b.cb == 0) {
  123. cb = 0;
  124. lpb = NULL;
  125. } else {
  126. cb = b.cb;
  127. lpb = new BYTE[cb];
  128. memcpy(lpb, b.lpb, cb);
  129. if (!bcheap)
  130. delete[] lpbPrev;
  131. }
  132. bcheap = false;
  133. return *this;
  134. }
  135. BYTE *lpb;
  136. ULONG cb;
  137. bool bcheap;
  138. };
  139. struct lessBinaryArray {
  140. bool operator()(const BinaryArray& a, const BinaryArray& b) const
  141. {
  142. if (a.cb < b.cb || (a.cb == b.cb && memcmp(a.lpb, b.lpb, a.cb) < 0) )
  143. return true;
  144. return false;
  145. }
  146. };
  147. // FLAGS: \Seen \Answered \Flagged \Deleted \Draft \Recent
  148. class IMAP _kc_final : public ClientProto {
  149. public:
  150. IMAP(const char *szServerPath, ECChannel *lpChannel, ECLogger *lpLogger, ECConfig *lpConfig);
  151. ~IMAP();
  152. int getTimeoutMinutes();
  153. bool isIdle() const { return m_bIdleMode; }
  154. bool isContinue() const { return m_bContinue; }
  155. HRESULT HrSendGreeting(const std::string &strHostString);
  156. HRESULT HrCloseConnection(const std::string &strQuitMsg);
  157. HRESULT HrProcessCommand(const std::string &strInput);
  158. HRESULT HrProcessContinue(const std::string &strInput);
  159. HRESULT HrDone(bool bSendResponse);
  160. private:
  161. void CleanupObject();
  162. void ReleaseContentsCache();
  163. std::string GetCapabilityString(bool bAllFlags);
  164. HRESULT HrSplitInput(const string &strInput, vector<string> &lstWords);
  165. HRESULT HrSplitPath(const wstring &strInput, vector<wstring> &lstFolders);
  166. HRESULT HrUnsplitPath(const vector<wstring> &lstFolders, wstring &strPath);
  167. // All IMAP4rev1 commands
  168. HRESULT HrCmdCapability(const string &strTag);
  169. template<bool> HRESULT HrCmdNoop(const std::string &tag);
  170. HRESULT HrCmdNoop(const std::string &tag, bool check);
  171. HRESULT HrCmdLogout(const string &strTag);
  172. HRESULT HrCmdStarttls(const string &strTag);
  173. HRESULT HrCmdAuthenticate(const string &strTag, string strAuthMethod, const string &strAuthData);
  174. HRESULT HrCmdLogin(const std::string &tag, const std::vector<std::string> &args);
  175. template<bool> HRESULT HrCmdSelect(const std::string &tag, const std::vector<std::string> &args);
  176. HRESULT HrCmdSelect(const std::string &tag, const std::vector<std::string> &args, bool read_only);
  177. HRESULT HrCmdCreate(const std::string &tag, const std::vector<std::string> &args);
  178. HRESULT HrCmdDelete(const std::string &tag, const std::vector<std::string> &args);
  179. HRESULT HrCmdRename(const std::string &tag, const std::vector<std::string> &args);
  180. template<bool> HRESULT HrCmdSubscribe(const std::string &tag, const std::vector<std::string> &args);
  181. HRESULT HrCmdSubscribe(const std::string &tag, const std::vector<std::string> &args, bool subscribe);
  182. template<bool> HRESULT HrCmdList(const std::string &tag, const std::vector<std::string> &args);
  183. HRESULT HrCmdList(const std::string &tag, const std::vector<std::string> &args, bool sub_only);
  184. HRESULT get_uid_next(KFolder &&status_folder, const std::string &tag, ULONG &uid_next);
  185. HRESULT get_recent(KFolder &&folder, const std::string &tag, ULONG &recent, const ULONG &messages);
  186. HRESULT HrCmdStatus(const std::string &tag, const std::vector<std::string> &args);
  187. HRESULT HrCmdAppend(const string &strTag, const string &strFolder, const string &strData, string strFlags=string(), const string &strTime=string());
  188. HRESULT HrCmdClose(const string &strTag);
  189. HRESULT HrCmdExpunge(const string &strTag, const string &strSeqSet);
  190. HRESULT HrCmdSearch(const string &strTag, vector<string> &lstSearchCriteria, bool bUidMode);
  191. HRESULT HrCmdFetch(const string &strTag, const string &strSeqSet, const string &strMsgDataItemNames, bool bUidMode);
  192. HRESULT HrCmdStore(const string &strTag, const string &strSeqSet, const string &strMsgDataItemName, const string &strMsgDataItemValue, bool bUidMode);
  193. HRESULT HrCmdCopy(const string &strTag, const string &strSeqSet, const string &strFolder, bool bUidMode);
  194. HRESULT HrCmdUidXaolMove(const string &strTag, const string &strSeqSet, const string &strFolder);
  195. HRESULT HrCmdIdle(const string &strTag);
  196. HRESULT HrCmdNamespace(const string &strTag);
  197. HRESULT HrCmdGetQuotaRoot(const std::string &tag, const std::vector<std::string> &args);
  198. HRESULT HrCmdGetQuota(const std::string &tag, const std::vector<std::string> &args);
  199. HRESULT HrCmdSetQuota(const std::string &tag, const std::vector<std::string> &args);
  200. /* Untagged response, * or + */
  201. void HrResponse(const std::string &untag, const std::string &resp);
  202. /* Tagged response with result OK, NO or BAD */
  203. void HrResponse(const std::string &result, const std::string &tag, const std::string &resp);
  204. static LONG __stdcall IdleAdviseCallback(void *ctx, ULONG numnotif, LPNOTIFICATION);
  205. bool bOnlyMailFolders;
  206. bool bShowPublicFolder;
  207. // All data per folder for the folderlist
  208. struct SFolder {
  209. BinaryArray sEntryID; // EntryID of folder
  210. wstring strFolderName; // Folder name
  211. bool bActive; // Subscribed folder
  212. bool bMailFolder; // E-mail type folder
  213. bool bSpecialFolder; // 'special' folder (eg inbox)
  214. bool bHasSubfolders; // Has child folders
  215. list<SFolder>::const_iterator lpParentFolder;
  216. };
  217. // All data to be mapped per mail in the current folder
  218. // Used class to be able to use sort
  219. class SMail {
  220. public:
  221. BinaryArray sEntryID; // EntryID of message
  222. BinaryArray sInstanceKey; // Instance key of message
  223. ULONG ulUid; // PR_EC_IMAP_UID of message
  224. bool bRecent; // \Recent flag
  225. std::string strFlags; // String of all flags, including \Recent
  226. bool operator < (SMail sMail) const {
  227. return this->ulUid < sMail.ulUid;
  228. }
  229. bool operator < (ULONG ulUid) const {
  230. return this->ulUid < ulUid;
  231. }
  232. operator ULONG() const {
  233. return this->ulUid;
  234. }
  235. bool operator == (ULONG ulUid) const {
  236. return this->ulUid == ulUid;
  237. }
  238. };
  239. IMAPISession *lpSession = nullptr;
  240. IAddrBook *lpAddrBook = nullptr;
  241. KCHL::memory_ptr<SPropTagArray> m_lpsIMAPTags;
  242. // current folder name
  243. wstring strCurrentFolder;
  244. IMAPITable *m_lpTable = nullptr; /* current contents table */
  245. vector<string> m_vTableDataColumns; /* current dataitems that caused the setcolumns on the table */
  246. // true if folder is opened with examine
  247. bool bCurrentFolderReadOnly = false;
  248. // vector of mails in the current folder. The index is used for mail number.
  249. vector<SMail> lstFolderMailEIDs;
  250. IMsgStore *lpStore = nullptr, *lpPublicStore = nullptr;
  251. // special folder entryids (not able to move/delete inbox and such ...)
  252. set<BinaryArray, lessBinaryArray> lstSpecialEntryIDs;
  253. // Message cache
  254. string m_strCache;
  255. ULONG m_ulCacheUID = 0;
  256. // Folder cache
  257. unsigned int cache_folders_time_limit = 0;
  258. time_t cache_folders_last_used = 0;
  259. std::list<SFolder> cached_folders;
  260. /* A command has sent a continuation response, and requires more
  261. * data from the client. This is currently only used in the
  262. * AUTHENTICATE command, other continuations are already handled
  263. * in the main loop. m_bContinue marks this. */
  264. bool m_bContinue = false;
  265. string m_strContinueTag;
  266. // Idle mode variables
  267. bool m_bIdleMode = false;
  268. IMAPIAdviseSink *m_lpIdleAdviseSink = nullptr;
  269. ULONG m_ulIdleAdviseConnection = 0;
  270. string m_strIdleTag;
  271. IMAPITable *m_lpIdleTable = nullptr;
  272. std::mutex m_mIdleLock;
  273. ULONG m_ulLastUid = 0, m_ulErrors = 0;
  274. wstring m_strwUsername;
  275. delivery_options dopt;
  276. HRESULT HrPrintQuotaRoot(const string& strTag);
  277. HRESULT HrFindFolder(const wstring& strFolder, bool bReadOnly, IMAPIFolder **lppFolder);
  278. HRESULT HrFindFolderEntryID(const wstring& strFolder, ULONG *lpcbEntryID, LPENTRYID *lppEntryID);
  279. HRESULT HrFindFolderPartial(const wstring& strFolder, IMAPIFolder **lppFolder, wstring *strNotFound);
  280. HRESULT HrFindSubFolder(IMAPIFolder *lpFolder, const wstring& strFolder, ULONG *lpcbEntryID, LPENTRYID *lppEntryID);
  281. bool IsSpecialFolder(IMAPIFolder *lpFolder);
  282. bool IsSpecialFolder(ULONG cbEntryID, LPENTRYID lpEntryID);
  283. bool IsMailFolder(IMAPIFolder *lpFolder);
  284. bool IsSentItemFolder(IMAPIFolder *lpFolder);
  285. HRESULT HrOpenParentFolder(ULONG cbEntryID, LPENTRYID lpEntryID, IMAPIFolder **lppFolder);
  286. HRESULT HrOpenParentFolder(IMAPIFolder *lpFolder, IMAPIFolder **lppFolder);
  287. HRESULT HrGetFolderList(list<SFolder> &lstFolders);
  288. /* subscribed folders */
  289. vector<BinaryArray> m_vSubscriptions;
  290. HRESULT HrGetSubscribedList();
  291. HRESULT HrSetSubscribedList();
  292. HRESULT ChangeSubscribeList(bool bSubscribe, ULONG cbEntryID, LPENTRYID lpEntryID);
  293. HRESULT HrMakeSpecialsList();
  294. HRESULT HrRefreshFolderMails(bool bInitialLoad, bool bResetRecent, unsigned int *lpulUnseen, ULONG *lpulUIDValidity = NULL);
  295. HRESULT HrGetSubTree(list<SFolder> &folders, const SBinary &in_entry_id, const wstring &in_folder_name, list<SFolder>::const_iterator parent_folder);
  296. HRESULT HrGetFolderPath(list<SFolder>::const_iterator lpFolder, const list<SFolder> &lstFolder, wstring &strPath);
  297. HRESULT HrGetDataItems(string strMsgDataItemNames, vector<string> &lstDataItems);
  298. HRESULT HrSemicolonToComma(string &strData);
  299. // fetch calls an other fetch depending on the data items requested
  300. HRESULT HrPropertyFetch(list<ULONG> &lstMails, vector<string> &lstDataItems);
  301. HRESULT HrPropertyFetchRow(LPSPropValue lpProps, ULONG cValues, string &strResponse, ULONG ulMailnr, bool bForceFlags, const vector<string> &lstDataItems);
  302. std::string HrEnvelopeRecipients(LPSRowSet lpRows, ULONG ulType, std::string& strCharset, bool bIgnore);
  303. std::string HrEnvelopeSender(LPMESSAGE lpMessage, ULONG ulTagName, ULONG ulTagEmail, std::string& strCharset, bool bIgnore);
  304. HRESULT HrGetMessageEnvelope(string &strResponse, LPMESSAGE lpMessage);
  305. HRESULT HrGetMessageFlags(string &strResponse, LPMESSAGE lpMessage, bool bRecent);
  306. HRESULT HrGetMessagePart(string &strMessagePart, string &strMessage, string strPartName);
  307. ULONG LastOrNumber(const char *szNr, bool bUID);
  308. HRESULT HrParseSeqSet(const string &strSeqSet, list<ULONG> &lstMails);
  309. HRESULT HrParseSeqUidSet(const string &strSeqSet, list<ULONG> &lstMails);
  310. HRESULT HrSeqUidSetToRestriction(const string &strSeqSet, std::unique_ptr<ECRestriction> &);
  311. HRESULT HrStore(const list<ULONG> &lstMails, string strMsgDataItemName, string strMsgDataItemValue, bool *lpbDoDelete);
  312. HRESULT HrCopy(const list<ULONG> &lstMails, const string &strFolder, bool bMove);
  313. HRESULT HrSearchNU(const std::vector<std::string> &cond, ULONG startcond, std::list<ULONG> &mailnr);
  314. HRESULT HrSearch(std::vector<std::string> &&cond, ULONG startcond, std::list<ULONG> &mailnr);
  315. string GetHeaderValue(const string &strMessage, const string &strHeader, const string &strDefault);
  316. HRESULT HrGetBodyStructure(bool bExtended, string &strBodyStructure, const string& strMessage);
  317. HRESULT HrGetEmailAddress(LPSPropValue lpPropValues, ULONG ulAddrType, ULONG ulEntryID, ULONG ulName, ULONG ulEmail, string strHeaderName, string *strHeaders);
  318. // Make the string uppercase
  319. bool CaseCompare(const string& strA, const string& strB);
  320. // IMAP4rev1 date format: 01-Jan-2000 00:00 +0000
  321. string FileTimeToString(FILETIME sFiletime);
  322. FILETIME StringToFileTime(string strTime, bool bDateOnly = false);
  323. // add 24 hour to the time to be able to check if a time is on a date
  324. FILETIME AddDay(FILETIME sFileTime);
  325. // escape (quote) a unicode string to a specific charset in quoted-printable header format
  326. string EscapeString(WCHAR *input, std::string& charset, bool bIgnore = false);
  327. // escape (quote) a string for a quoted-text (between "")
  328. string EscapeStringQT(const string &str);
  329. // Folder names are in a *modified* utf-7 form. See RFC2060, chapter 5.1.3
  330. HRESULT MAPI2IMAPCharset(const wstring& input, string& output);
  331. HRESULT IMAP2MAPICharset(const string& input, wstring& output);
  332. // Match a folder path
  333. bool MatchFolderPath(wstring strFolder, const wstring& strPattern);
  334. // Various conversion functions
  335. string PropsToFlags(LPSPropValue lpProps, unsigned int cValues, bool bRecent, bool bRead);
  336. void HrParseHeaders(const std::string &, std::list<std::pair<std::string, std::string> > &);
  337. void HrGetSubString(string &strOutput, const std::string &strInput, const std::string &strBegin, const std::string &strEnd);
  338. void HrTokenize(std::set<std::string> &setTokens, const std::string &strInput);
  339. HRESULT HrExpungeDeleted(const string &strTag, const string &strCommand, std::unique_ptr<ECRestriction> &&);
  340. };
  341. /** @} */
  342. #endif