aboutsummaryrefslogtreecommitdiffstats
path: root/mediagoblin/tools
diff options
context:
space:
mode:
Diffstat (limited to 'mediagoblin/tools')
-rw-r--r--mediagoblin/tools/crypto.py6
-rw-r--r--mediagoblin/tools/mail.py11
-rw-r--r--mediagoblin/tools/metadata.py222
-rw-r--r--mediagoblin/tools/request.py20
-rw-r--r--mediagoblin/tools/response.py8
-rw-r--r--mediagoblin/tools/translate.py2
6 files changed, 262 insertions, 7 deletions
diff --git a/mediagoblin/tools/crypto.py b/mediagoblin/tools/crypto.py
index cf6d31f6..c85ecd4a 100644
--- a/mediagoblin/tools/crypto.py
+++ b/mediagoblin/tools/crypto.py
@@ -27,8 +27,7 @@ from mediagoblin import mg_globals
_log = logging.getLogger(__name__)
# produces base64 alphabet
-alphabet = string.ascii_letters + "-_"
-base = len(alphabet)
+ALPHABET = string.ascii_letters + "-_"
# Use the system (hardware-based) random number generator if it exists.
# -- this optimization is lifted from Django
@@ -117,8 +116,9 @@ def get_timed_signer_url(namespace):
return itsdangerous.URLSafeTimedSerializer(__itsda_secret,
salt=namespace)
-def random_string(length):
+def random_string(length, alphabet=ALPHABET):
""" Returns a URL safe base64 encoded crypographically strong string """
+ base = len(alphabet)
rstring = ""
for i in range(length):
n = getrandbits(6) # 6 bytes = 2^6 = 64
diff --git a/mediagoblin/tools/mail.py b/mediagoblin/tools/mail.py
index ad2e5a19..ab3e0eaa 100644
--- a/mediagoblin/tools/mail.py
+++ b/mediagoblin/tools/mail.py
@@ -16,7 +16,9 @@
from __future__ import print_function, unicode_literals
+import six
import smtplib
+import sys
from mediagoblin import mg_globals, messages
from mediagoblin._compat import MIMEText
from mediagoblin.tools import common
@@ -66,6 +68,8 @@ class FakeMhost(object):
'to': to_addrs,
'message': message})
+ def starttls(self):
+ raise smtplib.SMTPException("No STARTTLS here")
def _clear_test_inboxes():
global EMAIL_TEST_INBOX
@@ -105,6 +109,13 @@ def send_email(from_addr, to_addrs, subject, message_body):
if not mg_globals.app_config['email_smtp_host']: # e.g. host = ''
mhost.connect() # We SMTP.connect explicitly
+ try:
+ mhost.starttls()
+ except smtplib.SMTPException:
+ # Only raise an exception if we're forced to
+ if mg_globals.app_config['email_smtp_force_starttls']:
+ six.reraise(*sys.exc_info())
+
if ((not common.TESTS_ENABLED)
and (mg_globals.app_config['email_smtp_user']
or mg_globals.app_config['email_smtp_pass'])):
diff --git a/mediagoblin/tools/metadata.py b/mediagoblin/tools/metadata.py
new file mode 100644
index 00000000..bfefcac9
--- /dev/null
+++ b/mediagoblin/tools/metadata.py
@@ -0,0 +1,222 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import os
+import copy
+import json
+import re
+from pkg_resources import resource_filename
+
+import dateutil.parser
+from pyld import jsonld
+from jsonschema import validate, FormatChecker, draft4_format_checker
+from jsonschema.compat import str_types
+
+from mediagoblin.tools.pluginapi import hook_handle
+
+
+
+########################################################
+## Set up the MediaGoblin format checker for json-schema
+########################################################
+
+URL_REGEX = re.compile(
+ r'^[a-z]+://([^/:]+|([0-9]{1,3}\.){3}[0-9]{1,3})(:[0-9]+)?(\/.*)?$',
+ re.IGNORECASE)
+
+def is_uri(instance):
+ """
+ jsonschema uri validator
+ """
+ if not isinstance(instance, str_types):
+ return True
+
+ return URL_REGEX.match(instance)
+
+def is_datetime(instance):
+ """
+ Is a date or datetime readable string.
+ """
+ if not isinstance(instance, str_types):
+ return True
+
+ return dateutil.parser.parse(instance)
+
+
+class DefaultChecker(FormatChecker):
+ """
+ Default MediaGoblin format checker... extended to include a few extra things
+ """
+ checkers = copy.deepcopy(draft4_format_checker.checkers)
+
+
+DefaultChecker.checkers[u"uri"] = (is_uri, ())
+DefaultChecker.checkers[u"date-time"] = (is_datetime, (ValueError, TypeError))
+DEFAULT_CHECKER = DefaultChecker()
+
+# Crappy default schema, checks for things we deem important
+
+DEFAULT_SCHEMA = {
+ "$schema": "http://json-schema.org/schema#",
+
+ "type": "object",
+ "properties": {
+ "license": {
+ "format": "uri",
+ "type": "string",
+ },
+ "dcterms:created": {
+ "format": "date-time",
+ "type": "string",
+ },
+ "dc:created": {
+ "format": "date-time",
+ "type": "string",
+ }
+ },
+}
+
+
+def load_resource(package, resource_path):
+ """
+ Load a resource, return it as a string.
+
+ Args:
+ - package: package or module name. Eg "mediagoblin.media_types.audio"
+ - resource_path: path to get to this resource, a list of
+ directories and finally a filename. Will be joined with
+ os.path.sep.
+ """
+ filename = resource_filename(package, os.path.sep.join(resource_path))
+ return file(filename).read()
+
+def load_resource_json(package, resource_path):
+ """
+ Load a resource json file, return a dictionary.
+
+ Args:
+ - package: package or module name. Eg "mediagoblin.media_types.audio"
+ - resource_path: path to get to this resource, a list of
+ directories and finally a filename. Will be joined with
+ os.path.sep.
+ """
+ return json.loads(load_resource(package, resource_path))
+
+
+##################################
+## Load the MediaGoblin core files
+##################################
+
+
+BUILTIN_CONTEXTS = {
+ "http://www.w3.org/2013/json-ld-context/rdfa11": load_resource(
+ "mediagoblin", ["static", "metadata", "rdfa11.jsonld"])}
+
+
+_CONTEXT_CACHE = {}
+
+def load_context(url):
+ """
+ A self-aware document loader. For those contexts MediaGoblin
+ stores internally, load them from disk.
+ """
+ if url in _CONTEXT_CACHE:
+ return _CONTEXT_CACHE[url]
+
+ # See if it's one of our basic ones
+ document = BUILTIN_CONTEXTS.get(url, None)
+
+ # No? See if we have an internal schema for this
+ if document is None:
+ document = hook_handle(("context_url_data", url))
+
+ # Okay, if we've gotten a document by now... let's package it up
+ if document is not None:
+ document = {'contextUrl': None,
+ 'documentUrl': url,
+ 'document': document}
+
+ # Otherwise, use jsonld.load_document
+ else:
+ document = jsonld.load_document(url)
+
+ # cache
+ _CONTEXT_CACHE[url] = document
+ return document
+
+
+DEFAULT_CONTEXT = "http://www.w3.org/2013/json-ld-context/rdfa11"
+
+def compact_json(metadata, context=DEFAULT_CONTEXT):
+ """
+ Compact json with supplied context.
+
+ Note: Free floating" nodes are removed (eg a key just named
+ "bazzzzzz" which isn't specified in the context... something like
+ bazzzzzz:blerp will stay though. This is jsonld.compact behavior.
+ """
+ compacted = jsonld.compact(
+ metadata, context,
+ options={
+ "documentLoader": load_context,
+ # This allows for things like "license" and etc to be preserved
+ "expandContext": context,
+ "keepFreeFloatingNodes": False})
+
+ return compacted
+
+
+def compact_and_validate(metadata, context=DEFAULT_CONTEXT,
+ schema=DEFAULT_SCHEMA):
+ """
+ compact json with supplied context, check against schema for errors
+
+ raises an exception (jsonschema.exceptions.ValidationError) if
+ there's an error.
+
+ Note: Free floating" nodes are removed (eg a key just named
+ "bazzzzzz" which isn't specified in the context... something like
+ bazzzzzz:blerp will stay though. This is jsonld.compact behavior.
+
+ You may wish to do this validation yourself... this is just for convenience.
+ """
+ compacted = compact_json(metadata, context)
+ validate(metadata, schema, format_checker=DEFAULT_CHECKER)
+
+ return compacted
+
+
+def expand_json(metadata, context=DEFAULT_CONTEXT):
+ """
+ Expand json, but be sure to use our documentLoader.
+
+ By default this expands with DEFAULT_CONTEXT, but if you do not need this,
+ you can safely set this to None.
+
+ # @@: Is the above a good idea? Maybe it should be set to None by
+ # default.
+ """
+ options = {
+ "documentLoader": load_context}
+ if context is not None:
+ options["expandContext"] = context
+ return jsonld.expand(metadata, options=options)
+
+
+def rdfa_to_readable(rdfa_predicate):
+ readable = rdfa_predicate.split(u":")[1].capitalize()
+ return readable
diff --git a/mediagoblin/tools/request.py b/mediagoblin/tools/request.py
index d4739039..d2cb0f6a 100644
--- a/mediagoblin/tools/request.py
+++ b/mediagoblin/tools/request.py
@@ -16,7 +16,9 @@
import json
import logging
-from mediagoblin.db.models import User
+
+from mediagoblin.db.models import User, AccessToken
+from mediagoblin.oauth.tools.request import decode_authorization_header
_log = logging.getLogger(__name__)
@@ -31,6 +33,18 @@ def setup_user_in_request(request):
Examine a request and tack on a request.user parameter if that's
appropriate.
"""
+ # If API request the user will be associated with the access token
+ authorization = decode_authorization_header(request.headers)
+
+ if authorization.get(u"access_token"):
+ # Check authorization header.
+ token = authorization[u"oauth_token"]
+ token = AccessToken.query.filter_by(token=token).first()
+ if token is not None:
+ request.user = token.user
+ return
+
+
if 'user_id' not in request.session:
request.user = None
return
@@ -45,8 +59,8 @@ def setup_user_in_request(request):
def decode_request(request):
""" Decodes a request based on MIME-Type """
- data = request.get_data()
-
+ data = request.data
+
if request.content_type == json_encoded:
data = json.loads(data)
elif request.content_type == form_encoded or request.content_type == "":
diff --git a/mediagoblin/tools/response.py b/mediagoblin/tools/response.py
index 1380633f..e1c59e32 100644
--- a/mediagoblin/tools/response.py
+++ b/mediagoblin/tools/response.py
@@ -158,6 +158,14 @@ def json_response(serializable, _disable_cors=False, *args, **kw):
return response
+def json_error(error_str, status=400, *args, **kwargs):
+ """
+ This is like json_response but takes an error message in and formats
+ it in {"error": error_str}. This also sets the default HTTP status
+ code to 400.
+ """
+ return json_response({"error": error_str}, status=status, *args, **kwargs)
+
def form_response(data, *args, **kwargs):
"""
Responds using application/x-www-form-urlencoded and returns a werkzeug
diff --git a/mediagoblin/tools/translate.py b/mediagoblin/tools/translate.py
index 5ea73b71..a5e56cfe 100644
--- a/mediagoblin/tools/translate.py
+++ b/mediagoblin/tools/translate.py
@@ -33,7 +33,7 @@ TRANSLATIONS_PATH = pkg_resources.resource_filename(
'mediagoblin', 'i18n')
# Known RTL languages
-KNOWN_RTL = set(["ar", "fa", "zh","he","iw","ja","ur","yi","ji"])
+KNOWN_RTL = set(["ar", "fa", "he", "iw", "ur", "yi", "ji"])
def is_rtl(lang):
"""Returns true when the local language is right to left"""