summaryrefslogtreecommitdiffstats
path: root/mailnews/imap/src/nsImapFlagAndUidState.cpp
blob: deae831db325e35fe0f421bab3aadcfb33207ad6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "msgCore.h"  // for pre-compiled headers

#include "nsImapCore.h"
#include "nsImapFlagAndUidState.h"
#include "nsMsgUtils.h"
#include "prcmon.h"
#include "nspr.h"

NS_IMPL_ISUPPORTS(nsImapFlagAndUidState, nsIImapFlagAndUidState)

using namespace mozilla;

NS_IMETHODIMP nsImapFlagAndUidState::GetNumberOfMessages(int32_t *result)
{
  if (!result)
    return NS_ERROR_NULL_POINTER;
  *result = fUids.Length();
  return NS_OK;
}

NS_IMETHODIMP nsImapFlagAndUidState::GetUidOfMessage(int32_t zeroBasedIndex, uint32_t *aResult)
{
  NS_ENSURE_ARG_POINTER(aResult);

  PR_CEnterMonitor(this);
  *aResult = fUids.SafeElementAt(zeroBasedIndex, nsMsgKey_None);
  PR_CExitMonitor(this);
  return NS_OK;
}

NS_IMETHODIMP nsImapFlagAndUidState::GetMessageFlags(int32_t zeroBasedIndex, uint16_t *aResult)
{
  NS_ENSURE_ARG_POINTER(aResult);
  *aResult = fFlags.SafeElementAt(zeroBasedIndex, kNoImapMsgFlag);
  return NS_OK;
}

NS_IMETHODIMP nsImapFlagAndUidState::SetMessageFlags(int32_t zeroBasedIndex, unsigned short flags)
{
  if (zeroBasedIndex < (int32_t)fUids.Length())
    fFlags[zeroBasedIndex] = flags;
  return NS_OK;
}

NS_IMETHODIMP nsImapFlagAndUidState::GetNumberOfRecentMessages(int32_t *result)
{
  if (!result)
    return NS_ERROR_NULL_POINTER;
  
  PR_CEnterMonitor(this);
  uint32_t counter = 0;
  int32_t numUnseenMessages = 0;
  
  for (counter = 0; counter < fUids.Length(); counter++)
  {
    if (fFlags[counter] & kImapMsgRecentFlag)
      numUnseenMessages++;
  }
  PR_CExitMonitor(this);
  
  *result = numUnseenMessages;
  
  return NS_OK;
}

NS_IMETHODIMP nsImapFlagAndUidState::GetPartialUIDFetch(bool *aPartialUIDFetch)
{
  NS_ENSURE_ARG_POINTER(aPartialUIDFetch);
  *aPartialUIDFetch = fPartialUIDFetch;
  return NS_OK;
}

/* amount to expand for imap entry flags when we need more */

nsImapFlagAndUidState::nsImapFlagAndUidState(int32_t numberOfMessages)
  : fUids(numberOfMessages),
    fFlags(numberOfMessages),
    m_customFlagsHash(10),
    m_customAttributesHash(10),
    mLock("nsImapFlagAndUidState.mLock")
{
  fSupportedUserFlags = 0;
  fNumberDeleted = 0;
  fPartialUIDFetch = true;
}

nsImapFlagAndUidState::~nsImapFlagAndUidState()
{
}

NS_IMETHODIMP
nsImapFlagAndUidState::OrSupportedUserFlags(uint16_t flags)
{
  fSupportedUserFlags |= flags;
  return NS_OK;
}

NS_IMETHODIMP
nsImapFlagAndUidState::GetSupportedUserFlags(uint16_t *aFlags)
{
  NS_ENSURE_ARG_POINTER(aFlags);
  *aFlags = fSupportedUserFlags;
  return NS_OK;
}

// we need to reset our flags, (re-read all) but chances are the memory allocation needed will be
// very close to what we were already using

NS_IMETHODIMP nsImapFlagAndUidState::Reset()
{
  PR_CEnterMonitor(this);
  fNumberDeleted = 0;
  m_customFlagsHash.Clear();
  fUids.Clear();
  fFlags.Clear();
  fPartialUIDFetch = true;
  PR_CExitMonitor(this);
  return NS_OK;
}


// Remove (expunge) a message from our array, since now it is gone for good

NS_IMETHODIMP nsImapFlagAndUidState::ExpungeByIndex(uint32_t msgIndex)
{
  // protect ourselves in case the server gave us an index key of -1 or 0
  if ((int32_t) msgIndex <= 0)
    return NS_ERROR_INVALID_ARG;

  if ((uint32_t) fUids.Length() < msgIndex)
    return NS_ERROR_INVALID_ARG;

  PR_CEnterMonitor(this);
  msgIndex--;  // msgIndex is 1-relative
  if (fFlags[msgIndex] & kImapMsgDeletedFlag) // see if we already had counted this one as deleted
    fNumberDeleted--;
  fUids.RemoveElementAt(msgIndex);
  fFlags.RemoveElementAt(msgIndex);
  PR_CExitMonitor(this);
  return NS_OK;
}


// adds to sorted list, protects against duplicates and going past array bounds.
NS_IMETHODIMP nsImapFlagAndUidState::AddUidFlagPair(uint32_t uid, imapMessageFlagsType flags, uint32_t zeroBasedIndex)
{
  if (uid == nsMsgKey_None) // ignore uid of -1
    return NS_OK;
  // check for potential overflow in buffer size for uid array
  if (zeroBasedIndex > 0x3FFFFFFF)
    return NS_ERROR_INVALID_ARG;
  PR_CEnterMonitor(this);
  // make sure there is room for this pair
  if (zeroBasedIndex >= fUids.Length())
  {
    int32_t sizeToGrowBy = zeroBasedIndex - fUids.Length() + 1;
    fUids.InsertElementsAt(fUids.Length(), sizeToGrowBy, 0);
    fFlags.InsertElementsAt(fFlags.Length(), sizeToGrowBy, 0);
  }

  fUids[zeroBasedIndex] = uid;
  fFlags[zeroBasedIndex] = flags;
  if (flags & kImapMsgDeletedFlag)
    fNumberDeleted++;
  PR_CExitMonitor(this);
  return NS_OK;
}


NS_IMETHODIMP nsImapFlagAndUidState::GetNumberOfDeletedMessages(int32_t *numDeletedMessages)
{
  NS_ENSURE_ARG_POINTER(numDeletedMessages);
  *numDeletedMessages = NumberOfDeletedMessages();
  return NS_OK;
}

int32_t nsImapFlagAndUidState::NumberOfDeletedMessages()
{
  return fNumberDeleted;
}
	
// since the uids are sorted, start from the back (rb)

uint32_t  nsImapFlagAndUidState::GetHighestNonDeletedUID()
{
  uint32_t msgIndex = fUids.Length();
  do 
  {
    if (msgIndex <= 0)
      return(0);
    msgIndex--;
    if (fUids[msgIndex] && !(fFlags[msgIndex] & kImapMsgDeletedFlag))
      return fUids[msgIndex];
  }
  while (msgIndex > 0);
  return 0;
}


// Has the user read the last message here ? Used when we first open the inbox to see if there
// really is new mail there.

bool nsImapFlagAndUidState::IsLastMessageUnseen()
{
  uint32_t msgIndex = fUids.Length();
  
  if (msgIndex <= 0)
    return false;
  msgIndex--;
  // if last message is deleted, it was probably filtered the last time around
  if (fUids[msgIndex] && (fFlags[msgIndex] & (kImapMsgSeenFlag | kImapMsgDeletedFlag)))
    return false;
  return true; 
}

// find a message flag given a key with non-recursive binary search, since some folders
// may have thousand of messages, once we find the key set its index, or the index of
// where the key should be inserted

imapMessageFlagsType nsImapFlagAndUidState::GetMessageFlagsFromUID(uint32_t uid, bool *foundIt, int32_t *ndx)
{
  PR_CEnterMonitor(this);
  *ndx = (int32_t) fUids.IndexOfFirstElementGt(uid) - 1;
  *foundIt = *ndx >= 0 && fUids[*ndx] == uid;
  imapMessageFlagsType retFlags = (*foundIt) ? fFlags[*ndx] : kNoImapMsgFlag;
  PR_CExitMonitor(this);
  return retFlags;
}

NS_IMETHODIMP nsImapFlagAndUidState::AddUidCustomFlagPair(uint32_t uid, const char *customFlag)
{
  if (!customFlag)
    return NS_OK;

  MutexAutoLock mon(mLock);
  nsCString ourCustomFlags;
  nsCString oldValue;
  if (m_customFlagsHash.Get(uid, &oldValue))
  {
    // We'll store multiple keys as space-delimited since space is not
    // a valid character in a keyword. First, we need to look for the
    // customFlag in the existing flags;
    nsDependentCString customFlagString(customFlag);
    int32_t existingCustomFlagPos = oldValue.Find(customFlagString);
    uint32_t customFlagLen = customFlagString.Length();
    while (existingCustomFlagPos != kNotFound)
    {
      // if existing flags ends with this exact flag, or flag + ' '
      // and the flag is at the beginning of the string or there is ' ' + flag
      // then we have this flag already;
      if (((oldValue.Length() == existingCustomFlagPos + customFlagLen) ||
           (oldValue.CharAt(existingCustomFlagPos + customFlagLen) == ' ')) &&
           ((existingCustomFlagPos == 0) ||
            (oldValue.CharAt(existingCustomFlagPos - 1) == ' ')))
        return NS_OK;
      // else, advance to next flag
      existingCustomFlagPos = MsgFind(oldValue, customFlagString, false, existingCustomFlagPos + customFlagLen);
    }
    ourCustomFlags.Assign(oldValue);
    ourCustomFlags.AppendLiteral(" ");
    ourCustomFlags.Append(customFlag);
    m_customFlagsHash.Remove(uid);
  }
  else
  {
    ourCustomFlags.Assign(customFlag);
  }
  m_customFlagsHash.Put(uid, ourCustomFlags);
  return NS_OK;
}

NS_IMETHODIMP nsImapFlagAndUidState::GetCustomFlags(uint32_t uid, char **customFlags)
{
  MutexAutoLock mon(mLock);
  nsCString value;
  if (m_customFlagsHash.Get(uid, &value))
  {
    *customFlags = NS_strdup(value.get());
    return (*customFlags) ? NS_OK : NS_ERROR_FAILURE;
  }
  *customFlags = nullptr;
  return NS_OK;
}

NS_IMETHODIMP nsImapFlagAndUidState::ClearCustomFlags(uint32_t uid)
{
  MutexAutoLock mon(mLock);
  m_customFlagsHash.Remove(uid);
  return NS_OK;
}

NS_IMETHODIMP nsImapFlagAndUidState::SetCustomAttribute(uint32_t aUid,
                                                        const nsACString &aCustomAttributeName,
                                                        const nsACString &aCustomAttributeValue)
{
  nsCString key;
  key.AppendInt((int64_t)aUid);
  key.Append(aCustomAttributeName);
  nsCString value;
  value.Assign(aCustomAttributeValue);
  m_customAttributesHash.Put(key, value);
  return NS_OK;
}

NS_IMETHODIMP nsImapFlagAndUidState::GetCustomAttribute(uint32_t aUid,
                                                        const nsACString &aCustomAttributeName,
                                                        nsACString &aCustomAttributeValue)
{
  nsCString key;
  key.AppendInt((int64_t)aUid);
  key.Append(aCustomAttributeName);
  nsCString val;
  m_customAttributesHash.Get(key, &val);
  aCustomAttributeValue.Assign(val);
  return NS_OK;
}