\FF\D8\FF\E0\00JFIF\00\00\00d\00d\00\00\FF\FE\00\border bs:0 bc:#000000 ps:0 pc:#ffffff es:0 ec:#000000 ck:feee6c715d26fd9f38b0ca4278c05026\FF\DB\00C\00P7<F<2PFAFZUP_xxnnx\F5\AF\B9\91\C8\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\DB\00CUZZxix‚\EB\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\C0\00\00b\00d\00\FF\C4\00\00\00\00\00\00\00\00\00\00\00\00\00\00\FF\C4\00\00\00\00\00\00\00\00\00\00\001A!Q\FF\C4\00\00\00\00\00\00\00\00\00\00\00\00\00\FF\C4\00\00\00\00\00\00\00\00\00\00!1AQ\FF\DA\00\00\00?\00\F6\00\00\00\00\00\00\00\00\00\00\A0\00\00\C5\CF\F8\E7Ó\FCjD~\B9^\AD\%\B3]\D8cs-\BBs,-\A0\00"\80\00%\B83rÏ^K~F\A4ea\00E\00\C6\EE=u\B1\9B\D1\00@\00r\BE8\F9:\FE5#.M\00\E7\CB\C9q\CBq\D6\F2\BE\B7\C7>\C9n5×\D6?\BDê\9Ds\EBp\9F[`8m\B7)o\B5\E8\E6I\99\FE3]]A2\BA\8Cw\D6E\93\\DEv\C8\009\F2\F1NI?uc\\F5\EA\96k\xN<~buv\EA\C8\D7	\8B\84\CEcxI\BBg\AE\9E=\D6+n\EC\80\C8A\8C\AE\EB\CF\D5\DA\E9"2\A4\B9j5\EB\F3W\B63\96\B30Yu\DA\FC8\ED\DF\E7Ms\FB\F1\8E\B3\FA\EA\E8\E6(\883zs\F2_\8DFk\8Bh
\00\8C\DCw\D3R\B5+6X\BA\B2\C4j\AB0\B4\FCMw\C2I\8E\9B\E3\A9~9u\FA\D3l\80\C8%p\EE\FDn2€
\00$\FEj\C4e\A9\DB\~\95\A7\A5\80EK\BB\8DDsP\00@@AD'k\CF\E8\DB\D2(\80\9AK\D3\85\D6lb\F2\BA\8C*\80\00)\95
59\A3R:\F3\CE"\B6\80\88\00\00i1u4ê\E9\F2á\A6\A2\FACM\93WMb*\E0*\00\00\00\00\00(\A8\80\00\00\80\00\00\00\00\00\00\00\00\00\FF\D9<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>C///</title>
</head>
# -*- coding: utf-8 -*-
"""
ldap.syncrepl - for implementing syncrepl consumer (see RFC 4533)

See http://www.python-ldap.org/ for project details.

$Id: syncrepl.py,v 1.3 2012/08/09 07:18:31 stroeder Exp $
"""

#__all__ = [
#  '',
#  '',
#]

from uuid import UUID

# Imports from python-ldap 2.4+
import ldap.ldapobject
from ldap.controls import RequestControl,ResponseControl,KNOWN_RESPONSE_CONTROLS

# Imports from pyasn1
from pyasn1.type import tag,namedtype,namedval,univ,constraint
from pyasn1.codec.ber import encoder,decoder

__all__ = [ 'SyncreplConsumer' ]

# RFC 4533:
#
#       syncUUID ::= OCTET STRING (SIZE(16))
#       syncCookie ::= OCTET STRING

class syncUUID(univ.OctetString):
    subtypeSpec = constraint.ValueSizeConstraint(16,16)

class syncCookie(univ.OctetString):
    pass

# 2.2.  Sync Request Control
#
#    The Sync Request Control is an LDAP Control [RFC4511] where the
#    controlType is the object identifier 1.3.6.1.4.1.4203.1.9.1.1 and the
#    controlValue, an OCTET STRING, contains a BER-encoded
#    syncRequestValue.  The criticality field is either TRUE or FALSE.
#
#       syncRequestValue ::= SEQUENCE {
#           mode ENUMERATED {
#               -- 0 unused
#               refreshOnly       (1),
#               -- 2 reserved
#               refreshAndPersist (3)
#           },
#           cookie     syncCookie OPTIONAL,
#           reloadHint BOOLEAN DEFAULT FALSE
#       }
#
#    The Sync Request Control is only applicable to the SearchRequest
#    Message.

class syncRequestMode(univ.Enumerated):
    namedValues = namedval.NamedValues(
        ('refreshOnly', 1),
        ('refreshAndPersist', 3)
    )
    subtypeSpec = univ.Enumerated.subtypeSpec + constraint.SingleValueConstraint(1,3)

class syncRequestValue(univ.Sequence):
    componentType = namedtype.NamedTypes(
        namedtype.NamedType('mode', syncRequestMode()),
        namedtype.OptionalNamedType('cookie', syncCookie()),
        namedtype.DefaultedNamedType('reloadHint', univ.Boolean(False))
    )

class SyncRequestControl(RequestControl):
    controlType = '1.3.6.1.4.1.4203.1.9.1.1'

    def __init__(self, criticality=1, cookie=None, mode='refreshOnly', reloadHint=False):
        self.criticality = criticality
        self.cookie = cookie
        self.mode = mode
        self.reloadHint = reloadHint

    def encodeControlValue(self):
        r = syncRequestValue()
        r.setComponentByName('mode', syncRequestMode(self.mode))
        if self.cookie is not None:
            r.setComponentByName('cookie', syncCookie(self.cookie))
        if self.reloadHint:
            r.setComponentbyName('reloadHint', univ.Boolean(self.reloadHint))
        return encoder.encode(r)

# 2.3.  Sync State Control
#
#    The Sync State Control is an LDAP Control [RFC4511] where the
#    controlType is the object identifier 1.3.6.1.4.1.4203.1.9.1.2 and the
#    controlValue, an OCTET STRING, contains a BER-encoded syncStateValue.
#    The criticality is FALSE.
#
#       syncStateValue ::= SEQUENCE {
#           state ENUMERATED {
#               present (0),
#               add (1),
#               modify (2),
#               delete (3)
#           },
#           entryUUID syncUUID,
#           cookie    syncCookie OPTIONAL
#       }
#
#    The Sync State Control is only applicable to SearchResultEntry and
#    SearchResultReference Messages.

class syncStateOp(univ.Enumerated):
    namedValues = namedval.NamedValues(
        ('present', 0),
        ('add', 1),
        ('modify', 2),
        ('delete', 3)
    )
    subtypeSpec = univ.Enumerated.subtypeSpec + constraint.SingleValueConstraint(0,1,2,3)

class syncStateValue(univ.Sequence):
    componentType = namedtype.NamedTypes(
        namedtype.NamedType('state', syncStateOp()),
        namedtype.NamedType('entryUUID', syncUUID()),
        namedtype.OptionalNamedType('cookie', syncCookie())
    )

class SyncStateControl(ResponseControl):
    controlType = '1.3.6.1.4.1.4203.1.9.1.2'
    opnames = ( 'present', 'add', 'modify', 'delete' )

    def decodeControlValue(self, encodedControlValue):
        d = decoder.decode(encodedControlValue, asn1Spec = syncStateValue())
        state = d[0].getComponentByName('state')
        uuid = UUID(bytes=d[0].getComponentByName('entryUUID'))
        self.cookie = d[0].getComponentByName('cookie')
        self.state = self.__class__.opnames[int(state)]
        self.entryUUID = str(uuid)
        if self.cookie is not None:
            self.cookie = str(self.cookie)

KNOWN_RESPONSE_CONTROLS[SyncStateControl.controlType] = SyncStateControl

# 2.4.  Sync Done Control
#
#    The Sync Done Control is an LDAP Control [RFC4511] where the
#    controlType is the object identifier 1.3.6.1.4.1.4203.1.9.1.3 and the
#    controlValue contains a BER-encoded syncDoneValue.  The criticality
#    is FALSE (and hence absent).
#
#       syncDoneValue ::= SEQUENCE {
#           cookie          syncCookie OPTIONAL,
#           refreshDeletes  BOOLEAN DEFAULT FALSE
#       }
#
#    The Sync Done Control is only applicable to the SearchResultDone
#    Message.

class syncDoneValue(univ.Sequence):
    componentType = namedtype.NamedTypes(
        namedtype.OptionalNamedType('cookie', syncCookie()),
        namedtype.DefaultedNamedType('refreshDeletes', univ.Boolean(False))
    )

class SyncDoneControl(ResponseControl):
    controlType = '1.3.6.1.4.1.4203.1.9.1.3'

    def decodeControlValue(self, encodedControlValue):
        d = decoder.decode(encodedControlValue, asn1Spec = syncDoneValue())
        self.cookie = d[0].getComponentByName('cookie')
        self.refreshDeletes = d[0].getComponentByName('refreshDeletes')
        if self.cookie is not None:
            self.cookie = str(self.cookie)
        if self.refreshDeletes is not None:
            self.refreshDeletes = bool(self.refreshDeletes)

KNOWN_RESPONSE_CONTROLS[SyncDoneControl.controlType] = SyncDoneControl


# 2.5.  Sync Info Message
#
#    The Sync Info Message is an LDAP Intermediate Response Message
#    [RFC4511] where responseName is the object identifier
#    1.3.6.1.4.1.4203.1.9.1.4 and responseValue contains a BER-encoded
#    syncInfoValue.  The criticality is FALSE (and hence absent).
#
#       syncInfoValue ::= CHOICE {
#           newcookie      [0] syncCookie,
#           refreshDelete  [1] SEQUENCE {
#               cookie         syncCookie OPTIONAL,
#               refreshDone    BOOLEAN DEFAULT TRUE
#           },
#           refreshPresent [2] SEQUENCE {
#               cookie         syncCookie OPTIONAL,
#               refreshDone    BOOLEAN DEFAULT TRUE
#           },
#           syncIdSet      [3] SEQUENCE {
#               cookie         syncCookie OPTIONAL,
#               refreshDeletes BOOLEAN DEFAULT FALSE,
#               syncUUIDs      SET OF syncUUID
#           }
#       }
#

class refreshDelete(univ.Sequence):
    componentType = namedtype.NamedTypes(
        namedtype.OptionalNamedType('cookie', syncCookie()),
        namedtype.DefaultedNamedType('refreshDone', univ.Boolean(True))
    )

class refreshPresent(univ.Sequence):
    componentType = namedtype.NamedTypes(
        namedtype.OptionalNamedType('cookie', syncCookie()),
        namedtype.DefaultedNamedType('refreshDone', univ.Boolean(True))
    )

class syncUUIDs(univ.SetOf):
    componentType = syncUUID()

class syncIdSet(univ.Sequence):
    componentType = namedtype.NamedTypes(
        namedtype.OptionalNamedType('cookie', syncCookie()),
        namedtype.DefaultedNamedType('refreshDeletes', univ.Boolean(False)),
        namedtype.NamedType('syncUUIDs', syncUUIDs())
    )

class syncInfoValue(univ.Choice):
    componentType = namedtype.NamedTypes(
        namedtype.NamedType(
            'newcookie',
            syncCookie().subtype(
                implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)
            )
        ),
        namedtype.NamedType(
            'refreshDelete',
            refreshDelete().subtype(
                implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)
            )
        ),
        namedtype.NamedType(
            'refreshPresent',
            refreshPresent().subtype(
                implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2)
            )
        ),
        namedtype.NamedType(
            'syncIdSet',
            syncIdSet().subtype(
                implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)
            )
        )
    )

class SyncInfoMessage:
    responseName = '1.3.6.1.4.1.4203.1.9.1.4'

    def __init__(self, encodedMessage):
        d = decoder.decode(encodedMessage, asn1Spec = syncInfoValue())
        self.newcookie = None
        self.refreshDelete = None
        self.refreshPresent = None
        self.syncIdSet = None

        for attr in [ 'newcookie', 'refreshDelete', 'refreshPresent', 'syncIdSet']:
            comp = d[0].getComponentByName(attr)

            if comp is not None:

                if attr == 'newcookie':
                    self.newcookie = str(comp)
                    return

                val = dict()

                cookie = comp.getComponentByName('cookie')
                if cookie is not None:
                    val['cookie'] = str(cookie)

                if attr.startswith('refresh'):
                    val['refreshDone'] = bool(comp.getComponentByName('refreshDone'))
                elif attr == 'syncIdSet':
                    uuids = []
                    ids = comp.getComponentByName('syncUUIDs')
                    for i in range(len(ids)):
                        uuid = UUID(bytes=str(ids.getComponentByPosition(i)))
                        uuids.append(str(uuid))
                    val['syncUUIDs'] = uuids
                    val['refreshDeletes'] = bool(comp.getComponentByName('refreshDeletes'))

                setattr(self,attr,val)
                return


class SyncreplConsumer:
    """
    SyncreplConsumer - LDAP syncrepl consumer object.
    """

    def syncrepl_search(self, base, scope, mode='refreshOnly', cookie=None, **search_args):
        """
        Starts syncrepl search operation.

        base, scope, and search_args are passed along to
        self.search_ext unmodified (aside from adding a Sync
        Request control to any serverctrls provided).

        mode provides syncrepl mode. Can be 'refreshOnly'
        to finish after synchronization, or
        'refreshAndPersist' to persist (continue to
        receive updates) after synchronization.

        cookie: an opaque value representing the replication
        state of the client.  Subclasses should override
        the syncrepl_set_cookie() and syncrepl_get_cookie()
        methods to store the cookie appropriately, rather than
        passing it.

        """
        if cookie is None:
            cookie = self.syncrepl_get_cookie()

        syncreq = SyncRequestControl(cookie=cookie, mode=mode)

        if 'serverctrls' in search_args:
            search_args['serverctrls'] += [syncreq]
        else:
            search_args['serverctrls'] = [syncreq]

        self.__refreshDone = False
        return self.search_ext(base, scope, **search_args)

    def syncrepl_poll(self, msgid=-1, timeout=None, all=0):
        """
        polls for and processes responses to the syncrepl_search() operation.
        Returns False when operation finishes, True if it is in progress, or
        raises an exception on error.

        If timeout is specified, raises ldap.TIMEOUT in the event of a timeout.

        If all is set to a nonzero value, poll() will return only when finished
        or when an exception is raised.

        """
        while True:
            type, msg, mid, ctrls, n, v = self.result4(
                    msgid=msgid, timeout=timeout,
                    add_intermediates=1, add_ctrls=1, all = 0
                    )

            if type == 101:
                # search result. This marks the end of a refreshOnly session.
                # look for a SyncDone control, save the cookie, and if necessary
                # delete non-present entries.
                for c in ctrls:
                    if c.__class__.__name__ != 'SyncDoneControl':
                        continue
                    self.syncrepl_present(None,refreshDeletes=c.refreshDeletes)
                    if c.cookie is not None:
                        self.syncrepl_set_cookie(c.cookie)

                return False

            elif type == 100:
                # search entry with associated SyncState control
                for m in msg:
                    dn, attrs, ctrls = m
                    for c in ctrls:
                        if c.__class__.__name__ != 'SyncStateControl':
                            continue
                        if c.state == 'present':
                            self.syncrepl_present([c.entryUUID])
                        elif c.state == 'delete':
                            self.syncrepl_delete([c.entryUUID])
                        else:
                            self.syncrepl_entry(dn, attrs, c.entryUUID)
                            if self.__refreshDone is False:
                                self.syncrepl_present([c.entryUUID])
                        if c.cookie is not None:
                            self.syncrepl_set_cookie(c.cookie)
                        break

            elif type == 121:
                # Intermediate message. If it is a SyncInfoMessage, parse it
                for m in msg:
                    rname, resp, ctrls = m
                    if rname != SyncInfoMessage.responseName:
                        continue
                    sim = SyncInfoMessage(resp)
                    if sim.newcookie is not None:
                        self.syncrepl_set_cookie(sim.newcookie)
                    elif sim.refreshPresent is not None:
                        self.syncrepl_present(None, refreshDeletes=False)
                        if 'cookie' in sim.refreshPresent:
                            self.syncrepl_set_cookie(sim.refreshPresent['cookie'])
                        if sim.refreshPresent['refreshDone']:
                            self.__refreshDone = True
                            self.syncrepl_refreshdone()
                    elif sim.refreshDelete is not None:
                        self.syncrepl_present(None, refreshDeletes=True)
                        if 'cookie' in sim.refreshDelete:
                            self.syncrepl_set_cookie(sim.refreshDelete['cookie'])
                        if sim.refreshDelete['refreshDone']:
                            self.__refreshDone = True
                            self.syncrepl_refreshdone()
                    elif sim.syncIdSet is not None:
                        if sim.syncIdSet['refreshDeletes'] is True:
                            self.syncrepl_delete(sim.syncIdSet['syncUUIDs'])
                        else:
                            self.syncrepl_present(sim.syncIdSet['syncUUIDs'])
                        if 'cookie' in sim.syncIdSet:
                            self.syncrepl_set_cookie(sim.syncIdSet['cookie'])
                        pass

            if all == 0:
                return True


    # virtual methods -- subclass must override these to do useful work

    def syncrepl_set_cookie(self, cookie):
        """
        Called by syncrepl_poll() to store a new cookie provided by the server.
        """
        pass

    def syncrepl_get_cookie(self):
        """
        Called by syncrepl_search() to retreive the cookie stored by syncrepl_s