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()
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)
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)
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()
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"
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()
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)
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()
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)
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
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()
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)