1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "msgCore.h"  // precompiled header...
7 #include "nsMsgImapCID.h"
8 
9 #include "nsImapUrl.h"
10 #include "nsIImapHostSessionList.h"
11 #include "nsThreadUtils.h"
12 #include "nsString.h"
13 #include "prmem.h"
14 #include "plstr.h"
15 #include "prprf.h"
16 #include "nsMemory.h"
17 #include "nsCOMPtr.h"
18 #include "nsMsgBaseCID.h"
19 #include "nsImapUtils.h"
20 #include "nsIImapMockChannel.h"
21 #include "nsIImapMailFolderSink.h"
22 #include "nsIImapMessageSink.h"
23 #include "nsIImapServerSink.h"
24 #include "nsImapNamespace.h"
25 #include "nsICacheEntry.h"
26 #include "nsIMsgFolder.h"
27 #include "nsMsgUtils.h"
28 #include "nsIMsgHdr.h"
29 #include "nsServiceManagerUtils.h"
30 #include "mozilla/Logging.h"
31 
32 using namespace mozilla;
33 extern LazyLogModule IMAPCache;  // defined in nsImapProtocol.cpp
34 
35 static NS_DEFINE_CID(kCImapHostSessionListCID, NS_IIMAPHOSTSESSIONLIST_CID);
36 
nsImapUrl()37 nsImapUrl::nsImapUrl() : mLock("nsImapUrl.mLock") {
38   m_listOfMessageIds = nullptr;
39   m_sourceCanonicalFolderPathSubString = nullptr;
40   m_destinationCanonicalFolderPathSubString = nullptr;
41   m_listOfMessageIds = nullptr;
42   m_tokenPlaceHolder = nullptr;
43   m_searchCriteriaString = nullptr;
44   m_idsAreUids = false;
45   m_mimePartSelectorDetected = false;
46   m_allowContentChange = true;   // assume we can do MPOD.
47   m_fetchPartsOnDemand = false;  // but assume we're not doing it :-)
48   m_msgLoadingFromCache = false;
49   m_storeResultsOffline = false;
50   m_storeOfflineOnFallback = false;
51   m_localFetchOnly = false;
52   m_rerunningUrl = false;
53   m_moreHeadersToDownload = false;
54   m_externalLinkUrl = true;  // we'll start this at true, and set it false in
55                              // nsImapService::CreateStartOfImapUrl
56   m_contentModified = IMAP_CONTENT_NOT_MODIFIED;
57   m_validUrl = true;  // assume the best.
58   m_flags = 0;
59   m_extraStatus = ImapStatusNone;
60   m_onlineSubDirSeparator = '/';
61   m_imapAction = 0;
62   mOverrideCharset = false;
63 
64   // ** jt - the following are not ref counted
65   m_copyState = nullptr;
66   m_file = nullptr;
67   m_imapMailFolderSink = nullptr;
68   m_imapMessageSink = nullptr;
69   m_addDummyEnvelope = false;
70   m_canonicalLineEnding = false;
71 }
72 
~nsImapUrl()73 nsImapUrl::~nsImapUrl() {
74   PR_FREEIF(m_listOfMessageIds);
75   PR_FREEIF(m_destinationCanonicalFolderPathSubString);
76   PR_FREEIF(m_sourceCanonicalFolderPathSubString);
77   PR_FREEIF(m_searchCriteriaString);
78 }
79 
NS_IMPL_ADDREF_INHERITED(nsImapUrl,nsMsgMailNewsUrl)80 NS_IMPL_ADDREF_INHERITED(nsImapUrl, nsMsgMailNewsUrl)
81 
82 NS_IMPL_RELEASE_INHERITED(nsImapUrl, nsMsgMailNewsUrl)
83 
84 NS_INTERFACE_MAP_BEGIN(nsImapUrl)
85   NS_INTERFACE_MAP_ENTRY(nsIImapUrl)
86   NS_INTERFACE_MAP_ENTRY(nsIMsgMessageUrl)
87   NS_INTERFACE_MAP_ENTRY(nsIMsgI18NUrl)
88 NS_INTERFACE_MAP_END_INHERITING(nsMsgMailNewsUrl)
89 
90 ////////////////////////////////////////////////////////////////////////////////////
91 // Begin nsIImapUrl specific support
92 ////////////////////////////////////////////////////////////////////////////////////
93 
94 NS_IMETHODIMP nsImapUrl::GetRequiredImapState(nsImapState* aImapUrlState) {
95   if (aImapUrlState) {
96     // the imap action determines the state we must be in...check the
97     // the imap action.
98 
99     if (m_imapAction & 0x10000000)
100       *aImapUrlState = nsImapSelectedState;
101     else
102       *aImapUrlState = nsImapAuthenticatedState;
103   }
104 
105   return NS_OK;
106 }
107 
GetImapAction(nsImapAction * aImapAction)108 NS_IMETHODIMP nsImapUrl::GetImapAction(nsImapAction* aImapAction) {
109   *aImapAction = m_imapAction;
110   return NS_OK;
111 }
112 
SetImapAction(nsImapAction aImapAction)113 NS_IMETHODIMP nsImapUrl::SetImapAction(nsImapAction aImapAction) {
114   m_imapAction = aImapAction;
115   return NS_OK;
116 }
117 
GetFolder(nsIMsgFolder ** aMsgFolder)118 NS_IMETHODIMP nsImapUrl::GetFolder(nsIMsgFolder** aMsgFolder) {
119   NS_ENSURE_ARG_POINTER(aMsgFolder);
120   NS_ENSURE_ARG_POINTER(m_imapFolder);
121 
122   nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(m_imapFolder);
123   folder.forget(aMsgFolder);
124   return NS_OK;
125 }
126 
SetFolder(nsIMsgFolder * aMsgFolder)127 NS_IMETHODIMP nsImapUrl::SetFolder(nsIMsgFolder* aMsgFolder) {
128   nsresult rv;
129   m_imapFolder = do_GetWeakReference(aMsgFolder, &rv);
130   if (aMsgFolder) {
131     nsCOMPtr<nsIMsgIncomingServer> incomingServer;
132     aMsgFolder->GetServer(getter_AddRefs(incomingServer));
133     if (incomingServer) incomingServer->GetKey(m_serverKey);
134   }
135   return rv;
136 }
137 
GetImapMailFolderSink(nsIImapMailFolderSink ** aImapMailFolderSink)138 NS_IMETHODIMP nsImapUrl::GetImapMailFolderSink(
139     nsIImapMailFolderSink** aImapMailFolderSink) {
140   NS_ENSURE_ARG_POINTER(aImapMailFolderSink);
141   if (!m_imapMailFolderSink)
142     return NS_ERROR_NULL_POINTER;  // no assert, so don't use NS_ENSURE_POINTER.
143 
144   nsCOMPtr<nsIImapMailFolderSink> folderSink =
145       do_QueryReferent(m_imapMailFolderSink);
146   folderSink.forget(aImapMailFolderSink);
147   return NS_OK;
148 }
149 
SetImapMailFolderSink(nsIImapMailFolderSink * aImapMailFolderSink)150 NS_IMETHODIMP nsImapUrl::SetImapMailFolderSink(
151     nsIImapMailFolderSink* aImapMailFolderSink) {
152   nsresult rv;
153   m_imapMailFolderSink = do_GetWeakReference(aImapMailFolderSink, &rv);
154   return rv;
155 }
156 
GetImapMessageSink(nsIImapMessageSink ** aImapMessageSink)157 NS_IMETHODIMP nsImapUrl::GetImapMessageSink(
158     nsIImapMessageSink** aImapMessageSink) {
159   NS_ENSURE_ARG_POINTER(aImapMessageSink);
160   NS_ENSURE_ARG_POINTER(m_imapMessageSink);
161 
162   nsCOMPtr<nsIImapMessageSink> messageSink =
163       do_QueryReferent(m_imapMessageSink);
164   messageSink.forget(aImapMessageSink);
165   return NS_OK;
166 }
167 
SetImapMessageSink(nsIImapMessageSink * aImapMessageSink)168 NS_IMETHODIMP nsImapUrl::SetImapMessageSink(
169     nsIImapMessageSink* aImapMessageSink) {
170   nsresult rv;
171   m_imapMessageSink = do_GetWeakReference(aImapMessageSink, &rv);
172   return rv;
173 }
174 
GetImapServerSink(nsIImapServerSink ** aImapServerSink)175 NS_IMETHODIMP nsImapUrl::GetImapServerSink(
176     nsIImapServerSink** aImapServerSink) {
177   NS_ENSURE_ARG_POINTER(aImapServerSink);
178   NS_ENSURE_ARG_POINTER(m_imapServerSink);
179 
180   nsCOMPtr<nsIImapServerSink> serverSink = do_QueryReferent(m_imapServerSink);
181   serverSink.forget(aImapServerSink);
182   return NS_OK;
183 }
184 
SetImapServerSink(nsIImapServerSink * aImapServerSink)185 NS_IMETHODIMP nsImapUrl::SetImapServerSink(nsIImapServerSink* aImapServerSink) {
186   nsresult rv;
187   m_imapServerSink = do_GetWeakReference(aImapServerSink, &rv);
188   return rv;
189 }
190 
191 ////////////////////////////////////////////////////////////////////////////////////
192 // End nsIImapUrl specific support
193 ////////////////////////////////////////////////////////////////////////////////////
194 
SetSpecInternal(const nsACString & aSpec)195 nsresult nsImapUrl::SetSpecInternal(const nsACString& aSpec) {
196   nsresult rv = nsMsgMailNewsUrl::SetSpecInternal(aSpec);
197   if (NS_SUCCEEDED(rv)) {
198     m_validUrl = true;  // assume the best.
199     rv = ParseUrl();
200   }
201   return rv;
202 }
203 
SetQuery(const nsACString & aQuery)204 nsresult nsImapUrl::SetQuery(const nsACString& aQuery) {
205   nsresult rv = nsMsgMailNewsUrl::SetQuery(aQuery);
206   if (NS_SUCCEEDED(rv)) rv = ParseUrl();
207   return rv;
208 }
209 
ParseUrl()210 nsresult nsImapUrl::ParseUrl() {
211   nsresult rv = NS_OK;
212   // extract the user name
213   GetUserPass(m_userName);
214 
215   nsAutoCString imapPartOfUrl;
216   rv = GetPathQueryRef(imapPartOfUrl);
217   nsAutoCString unescapedImapPartOfUrl;
218   MsgUnescapeString(imapPartOfUrl, 0, unescapedImapPartOfUrl);
219   if (NS_SUCCEEDED(rv) && !unescapedImapPartOfUrl.IsEmpty()) {
220     ParseImapPart(unescapedImapPartOfUrl.BeginWriting() +
221                   1);  // GetPath leaves leading '/' in the path!!!
222   }
223 
224   return NS_OK;
225 }
226 
CreateSearchCriteriaString(char ** aResult)227 NS_IMETHODIMP nsImapUrl::CreateSearchCriteriaString(char** aResult) {
228   // this method should only be called from the imap thread...
229   // o.t. add lock protection..
230   if (nullptr == aResult || !m_searchCriteriaString)
231     return NS_ERROR_NULL_POINTER;
232   *aResult = strdup(m_searchCriteriaString);
233   return NS_OK;
234 }
235 
236 // this method gets called from the UI thread and the imap thread
GetListOfMessageIds(nsACString & aResult)237 NS_IMETHODIMP nsImapUrl::GetListOfMessageIds(nsACString& aResult) {
238   MutexAutoLock mon(mLock);
239   if (!m_listOfMessageIds) return NS_ERROR_NULL_POINTER;
240 
241   int32_t bytesToCopy = strlen(m_listOfMessageIds);
242 
243   // mime may have glommed a "&part=" for a part download
244   // we return the entire message and let mime extract
245   // the part. Pop and news work this way also.
246   // this algorithm truncates the "&part" string.
247   char* currentChar = m_listOfMessageIds;
248   while (*currentChar && (*currentChar != '?')) currentChar++;
249   if (*currentChar == '?') bytesToCopy = currentChar - m_listOfMessageIds;
250 
251   // we should also strip off anything after "/;section="
252   // since that can specify an IMAP MIME part
253   char* wherePart = PL_strstr(m_listOfMessageIds, "/;section=");
254   if (wherePart)
255     bytesToCopy =
256         std::min(bytesToCopy, int32_t(wherePart - m_listOfMessageIds));
257 
258   aResult.Assign(m_listOfMessageIds, bytesToCopy);
259   return NS_OK;
260 }
261 
GetCommand(nsACString & result)262 NS_IMETHODIMP nsImapUrl::GetCommand(nsACString& result) {
263   result = m_command;
264   return NS_OK;
265 }
266 
GetCustomAttributeToFetch(nsACString & result)267 NS_IMETHODIMP nsImapUrl::GetCustomAttributeToFetch(nsACString& result) {
268   result = m_msgFetchAttribute;
269   return NS_OK;
270 }
271 
GetCustomAttributeResult(nsACString & result)272 NS_IMETHODIMP nsImapUrl::GetCustomAttributeResult(nsACString& result) {
273   result = m_customAttributeResult;
274   return NS_OK;
275 }
276 
SetCustomAttributeResult(const nsACString & result)277 NS_IMETHODIMP nsImapUrl::SetCustomAttributeResult(const nsACString& result) {
278   m_customAttributeResult = result;
279   return NS_OK;
280 }
281 
GetCustomCommandResult(nsACString & result)282 NS_IMETHODIMP nsImapUrl::GetCustomCommandResult(nsACString& result) {
283   result = m_customCommandResult;
284   return NS_OK;
285 }
286 
SetCustomCommandResult(const nsACString & result)287 NS_IMETHODIMP nsImapUrl::SetCustomCommandResult(const nsACString& result) {
288   m_customCommandResult = result;
289   return NS_OK;
290 }
291 
GetCustomAddFlags(nsACString & aResult)292 NS_IMETHODIMP nsImapUrl::GetCustomAddFlags(nsACString& aResult) {
293   aResult = m_customAddFlags;
294   return NS_OK;
295 }
296 
GetCustomSubtractFlags(nsACString & aResult)297 NS_IMETHODIMP nsImapUrl::GetCustomSubtractFlags(nsACString& aResult) {
298   aResult = m_customSubtractFlags;
299   return NS_OK;
300 }
301 
GetImapPartToFetch(char ** result)302 NS_IMETHODIMP nsImapUrl::GetImapPartToFetch(char** result) {
303   //  here's the old code....
304 
305   // unfortunately an imap part can have the form: /;section= OR
306   // it can have the form ?section=. We need to look for both.
307   if (m_listOfMessageIds) {
308     char* wherepart = PL_strstr(m_listOfMessageIds, ";section=");
309     if (!wherepart)  // look for ?section too....
310       wherepart = PL_strstr(m_listOfMessageIds, "?section=");
311     if (wherepart) {
312       wherepart += 9;  // strlen("/;section=")
313       char* wherelibmimepart = PL_strstr(wherepart, "&part=");
314       if (!wherelibmimepart) wherelibmimepart = PL_strstr(wherepart, "?part=");
315       int numCharsToCopy = (wherelibmimepart)
316                                ? wherelibmimepart - wherepart
317                                : PL_strlen(m_listOfMessageIds) -
318                                      (wherepart - m_listOfMessageIds);
319       if (numCharsToCopy) {
320         *result = (char*)PR_Malloc(sizeof(char) * (numCharsToCopy + 1));
321         if (*result) {
322           PL_strncpy(*result, wherepart, numCharsToCopy + 1);  // appends a \0
323           (*result)[numCharsToCopy] = '\0';
324         }
325       }
326     }  // if we got a wherepart
327   }    // if we got a m_listOfMessageIds
328   return NS_OK;
329 }
330 
GetOnlineSubDirSeparator(char * separator)331 NS_IMETHODIMP nsImapUrl::GetOnlineSubDirSeparator(char* separator) {
332   if (separator) {
333     *separator = m_onlineSubDirSeparator;
334     return NS_OK;
335   }
336   return NS_ERROR_NULL_POINTER;
337 }
338 
GetNumBytesToFetch(int32_t * aNumBytesToFetch)339 NS_IMETHODIMP nsImapUrl::GetNumBytesToFetch(int32_t* aNumBytesToFetch) {
340   NS_ENSURE_ARG_POINTER(aNumBytesToFetch);
341   *aNumBytesToFetch = m_numBytesToFetch;
342   return NS_OK;
343 }
344 
345 NS_IMETHODIMP
SetOnlineSubDirSeparator(char onlineDirSeparator)346 nsImapUrl::SetOnlineSubDirSeparator(char onlineDirSeparator) {
347   m_onlineSubDirSeparator = onlineDirSeparator;
348   return NS_OK;
349 }
350 
351 // this method is only called from the imap thread
MessageIdsAreUids(bool * result)352 NS_IMETHODIMP nsImapUrl::MessageIdsAreUids(bool* result) {
353   *result = m_idsAreUids;
354   return NS_OK;
355 }
356 
357 NS_IMETHODIMP
SetExtraStatus(int32_t aExtraStatus)358 nsImapUrl::SetExtraStatus(int32_t aExtraStatus) {
359   m_extraStatus = aExtraStatus;
360   return NS_OK;
361 }
362 
GetExtraStatus(int32_t * aResult)363 NS_IMETHODIMP nsImapUrl::GetExtraStatus(int32_t* aResult) {
364   NS_ENSURE_ARG_POINTER(aResult);
365   *aResult = m_extraStatus;
366   return NS_OK;
367 }
368 
369 // this method is only called from the imap thread
GetMsgFlags(imapMessageFlagsType * result)370 NS_IMETHODIMP nsImapUrl::GetMsgFlags(
371     imapMessageFlagsType* result)  // kAddMsgFlags or kSubtractMsgFlags only
372 {
373   *result = m_flags;
374   return NS_OK;
375 }
376 
ParseImapPart(char * imapPartOfUrl)377 void nsImapUrl::ParseImapPart(char* imapPartOfUrl) {
378   m_tokenPlaceHolder = imapPartOfUrl;
379   m_urlidSubString = m_tokenPlaceHolder ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR,
380                                                     &m_tokenPlaceHolder)
381                                         : (char*)NULL;
382 
383   if (!m_urlidSubString) {
384     m_validUrl = false;
385     return;
386   }
387 
388   if (!PL_strcasecmp(m_urlidSubString, "fetch")) {
389     m_imapAction = nsImapMsgFetch;
390     ParseUidChoice();
391     PR_FREEIF(m_sourceCanonicalFolderPathSubString);
392     ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
393     ParseListOfMessageIds();
394     // if fetched by spam filter, the action will be changed to
395     // nsImapMsgFetchPeek
396   } else {
397     if (!PL_strcasecmp(m_urlidSubString, "header")) {
398       m_imapAction = nsImapMsgHeader;
399       ParseUidChoice();
400       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
401       ParseListOfMessageIds();
402     } else if (!PL_strcasecmp(m_urlidSubString, "customFetch")) {
403       ParseUidChoice();
404       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
405       ParseListOfMessageIds();
406       ParseCustomMsgFetchAttribute();
407     } else if (!PL_strcasecmp(m_urlidSubString, "previewBody")) {
408       ParseUidChoice();
409       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
410       ParseListOfMessageIds();
411       ParseNumBytes();
412     } else if (!PL_strcasecmp(m_urlidSubString, "deletemsg")) {
413       m_imapAction = nsImapDeleteMsg;
414       ParseUidChoice();
415       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
416       ParseListOfMessageIds();
417     } else if (!PL_strcasecmp(m_urlidSubString, "uidexpunge")) {
418       m_imapAction = nsImapUidExpunge;
419       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
420       ParseListOfMessageIds();
421     } else if (!PL_strcasecmp(m_urlidSubString, "deleteallmsgs")) {
422       m_imapAction = nsImapDeleteAllMsgs;
423       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
424     } else if (!PL_strcasecmp(m_urlidSubString, "addmsgflags")) {
425       m_imapAction = nsImapAddMsgFlags;
426       ParseUidChoice();
427       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
428       ParseListOfMessageIds();
429       ParseMsgFlags();
430     } else if (!PL_strcasecmp(m_urlidSubString, "subtractmsgflags")) {
431       m_imapAction = nsImapSubtractMsgFlags;
432       ParseUidChoice();
433       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
434       ParseListOfMessageIds();
435       ParseMsgFlags();
436     } else if (!PL_strcasecmp(m_urlidSubString, "setmsgflags")) {
437       m_imapAction = nsImapSetMsgFlags;
438       ParseUidChoice();
439       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
440       ParseListOfMessageIds();
441       ParseMsgFlags();
442     } else if (!PL_strcasecmp(m_urlidSubString, "onlinecopy")) {
443       m_imapAction = nsImapOnlineCopy;
444       ParseUidChoice();
445       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
446       ParseListOfMessageIds();
447       ParseFolderPath(&m_destinationCanonicalFolderPathSubString);
448     } else if (!PL_strcasecmp(m_urlidSubString, "onlinemove")) {
449       m_imapAction = nsImapOnlineMove;
450       ParseUidChoice();
451       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
452       ParseListOfMessageIds();
453       ParseFolderPath(&m_destinationCanonicalFolderPathSubString);
454     } else if (!PL_strcasecmp(m_urlidSubString, "onlinetoofflinecopy")) {
455       m_imapAction = nsImapOnlineToOfflineCopy;
456       ParseUidChoice();
457       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
458       ParseListOfMessageIds();
459       ParseFolderPath(&m_destinationCanonicalFolderPathSubString);
460     } else if (!PL_strcasecmp(m_urlidSubString, "onlinetoofflinemove")) {
461       m_imapAction = nsImapOnlineToOfflineMove;
462       ParseUidChoice();
463       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
464       ParseListOfMessageIds();
465       ParseFolderPath(&m_destinationCanonicalFolderPathSubString);
466     } else if (!PL_strcasecmp(m_urlidSubString, "offlinetoonlinecopy")) {
467       m_imapAction = nsImapOfflineToOnlineMove;
468       ParseFolderPath(&m_destinationCanonicalFolderPathSubString);
469     } else if (!PL_strcasecmp(m_urlidSubString, "search")) {
470       m_imapAction = nsImapSearch;
471       ParseUidChoice();
472       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
473       ParseSearchCriteriaString();
474     } else if (!PL_strcasecmp(m_urlidSubString, "test")) {
475       m_imapAction = nsImapTest;
476     } else if (!PL_strcasecmp(m_urlidSubString, "select")) {
477       m_imapAction = nsImapSelectFolder;
478       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
479       if (m_tokenPlaceHolder && *m_tokenPlaceHolder)
480         ParseListOfMessageIds();
481       else
482         m_listOfMessageIds = PL_strdup("");
483     } else if (!PL_strcasecmp(m_urlidSubString, "liteselect")) {
484       m_imapAction = nsImapLiteSelectFolder;
485       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
486     } else if (!PL_strcasecmp(m_urlidSubString, "selectnoop")) {
487       m_imapAction = nsImapSelectNoopFolder;
488       m_listOfMessageIds = PL_strdup("");
489       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
490     } else if (!PL_strcasecmp(m_urlidSubString, "expunge")) {
491       m_imapAction = nsImapExpungeFolder;
492       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
493       m_listOfMessageIds = PL_strdup("");  // no ids to UNDO
494     } else if (!PL_strcasecmp(m_urlidSubString, "create")) {
495       m_imapAction = nsImapCreateFolder;
496       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
497     } else if (!PL_strcasecmp(m_urlidSubString, "ensureExists")) {
498       m_imapAction = nsImapEnsureExistsFolder;
499       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
500     } else if (!PL_strcasecmp(m_urlidSubString, "discoverchildren")) {
501       m_imapAction = nsImapDiscoverChildrenUrl;
502       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
503     } else if (!PL_strcasecmp(m_urlidSubString, "discoverallboxes")) {
504       m_imapAction = nsImapDiscoverAllBoxesUrl;
505     } else if (!PL_strcasecmp(m_urlidSubString,
506                               "discoverallandsubscribedboxes")) {
507       m_imapAction = nsImapDiscoverAllAndSubscribedBoxesUrl;
508     } else if (!PL_strcasecmp(m_urlidSubString, "delete")) {
509       m_imapAction = nsImapDeleteFolder;
510       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
511     } else if (!PL_strcasecmp(m_urlidSubString, "deletefolder")) {
512       m_imapAction = nsImapDeleteFolderAndMsgs;
513       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
514     } else if (!PL_strcasecmp(m_urlidSubString, "rename")) {
515       m_imapAction = nsImapRenameFolder;
516       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
517       ParseFolderPath(&m_destinationCanonicalFolderPathSubString);
518     } else if (!PL_strcasecmp(m_urlidSubString, "movefolderhierarchy")) {
519       m_imapAction = nsImapMoveFolderHierarchy;
520       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
521       if (m_tokenPlaceHolder && *m_tokenPlaceHolder)  // handle promote to root
522         ParseFolderPath(&m_destinationCanonicalFolderPathSubString);
523     } else if (!PL_strcasecmp(m_urlidSubString, "list")) {
524       m_imapAction = nsImapLsubFolders;
525       ParseFolderPath(&m_destinationCanonicalFolderPathSubString);
526     } else if (!PL_strcasecmp(m_urlidSubString, "biff")) {
527       m_imapAction = nsImapBiff;
528       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
529       ParseListOfMessageIds();
530     } else if (!PL_strcasecmp(m_urlidSubString, "netscape")) {
531       m_imapAction = nsImapGetMailAccountUrl;
532     } else if (!PL_strcasecmp(m_urlidSubString, "appendmsgfromfile")) {
533       m_imapAction = nsImapAppendMsgFromFile;
534       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
535     } else if (!PL_strcasecmp(m_urlidSubString, "appenddraftfromfile")) {
536       m_imapAction = nsImapAppendDraftFromFile;
537       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
538       ParseUidChoice();
539       if (m_tokenPlaceHolder && *m_tokenPlaceHolder)
540         ParseListOfMessageIds();
541       else
542         m_listOfMessageIds = strdup("");
543     } else if (!PL_strcasecmp(m_urlidSubString, "subscribe")) {
544       m_imapAction = nsImapSubscribe;
545       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
546     } else if (!PL_strcasecmp(m_urlidSubString, "unsubscribe")) {
547       m_imapAction = nsImapUnsubscribe;
548       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
549     } else if (!PL_strcasecmp(m_urlidSubString, "refreshacl")) {
550       m_imapAction = nsImapRefreshACL;
551       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
552     } else if (!PL_strcasecmp(m_urlidSubString, "refreshfolderurls")) {
553       m_imapAction = nsImapRefreshFolderUrls;
554       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
555     } else if (!PL_strcasecmp(m_urlidSubString, "refreshallacls")) {
556       m_imapAction = nsImapRefreshAllACLs;
557     } else if (!PL_strcasecmp(m_urlidSubString, "listfolder")) {
558       m_imapAction = nsImapListFolder;
559       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
560     } else if (!PL_strcasecmp(m_urlidSubString, "upgradetosubscription")) {
561       m_imapAction = nsImapUpgradeToSubscription;
562       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
563     } else if (!PL_strcasecmp(m_urlidSubString, "folderstatus")) {
564       m_imapAction = nsImapFolderStatus;
565       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
566     } else if (!PL_strcasecmp(m_urlidSubString, "verifyLogon")) {
567       m_imapAction = nsImapVerifylogon;
568     } else if (m_imapAction == nsIImapUrl::nsImapUserDefinedMsgCommand) {
569       m_command = m_urlidSubString;  // save this
570       ParseUidChoice();
571       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
572       ParseListOfMessageIds();
573     } else if (m_imapAction == nsIImapUrl::nsImapMsgStoreCustomKeywords) {
574       ParseUidChoice();
575       ParseFolderPath(&m_sourceCanonicalFolderPathSubString);
576       ParseListOfMessageIds();
577       bool addKeyword = (m_tokenPlaceHolder && *m_tokenPlaceHolder != '>');
578       // if we're not adding a keyword, m_tokenPlaceHolder will now look like
579       // >keywordToSubtract> and strtok will leave flagsPtr pointing to
580       // keywordToSubtract. So detect this case and only set the
581       // customSubtractFlags.
582       char* flagsPtr = m_tokenPlaceHolder ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR,
583                                                       &m_tokenPlaceHolder)
584                                           : (char*)nullptr;
585       if (addKeyword) {
586         m_customAddFlags.Assign(flagsPtr);
587         flagsPtr = m_tokenPlaceHolder ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR,
588                                                   &m_tokenPlaceHolder)
589                                       : (char*)nullptr;
590       }
591       m_customSubtractFlags.Assign(flagsPtr);
592     } else {
593       m_validUrl = false;
594     }
595   }
596 }
597 
598 // Returns NULL if nothing was done.
599 // Otherwise, returns a newly allocated name.
AddOnlineDirectoryIfNecessary(const char * onlineMailboxName,char ** directory)600 NS_IMETHODIMP nsImapUrl::AddOnlineDirectoryIfNecessary(
601     const char* onlineMailboxName, char** directory) {
602   nsresult rv;
603   nsString onlineDirString;
604   char* newOnlineName = nullptr;
605 
606   nsCOMPtr<nsIImapHostSessionList> hostSessionList =
607       do_GetService(kCImapHostSessionListCID, &rv);
608   if (NS_FAILED(rv)) return rv;
609   rv = hostSessionList->GetOnlineDirForHost(m_serverKey.get(), onlineDirString);
610   nsAutoCString onlineDir;
611   LossyCopyUTF16toASCII(onlineDirString, onlineDir);
612 
613   nsImapNamespace* ns = nullptr;
614   rv = hostSessionList->GetNamespaceForMailboxForHost(m_serverKey.get(),
615                                                       onlineMailboxName, ns);
616   if (!ns)
617     hostSessionList->GetDefaultNamespaceOfTypeForHost(m_serverKey.get(),
618                                                       kPersonalNamespace, ns);
619 
620   if (onlineDir.IsEmpty() && ns) onlineDir = ns->GetPrefix();
621 
622   // If this host has an online server directory configured
623   if (onlineMailboxName && !onlineDir.IsEmpty()) {
624     if (PL_strcasecmp(onlineMailboxName, "INBOX")) {
625       NS_ASSERTION(ns, "couldn't find namespace for host");
626       nsAutoCString onlineDirWithDelimiter(onlineDir);
627       // make sure the onlineDir ends with the hierarchy delimiter
628       if (ns) {
629         char delimiter = ns->GetDelimiter();
630         if (delimiter && delimiter != kOnlineHierarchySeparatorUnknown) {
631           // try to change the canonical online dir name to real dir name first
632           onlineDirWithDelimiter.ReplaceChar('/', delimiter);
633           // make sure the last character is the delimiter
634           if (onlineDirWithDelimiter.Last() != delimiter)
635             onlineDirWithDelimiter += delimiter;
636           if (!*onlineMailboxName)
637             onlineDirWithDelimiter.SetLength(onlineDirWithDelimiter.Length() -
638                                              1);
639         }
640       }
641       if (ns && (PL_strlen(ns->GetPrefix()) != 0) &&
642           !onlineDirWithDelimiter.Equals(ns->GetPrefix())) {
643         // check that onlineMailboxName doesn't start with the namespace. If
644         // that's the case, we don't want to prepend the online dir.
645         if (PL_strncmp(onlineMailboxName, ns->GetPrefix(),
646                        PL_strlen(ns->GetPrefix()))) {
647           // The namespace for this mailbox is the root ("").
648           // Prepend the online server directory
649           int finalLen =
650               onlineDirWithDelimiter.Length() + strlen(onlineMailboxName) + 1;
651           newOnlineName = (char*)PR_Malloc(finalLen);
652           if (newOnlineName) {
653             PL_strcpy(newOnlineName, onlineDirWithDelimiter.get());
654             PL_strcat(newOnlineName, onlineMailboxName);
655           }
656         }
657       }
658       // just prepend the online server directory if it doesn't start with it
659       // already
660       else if (strncmp(onlineMailboxName, onlineDirWithDelimiter.get(),
661                        onlineDirWithDelimiter.Length())) {
662         newOnlineName = (char*)PR_Malloc(strlen(onlineMailboxName) +
663                                          onlineDirWithDelimiter.Length() + 1);
664         if (newOnlineName) {
665           PL_strcpy(newOnlineName, onlineDirWithDelimiter.get());
666           PL_strcat(newOnlineName, onlineMailboxName);
667         }
668       }
669     }
670   }
671   if (directory)
672     *directory = newOnlineName;
673   else if (newOnlineName)
674     free(newOnlineName);
675   return rv;
676 }
677 
678 // Converts from canonical format (hierarchy is indicated by '/' and all real
679 // slashes ('/') are escaped) to the real online name on the server.
AllocateServerPath(const char * canonicalPath,char onlineDelimiter,char ** aAllocatedPath)680 NS_IMETHODIMP nsImapUrl::AllocateServerPath(const char* canonicalPath,
681                                             char onlineDelimiter,
682                                             char** aAllocatedPath) {
683   nsresult retVal = NS_OK;
684   char* rv = NULL;
685   char delimiterToUse = onlineDelimiter;
686   if (onlineDelimiter == kOnlineHierarchySeparatorUnknown)
687     GetOnlineSubDirSeparator(&delimiterToUse);
688   NS_ASSERTION(delimiterToUse != kOnlineHierarchySeparatorUnknown,
689                "hierarchy separator unknown");
690   if (canonicalPath)
691     rv = ReplaceCharsInCopiedString(canonicalPath, '/', delimiterToUse);
692   else
693     rv = strdup("");
694 
695   if (delimiterToUse != '/') UnescapeSlashes(rv);
696   char* onlineNameAdded = nullptr;
697   AddOnlineDirectoryIfNecessary(rv, &onlineNameAdded);
698   if (onlineNameAdded) {
699     free(rv);
700     rv = onlineNameAdded;
701   }
702 
703   if (aAllocatedPath)
704     *aAllocatedPath = rv;
705   else
706     free(rv);
707 
708   return retVal;
709 }
710 
711 // escape '/' as ^, ^ -> ^^ - use UnescapeSlashes to revert
EscapeSlashes(const char * sourcePath,char ** resultPath)712 /* static */ nsresult nsImapUrl::EscapeSlashes(const char* sourcePath,
713                                                char** resultPath) {
714   NS_ENSURE_ARG(sourcePath);
715   NS_ENSURE_ARG(resultPath);
716   int32_t extra = 0;
717   int32_t len = strlen(sourcePath);
718   const char* src = sourcePath;
719   int32_t i;
720   for (i = 0; i < len; i++) {
721     if (*src == '^') extra += 1; /* ^ -> ^^ */
722     src++;
723   }
724   char* result = (char*)moz_xmalloc(len + extra + 1);
725   if (!result) return NS_ERROR_OUT_OF_MEMORY;
726 
727   unsigned char* dst = (unsigned char*)result;
728   src = sourcePath;
729   for (i = 0; i < len; i++) {
730     unsigned char c = *src++;
731     if (c == '/')
732       *dst++ = '^';
733     else if (c == '^') {
734       *dst++ = '^';
735       *dst++ = '^';
736     } else
737       *dst++ = c;
738   }
739   *dst = '\0'; /* tack on eos */
740   *resultPath = result;
741   return NS_OK;
742 }
743 
unescapeSlashes(char * path,size_t * newLength)744 static void unescapeSlashes(char* path, size_t* newLength) {
745   char* src = path;
746   char* start = src;
747   char* dst = path;
748 
749   while (*src) {
750     if (*src == '^') {
751       if (*(src + 1) == '^') {
752         *dst++ = '^';
753         src++;  // skip over second '^'
754       } else
755         *dst++ = '/';
756       src++;
757     } else
758       *dst++ = *src++;
759   }
760 
761   *newLength = dst - start;
762 }
763 
UnescapeSlashes(char * path)764 /* static */ nsresult nsImapUrl::UnescapeSlashes(char* path) {
765   size_t newLength;
766   unescapeSlashes(path, &newLength);
767   path[newLength] = 0;
768   return NS_OK;
769 }
770 
UnescapeSlashes(nsACString & path)771 /* static */ nsresult nsImapUrl::UnescapeSlashes(nsACString& path) {
772   size_t newLength;
773   unescapeSlashes(path.BeginWriting(), &newLength);
774   path.SetLength(newLength);
775   return NS_OK;
776 }
777 
ConvertToCanonicalFormat(const char * folderName,char onlineDelimiter,char ** resultingCanonicalPath)778 /*  static */ nsresult nsImapUrl::ConvertToCanonicalFormat(
779     const char* folderName, char onlineDelimiter,
780     char** resultingCanonicalPath) {
781   // Now, start the conversion to canonical form.
782 
783   char* canonicalPath;
784   if (onlineDelimiter != '/') {
785     nsCString escapedPath;
786 
787     EscapeSlashes(folderName, getter_Copies(escapedPath));
788     canonicalPath =
789         ReplaceCharsInCopiedString(escapedPath.get(), onlineDelimiter, '/');
790   } else {
791     canonicalPath = strdup(folderName);
792   }
793   if (canonicalPath) *resultingCanonicalPath = canonicalPath;
794 
795   return (canonicalPath) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
796 }
797 
798 // Converts the real online name on the server to canonical format:
799 // result is hierarchy is indicated by '/' and all real slashes ('/') are
800 // escaped. This method is only called from the IMAP thread.
AllocateCanonicalPath(const char * serverPath,char onlineDelimiter,char ** allocatedPath)801 NS_IMETHODIMP nsImapUrl::AllocateCanonicalPath(const char* serverPath,
802                                                char onlineDelimiter,
803                                                char** allocatedPath) {
804   nsresult rv = NS_ERROR_NULL_POINTER;
805   char delimiterToUse = onlineDelimiter;
806   char* serverKey = nullptr;
807   nsString aString;
808   char* currentPath = (char*)serverPath;
809   nsAutoCString onlineDir;
810   nsCOMPtr<nsIMsgIncomingServer> server;
811 
812   nsCOMPtr<nsIImapHostSessionList> hostSessionList =
813       do_GetService(kCImapHostSessionListCID, &rv);
814 
815   *allocatedPath = nullptr;
816 
817   if (onlineDelimiter == kOnlineHierarchySeparatorUnknown ||
818       onlineDelimiter == 0)
819     GetOnlineSubDirSeparator(&delimiterToUse);
820 
821   NS_ASSERTION(serverPath, "Oops... null serverPath");
822 
823   if (!serverPath || NS_FAILED(rv)) goto done;
824 
825   hostSessionList->GetOnlineDirForHost(m_serverKey.get(), aString);
826   // First we have to check to see if we should strip off an online server
827   // subdirectory
828   // If this host has an online server directory configured
829   LossyCopyUTF16toASCII(aString, onlineDir);
830 
831   if (currentPath && !onlineDir.IsEmpty()) {
832     // By definition, the online dir must be at the root.
833     if (delimiterToUse && delimiterToUse != kOnlineHierarchySeparatorUnknown) {
834       // try to change the canonical online dir name to real dir name first
835       onlineDir.ReplaceChar('/', delimiterToUse);
836       // Add the delimiter
837       if (onlineDir.Last() != delimiterToUse) onlineDir += delimiterToUse;
838     }
839     int len = onlineDir.Length();
840     if (!PL_strncmp(onlineDir.get(), currentPath, len)) {
841       // This online path begins with the server sub directory
842       currentPath += len;
843 
844       // This might occur, but it's most likely something not good.
845       // Basically, it means we're doing something on the online sub directory
846       // itself.
847       NS_ASSERTION(*currentPath, "Oops ... null currentPath");
848       // Also make sure that the first character in the mailbox name is not '/'.
849       NS_ASSERTION(*currentPath != '/',
850                    "Oops ... currentPath starts with a slash");
851     }
852   }
853 
854   if (!currentPath) goto done;
855 
856   rv = ConvertToCanonicalFormat(currentPath, delimiterToUse, allocatedPath);
857 
858 done:
859   PR_Free(serverKey);
860   return rv;
861 }
862 
863 // this method is only called from the imap thread
CreateServerSourceFolderPathString(char ** result)864 NS_IMETHODIMP nsImapUrl::CreateServerSourceFolderPathString(char** result) {
865   NS_ENSURE_ARG_POINTER(result);
866   AllocateServerPath(m_sourceCanonicalFolderPathSubString,
867                      kOnlineHierarchySeparatorUnknown, result);
868   return NS_OK;
869 }
870 
871 // this method is called from the imap thread AND the UI thread...
CreateCanonicalSourceFolderPathString(char ** result)872 NS_IMETHODIMP nsImapUrl::CreateCanonicalSourceFolderPathString(char** result) {
873   NS_ENSURE_ARG_POINTER(result);
874   MutexAutoLock mon(mLock);
875   *result = strdup(m_sourceCanonicalFolderPathSubString
876                        ? m_sourceCanonicalFolderPathSubString
877                        : "");
878   return (*result) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
879 }
880 
881 // this method is called from the imap thread AND the UI thread...
CreateServerDestinationFolderPathString(char ** result)882 NS_IMETHODIMP nsImapUrl::CreateServerDestinationFolderPathString(
883     char** result) {
884   NS_ENSURE_ARG_POINTER(result);
885   MutexAutoLock mon(mLock);
886   nsresult rv = AllocateServerPath(m_destinationCanonicalFolderPathSubString,
887                                    kOnlineHierarchySeparatorUnknown, result);
888   return (*result) ? rv : NS_ERROR_OUT_OF_MEMORY;
889 }
890 
891 // for enabling or disabling mime parts on demand. Setting this to true says we
892 // can use mime parts on demand, if we chose.
SetAllowContentChange(bool allowContentChange)893 NS_IMETHODIMP nsImapUrl::SetAllowContentChange(bool allowContentChange) {
894   m_allowContentChange = allowContentChange;
895   return NS_OK;
896 }
897 
SetContentModified(nsImapContentModifiedType contentModified)898 NS_IMETHODIMP nsImapUrl::SetContentModified(
899     nsImapContentModifiedType contentModified) {
900   m_contentModified = contentModified;
901   nsCOMPtr<nsICacheEntry> cacheEntry;
902   nsresult res = GetMemCacheEntry(getter_AddRefs(cacheEntry));
903   if (NS_SUCCEEDED(res) && cacheEntry) {
904     const char* contentModifiedAnnotation = "";
905     switch (m_contentModified) {
906       case IMAP_CONTENT_NOT_MODIFIED:
907         contentModifiedAnnotation = "Not Modified";
908         break;
909       case IMAP_CONTENT_MODIFIED_VIEW_INLINE:
910         contentModifiedAnnotation = "Modified View Inline";
911         break;
912       case IMAP_CONTENT_MODIFIED_VIEW_AS_LINKS:
913         contentModifiedAnnotation = "Modified View As Link";
914         break;
915       case IMAP_CONTENT_FORCE_CONTENT_NOT_MODIFIED:
916         contentModifiedAnnotation = "Force Content Not Modified";
917         break;
918     }
919     MOZ_LOG(IMAPCache, LogLevel::Debug,
920             ("SetContentModified(): Set annotation to |%s|",
921              contentModifiedAnnotation));
922     cacheEntry->SetMetaDataElement("ContentModified",
923                                    contentModifiedAnnotation);
924   } else {
925     MOZ_LOG(IMAPCache, LogLevel::Debug,
926             ("SetContentModified(): Set annotation FAILED -- no cacheEntry"));
927   }
928   return NS_OK;
929 }
930 
GetContentModified(nsImapContentModifiedType * contentModified)931 NS_IMETHODIMP nsImapUrl::GetContentModified(
932     nsImapContentModifiedType* contentModified) {
933   if (!contentModified) return NS_ERROR_NULL_POINTER;
934 
935   *contentModified = m_contentModified;
936   return NS_OK;
937 }
938 
SetFetchPartsOnDemand(bool fetchPartsOnDemand)939 NS_IMETHODIMP nsImapUrl::SetFetchPartsOnDemand(bool fetchPartsOnDemand) {
940   m_fetchPartsOnDemand = fetchPartsOnDemand;
941   return NS_OK;
942 }
943 
GetFetchPartsOnDemand(bool * fetchPartsOnDemand)944 NS_IMETHODIMP nsImapUrl::GetFetchPartsOnDemand(bool* fetchPartsOnDemand) {
945   if (!fetchPartsOnDemand) return NS_ERROR_NULL_POINTER;
946 
947   *fetchPartsOnDemand = m_fetchPartsOnDemand;
948   return NS_OK;
949 }
950 
SetMimePartSelectorDetected(bool mimePartSelectorDetected)951 NS_IMETHODIMP nsImapUrl::SetMimePartSelectorDetected(
952     bool mimePartSelectorDetected) {
953   m_mimePartSelectorDetected = mimePartSelectorDetected;
954   return NS_OK;
955 }
956 
GetMimePartSelectorDetected(bool * mimePartSelectorDetected)957 NS_IMETHODIMP nsImapUrl::GetMimePartSelectorDetected(
958     bool* mimePartSelectorDetected) {
959   if (!mimePartSelectorDetected) return NS_ERROR_NULL_POINTER;
960 
961   *mimePartSelectorDetected = m_mimePartSelectorDetected;
962   return NS_OK;
963 }
964 
965 // this method is only called from the UI thread.
SetCopyState(nsISupports * copyState)966 NS_IMETHODIMP nsImapUrl::SetCopyState(nsISupports* copyState) {
967   MutexAutoLock mon(mLock);
968   m_copyState = copyState;
969   return NS_OK;
970 }
971 
972 // this method is only called from the imap thread..but we still
973 // need a monitor 'cause the setter is called from the UI thread.
GetCopyState(nsISupports ** copyState)974 NS_IMETHODIMP nsImapUrl::GetCopyState(nsISupports** copyState) {
975   NS_ENSURE_ARG_POINTER(copyState);
976   MutexAutoLock mon(mLock);
977   NS_IF_ADDREF(*copyState = m_copyState);
978 
979   return NS_OK;
980 }
981 
982 NS_IMETHODIMP
SetMsgFile(nsIFile * aFile)983 nsImapUrl::SetMsgFile(nsIFile* aFile) {
984   nsresult rv = NS_OK;
985   MutexAutoLock mon(mLock);
986   m_file = aFile;
987   return rv;
988 }
989 
990 NS_IMETHODIMP
GetMsgFile(nsIFile ** aFile)991 nsImapUrl::GetMsgFile(nsIFile** aFile) {
992   NS_ENSURE_ARG_POINTER(aFile);
993 
994   MutexAutoLock mon(mLock);
995   NS_IF_ADDREF(*aFile = m_file);
996   return NS_OK;
997 }
998 
999 // this method is called from the UI thread..
GetMockChannel(nsIImapMockChannel ** aChannel)1000 NS_IMETHODIMP nsImapUrl::GetMockChannel(nsIImapMockChannel** aChannel) {
1001   NS_ENSURE_ARG_POINTER(aChannel);
1002   MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread(),
1003                         "should only access mock channel on ui thread");
1004   *aChannel = nullptr;
1005   nsCOMPtr<nsIImapMockChannel> channel(do_QueryReferent(m_channelWeakPtr));
1006   channel.forget(aChannel);
1007   return *aChannel ? NS_OK : NS_ERROR_FAILURE;
1008 }
1009 
SetMockChannel(nsIImapMockChannel * aChannel)1010 NS_IMETHODIMP nsImapUrl::SetMockChannel(nsIImapMockChannel* aChannel) {
1011   MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread(),
1012                         "should only access mock channel on ui thread");
1013   m_channelWeakPtr = do_GetWeakReference(aChannel);
1014   return NS_OK;
1015 }
1016 
GetAllowContentChange(bool * result)1017 NS_IMETHODIMP nsImapUrl::GetAllowContentChange(bool* result) {
1018   NS_ENSURE_ARG_POINTER(result);
1019   *result = m_allowContentChange;
1020   return NS_OK;
1021 }
1022 
Clone(nsIURI ** _retval)1023 nsresult nsImapUrl::Clone(nsIURI** _retval) {
1024   nsresult rv = nsMsgMailNewsUrl::Clone(_retval);
1025   NS_ENSURE_SUCCESS(rv, rv);
1026   // also clone the mURI member, because GetUri below won't work if
1027   // mURI isn't set due to escaping issues.
1028   nsCOMPtr<nsIMsgMessageUrl> clonedUrl = do_QueryInterface(*_retval);
1029   if (clonedUrl) clonedUrl->SetUri(mURI);
1030   return rv;
1031 }
1032 
GetNormalizedSpec(nsACString & aPrincipalSpec)1033 NS_IMETHODIMP nsImapUrl::GetNormalizedSpec(nsACString& aPrincipalSpec) {
1034   // URLs look like this:
1035   // imap://user@domain@server:port/fetch>UID>folder>nn
1036   // We simply strip any query part beginning with ? & or /;
1037   // Normalized spec: imap://user@domain@server:port/fetch>UID>folder>nn
1038   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsURL;
1039   QueryInterface(NS_GET_IID(nsIMsgMailNewsUrl), getter_AddRefs(mailnewsURL));
1040 
1041   nsAutoCString spec;
1042   mailnewsURL->GetSpecIgnoringRef(spec);
1043 
1044   // Strip any query part beginning with ? or /;
1045   MsgRemoveQueryPart(spec);
1046 
1047   aPrincipalSpec.Assign(spec);
1048   return NS_OK;
1049 }
1050 
SetUri(const nsACString & aURI)1051 NS_IMETHODIMP nsImapUrl::SetUri(const nsACString& aURI) {
1052   mURI = aURI;
1053   return NS_OK;
1054 }
1055 
GetUri(nsACString & aURI)1056 NS_IMETHODIMP nsImapUrl::GetUri(nsACString& aURI) {
1057   nsresult rv = NS_OK;
1058   if (!mURI.IsEmpty())
1059     aURI = mURI;
1060   else {
1061     uint32_t key =
1062         m_listOfMessageIds ? strtoul(m_listOfMessageIds, nullptr, 10) : 0;
1063     nsCString canonicalPath;
1064     AllocateCanonicalPath(m_sourceCanonicalFolderPathSubString,
1065                           m_onlineSubDirSeparator,
1066                           (getter_Copies(canonicalPath)));
1067     nsCString fullFolderPath("/");
1068     fullFolderPath.Append(m_userName);
1069     nsAutoCString hostName;
1070     rv = GetHost(hostName);
1071     fullFolderPath.Append('@');
1072     fullFolderPath.Append(hostName);
1073     fullFolderPath.Append('/');
1074     fullFolderPath.Append(canonicalPath);
1075 
1076     nsCString baseMessageURI;
1077     nsCreateImapBaseMessageURI(fullFolderPath, baseMessageURI);
1078     rv = nsBuildImapMessageURI(baseMessageURI.get(), key, aURI);
1079   }
1080   return rv;
1081 }
1082 
NS_IMPL_GETSET(nsImapUrl,AddDummyEnvelope,bool,m_addDummyEnvelope)1083 NS_IMPL_GETSET(nsImapUrl, AddDummyEnvelope, bool, m_addDummyEnvelope)
1084 NS_IMPL_GETSET(nsImapUrl, CanonicalLineEnding, bool, m_canonicalLineEnding)
1085 NS_IMETHODIMP nsImapUrl::GetMsgLoadingFromCache(bool* result) {
1086   NS_ENSURE_ARG_POINTER(result);
1087   *result = m_msgLoadingFromCache;
1088   return NS_OK;
1089 }
NS_IMPL_GETSET(nsImapUrl,LocalFetchOnly,bool,m_localFetchOnly)1090 NS_IMPL_GETSET(nsImapUrl, LocalFetchOnly, bool, m_localFetchOnly)
1091 NS_IMPL_GETSET(nsImapUrl, ExternalLinkUrl, bool, m_externalLinkUrl)
1092 NS_IMPL_GETSET(nsImapUrl, RerunningUrl, bool, m_rerunningUrl)
1093 NS_IMPL_GETSET(nsImapUrl, ValidUrl, bool, m_validUrl)
1094 NS_IMPL_GETSET(nsImapUrl, MoreHeadersToDownload, bool, m_moreHeadersToDownload)
1095 
1096 NS_IMETHODIMP nsImapUrl::SetMsgLoadingFromCache(bool loadingFromCache) {
1097   nsresult rv = NS_OK;
1098   m_msgLoadingFromCache = loadingFromCache;
1099   return rv;
1100 }
1101 
SetMessageFile(nsIFile * aFile)1102 NS_IMETHODIMP nsImapUrl::SetMessageFile(nsIFile* aFile) {
1103   m_messageFile = aFile;
1104   return NS_OK;
1105 }
1106 
GetMessageFile(nsIFile ** aFile)1107 NS_IMETHODIMP nsImapUrl::GetMessageFile(nsIFile** aFile) {
1108   if (aFile) NS_IF_ADDREF(*aFile = m_messageFile);
1109   return NS_OK;
1110 }
1111 
IsUrlType(uint32_t type,bool * isType)1112 NS_IMETHODIMP nsImapUrl::IsUrlType(uint32_t type, bool* isType) {
1113   NS_ENSURE_ARG(isType);
1114 
1115   switch (type) {
1116     case nsIMsgMailNewsUrl::eCopy:
1117       *isType = ((m_imapAction == nsIImapUrl::nsImapOnlineCopy) ||
1118                  (m_imapAction == nsIImapUrl::nsImapOnlineToOfflineCopy) ||
1119                  (m_imapAction == nsIImapUrl::nsImapOfflineToOnlineCopy));
1120       break;
1121     case nsIMsgMailNewsUrl::eMove:
1122       *isType = ((m_imapAction == nsIImapUrl::nsImapOnlineMove) ||
1123                  (m_imapAction == nsIImapUrl::nsImapOnlineToOfflineMove) ||
1124                  (m_imapAction == nsIImapUrl::nsImapOfflineToOnlineMove));
1125       break;
1126     case nsIMsgMailNewsUrl::eDisplay:
1127       *isType = (m_imapAction == nsIImapUrl::nsImapMsgFetch ||
1128                  m_imapAction == nsIImapUrl::nsImapMsgFetchPeek);
1129       break;
1130     default:
1131       *isType = false;
1132   };
1133 
1134   return NS_OK;
1135 }
1136 
1137 NS_IMETHODIMP
GetOriginalSpec(nsACString & aSpec)1138 nsImapUrl::GetOriginalSpec(nsACString& aSpec) {
1139   return NS_ERROR_NOT_IMPLEMENTED;
1140 }
1141 
1142 NS_IMETHODIMP
SetOriginalSpec(const nsACString & aSpec)1143 nsImapUrl::SetOriginalSpec(const nsACString& aSpec) {
1144   return NS_ERROR_NOT_IMPLEMENTED;
1145 }
1146 
ReplaceCharsInCopiedString(const char * stringToCopy,char oldChar,char newChar)1147 char* nsImapUrl::ReplaceCharsInCopiedString(const char* stringToCopy,
1148                                             char oldChar, char newChar) {
1149   char oldCharString[2];
1150   *oldCharString = oldChar;
1151   *(oldCharString + 1) = 0;
1152 
1153   char* translatedString = PL_strdup(stringToCopy);
1154   char* currentSeparator = PL_strstr(translatedString, oldCharString);
1155 
1156   while (currentSeparator) {
1157     *currentSeparator = newChar;
1158     currentSeparator = PL_strstr(currentSeparator + 1, oldCharString);
1159   }
1160 
1161   return translatedString;
1162 }
1163 
1164 ////////////////////////////////////////////////////////////////////////////////////
1165 // End of functions which should be made obsolete after modifying nsIURI
1166 ////////////////////////////////////////////////////////////////////////////////////
1167 
ParseFolderPath(char ** resultingCanonicalPath)1168 void nsImapUrl::ParseFolderPath(char** resultingCanonicalPath) {
1169   char* resultPath = m_tokenPlaceHolder ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR,
1170                                                     &m_tokenPlaceHolder)
1171                                         : (char*)NULL;
1172 
1173   if (!resultPath) {
1174     m_validUrl = false;
1175     return;
1176   }
1177   NS_ASSERTION(*resultingCanonicalPath == nullptr, "whoops, mem leak");
1178 
1179   char dirSeparator = *resultPath;
1180 
1181   nsCString unescapedResultingCanonicalPath;
1182   MsgUnescapeString(nsDependentCString(resultPath + 1), 0,
1183                     unescapedResultingCanonicalPath);
1184   *resultingCanonicalPath = ToNewCString(unescapedResultingCanonicalPath);
1185   // The delimiter will be set for a given URL, but will not be statically
1186   // available from an arbitrary URL.  It is the creator's responsibility to
1187   // fill in the correct delimiter from the folder's namespace when creating the
1188   // URL.
1189   if (dirSeparator != kOnlineHierarchySeparatorUnknown)
1190     SetOnlineSubDirSeparator(dirSeparator);
1191 
1192   // if dirSeparator == kOnlineHierarchySeparatorUnknown, then this must be a
1193   // create of a top level imap box.  If there is an online subdir, we will
1194   // automatically use its separator.  If there is not an online subdir, we
1195   // don't need a separator.
1196 }
1197 
ParseSearchCriteriaString()1198 void nsImapUrl::ParseSearchCriteriaString() {
1199   if (m_tokenPlaceHolder) {
1200     int quotedFlag = false;
1201 
1202     // skip initial separator
1203     while (*m_tokenPlaceHolder == *IMAP_URL_TOKEN_SEPARATOR)
1204       m_tokenPlaceHolder++;
1205 
1206     char* saveTokenPlaceHolder = m_tokenPlaceHolder;
1207 
1208     //    m_searchCriteriaString = m_tokenPlaceHolder;
1209 
1210     // looking for another separator outside quoted string
1211     while (*m_tokenPlaceHolder) {
1212       if (*m_tokenPlaceHolder == '\\' && *(m_tokenPlaceHolder + 1) == '"')
1213         m_tokenPlaceHolder++;
1214       else if (*m_tokenPlaceHolder == '"')
1215         quotedFlag = !quotedFlag;
1216       else if (!quotedFlag &&
1217                *m_tokenPlaceHolder == *IMAP_URL_TOKEN_SEPARATOR) {
1218         *m_tokenPlaceHolder = '\0';
1219         m_tokenPlaceHolder++;
1220         break;
1221       }
1222       m_tokenPlaceHolder++;
1223     }
1224     m_searchCriteriaString = PL_strdup(saveTokenPlaceHolder);
1225     if (*m_tokenPlaceHolder == '\0') m_tokenPlaceHolder = NULL;
1226 
1227     if (*m_searchCriteriaString == '\0') m_searchCriteriaString = (char*)NULL;
1228   } else
1229     m_searchCriteriaString = (char*)NULL;
1230   if (!m_searchCriteriaString) m_validUrl = false;
1231 }
1232 
ParseUidChoice()1233 void nsImapUrl::ParseUidChoice() {
1234   char* uidChoiceString =
1235       m_tokenPlaceHolder
1236           ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR, &m_tokenPlaceHolder)
1237           : (char*)NULL;
1238   if (!uidChoiceString)
1239     m_validUrl = false;
1240   else
1241     m_idsAreUids = strcmp(uidChoiceString, "UID") == 0;
1242 }
1243 
ParseMsgFlags()1244 void nsImapUrl::ParseMsgFlags() {
1245   char* flagsPtr = m_tokenPlaceHolder ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR,
1246                                                   &m_tokenPlaceHolder)
1247                                       : (char*)NULL;
1248   if (flagsPtr) {
1249     // the url is encodes the flags byte as ascii
1250     int intFlags = atoi(flagsPtr);
1251     m_flags = (imapMessageFlagsType)intFlags;  // cast here
1252   } else
1253     m_flags = 0;
1254 }
1255 
ParseListOfMessageIds()1256 void nsImapUrl::ParseListOfMessageIds() {
1257   m_listOfMessageIds = m_tokenPlaceHolder ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR,
1258                                                       &m_tokenPlaceHolder)
1259                                           : (char*)NULL;
1260   if (!m_listOfMessageIds)
1261     m_validUrl = false;
1262   else {
1263     m_listOfMessageIds = strdup(m_listOfMessageIds);
1264     m_mimePartSelectorDetected = PL_strstr(m_listOfMessageIds, "&part=") != 0 ||
1265                                  PL_strstr(m_listOfMessageIds, "?part=") != 0;
1266 
1267     // if we're asking for just the body, don't download the whole message. see
1268     // nsMsgQuote::QuoteMessage() for the "header=" settings when replying to
1269     // msgs.
1270     if (!m_fetchPartsOnDemand)
1271       m_fetchPartsOnDemand =
1272           (PL_strstr(m_listOfMessageIds, "?header=quotebody") != 0 ||
1273            PL_strstr(m_listOfMessageIds, "?header=only") != 0);
1274     // if it's a spam filter trying to fetch the msg, don't let it get marked
1275     // read.
1276     if (PL_strstr(m_listOfMessageIds, "?header=filter") != 0)
1277       m_imapAction = nsImapMsgFetchPeek;
1278   }
1279 }
1280 
ParseCustomMsgFetchAttribute()1281 void nsImapUrl::ParseCustomMsgFetchAttribute() {
1282   m_msgFetchAttribute = m_tokenPlaceHolder ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR,
1283                                                        &m_tokenPlaceHolder)
1284                                            : (char*)nullptr;
1285 }
1286 
ParseNumBytes()1287 void nsImapUrl::ParseNumBytes() {
1288   const char* numBytes =
1289       (m_tokenPlaceHolder)
1290           ? NS_strtok(IMAP_URL_TOKEN_SEPARATOR, &m_tokenPlaceHolder)
1291           : 0;
1292   m_numBytesToFetch = numBytes ? atoi(numBytes) : 0;
1293 }
1294 
1295 // nsIMsgI18NUrl support
1296 
GetMsgFolder(nsIMsgFolder ** msgFolder)1297 nsresult nsImapUrl::GetMsgFolder(nsIMsgFolder** msgFolder) {
1298   // if we have a RDF URI, then try to get the folder for that URI and then ask
1299   // the folder for it's charset....
1300 
1301   nsCString uri;
1302   GetUri(uri);
1303   NS_ENSURE_TRUE(!uri.IsEmpty(), NS_ERROR_FAILURE);
1304 
1305   nsCOMPtr<nsIMsgDBHdr> msg;
1306   GetMsgDBHdrFromURI(uri, getter_AddRefs(msg));
1307   NS_ENSURE_TRUE(msg, NS_ERROR_FAILURE);
1308   nsresult rv = msg->GetFolder(msgFolder);
1309   NS_ENSURE_SUCCESS(rv, rv);
1310   NS_ENSURE_TRUE(msgFolder, NS_ERROR_FAILURE);
1311 
1312   return NS_OK;
1313 }
1314 
GetOverRideCharset(bool * aOverride)1315 NS_IMETHODIMP nsImapUrl::GetOverRideCharset(bool* aOverride) {
1316   *aOverride = mOverrideCharset;
1317   return NS_OK;
1318 }
1319 
SetOverRideCharset(bool aOverride)1320 NS_IMETHODIMP nsImapUrl::SetOverRideCharset(bool aOverride) {
1321   mOverrideCharset = aOverride;
1322   return NS_OK;
1323 }
1324 
GetStoreResultsOffline(bool * aStoreResultsOffline)1325 NS_IMETHODIMP nsImapUrl::GetStoreResultsOffline(bool* aStoreResultsOffline) {
1326   NS_ENSURE_ARG_POINTER(aStoreResultsOffline);
1327   *aStoreResultsOffline = m_storeResultsOffline;
1328   return NS_OK;
1329 }
1330 
SetStoreResultsOffline(bool aStoreResultsOffline)1331 NS_IMETHODIMP nsImapUrl::SetStoreResultsOffline(bool aStoreResultsOffline) {
1332   m_storeResultsOffline = aStoreResultsOffline;
1333   return NS_OK;
1334 }
1335 
GetStoreOfflineOnFallback(bool * aStoreOfflineOnFallback)1336 NS_IMETHODIMP nsImapUrl::GetStoreOfflineOnFallback(
1337     bool* aStoreOfflineOnFallback) {
1338   NS_ENSURE_ARG_POINTER(aStoreOfflineOnFallback);
1339   *aStoreOfflineOnFallback = m_storeOfflineOnFallback;
1340   return NS_OK;
1341 }
1342 
SetStoreOfflineOnFallback(bool aStoreOfflineOnFallback)1343 NS_IMETHODIMP nsImapUrl::SetStoreOfflineOnFallback(
1344     bool aStoreOfflineOnFallback) {
1345   m_storeOfflineOnFallback = aStoreOfflineOnFallback;
1346   return NS_OK;
1347 }
1348 
GetMessageHeader(nsIMsgDBHdr ** aMsgHdr)1349 NS_IMETHODIMP nsImapUrl::GetMessageHeader(nsIMsgDBHdr** aMsgHdr) {
1350   nsCString uri;
1351   nsresult rv = GetUri(uri);
1352   NS_ENSURE_SUCCESS(rv, rv);
1353   return GetMsgDBHdrFromURI(uri, aMsgHdr);
1354 }
1355 
SetMessageHeader(nsIMsgDBHdr * aMsgHdr)1356 NS_IMETHODIMP nsImapUrl::SetMessageHeader(nsIMsgDBHdr* aMsgHdr) {
1357   return NS_ERROR_NOT_IMPLEMENTED;
1358 }
1359