from AccessControl import getSecurityManager
from StringIO import StringIO
from ZODB.POSException import ConflictError
from ftw.bridge.client.exceptions import MaintenanceError
from ftw.bridge.client.interfaces import IBrainSerializer
from ftw.bridge.client.interfaces import IBridgeConfig
from ftw.bridge.client.interfaces import IBridgeRequest
from ftw.bridge.client.interfaces import PORTAL_URL_PLACEHOLDER
from ftw.bridge.client.utils import json
from import getSite
from zope.component import getUtility
from zope.interface import implements
import os.path
import sys
import types
import urllib
import urllib2
import urlparse
def replace_placeholder_in_data(data, public_url):
    if isinstance(data, types.StringTypes):
        return data.replace(PORTAL_URL_PLACEHOLDER, public_url)
    elif isinstance(data, types.DictType):
        for key, value in data.items():
            data[key] = replace_placeholder_in_data(value, public_url)
        return data
    elif isinstance(data, (types.ListType, types.TupleType)):
        new = []
        for value in data:
            new.append(replace_placeholder_in_data(value, public_url))
        return new
        return data
class BridgeRequest(object):
    def __call__(self, target, path, headers=None, data=None, silent=False):
        """Makes a request to a remote client.
            return self._request(target, path, headers, data)
        except (urllib2.HTTPError, urllib2.URLError), exc:
            if getattr(exc, 'code', None) == 503:
                raise MaintenanceError()
            elif silent:
                return None
    def get_json(self, *args, **kwargs):
        """Makes a request to a JSON view on a remote client, return the
        converted python objects.
        response = self(*args, **kwargs)
        if response is None:
            return None
            return json.loads(
    def search_catalog(self, target, query, limit=50, batching_start=0):
        path = '@@bridge-search-catalog'
        query['batching_start'] = batching_start
        data = {'query': json.dumps(query),
                'limit': limit}
        response = self(target, path, data=data)
        data = json.loads(
        total_length = int(response.headers.get(
                'X-total_results_length', '0'))
        serializer = getUtility(IBrainSerializer)
        return serializer.deserialize_brains(data, total_length)
    def _get_url(self, config, target, path):
        if path.startswith('/'):
            path = path[1:]
        return '/'.join((config.get_url(), target, path))
    def _get_headers(self, config, additional_headers):
        headers = {
            'X-BRIDGE-ORIGIN': config.get_client_id(),
            'X-BRIDGE-AC': self._get_current_userid()}
        if additional_headers:
        return headers
    def _get_current_userid(self):
        return getSecurityManager().getUser().getId()
    def _request(self, target, path, headers, data):
        config = getUtility(IBridgeConfig)
        if config.get_client_id() == target:
            return self._do_traverse(path, headers, data)
            url = self._get_url(config, target, path)
            headers = self._get_headers(config, headers)
            return self._do_request(url, headers, data)
    def _do_request(self, url, headers, data):
        handler = urllib2.ProxyHandler({})
        opener = urllib2.build_opener(handler)
        if data:
            data = urllib.urlencode(data)
        request = urllib2.Request(url, data, headers)
    def _do_traverse(self, path, headers, data):
        portal = getSite()
        public_url = portal.absolute_url() + '/'
        parsed_path = urlparse.urlparse(path)
        form_data = dict(urlparse.parse_qsl(parsed_path.query))
        if data:
        replace_placeholder_in_data(form_data, public_url)
        request = portal.REQUEST
        # we need to back up the request data and set them new for the
        # view which is called with the same request (restrictedTraverse)
        ori_form = request.form
        ori_response_headers = request.RESPONSE.headers
        request.RESPONSE.headers = {}
        request.form = form_data
        response_url = os.path.join(portal.absolute_url(), parsed_path.path)
        response = None
            response_data = portal.restrictedTraverse(parsed_path.path)()
        except ConflictError:
        except Exception, exc:
            # restore the request
            request.form = ori_form
            request.RESPONSE.headers = ori_response_headers
            code = 500
            msg = str(exc)
            hdrs = {}
            fp = StringIO(msg)
            raise urllib2.HTTPError(response_url, code, msg, hdrs, fp)
            # restore the request
            response_headers = request.RESPONSE.headers
            request.form = ori_form
            request.RESPONSE.headers = ori_response_headers
            response_data = StringIO(response_data.replace(
                    PORTAL_URL_PLACEHOLDER, public_url))
            response = urllib.addinfourl(
                response_data, headers=response_headers,
                url=response_url, code=200)
        return response