'Fetch updates from the peer repository and import them
into the local database (active pull)
@param task: the synchronization task (sync_task Row)
@param onconflict: callback for automatic conflict resolution
@return: tuple (error, mtime), with error=None if successful,
else error=message, and mtime=modification timestamp
of the youngest record sent'

def pull(self, task, onconflict=None):

    _debug = current.log.debug
    repository = self.repository
    xml = current.xml
    config = repository.config
    resource_name = task.resource_name
    _debug(('S3Sync:    pull    %s    from    %s' % (resource_name, repository.url)))
    url = ('%s/sync/sync.xml?resource=%s&repository=%s' % (repository.url, resource_name, config.uuid))
    last_pull = task.last_pull
    if (last_pull and (task.update_policy not in ('THIS', 'OTHER'))):
        url += ('&msince=%s' % s3_encode_iso_datetime(last_pull))
    if (task.components is False):
        url += '&components=None'
    url += '&include_deleted=True'
    filters = current.sync.get_filters(task.id)
    resource_name = task.resource_name
    from urllib import quote
    for tablename in filters:
        prefix = ('~' if ((not tablename) or (tablename == resource_name)) else tablename)
        for (k, v) in filters[tablename].items():
            urlfilter = ('[%s]%s=%s' % (prefix, k, quote(v)))
            url += ('&%s' % urlfilter)
    _debug(('...pull    from    URL    %s' % url))
    url_split = url.split('://', 1)
    if (len(url_split) == 2):
        protocol = url_split[0]
    else:
        protocol = 'http'
    req = urllib2.Request(url=url)
    handlers = []
    proxy = (repository.proxy or config.proxy or None)
    if proxy:
        _debug(('S3Sync:    pull,    using    proxy=%s' % proxy))
        proxy_handler = urllib2.ProxyHandler({protocol: proxy})
        handlers.append(proxy_handler)
    username = repository.username
    password = repository.password
    if (username and password):
        import base64
        base64string = base64.encodestring(('%s:%s' % (username, password)))[:(-1)]
        req.add_header('Authorization', ('Basic    %s' % base64string))
        passwd_manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
        passwd_manager.add_password(realm=None, uri=url, user=username, passwd=password)
        auth_handler = urllib2.HTTPBasicAuthHandler(passwd_manager)
        handlers.append(auth_handler)
    if handlers:
        opener = urllib2.build_opener(*handlers)
        urllib2.install_opener(opener)
    remote = False
    action = 'fetch'
    response = None
    output = None
    log = repository.log
    try:
        f = urllib2.urlopen(req)
    except urllib2.HTTPError as e:
        result = log.ERROR
        remote = True
        code = e.code
        message = e.read()
        try:
            message_json = json.loads(message)
            message = message_json.get('message', message)
        except:
            pass
        message = ('<message>%s</message>' % message)
        try:
            markup = etree.XML(message)
            message = markup.xpath('.//text()')
            if message:
                message = '    '.join(message)
            else:
                message = ''
        except etree.XMLSyntaxError:
            pass
        output = xml.json_message(False, code, message, tree=None)
    except:
        result = log.FATAL
        code = 400
        message = sys.exc_info()[1]
        output = xml.json_message(False, code, message)
    else:
        result = log.SUCCESS
        response = f
    mtime = None
    if response:
        strategy = task.strategy
        update_policy = task.update_policy
        conflict_policy = task.conflict_policy
        success = True
        message = ''
        action = 'import'
        resource = current.s3db.resource(resource_name)
        if onconflict:
            onconflict_callback = (lambda item: onconflict(item, repository, resource))
        else:
            onconflict_callback = None
        count = 0
        try:
            success = resource.import_xml(response, ignore_errors=True, strategy=strategy, update_policy=update_policy, conflict_policy=conflict_policy, last_sync=last_pull, onconflict=onconflict_callback)
            count = resource.import_count
        except IOError as e:
            result = log.FATAL
            message = ('%s' % e)
            output = xml.json_message(False, 400, message)
        except Exception as e:
            result = log.FATAL
            message = ('Uncaught    Exception    During    Import:    %s' % traceback.format_exc())
            output = xml.json_message(False, 500, sys.exc_info()[1])
        mtime = resource.mtime
        if (resource.error_tree is not None):
            result = log.WARNING
            message = ('%s' % resource.error)
            for element in resource.error_tree.findall('resource'):
                for field in element.findall('data[@error]'):
                    error_msg = field.get('error', None)
                    if error_msg:
                        msg = ('(UID:    %s)    %s.%s=%s:    %s' % (element.get('uuid', None), element.get('name', None), field.get('field', None), field.get('value', field.text), field.get('error', None)))
                        message = ('%s,    %s' % (message, msg))
        if (not success):
            result = log.FATAL
            if (not message):
                message = ('%s' % resource.error)
            output = xml.json_message(False, 400, message)
            mtime = None
        elif (not message):
            message = ('Data    imported    successfully    (%s    records)' % count)
    elif (result == log.SUCCESS):
        result = log.ERROR
        remote = True
        message = 'No    data    received    from    peer'
    log.write(repository_id=repository.id, resource_name=task.resource_name, transmission=log.OUT, mode=log.PULL, action=action, remote=remote, result=result, message=message)
    _debug(('S3Sync:    import    %s:    %s' % (result, message)))
    return (output, mtime)
