예제 #1
0
    def GET_happening_now(self):
        """ Get some basic information about the currently featured live thread.

            Returns an empty 204 response for api requests if no thread is currently featured.

            See also: [/api/live/*thread*/about](#GET_api_live_{thread}_about).
        """

        if not is_api() or not feature.is_enabled('live_happening_now'):
            self.abort404()

        event_id = NamedGlobals.get(HAPPENING_NOW_KEY, None)
        if not event_id:
            response.status_code = 204
            return

        try:
            event = LiveUpdateEvent._byID(event_id)
        except NotFound:
            response.status_code = 204
            return
        else:
            c.liveupdate_event = event
            content = Wrapped(event)
            return pages.LiveUpdateEventPage(content).render()
예제 #2
0
    def run_sitewide_ratelimits(self):
        """Ratelimit users and add ratelimit headers to the response.

        Headers added are:
        X-Ratelimit-Used: Number of requests used in this period
        X-Ratelimit-Remaining: Number of requests left to use
        X-Ratelimit-Reset: Approximate number of seconds to end of period

        This function only has an effect if one of
        g.RL_SITEWIDE_ENABLED or g.RL_OAUTH_SITEWIDE_ENABLED
        are set to 'true' in the app configuration

        If the ratelimit is exceeded, a 429 response will be sent,
        unless the app configuration has g.ENFORCE_RATELIMIT off.
        Headers will be sent even on aborted requests.

        """
        if c.cdn_cacheable or not is_api():
            # No ratelimiting or headers for:
            # * Web requests (HTML)
            # * CDN requests (logged out via www.reddit.com)
            return
        elif c.oauth_user and g.RL_OAUTH_SITEWIDE_ENABLED:
            max_reqs = g.RL_OAUTH_MAX_REQS
            period = g.RL_OAUTH_RESET_SECONDS
            # Convert client_id to ascii str for use as memcache key
            client_id = c.oauth2_access_token.client_id.encode("ascii")
            # OAuth2 ratelimits are per user-app combination
            key = 'siterl-oauth-' + c.user._id36 + ":" + client_id
        elif g.RL_SITEWIDE_ENABLED:
            max_reqs = g.RL_MAX_REQS
            period = g.RL_RESET_SECONDS
            # API (non-oauth) limits are per-ip
            key = 'siterl-api-' + request.ip
        else:
            # Not in a context where sitewide ratelimits are on
            return

        period_start, retry_after = _get_ratelimit_timeslice(period)
        key += time.strftime("-%H%M%S", period_start)

        g.ratelimitcache.add(key, 0, time=retry_after + 1)

        # Increment the key to track the current request
        recent_reqs = g.ratelimitcache.incr(key)
        reqs_remaining = max(0, max_reqs - recent_reqs)

        c.ratelimit_headers = {
            "X-Ratelimit-Used": str(recent_reqs),
            "X-Ratelimit-Reset": str(retry_after),
            "X-Ratelimit-Remaining": str(reqs_remaining),
        }

        if reqs_remaining <= 0 and g.ENFORCE_RATELIMIT:
            # For non-abort situations, the headers will be added in post(),
            # to avoid including them in a pagecache
            response.headers.update(c.ratelimit_headers)
            abort(429)
예제 #3
0
    def GET_listing(self, events, **env):
        """Get a listing of live events by id."""
        if not is_api():
            self.abort404()
        if not events:
            return self.abort404()

        self.names = [e for e in events]
        return ListingController.GET_listing(self, **env)
예제 #4
0
    def GET_about(self):
        """Get some basic information about the live thread.

        See also: [/api/live/*thread*/edit](#POST_api_live_{thread}_edit).

        """
        if not is_api():
            self.abort404()
        content = Wrapped(c.liveupdate_event)
        return pages.LiveUpdateEventPage(content=content).render()
예제 #5
0
def set_content_type():
    e = request.environ
    c.render_style = e['render_style']
    response.content_type = e['content_type']

    if e.has_key('extension'):
        c.extension = ext = e['extension']
        if ext in ('embed', 'wired', 'widget'):
            wrapper = request.params.get("callback", "document.write")
            wrapper = filters._force_utf8(wrapper)
            if not valid_jsonp_callback(wrapper):
                abort(BadRequestError(errors.BAD_JSONP_CALLBACK))

            # force logged-out state since these can be accessed cross-domain
            c.user = UnloggedUser(get_browser_langs())
            c.user_is_loggedin = False

            def to_js(content):
                return wrapper + "(" + utils.string2js(content) + ");"

            c.response_wrapper = to_js
        if ext in ("rss", "api", "json") and request.method.upper() == "GET":
            user = valid_feed(request.GET.get("user"), request.GET.get("feed"),
                              request.path)
            if user and not g.read_only_mode:
                c.user = user
                c.user_is_loggedin = True
        if ext in ("mobile", "m") and not request.GET.get("keep_extension"):
            try:
                if request.cookies['reddit_mobility'] == "compact":
                    c.extension = "compact"
                    c.render_style = "compact"
            except (ValueError, KeyError):
                c.suggest_compact = True
        if ext in ("mobile", "m", "compact"):
            if request.GET.get("keep_extension"):
                c.cookies['reddit_mobility'] = Cookie(ext, expires=NEVER)
    # allow JSONP requests to generate callbacks, but do not allow
    # the user to be logged in for these
    callback = request.GET.get("jsonp")
    if is_api() and request.method.upper() == "GET" and callback:
        if not valid_jsonp_callback(callback):
            abort(BadRequestError(errors.BAD_JSONP_CALLBACK))
        c.allowed_callback = callback
        c.user = UnloggedUser(get_browser_langs())
        c.user_is_loggedin = False
        response.content_type = "application/javascript"
예제 #6
0
    def GET_about(self):
        """Get some basic information about the live thread.

        See also: [/api/live/*thread*/edit](#POST_api_live_{thread}_edit).

        """
        if not is_api():
            self.abort404()

        content = Wrapped(c.liveupdate_event)

        if c.liveupdate_event._date >= g.liveupdate_min_date_viewcounts:
            view_counts = ViewCountsQuery.execute(
                [c.liveupdate_event._fullname])
            content.total_views = view_counts.get(c.liveupdate_event._fullname)

        return pages.LiveUpdateEventPage(content=content).render()
예제 #7
0
    def GET_listing(self, events, **env):
        """Get a listing of live events by id."""
        if not is_api():
            self.abort404()
        if not events:
            return self.abort404()

        self.names = []
        for item in self.splitter.split(events):
            # we originally overrode the fullname for live events to look like
            # regular IDs (no prefix). this snuck out to the world through this
            # api endpoint. to provide backwards compatibility, we'll prepend
            # the prefix if the requester doesn't send a proper fullname.
            if not item.startswith("LiveUpdateEvent_"):
                self.names.append("LiveUpdateEvent_" + item)
            else:
                self.names.append(item)

        return ListingController.GET_listing(self, **env)
예제 #8
0
    def GET_happening_now(self):
        """ Get some basic information about the currently featured live thread.

            Returns an empty 204 response for api requests if no thread is currently featured.

            See also: [/api/live/*thread*/about](#GET_api_live_{thread}_about).
        """

        if not is_api():
            self.abort404()

        featured_event = get_featured_event()
        if not featured_event:
            response.status_code = 204
            return

        c.liveupdate_event = featured_event
        content = Wrapped(featured_event)
        return pages.LiveUpdateEventPage(content).render()
예제 #9
0
 def GET_listing(self, where, mark, message, subwhere = None, **env):
     if not (c.default_sr or c.site.is_moderator(c.user) or c.user_is_admin):
         abort(403, "forbidden")
     if isinstance(c.site, MultiReddit):
         if not (c.user_is_admin or c.site.is_moderator(c.user)):
             self.abort403()
         self.where = "multi"
     elif isinstance(c.site, ModSR) or not c.default_sr:
         self.where = "moderator"
     else:
         self.where = where
     self.subwhere = subwhere
     if mark is not None:
         self.mark = mark
     elif is_api():
         self.mark = 'false'
     elif c.render_style and c.render_style == "xml":
         self.mark = 'false'
     else:
         self.mark = 'true'
     self.message = message
     return ListingController.GET_listing(self, **env)
예제 #10
0
def set_content_type():
    e = request.environ
    c.render_style = e['render_style']
    c.response_content_type = e['content_type']

    if e.has_key('extension'):
        c.extension = ext = e['extension']
        if ext in ('embed', 'wired', 'widget'):

            def to_js(content):
                return utils.to_js(content,
                                   callback=request.params.get(
                                       "callback", "document.write"))

            c.response_wrappers.append(to_js)
        if ext in ("rss", "api", "json") and request.method.upper() == "GET":
            user = valid_feed(request.GET.get("user"), request.GET.get("feed"),
                              request.path)
            if user and not g.read_only_mode:
                c.user = user
                c.user_is_loggedin = True
        if ext in ("mobile", "m") and not request.GET.get("keep_extension"):
            try:
                if request.cookies['reddit_mobility'] == "compact":
                    c.extension = "compact"
                    c.render_style = "compact"
            except (ValueError, KeyError):
                c.suggest_compact = True
        if ext in ("mobile", "m", "compact"):
            if request.GET.get("keep_extension"):
                c.cookies['reddit_mobility'] = Cookie(ext, expires=NEVER)
    # allow JSONP requests to generate callbacks, but do not allow
    # the user to be logged in for these
    if (is_api() and request.method.upper() == "GET"
            and request.GET.get("jsonp")):
        c.allowed_callback = request.GET['jsonp']
        c.user = UnloggedUser(get_browser_langs())
        c.user_is_loggedin = False
예제 #11
0
 def GET_about(self, vuser):
     """Return information about the user, including karma and gold status."""
     if not is_api() or not vuser:
         return self.abort404()
     return Reddit(content=Wrapped(vuser)).render()
예제 #12
0
def handle_register(
    controller, form, responder, name, email,
    password, rem=None, newsletter_subscribe=False,
    sponsor=False, signature=None, **kwargs
):
    # check captcha before register (if any) since its answer might
    # change once c.user is set.
    captcha_shown = not signature and need_provider_captcha("register")

    def _event(error, captcha_shown=captcha_shown):
        g.events.login_event(
            'register_attempt',
            error_msg=error,
            user_name=request.urlvars.get('url_user'),
            email=request.POST.get('email'),
            remember_me=rem,
            newsletter=newsletter_subscribe,
            captcha_shown=captcha_shown,
            signature=signature,
            request=request,
            context=c)

    if signature and not signature.is_valid():
        _event(error="SIGNATURE")
        abort(403)

    if responder.has_errors('user', errors.USERNAME_TOO_SHORT):
        _event(error='USERNAME_TOO_SHORT')

    elif responder.has_errors('user', errors.USERNAME_INVALID_CHARACTERS):
        _event(error='USERNAME_INVALID_CHARACTERS')

    elif responder.has_errors('user', errors.USERNAME_TAKEN_DEL):
        _event(error='USERNAME_TAKEN_DEL')

    elif responder.has_errors('user', errors.USERNAME_TAKEN):
        _event(error='USERNAME_TAKEN')

    elif responder.has_errors('email', errors.BAD_EMAIL):
        _event(error='BAD_EMAIL')

    elif responder.has_errors('passwd', errors.SHORT_PASSWORD):
        _event(error='SHORT_PASSWORD')

    elif responder.has_errors('passwd', errors.BAD_PASSWORD):
        # BAD_PASSWORD is set when SHORT_PASSWORD is set
        _event(error='BAD_PASSWORD')

    elif responder.has_errors('passwd2', errors.BAD_PASSWORD_MATCH):
        _event(error='BAD_PASSWORD_MATCH')

    elif responder.has_errors('ratelimit', errors.RATELIMIT):
        _event(error='RATELIMIT')

    elif newsletter_subscribe and not email:
        c.errors.add(errors.NEWSLETTER_NO_EMAIL, field="email")
        form.has_errors("email", errors.NEWSLETTER_NO_EMAIL)
        _event(error='NEWSLETTER_NO_EMAIL')

    elif sponsor and not email:
        c.errors.add(errors.SPONSOR_NO_EMAIL, field="email")
        form.has_errors("email", errors.SPONSOR_NO_EMAIL)
        _event(error='SPONSOR_NO_EMAIL')

    # last but not least, we have to check the captcha
    elif (not signature and not g.disable_captcha and
          not valid_provider_captcha(responder, "register")):
        _event(error='BAD_CAPTCHA')

    else:
        try:
            user = register(name, password, request.ip)
        except AccountExists:
            c.errors.add(errors.USERNAME_TAKEN, field="user")
            form.has_errors("user", errors.USERNAME_TAKEN)
            _event(error='USERNAME_TAKEN')
            return

        VRatelimit.ratelimit(rate_ip=True, prefix="rate_register_")

        # anything else we know (email, languages)?
        if email:
            user.set_email(email)
            emailer.verify_email(user)

        user.pref_lang = c.lang

        if (is_api("html") and
                feature.is_enabled("new_user_onboarding", user=user)):
            user.has_been_onboarded = False

        user._commit()

        amqp.add_item('new_account', user._fullname)

        hooks.get_hook("account.registered").call(user=user)

        reject = hooks.get_hook("account.spotcheck").call(account=user)
        if any(reject):
            _event(error='ACCOUNT_SPOTCHECK')
            return

        if newsletter_subscribe and email:
            try:
                newsletter.add_subscriber(email, source="register")
            except newsletter.NewsletterError as e:
                g.log.warning("Failed to subscribe: %r" % e)

        controller._login(responder, user, rem)
        _event(error=None)