def test_optimize_doubleand(root):
    restriction = SAndRestriction([
            SAndRestriction([
                    SContentRestriction(FL_SUBSTRING | FL_IGNORECASE, PR_SUBJECT, SPropValue(PR_SUBJECT, b'unknown')),
                    SContentRestriction(FL_SUBSTRING | FL_IGNORECASE, PR_SUBJECT, SPropValue(PR_SUBJECT, b'unknown'))
                    ])
            ])
    assert_no_results(root, restriction)
def test_optimize_ormultianndfalse(root):
    restriction = SOrRestriction([
    SAndRestriction([
            SNotRestriction(SExistRestriction(PR_SUBJECT)),
            SExistRestriction(PR_SUBJECT)
            ]),
    SAndRestriction([
            SContentRestriction(FL_SUBSTRING | FL_IGNORECASE, PR_SUBJECT, SPropValue(PR_SUBJECT, b'unknown')),
            SContentRestriction(FL_SUBSTRING | FL_IGNORECASE, PR_SUBJECT, SPropValue(PR_SUBJECT, b'unknown'))
            ])
    ])
    assert_no_results(root, restriction)
Example #3
0
    def occurrences(self, start=None, end=None):
        if start and end:
            startstamp = time.mktime(start.timetuple())
            endstamp = time.mktime(end.timetuple())

            # XXX use shortcuts and default type (database) to avoid MAPI snake wrestling
            NAMED_PROPS = [
                MAPINAMEID(PSETID_Appointment, MNID_ID, x)
                for x in (33293, 33294, 33315)
            ]
            ids = self.mapiobj.GetIDsFromNames(NAMED_PROPS, 0)
            startdate = ids[0] | PT_SYSTIME
            enddate = ids[1] | PT_SYSTIME
            recurring = ids[2] | PT_BOOLEAN

            # only look at non-recurring items which overlap and all recurring items
            restriction = SOrRestriction([
                SAndRestriction([
                    SPropertyRestriction(
                        RELOP_GT, enddate,
                        SPropValue(enddate, unixtime(startstamp))),
                    SPropertyRestriction(
                        RELOP_LT, startdate,
                        SPropValue(startdate, unixtime(endstamp))),
                ]),
                SAndRestriction([
                    SPropertyRestriction(RELOP_EQ, recurring,
                                         SPropValue(recurring, True))
                ])
            ])

            table = Table(
                self.server,
                self.mapiobj,
                self.mapiobj.GetContentsTable(MAPI_DEFERRED_ERRORS),
                PR_CONTAINER_CONTENTS,
                columns=[PR_ENTRYID],
            )
            table.mapitable.Restrict(restriction, 0)
            for row in table.rows():
                entryid = _benc(row[0].value)
                for occurrence in self.item(entryid).occurrences(start, end):
                    yield occurrence

        else:
            for item in self:
                for occurrence in item.occurrences(start, end):
                    yield occurrence
Example #4
0
    def restriction(self, type_, store):
        if self.field:
            # determine proptag for term, eg 'subject'
            proptag = TYPE_KEYWORD_PROPMAP[type_][self.field]
            flag = None
            subobj = None
            recipient_type = None

            # property in sub-object (attachments/recipient): use sub-restriction
            if isinstance(proptag, tuple):
                if(proptag[0]) == PR_MESSAGE_ATTACHMENTS:
                    subobj, proptag = proptag
                elif(proptag[0]) == PR_MESSAGE_RECIPIENTS:
                    subobj, proptag, recipient_type = proptag
                elif len(proptag) == 2:
                    proptag, flag = proptag

                # named property: resolve local proptag
                elif len(proptag) == 4:
                    proptag = store._name_id(proptag[:3]) | proptag[3]

            # make restriction on proptag(s)
            if isinstance(proptag, list):
                restr = SOrRestriction([
                    self.prop_restriction(proptag, flag) for proptag in proptag
                ])
            else:
                restr = self.prop_restriction(proptag, flag)

            # turn restriction into sub-restriction
            if subobj:
                if recipient_type is not None:
                    restr = SAndRestriction([
                        restr,
                        SPropertyRestriction(
                                RELOP_EQ,
                                PR_RECIPIENT_TYPE,
                                SPropValue(PR_RECIPIENT_TYPE, recipient_type)
                        )
                    ])
                restr = SSubRestriction(subobj, restr)

        else:
            defaults = [(store._name_id(proptag[:3]) | proptag[3])
                           if isinstance(proptag, tuple) else proptag
                               for proptag in DEFAULT_PROPTAGS[type_]]

            restr = SOrRestriction([
                       SContentRestriction(
                           FL_SUBSTRING | FL_IGNORECASE,
                           p,
                           SPropValue(p, self.value)
                       ) for p in defaults
                   ])

        if self.sign == '-':
            restr = SNotRestriction(restr)

        return restr
def test_optimize_orandfalse(root):
    restriction = SOrRestriction([
            SAndRestriction([
                    SNotRestriction(SExistRestriction(PR_SUBJECT)),
                    SExistRestriction(PR_SUBJECT)
                    ])
            ])
    assert_no_results(root, restriction)
Example #6
0
def _interval_restriction(proptag, start, end):
    start = datetime_to_filetime(start)
    end = datetime_to_filetime(end)

    return SAndRestriction([
        SPropertyRestriction(RELOP_GE, proptag, SPropValue(proptag, start)),
        SPropertyRestriction(RELOP_LT, proptag, SPropValue(proptag, end))
    ])
Example #7
0
def GetUserList(session, companyname=None, flags=0):
    restriction = SAndRestriction([
        SPropertyRestriction(RELOP_EQ, PR_OBJECT_TYPE,
                             SPropValue(PR_OBJECT_TYPE, MAPI_MAILUSER)),
        SPropertyRestriction(RELOP_NE, PR_DISPLAY_TYPE,
                             SPropValue(PR_DISPLAY_TYPE, DT_REMOTE_MAILUSER))
    ])
    return GetAbObjectList(session, restriction, companyname, flags)
Example #8
0
    def _save_rule(store, userids, deletion):
        # remove existing rule # XXX update
        for rule in store.inbox.rules():
            if rule.mapirow[PR_RULE_PROVIDER_W] == u'Schedule+ EMS Interface' and \
               PR_RULE_ID in rule.mapirow:
                pr_rule_id = rule.mapirow[PR_RULE_ID]

                rulerows = [
                    ROWENTRY(ROW_REMOVE, [SPropValue(PR_RULE_ID, pr_rule_id)])
                ]
                table = store.inbox.mapiobj.OpenProperty(
                    PR_RULES_TABLE, IID_IExchangeModifyTable, 0, 0)
                table.ModifyTable(0, rulerows)

        # create new rule
        row = [
            SPropValue(PR_RULE_LEVEL, 0),
            SPropValue(PR_RULE_NAME_W, u"Delegate Meetingrequest service"),
            SPropValue(PR_RULE_PROVIDER_W, u"Schedule+ EMS Interface"),
            SPropValue(PR_RULE_SEQUENCE, 0),
            SPropValue(PR_RULE_STATE, 1),
            SPropValue(PR_RULE_PROVIDER_DATA, b''),
        ]

        actions = []
        userprops = []
        for userid in userids:
            user = store.server.gab.OpenEntry(userid, None, MAPI_BEST_ACCESS)
            props = user.GetProps(USERPROPS, MAPI_UNICODE)
            # Hardcode recipient type to TO
            props.append(SPropValue(PR_RECIPIENT_TYPE, MAPI_TO))
            userprops.append(props)

        actions.append(
            ACTION(ACTTYPE.OP_DELEGATE, 0, None, None, 0,
                   actFwdDelegate(userprops)))
        if deletion:
            actions.append(ACTION(ACTTYPE.OP_DELETE, 0, None, None, 0, None))
        row.append(SPropValue(PR_RULE_ACTIONS, ACTIONS(1, actions)))

        cond = SAndRestriction([
            SContentRestriction(
                FL_PREFIX, PR_MESSAGE_CLASS_W,
                SPropValue(PR_MESSAGE_CLASS_W, u"IPM.Schedule.Meeting")),
            SNotRestriction(SExistRestriction(PR_DELEGATED_BY_RULE)),
            SOrRestriction([
                SNotRestriction(SExistRestriction(PR_SENSITIVITY)),
                SPropertyRestriction(RELOP_NE, PR_SENSITIVITY,
                                     SPropValue(PR_SENSITIVITY, 2))
            ])
        ])
        row.append(SPropValue(PR_RULE_CONDITION, cond))
        rulerows = [ROWENTRY(ROW_ADD, row)]
        table = store.inbox.mapiobj.OpenProperty(PR_RULES_TABLE,
                                                 IID_IExchangeModifyTable, 0,
                                                 0)
        table.ModifyTable(0, rulerows)
Example #9
0
 def restriction(self, type_, store):
     if self.op == 'AND':
         return SAndRestriction(
             [arg.restriction(type_, store) for arg in self.args])
     elif self.op == 'OR':
         return SOrRestriction(
             [arg.restriction(type_, store) for arg in self.args])
     elif self.op == 'NOT':
         return SNotRestriction(self.args[0].restriction(type_, store))
Example #10
0
def GetGab(session):
    ab = session.OpenAddressBook(0, None, 0)
    root = ab.OpenEntry(None, None, 0)
    table = root.GetHierarchyTable(0)
    table.SetColumns([PR_ENTRYID], TBL_BATCH)
    restriction = SOrRestriction([
        SPropertyRestriction(RELOP_EQ, PR_DISPLAY_TYPE,
                             SPropValue(PR_DISPLAY_TYPE, DT_GLOBAL)),
        SAndRestriction([
            SExistRestriction(PR_EMS_AB_CONTAINERID),
            SPropertyRestriction(RELOP_EQ, PR_EMS_AB_CONTAINERID,
                                 SPropValue(PR_EMS_AB_CONTAINERID, 0))
        ])
    ])
    table.FindRow(restriction, BOOKMARK_BEGINNING, 0)
    eid = table.QueryRows(1, 0)[0][0].Value
    return ab.OpenEntry(eid, None, 0)
Example #11
0
    def restriction(self, type_, store):
        if self.field:
            # determine proptag for term, eg 'subject'
            proptag = TYPE_KEYWORD_PROPMAP[type_][self.field]
            flag = None
            subobj = None
            recipient_type = None

            # property in sub-object (attachments/recipient): use sub-restriction
            if isinstance(proptag, tuple) and len(proptag) == 2:
                if(proptag[0]) == PR_MESSAGE_ATTACHMENTS:
                    subobj, proptag = proptag
                elif(proptag[0]) == PR_MESSAGE_RECIPIENTS:
                    subobj, recipient_type = proptag
                    proptag = PR_DISPLAY_NAME_W # TODO email
                else:
                    proptag, flag = proptag

            # named property: resolve local proptag
            elif isinstance(proptag, tuple) and len(proptag) == 4:
                proptag = store._name_id(proptag[:3]) | proptag[3]

            # comparison operator
            if self.op in ('<', '>', '>=', '<=', '<>'):
                if PROP_TYPE(proptag) == PT_SYSTIME:
                    d = dateutil.parser.parse(self.value, dayfirst=True)
                    d = datetime_to_filetime(d)
                    restr = SPropertyRestriction(
                                OP_RELOP[self.op],
                                proptag,
                                SPropValue(proptag, d)
                            )
                else:
                    value = self.value
                    unit = ''
                    if [x for x in ('KB', 'MB', 'GB') if value.endswith(x)]:
                        value, unit = value[:-2], value[-2:]

                    if PROP_TYPE(proptag) in (PT_FLOAT, PT_DOUBLE):
                        value = float(value)
                    else:
                        value = int(value)

                    if unit == 'KB':
                        value *= 1024
                    elif unit == 'MB':
                        value *= 1024**2
                    elif unit == 'GB':
                        value *= 1024**3

                    restr = SPropertyRestriction(
                                OP_RELOP[self.op],
                                proptag,
                                SPropValue(proptag, value)
                            )

            # contains/equals operator
            elif self.op in (':', '='):
                if PROP_TYPE(proptag) == PT_UNICODE:
                    restr = SContentRestriction(
                                FL_SUBSTRING | FL_IGNORECASE,
                                proptag,
                                SPropValue(proptag, self.value)
                            )

                elif flag or PROP_TYPE(proptag) == PT_BOOLEAN:
                    if flag:
                        restr = SBitMaskRestriction(
                                    BMR_NEZ if self.value in ('yes', 'true') else BMR_EQZ,
                                    proptag,
                                    flag
                                )
                    else:
                        restr = SPropertyRestriction(
                                    RELOP_EQ,
                                    proptag,
                                    SPropValue(proptag, self.value in ('yes', 'true'))
                                )

                elif PROP_TYPE(proptag) == PT_MV_UNICODE:
                    proptag2 = (proptag ^ PT_MV_UNICODE) | PT_UNICODE # funky!
                    restr = SContentRestriction(
                                FL_SUBSTRING | FL_IGNORECASE,
                                proptag,
                                SPropValue(proptag2, self.value)
                            )

                elif PROP_TYPE(proptag) in (PT_SHORT, PT_LONG, PT_LONGLONG, PT_FLOAT, PT_DOUBLE):
                    conv = float if PROP_TYPE(proptag) in (PT_FLOAT, PT_DOUBLE) else int
                    if '..' in self.value:
                        val1, val2 = self.value.split('..')
                        restr = SAndRestriction([
                            SPropertyRestriction(
                                RELOP_GE,
                                proptag,
                                SPropValue(proptag, conv(val1))
                            ),
                            SPropertyRestriction(
                                RELOP_LT,
                                proptag,
                                SPropValue(proptag, conv(val2))
                            )
                        ])
                    else:
                        restr = SPropertyRestriction(
                                    RELOP_EQ,
                                    proptag,
                                    SPropValue(proptag, conv(self.value))
                                )

                elif PROP_TYPE(proptag) == PT_SYSTIME:
                    if self.value == 'today':
                        d = datetime.datetime.now().date()
                        d2 = d + datetime.timedelta(days=1)
                        restr = _interval_restriction(proptag, d, d2)

                    elif self.value == 'yesterday':
                        d2 = datetime.datetime.now().date()
                        d = d2 - datetime.timedelta(days=1)
                        restr = _interval_restriction(proptag, d, d2)

                    elif self.value == 'this week':
                        d2 = datetime.datetime.now()
                        d = d2.date() - datetime.timedelta(days=d2.weekday())
                        restr = _interval_restriction(proptag, d, d2)

                    elif self.value == 'this month':
                        d2 = datetime.datetime.now()
                        d = d2.date() - datetime.timedelta(days=d2.day-1)
                        restr = _interval_restriction(proptag, d, d2)

                    elif self.value == 'last month':
                        now = datetime.datetime.now()
                        d2 = now.date() - datetime.timedelta(days=now.day-1)
                        d = (d2 - datetime.timedelta(days=1)).replace(day=1)
                        restr = _interval_restriction(proptag, d, d2)

                    elif self.value == 'this year':
                        d2 = datetime.datetime.now()
                        d = datetime.datetime(d2.year, 1, 1)
                        restr = _interval_restriction(proptag, d, d2)

                    elif self.value == 'last year':
                        now = datetime.datetime.now()
                        d2 = datetime.datetime(now.year, 1, 1)
                        d = datetime.datetime(d2.year-1, 1, 1)
                        restr = _interval_restriction(proptag, d, d2)

                    elif '..' in self.value:
                        date1, date2 = self.value.split('..') # TODO hours etc
                        d = dateutil.parser.parse(date1, dayfirst=True)
                        d2 = dateutil.parser.parse(date2, dayfirst=True)
                        restr = _interval_restriction(proptag, d, d2)

                    else:
                        d = dateutil.parser.parse(self.value, dayfirst=True) # TODO hours etc
                        d2 = d + datetime.timedelta(days=1)
                        restr = _interval_restriction(proptag, d, d2)

            # turn restriction into sub-restriction
            if subobj:
                if recipient_type is not None:
                    restr = SAndRestriction([
                        restr,
                        SPropertyRestriction(
                                RELOP_EQ,
                                PR_RECIPIENT_TYPE,
                                SPropValue(PR_RECIPIENT_TYPE, recipient_type)
                        )
                    ])
                restr = SSubRestriction(subobj, restr)

        else:
            defaults = [(store._name_id(proptag[:3]) | proptag[3])
                           if isinstance(proptag, tuple) else proptag
                               for proptag in DEFAULT_PROPTAGS[type_]]
            restr = SOrRestriction([
                       SContentRestriction(
                           FL_SUBSTRING | FL_IGNORECASE,
                           p,
                           SPropValue(p, self.value)
                       ) for p in defaults
                   ])

        if self.sign == '-':
            restr = SNotRestriction(restr)

        return restr
Example #12
0
def sync(server,
         syncobj,
         importer,
         state,
         max_changes,
         associated=False,
         window=None,
         begin=None,
         end=None,
         stats=None):
    log = server.log

    importer = TrackingContentsImporter(server, importer, stats)
    exporter = syncobj.OpenProperty(PR_CONTENTS_SYNCHRONIZER,
                                    IID_IExchangeExportChanges, 0, 0)

    stream = IStream()
    stream.Write(_bdec(state))
    stream.Seek(0, STREAM_SEEK_SET)

    restriction = None
    if window:
        # sync window of last N seconds
        propval = SPropValue(PR_MESSAGE_DELIVERY_TIME,
                             unixtime(int(time.time()) - window))
        restriction = SPropertyRestriction(RELOP_GE, PR_MESSAGE_DELIVERY_TIME,
                                           propval)

    elif begin or end:
        restrs = []
        if begin:
            propval = SPropValue(PR_MESSAGE_DELIVERY_TIME,
                                 unixtime(time.mktime(begin.timetuple())))
            restrs.append(
                SPropertyRestriction(RELOP_GE, PR_MESSAGE_DELIVERY_TIME,
                                     propval))
        if end:
            propval = SPropValue(PR_MESSAGE_DELIVERY_TIME,
                                 unixtime(time.mktime(end.timetuple())))
            restrs.append(
                SPropertyRestriction(RELOP_LT, PR_MESSAGE_DELIVERY_TIME,
                                     propval))
        if len(restrs) == 1:
            restriction = restrs[0]
        else:
            restriction = SAndRestriction(restrs)

    flags = SYNC_NORMAL | SYNC_UNICODE | SYNC_READ_STATE
    if associated:
        flags |= SYNC_ASSOCIATED
    try:
        if TESTING and os.getenv('PYKO_TEST_NOT_FOUND'):
            raise MAPIErrorNotFound()
        exporter.Config(stream, flags, importer, restriction, None, None, 0)
    except MAPIErrorNotFound:  # syncid purged because of 'sync_lifetime' option in server.cfg: get new syncid.
        log.warn(
            "Sync state does not exist on server (anymore); requesting new one"
        )

        syncid, changeid = struct.unpack('<II', _bdec(state))
        stream = IStream()
        stream.Write(struct.pack('<II', 0, changeid))
        stream.Seek(0, STREAM_SEEK_SET)

        exporter.Config(stream, flags, importer, restriction, None, None, 0)

    step = retry = changes = 0
    sleep_time = 0.4

    while True:
        try:
            try:
                if TESTING and os.getenv(
                        'PYKO_TEST_NETWORK_ERROR') and not importer.skip:
                    raise MAPIErrorNetworkError()
                (steps, step) = exporter.Synchronize(step)
            finally:
                importer.skip = False

            changes += 1
            retry = 0

            if (steps == step) or (max_changes and changes >= max_changes):
                break

        except MAPIError as e:
            log.warn(
                "Received a MAPI error or timeout (error=0x%x, retry=%d/5)",
                e.hr, retry)

            time.sleep(sleep_time)

            if sleep_time < 5.0:
                sleep_time *= 2.0

            if retry < 5:
                retry += 1
            else:
                log.error("Too many retries, skipping change")
                if stats is not None:
                    stats['errors'] += 1

                importer.skip = True  # in case of a timeout or other issue, try to skip the change after trying several times

                retry = 0

    exporter.UpdateState(stream)

    stream.Seek(0, STREAM_SEEK_SET)

    state = stream.Read(0xFFFFF)

    # because changes may be reordered for efficiency, we are not always
    # linearly following the change journal. so the current state cannot
    # always be represented as a single change id. instead, the current state
    # may contain changes which have been synced, relative to a certain
    # change id (so we have synced until this change id, plus these changes).

    # in pyko though, we always sync until there are no further changes,
    # so this should normally not occur.

    # TODO add an ICS flag to disable reordering!

    if len(state) != 8:
        log.error('sync state %d bytes, expect problems', len(state))

    return _benc(state)
Example #13
0
    def occurrences(self,
                    start=None,
                    end=None,
                    page_start=None,
                    page_limit=None,
                    order=None):
        count = 0
        pos = 0
        if start and end:
            startstamp = time.mktime(start.timetuple())
            endstamp = time.mktime(end.timetuple())

            # XXX use shortcuts and default type (database) to avoid MAPI snake wrestling
            NAMED_PROPS = [
                MAPINAMEID(PSETID_Appointment, MNID_ID, x)
                for x in (33285, 33293, 33294, 33315, 33301, 33333, 33334)
            ]
            ids = self.mapiobj.GetIDsFromNames(NAMED_PROPS, MAPI_CREATE)
            busystatus = ids[0] | PT_LONG
            startdate = ids[1] | PT_SYSTIME
            enddate = ids[2] | PT_SYSTIME
            recurring = ids[3] | PT_BOOLEAN
            all_day = ids[4] | PT_BOOLEAN
            clip_start = ids[5] | PT_SYSTIME
            clip_end = ids[6] | PT_SYSTIME

            restriction = SOrRestriction([
                # non-recurring: normal start/end
                SAndRestriction([
                    SPropertyRestriction(
                        RELOP_GT, enddate,
                        SPropValue(enddate, unixtime(startstamp))),
                    SPropertyRestriction(
                        RELOP_LT, startdate,
                        SPropValue(startdate, unixtime(endstamp))),
                ]),
                # recurring: range start/end
                SAndRestriction([
                    SPropertyRestriction(
                        RELOP_GT, clip_end,
                        SPropValue(clip_end, unixtime(startstamp))),
                    SPropertyRestriction(
                        RELOP_LT, clip_start,
                        SPropValue(clip_start, unixtime(endstamp))),
                ]),
                # exceptions: exception start/end in attachment
                SAndRestriction([
                    SPropertyRestriction(RELOP_EQ, recurring,
                                         SPropValue(recurring, True)),
                    SSubRestriction(
                        PR_MESSAGE_ATTACHMENTS,
                        SAndRestriction([
                            SPropertyRestriction(
                                RELOP_LT, PR_EXCEPTION_STARTTIME,
                                SPropValue(PR_EXCEPTION_STARTTIME,
                                           unixtime(endstamp))),
                            SPropertyRestriction(
                                RELOP_GT, PR_EXCEPTION_ENDTIME,
                                SPropValue(PR_EXCEPTION_ENDTIME,
                                           unixtime(startstamp))),
                        ]))
                ])
            ])

            columns = [
                PR_ENTRYID,
                PR_SUBJECT_W,  # watch out: table unicode data is max 255 chars
                PR_LAST_MODIFICATION_TIME,
                PR_CHANGE_KEY,
                startdate,
                enddate,
                recurring,
                all_day,
                busystatus
            ]

            table = Table(
                self.server,
                self.mapiobj,
                self.mapiobj.GetContentsTable(MAPI_DEFERRED_ERRORS),
                PR_CONTAINER_CONTENTS,
                columns=columns,
            )
            table.mapitable.Restrict(restriction, 0)
            for row in table.rows():
                item = _item.Item(self,
                                  entryid=row[0].value,
                                  content_flag=self.content_flag,
                                  cache=dict(zip(columns, row)))
                for occurrence in item.occurrences(start, end):
                    if page_start is None or pos >= page_start:
                        yield occurrence
                        count += 1
                    if page_limit is not None and count >= page_limit:
                        break
                    pos += 1
                if page_limit is not None and count >= page_limit:
                    break

        else:
            for item in self:
                for occurrence in item.occurrences(start, end):
                    if page_start is None or pos >= page_start:
                        yield occurrence
                        count += 1
                    if page_limit is not None and count >= page_limit:
                        break
                    pos += 1
                if page_limit is not None and count >= page_limit:
                    break
Example #14
0
    def prop_restriction(self, proptag, flag):
        # comparison operator
        if self.op in ('<', '>', '>=', '<=', '<>'):
            if PROP_TYPE(proptag) == PT_SYSTIME:
                d = dateutil.parser.parse(self.value)
                d = datetime_to_filetime(d)
                restr = SPropertyRestriction(
                            OP_RELOP[self.op],
                            proptag,
                            SPropValue(proptag, d)
                        )
            else:
                value = self.value
                unit = ''
                if [x for x in ('KB', 'MB', 'GB') if value.endswith(x)]:
                    value, unit = value[:-2], value[-2:]

                value = int(value)

                if unit == 'KB':
                    value *= 1024
                elif unit == 'MB':
                    value *= 1024**2
                elif unit == 'GB':
                    value *= 1024**3

                restr = SPropertyRestriction(
                            OP_RELOP[self.op],
                            proptag,
                            SPropValue(proptag, value)
                        )

        # contains/equals operator
        elif self.op in (':', '='):
            if PROP_TYPE(proptag) == PT_UNICODE:
                restr = SContentRestriction(
                            FL_SUBSTRING | FL_IGNORECASE,
                            proptag,
                            SPropValue(proptag, self.value)
                        )

            elif flag or PROP_TYPE(proptag) == PT_BOOLEAN:
                if flag:
                    restr = SBitMaskRestriction(
                                BMR_NEZ if self.value in ('yes', 'true') else BMR_EQZ,
                                proptag,
                                flag
                            )
                else:
                    restr = SPropertyRestriction(
                                RELOP_EQ,
                                proptag,
                                SPropValue(proptag, self.value in ('yes', 'true'))
                            )

            elif PROP_TYPE(proptag) == PT_MV_UNICODE:
                proptag2 = (proptag ^ PT_MV_UNICODE) | PT_UNICODE # funky!
                restr = SContentRestriction(
                            FL_SUBSTRING | FL_IGNORECASE,
                            proptag,
                            SPropValue(proptag2, self.value)
                        )

            elif PROP_TYPE(proptag) in (PT_SHORT, PT_LONG, PT_LONGLONG, PT_FLOAT, PT_DOUBLE):
                conv = float if PROP_TYPE(proptag) in (PT_FLOAT, PT_DOUBLE) else int
                if '..' in self.value:
                    val1, val2 = self.value.split('..')
                    restr = SAndRestriction([
                        SPropertyRestriction(
                            RELOP_GE,
                            proptag,
                            SPropValue(proptag, conv(val1))
                        ),
                        SPropertyRestriction(
                            RELOP_LT,
                            proptag,
                            SPropValue(proptag, conv(val2))
                        )
                    ])
                else:
                    restr = SPropertyRestriction(
                                RELOP_EQ,
                                proptag,
                                SPropValue(proptag, conv(self.value))
                            )

            elif PROP_TYPE(proptag) == PT_SYSTIME:
                if self.value == 'today':
                    d = datetime.datetime.now().date()
                    d2 = d + datetime.timedelta(days=1)
                    restr = _interval_restriction(proptag, d, d2)

                elif self.value == 'yesterday':
                    d2 = datetime.datetime.now().date()
                    d = d2 - datetime.timedelta(days=1)
                    restr = _interval_restriction(proptag, d, d2)

                elif self.value == 'this week':
                    d2 = datetime.datetime.now()
                    d = d2.date() - datetime.timedelta(days=d2.weekday())
                    restr = _interval_restriction(proptag, d, d2)

                elif self.value == 'this month':
                    d2 = datetime.datetime.now()
                    d = d2.date() - datetime.timedelta(days=d2.day-1)
                    restr = _interval_restriction(proptag, d, d2)

                elif self.value == 'last month':
                    now = datetime.datetime.now()
                    d2 = now.date() - datetime.timedelta(days=now.day-1)
                    d = (d2 - datetime.timedelta(days=1)).replace(day=1)
                    restr = _interval_restriction(proptag, d, d2)

                elif self.value == 'this year':
                    d2 = datetime.datetime.now()
                    d = datetime.datetime(d2.year, 1, 1)
                    restr = _interval_restriction(proptag, d, d2)

                elif self.value == 'last year':
                    now = datetime.datetime.now()
                    d2 = datetime.datetime(now.year, 1, 1)
                    d = datetime.datetime(d2.year-1, 1, 1)
                    restr = _interval_restriction(proptag, d, d2)

                elif '..' in self.value:
                    date1, date2 = self.value.split('..') # TODO hours etc
                    d = dateutil.parser.parse(date1)
                    d2 = dateutil.parser.parse(date2)
                    restr = _interval_restriction(proptag, d, d2)

                else:
                    d = dateutil.parser.parse(self.value) # TODO hours etc
                    d2 = d + datetime.timedelta(days=1)
                    restr = _interval_restriction(proptag, d, d2)

        return restr
Example #15
0
def sync(server, syncobj, importer, state, log, max_changes, associated=False, window=None, begin=None, end=None, stats=None):
    importer = TrackingContentsImporter(server, importer, log, stats)
    exporter = syncobj.OpenProperty(PR_CONTENTS_SYNCHRONIZER, IID_IExchangeExportChanges, 0, 0)

    stream = IStream()
    stream.Write(_bdec(state))
    stream.Seek(0, STREAM_SEEK_SET)

    restriction = None
    if window:
        # sync window of last N seconds
        propval = SPropValue(PR_MESSAGE_DELIVERY_TIME, unixtime(int(time.time()) - window))
        restriction = SPropertyRestriction(RELOP_GE, PR_MESSAGE_DELIVERY_TIME, propval)

    elif begin or end:
        restrs = []
        if begin:
            propval = SPropValue(PR_MESSAGE_DELIVERY_TIME, unixtime(time.mktime(begin.timetuple())))
            restrs.append(SPropertyRestriction(RELOP_GE, PR_MESSAGE_DELIVERY_TIME, propval))
        if end:
            propval = SPropValue(PR_MESSAGE_DELIVERY_TIME, unixtime(time.mktime(end.timetuple())))
            restrs.append(SPropertyRestriction(RELOP_LT, PR_MESSAGE_DELIVERY_TIME, propval))
        if len(restrs) == 1:
            restriction = restrs[0]
        else:
            restriction = SAndRestriction(restrs)

    flags = SYNC_NORMAL | SYNC_UNICODE
    if associated:
        flags |= SYNC_ASSOCIATED
    try:
        exporter.Config(stream, flags, importer, restriction, None, None, 0)
    except MAPIErrorNotFound: # syncid purged because of 'sync_lifetime' option in server.cfg: get new syncid.
        if log:
            log.warn("Sync state does not exist on server (anymore); requesting new one")

        syncid, changeid = struct.unpack('<II', _bdec(state))
        stream = IStream()
        stream.Write(struct.pack('<II', 0, changeid))
        stream.Seek(0, STREAM_SEEK_SET)

        exporter.Config(stream, flags, importer, restriction, None, None, 0)

    step = retry = changes = 0
    sleep_time = 0.4

    while True:
        try:
            try:
                (steps, step) = exporter.Synchronize(step)
            finally:
                importer.skip = False

            changes += 1
            retry = 0

            if (steps == step) or (max_changes and changes >= max_changes):
                break

        except MAPIError as e:
            if log:
                log.warn("Received a MAPI error or timeout (error=0x%x, retry=%d/5)", e.hr, retry)

            time.sleep(sleep_time)

            if sleep_time < 5.0:
                sleep_time *= 2.0

            if retry < 5:
                retry += 1
            else:
                if log:
                    log.error("Too many retries, skipping change")
                if stats:
                    stats['errors'] += 1

                importer.skip = True # in case of a timeout or other issue, try to skip the change after trying several times

                retry = 0

    exporter.UpdateState(stream)

    stream.Seek(0, STREAM_SEEK_SET)
    return _benc(stream.Read(0xFFFFF))
Example #16
0
    def users(self, remote=False, system=False, parse=True, page_start=None,
              page_limit=None, order=None, hidden=True, inactive=True,
              _server=None, _company=None, query=None):
        """Return all :class:`users <User>` on server.

        :param remote: Include users on remote server nodes (default False)
        :param system: Include system users (default False)
        :param hidden: Include hidden users (default True)
        :param inactive: Include inactive users (default True)
        :param query: Search query (optional)
        """

        # users specified on command-line
        if parse and getattr(self.options, 'users', None):
            for username in self.options.users:
                yield _user.User(username, self)
            return

        # global listing on multitenant setup
        if self.multitenant and not _company:
            if (page_limit is None and page_start is None and \
                query is None and order is None):
                for company in self.companies():
                    for user in company.users(remote=remote, system=system,
                            parse=parse, hidden=hidden, inactive=inactive):
                        yield user
                return
            else:
                raise NotSupportedError('unsupported method of user listing')

        # find right addressbook container (global or for company)
        gab = self.gab

        restriction = SAndRestriction([
            SPropertyRestriction(RELOP_EQ, PR_OBJECT_TYPE,
                SPropValue(PR_OBJECT_TYPE, MAPI_MAILUSER)),
            SPropertyRestriction(RELOP_NE, PR_DISPLAY_TYPE,
                SPropValue(PR_DISPLAY_TYPE, DT_REMOTE_MAILUSER))
        ])

        if _company and _company.name != 'Default':
            htable = gab.GetHierarchyTable(0)
            htable.SetColumns([PR_ENTRYID], TBL_BATCH)
            # TODO(longsleep): Find a way to avoid finding the limited table,
            # if the gab is itself already limited to the same.
            try:
                htable.FindRow(SContentRestriction(
                        FL_FULLSTRING | FL_IGNORECASE,
                        PR_DISPLAY_NAME_W,
                        SPropValue(PR_DISPLAY_NAME_W, _company.name)),
                    BOOKMARK_BEGINNING, 0)
            except MAPIErrorNotFound:
                # If not, found we do not have permission to access that row. and
                # instead fall back to the gab and let it handle access and
                # limits based on user authentication.
                container = gab
            else:
                row = htable.QueryRows(1, 0)[0]
                container = gab.OpenEntry(row[0].Value, None, 0)
        else:
            container = gab

        table = container.GetContentsTable(0)
        table.SetColumns([PR_ENTRYID], MAPI_UNICODE)

        # apply query restriction
        if query is not None:
            store = _store.Store(mapiobj=self.mapistore, server=self)
            restriction.lpRes.append(
                _query_to_restriction(query, 'user', store).mapiobj)

        table.Restrict(restriction, TBL_BATCH)

        # TODO apply order argument here

        def include(user, ecuser):
            return ((system or user.name != 'SYSTEM') and
                    (remote or ecuser.Servername in (self.name, '')) and
                    (hidden or not user.hidden) and
                    (inactive or user.active))

        # TODO simpler/faster if sa.GetUserList could do restrictions,
        # ordering, pagination..
        # since then we can always work with ecuser objects in bulk
        userid_ecuser = None
        if page_limit is None and page_start is None and query is None:
            userid_ecuser = {}
            if _company and _company.name != 'Default':
                ecusers = self.sa.GetUserList(
                    _company._eccompany.CompanyID, MAPI_UNICODE)
            else:
                ecusers = self.sa.GetUserList(None, MAPI_UNICODE)
            for ecuser in ecusers:
                userid_ecuser[ecuser.UserID] = ecuser

                # fast path: just get all users
                if order is None:
                    user = _user.User(server=self, ecuser=ecuser)
                    if include(user, ecuser):
                        yield user
            if order is None:
                return

        # loop over rows and paginate
        pos = 0
        count = 0

        while True:
            rows = table.QueryRows(50, 0)
            if not rows:
                break
            for row in rows:
                userid = row[0].Value
                if userid_ecuser is None:
                    try:
                        user = _user.User(server=self, userid=_benc(userid))
                    except NotFoundError:
                        continue
                else:
                    try:
                        user = _user.User(
                            server=self, ecuser=userid_ecuser[userid])
                    except KeyError:
                        continue

                if include(user, user._ecuser):
                    if page_start is None or pos >= page_start:
                        yield user
                        count += 1
                    if page_limit is not None and count >= page_limit:
                        return
                    pos += 1