示例#1
0
 async def get(self, uid: int, cached_msg: CachedMessage = None,
               requirement: FetchRequirement = FetchRequirement.METADATA) \
         -> Optional[Message]:
     redis = self._redis
     keys = self._keys
     ns_keys = self._ns_keys
     msg_keys = MessageKeys(keys, uid)
     await redis.unwatch()
     multi = redis.multi_exec()
     multi.sismember(keys.uids, uid)
     multi.smembers(msg_keys.flags)
     multi.hmget(msg_keys.immutable, b'time', b'emailid', b'threadid')
     multi.get(keys.abort)
     exists, flags, (time, email_id, thread_id), abort = \
         await multi.execute()
     MailboxAbort.assertFalse(abort)
     if not exists:
         if cached_msg is not None:
             if not isinstance(cached_msg, Message):
                 raise TypeError(cached_msg)
             return Message.copy_expunged(cached_msg)
         else:
             return None
     msg_flags = {Flag(flag) for flag in flags}
     msg_email_id = ObjectId.maybe(email_id)
     msg_thread_id = ObjectId.maybe(thread_id)
     msg_time = datetime.fromisoformat(time.decode('ascii'))
     return Message(uid,
                    msg_time,
                    msg_flags,
                    email_id=msg_email_id,
                    thread_id=msg_thread_id,
                    redis=redis,
                    ns_keys=ns_keys)
示例#2
0
 async def _load_demo_mailbox(cls, resource: str, name: str,
                              mbx: MailboxData) -> None:
     path = os.path.join('demo', name)
     msg_names = sorted(resource_listdir(resource, path))
     for msg_name in msg_names:
         if msg_name == '.readonly':
             mbx._readonly = True
             continue
         elif msg_name.startswith('.'):
             continue
         msg_path = os.path.join(path, msg_name)
         with closing(resource_stream(resource, msg_path)) as msg_stream:
             flags_line = msg_stream.readline()
             msg_timestamp = float(msg_stream.readline())
             msg_data = msg_stream.read()
         msg_dt = datetime.fromtimestamp(msg_timestamp, timezone.utc)
         msg_flags = {Flag(flag) for flag in flags_line.split()}
         if Recent in msg_flags:
             msg_flags.remove(Recent)
             msg_recent = True
         else:
             msg_recent = False
         msg = AppendMessage(msg_data, msg_dt, frozenset(msg_flags),
                             ExtensionOptions.empty())
         email_id, thread_id, ref = await mbx.save(msg_data)
         prepared = PreparedMessage(msg_dt, msg.flag_set, email_id,
                                    thread_id, msg.options, ref)
         await mbx.add(prepared, recent=msg_recent)
示例#3
0
 def open(cls: Type[_MFT], base_dir: str, fp: IO[str]) -> _MFT:
     ret = []
     for line in fp:
         i, kwd = line.split()
         if kwd.startswith('\\'):
             raise ValueError(kwd)
         ret.append((i, kwd))
     return cls([Flag(kwd) for _, kwd in sorted(ret)])
示例#4
0
 async def _get_updated(self, last_mod_seq: int) \
         -> Tuple[int, Sequence[Message], Sequence[int]]:
     redis = self._redis
     keys = self._keys
     ns_keys = self._ns_keys
     while True:
         pipe = watch_pipe(redis, keys.max_mod, keys.abort)
         pipe.zrangebyscore(keys.mod_seq, last_mod_seq)
         pipe.get(keys.abort)
         _, _, uids, abort = await pipe.execute()
         MailboxAbort.assertFalse(abort)
         multi = redis.multi_exec()
         multi.get(keys.max_mod)
         multi.zrangebyscore(keys.expunged, last_mod_seq)
         for uid in uids:
             msg_keys = MessageKeys(keys, uid)
             multi.echo(uid)
             multi.smembers(msg_keys.flags)
             multi.hmget(msg_keys.immutable, b'time', b'emailid',
                         b'threadid')
         try:
             results = await multi.execute()
         except MultiExecError:
             if await check_errors(multi):
                 raise
         else:
             break
     mod_seq = int(results[0] or 0)
     expunged = [int(uid) for uid in results[1]]
     updated: List[Message] = []
     for i in range(2, len(results), 3):
         msg_uid = int(results[i])
         msg_flags = {Flag(flag) for flag in results[i + 1]}
         time_b, email_id, thread_id = results[i + 2]
         msg_time = datetime.fromisoformat(time_b.decode('ascii'))
         msg = Message(msg_uid,
                       msg_time,
                       msg_flags,
                       email_id=ObjectId(email_id),
                       thread_id=ObjectId(thread_id),
                       redis=redis,
                       ns_keys=ns_keys)
         updated.append(msg)
     return mod_seq, updated, expunged
示例#5
0
    def to_maildir(self, flags: Iterable[Union[bytes, Flag]]) -> str:
        """Return the string of letter codes that are used to map to defined
        IMAP flags and keywords.

        Args:
            flags: The flags and keywords to map.

        """
        codes = []
        for flag in flags:
            if isinstance(flag, bytes):
                flag = Flag(flag)
            from_sys = self._from_sys.get(flag)
            if from_sys is not None:
                codes.append(from_sys)
            else:
                from_kwd = self._from_kwd.get(flag)
                if from_kwd is not None:
                    codes.append(from_kwd)
        return ''.join(codes)
示例#6
0
 async def update_flags(self, messages: Sequence[Message],
                        flag_set: FrozenSet[Flag], mode: FlagOp) -> None:
     redis = self._redis
     keys = self._keys
     messages = list(messages)
     if not messages:
         return
     uids = {msg.uid: msg for msg in messages}
     while True:
         pipe = watch_pipe(redis, keys.max_mod, keys.abort)
         pipe.smembers(keys.uids)
         pipe.mget(keys.max_mod, keys.abort)
         _, _, existing_uids, (max_mod, abort) = await pipe.execute()
         MailboxAbort.assertFalse(abort)
         update_uids = uids.keys() & {int(uid) for uid in existing_uids}
         if not update_uids:
             break
         new_mod = int(max_mod or 0) + 1
         new_flags: Dict[int, Awaitable[Sequence[bytes]]] = {}
         multi = redis.multi_exec()
         multi.set(keys.max_mod, new_mod)
         for msg in messages:
             msg_uid = msg.uid
             if msg_uid not in update_uids:
                 continue
             msg_keys = MessageKeys(keys, msg_uid)
             flag_vals = (flag.value for flag in flag_set)
             multi.zadd(keys.mod_seq, new_mod, msg_uid)
             if mode == FlagOp.REPLACE:
                 multi.delete(msg_keys.flags)
                 if flag_set:
                     multi.sadd(msg_keys.flags, *flag_vals)
                 if Deleted in flag_set:
                     multi.sadd(keys.deleted, msg_uid)
                 else:
                     multi.srem(keys.deleted, msg_uid)
                 if Seen not in flag_set:
                     multi.zadd(keys.unseen, msg_uid, msg_uid)
                 else:
                     multi.zrem(keys.unseen, msg_uid)
             elif mode == FlagOp.ADD and flag_set:
                 multi.sadd(msg_keys.flags, *flag_vals)
                 if Deleted in flag_set:
                     multi.sadd(keys.deleted, msg_uid)
                 if Seen in flag_set:
                     multi.zrem(keys.unseen, msg_uid)
             elif mode == FlagOp.DELETE and flag_set:
                 multi.srem(msg_keys.flags, *flag_vals)
                 if Deleted in flag_set:
                     multi.srem(keys.deleted, msg_uid)
                 if Seen in flag_set:
                     multi.zadd(keys.unseen, msg_uid, msg_uid)
             new_flags[msg_uid] = multi.smembers(msg_keys.flags)
         try:
             await multi.execute()
         except MultiExecError:
             if await check_errors(multi):
                 raise
         else:
             for msg_uid, msg_flags in new_flags.items():
                 msg = uids[msg_uid]
                 msg.permanent_flags = frozenset(
                     Flag(flag) for flag in await msg_flags)
             break
示例#7
0
import unittest
from datetime import datetime

from pymap.flags import FlagOp, PermanentFlags, SessionFlags
from pymap.message import BaseMessage
from pymap.parsing.command.select import SearchCommand, UidSearchCommand
from pymap.parsing.response import ResponseOk
from pymap.parsing.specials import SequenceSet, ObjectId
from pymap.parsing.specials.flag import Seen, Flagged, Flag
from pymap.selected import SelectedMailbox

_Keyword = Flag(b'$Keyword')


class _Message(BaseMessage):
    async def load_content(self, requirement):
        raise RuntimeError()


class TestSelectedMailbox(unittest.TestCase):
    def setUp(self) -> None:
        self.response = ResponseOk(b'.', b'testing')

    @classmethod
    def new_selected(cls, guid: bytes = b'test') -> SelectedMailbox:
        return SelectedMailbox(ObjectId(guid), False,
                               PermanentFlags([Seen, Flagged]),
                               SessionFlags([_Keyword]))

    @classmethod
    def set_messages(cls, selected: SelectedMailbox, expunged,