def as_dict(self): from mailpile.urlmap import UrlMap um = UrlMap(self.session) rv = { "command": self.command_name, "state": { "command_url": um.ui_url(self.command_obj), "context_url": um.context_url(self.command_obj), "query_args": self.command_obj.state_as_query_args(), "cache_id": self.command_obj.cache_id(), "context": self.command_obj.context or "", }, "status": self.status, "message": self.message, "result": self.result, "event_id": self.command_obj.event.event_id, "elapsed": "%.3f" % self.session.ui.time_elapsed, } csrf_token = self.session.ui.html_variables.get("csrf_token") if csrf_token: rv["state"]["csrf_token"] = csrf_token if self.error_info: rv["error"] = self.error_info for ui_key in [k for k in self.command_obj.data.keys() if k.startswith("ui_")]: rv[ui_key] = self.command_obj.data[ui_key][0] ev = self.command_obj.event if ev and ev.data.get("password_needed"): rv["password_needed"] = ev.private_data["password_needed"] return rv
def command(self, save=True, auto=False): session, config = self.session, self.session.config urlmap = UrlMap(session) res = {"api_methods": [], "javascript_classes": [], "css_files": []} for method in ("GET", "POST", "UPDATE", "DELETE"): for cmd in urlmap._api_commands(method, strict=True): cmdinfo = {"url": cmd.SYNOPSIS[2], "method": method} if hasattr(cmd, "HTTP_QUERY_VARS"): cmdinfo["query_vars"] = cmd.HTTP_QUERY_VARS if hasattr(cmd, "HTTP_POST_VARS"): cmdinfo["post_vars"] = cmd.HTTP_POST_VARS if hasattr(cmd, "HTTP_OPTIONAL_VARS"): cmdinfo["optional_vars"] = cmd.OPTIONAL_VARS res["api_methods"].append(cmdinfo) for cls, filename in config.plugins.get_js_classes().iteritems(): try: with open(filename, "rb") as fd: res["javascript_classes"].append({"classname": cls, "code": fd.read().decode("utf-8")}) except (OSError, IOError, UnicodeDecodeError): self._ignore_exception() for cls, filename in config.plugins.get_css_files().iteritems(): try: with open(filename, "rb") as fd: res["css_files"].append({"classname": cls, "css": fd.read().decode("utf-8")}) except (OSError, IOError, UnicodeDecodeError): self._ignore_exception() return res
def command(self, save=True, auto=False): res = { 'api_methods': [], 'javascript_classes': [], 'css_files': [] } if self.args: # Short-circuit if we're serving templates... return self._success(_('Serving up API content'), result=res) session, config = self.session, self.session.config urlmap = UrlMap(session) for method in ('GET', 'POST', 'UPDATE', 'DELETE'): for cmd in urlmap._api_commands(method, strict=True): cmdinfo = { "url": cmd.SYNOPSIS[2], "method": method } if hasattr(cmd, 'HTTP_QUERY_VARS'): cmdinfo["query_vars"] = cmd.HTTP_QUERY_VARS if hasattr(cmd, 'HTTP_POST_VARS'): cmdinfo["post_vars"] = cmd.HTTP_POST_VARS if hasattr(cmd, 'HTTP_OPTIONAL_VARS'): cmdinfo["optional_vars"] = cmd.OPTIONAL_VARS res['api_methods'].append(cmdinfo) created_js = [] for cls, filename in sorted(list( config.plugins.get_js_classes().iteritems())): try: parts = cls.split('.')[:-1] for i in range(1, len(parts)): parent = '.'.join(parts[:i+1]) if parent not in created_js: res['javascript_classes'].append({ 'classname': parent, 'code': '' }) created_js.append(parent) with open(filename, 'rb') as fd: res['javascript_classes'].append({ 'classname': cls, 'code': fd.read().decode('utf-8') }) created_js.append(cls) except (OSError, IOError, UnicodeDecodeError): self._ignore_exception() for cls, filename in sorted(list( config.plugins.get_css_files().iteritems())): try: with open(filename, 'rb') as fd: res['css_files'].append({ 'classname': cls, 'css': fd.read().decode('utf-8') }) except (OSError, IOError, UnicodeDecodeError): self._ignore_exception() return self._success(_('Generated Javascript API'), result=res)
def as_dict(self): from mailpile.urlmap import UrlMap um = UrlMap(self.session) rv = { 'command': self.command_name, 'state': { 'command_url': um.ui_url(self.command_obj), 'context_url': um.context_url(self.command_obj), 'query_args': self.command_obj.state_as_query_args(), 'cache_id': self.command_obj.cache_id(), 'context': self.command_obj.context or '' }, 'status': self.status, 'message': self.message, 'result': self.result, 'event_id': self.command_obj.event.event_id, 'elapsed': '%.3f' % self.session.ui.time_elapsed, } csrf_token = self.session.ui.html_variables.get('csrf_token') if csrf_token: rv['state']['csrf_token'] = csrf_token if self.error_info: rv['error'] = self.error_info for ui_key in [ k for k in self.command_obj.data.keys() if k.startswith('ui_') ]: rv[ui_key] = self.command_obj.data[ui_key][0] ev = self.command_obj.event if ev and ev.data.get('password_needed'): rv['password_needed'] = ev.private_data['password_needed'] return rv
def as_dict(self): from mailpile.urlmap import UrlMap um = UrlMap(self.session) rv = { 'command': self.command_name, 'state': { 'command_url': um.ui_url(self.command_obj), 'context_url': um.context_url(self.command_obj), 'query_args': self.command_obj.state_as_query_args(), 'cache_id': self.command_obj.cache_id(), 'context': self.command_obj.context or '' }, 'status': self.status, 'message': self.message, 'result': self.result, 'event_id': self.command_obj.event.event_id, 'elapsed': '%.3f' % self.session.ui.time_elapsed, } csrf_token = self.session.ui.html_variables.get('csrf_token') if csrf_token: rv['state']['csrf_token'] = csrf_token if self.error_info: rv['error'] = self.error_info for ui_key in [k for k in self.command_obj.data.keys() if k.startswith('ui_')]: rv[ui_key] = self.command_obj.data[ui_key][0] ev = self.command_obj.event if ev and ev.data.get('password_needed'): rv['password_needed'] = ev.private_data['password_needed'] return rv
def do_GET(self, post_data={}, suppress_body=False, method='GET'): (scheme, netloc, path, params, query, frag) = urlparse(self.path) query_data = parse_qs(query) path = unquote(path) # HTTP is stateless, so we create a new session for each request. config = self.server.session.config if 'http' in config.sys.debug: sys.stderr.write(('%s: %s qs=%s post=%s\n') % (method, path, query_data, post_data)) # Static things! if path == '/favicon.ico': path = '/static/favicon.ico' if path.startswith('/_/'): path = path[2:] if path.startswith('/static/'): return self.send_file(config, path[len('/static/'):]) session = Session(config) session.ui = HttpUserInteraction(self, config) idx = session.config.index session.ui.html_variables = { 'csrf': self.csrf(), 'http_host': self.headers.get('host', 'localhost'), 'http_hostname': self.http_host(), 'http_method': method, 'message_count': (idx and len(idx.INDEX) or 0), 'name': session.config.get_profile().get('name', 'Chelsea Manning'), 'title': 'Mailpile dummy title', 'url_protocol': self.headers.get('x-forwarded-proto', 'http'), 'mailpile_size': idx and len(idx.INDEX) or 0 } try: try: commands = UrlMap(session).map(self, method, path, query_data, post_data) except UsageError: if (not path.endswith('/') and not session.config.sys.debug and method == 'GET'): commands = UrlMap(session).map(self, method, path + '/', query_data, post_data) url = quote(path) + '/' if query: url += '?' + query return self.send_http_redirect(url) else: raise results = [cmd.run() for cmd in commands] session.ui.display_result(results[-1]) except UrlRedirectException, e: return self.send_http_redirect(e.url)
def GetTagInfo(cfg, tn, stats=False, unread=None, exclude=None, subtags=None): tag = GetTag(cfg, tn) tid = tag._key info = { 'tid': tid, 'url': UrlMap(config=cfg).url_tag(tid), } for k in tag.all_keys(): # if k not in INFO_HIDES_TAG_METADATA: info[k] = tag[k] if subtags: info['subtag_ids'] = [t._key for t in subtags] exclude = exclude or set() if stats and (unread is not None): messages = (cfg.index.TAGS.get(tid, set()) - exclude) stats_all = len(messages) info['stats'] = { 'all': stats_all, 'new': len(messages & unread), 'not': len(cfg.index.INDEX) - stats_all } if subtags: for subtag in subtags: messages |= cfg.index.TAGS.get(subtag._key, set()) info['stats'].update({ 'sum_all': len(messages), 'sum_new': len(messages & unread), }) return info
def GetTagInfo(cfg, tn, stats=False, unread=None, exclude=None, subtags=None): tag = GetTag(cfg, tn) tid = tag._key info = { 'tid': tid, 'url': UrlMap(config=cfg).url_tag(tid), } for k in tag.all_keys(): if k in ('display_order', ): if str(tag[k]) == 'nan': tag[k] = 0.01 * len(info) info[k] = tag[k] if subtags: info['subtag_ids'] = [t._key for t in subtags] exclude = exclude or set() if stats and (unread is not None): messages = (cfg.index.TAGS.get(tid, set()) - exclude) stats_all = len(messages) info['name'] = _(info['name']) info['stats'] = { 'all': stats_all, 'new': len(messages & unread), 'not': len(cfg.index.INDEX) - stats_all } if subtags: for subtag in subtags: messages |= cfg.index.TAGS.get(subtag._key, set()) info['stats'].update({ 'sum_all': len(messages), 'sum_new': len(messages & unread), }) return info
def cache_id(self, sqa=None): if self.COMMAND_CACHE_TTL < 1: return '' from mailpile.urlmap import UrlMap args = sorted(list((sqa or self.state_as_query_args()).iteritems())) # The replace() stuff makes these usable as CSS class IDs return ('%s-%s' % (UrlMap(self.session).ui_url(self), md5_hex( str(args)))).replace('/', '-').replace('.', '-')
def command(self, save=True, auto=False): session, config = self.session, self.session.config urlmap = UrlMap(session) res = { 'api_methods': [], 'javascript_classes': [], 'css_files': [] } for method in ('GET', 'POST', 'UPDATE', 'DELETE'): for cmd in urlmap._api_commands(method, strict=True): cmdinfo = { "url": cmd.SYNOPSIS[2], "method": method } if hasattr(cmd, 'HTTP_QUERY_VARS'): cmdinfo["query_vars"] = cmd.HTTP_QUERY_VARS if hasattr(cmd, 'HTTP_POST_VARS'): cmdinfo["post_vars"] = cmd.HTTP_POST_VARS if hasattr(cmd, 'HTTP_OPTIONAL_VARS'): cmdinfo["optional_vars"] = cmd.OPTIONAL_VARS res['api_methods'].append(cmdinfo) for cls, filename in config.plugins.get_js_classes().iteritems(): try: with open(filename, 'rb') as fd: res['javascript_classes'].append({ 'classname': cls, 'code': fd.read().decode('utf-8') }) except (OSError, IOError, UnicodeDecodeError): self._ignore_exception() for cls, filename in config.plugins.get_css_files().iteritems(): try: with open(filename, 'rb') as fd: res['css_files'].append({ 'classname': cls, 'css': fd.read().decode('utf-8') }) except (OSError, IOError, UnicodeDecodeError): self._ignore_exception() return res
def command(self, save=True, auto=False): res = {"api_methods": [], "javascript_classes": [], "css_files": []} if self.args: # Short-circuit if we're serving templates... return self._success(_("Serving up API content"), result=res) session, config = self.session, self.session.config urlmap = UrlMap(session) for method in ("GET", "POST", "UPDATE", "DELETE"): for cmd in urlmap._api_commands(method, strict=True): cmdinfo = {"url": cmd.SYNOPSIS[2], "method": method} if hasattr(cmd, "HTTP_QUERY_VARS"): cmdinfo["query_vars"] = cmd.HTTP_QUERY_VARS if hasattr(cmd, "HTTP_POST_VARS"): cmdinfo["post_vars"] = cmd.HTTP_POST_VARS if hasattr(cmd, "HTTP_OPTIONAL_VARS"): cmdinfo["optional_vars"] = cmd.OPTIONAL_VARS res["api_methods"].append(cmdinfo) created_js = [] for cls, filename in sorted(list(config.plugins.get_js_classes().iteritems())): try: parts = cls.split(".")[:-1] for i in range(1, len(parts)): parent = ".".join(parts[: i + 1]) if parent not in created_js: res["javascript_classes"].append({"classname": parent, "code": ""}) created_js.append(parent) with open(filename, "rb") as fd: res["javascript_classes"].append({"classname": cls, "code": fd.read().decode("utf-8")}) created_js.append(cls) except (OSError, IOError, UnicodeDecodeError): self._ignore_exception() for cls, filename in sorted(list(config.plugins.get_css_files().iteritems())): try: with open(filename, "rb") as fd: res["css_files"].append({"classname": cls, "css": fd.read().decode("utf-8")}) except (OSError, IOError, UnicodeDecodeError): self._ignore_exception() return self._success(_("Generated Javascript API"), result=res)
def as_dict(self): from mailpile.urlmap import UrlMap rv = { "command": self.command_name, "state": { "command_url": UrlMap.ui_url(self.command_obj), "context_url": UrlMap.context_url(self.command_obj), "query_args": self.command_obj.state_as_query_args(), }, "status": self.status, "message": self.message, "result": self.result, "elapsed": "%.3f" % self.session.ui.time_elapsed, } if self.error_info: rv["error"] = self.error_info for ui_key in [k for k in self.kwargs.keys() if k.startswith("ui_")]: rv[ui_key] = self.kwargs[ui_key] return rv
def command(self, save=True, auto=False): session, config = self.session, self.session.config urlmap = UrlMap(session) res = {'api_methods': [], 'javascript_classes': [], 'css_files': []} for method in ('GET', 'POST', 'UPDATE', 'DELETE'): for cmd in urlmap._api_commands(method, strict=True): cmdinfo = {"url": cmd.SYNOPSIS[2], "method": method} if hasattr(cmd, 'HTTP_QUERY_VARS'): cmdinfo["query_vars"] = cmd.HTTP_QUERY_VARS if hasattr(cmd, 'HTTP_POST_VARS'): cmdinfo["post_vars"] = cmd.HTTP_POST_VARS if hasattr(cmd, 'HTTP_OPTIONAL_VARS'): cmdinfo["optional_vars"] = cmd.OPTIONAL_VARS res['api_methods'].append(cmdinfo) for cls, filename in config.plugins.get_js_classes().iteritems(): try: with open(filename, 'rb') as fd: res['javascript_classes'].append({ 'classname': cls, 'code': fd.read().decode('utf-8') }) except (OSError, IOError, UnicodeDecodeError): self._ignore_exception() for cls, filename in config.plugins.get_css_files().iteritems(): try: with open(filename, 'rb') as fd: res['css_files'].append({ 'classname': cls, 'css': fd.read().decode('utf-8') }) except (OSError, IOError, UnicodeDecodeError): self._ignore_exception() return res
def command(self): result, idx = [], self._idx() args = [] search = {} for arg in self.args: if '=' in arg: kw, val = arg.split('=', 1) search[kw.strip()] = val.strip() else: args.append(arg) for kw in self.data: if kw in self.session.config.tags.rules: search[kw] = self.data[kw] wanted = [t.lower() for t in args if not t.startswith('!')] unwanted = [t[1:].lower() for t in args if t.startswith('!')] wanted.extend([t.lower() for t in self.data.get('only', [])]) unwanted.extend([t.lower() for t in self.data.get('not', [])]) for tag in self.session.config.get_tags(**search): if wanted and tag.slug.lower() not in wanted: continue if unwanted and tag.slug.lower() in unwanted: continue tid = tag._key info = { 'tid': tid, 'url': UrlMap(self.session).url_tag(tid), } for k in tag.all_keys(): if k not in self.HIDE_TAG_METADATA: info[k] = tag[k] info['stats'] = { 'all': int(idx.STATS.get(tid, [0, 0])[0]), 'new': int(idx.STATS.get(tid, [0, 0])[1]), 'not': len(idx.INDEX) - int(idx.STATS.get(tid, [0, 0])[0]) } subtags = self.session.config.get_tags(parent=tid) if subtags and '_recursing' not in self.data: info['subtags'] = ListTags(self.session, arg=[t.slug for t in subtags], data={ '_recursing': 1 }).run().result['tags'] result.append(info) return { 'search': search, 'wanted': wanted, 'unwanted': unwanted, 'tags': result }
def _explain_msg_summary(self, info): msg_ts = long(info[6], 36) days_ago = (time.time() - msg_ts) / (24*3600) msg_date = datetime.date.fromtimestamp(msg_ts) date = '%4.4d-%2.2d-%2.2d' % (msg_date.year, msg_date.month, msg_date.day) urlmap = UrlMap(self.session) expl = { 'idx': info[0], 'id': info[1], 'from': info[2], 'to': info[3], 'subject': info[4], 'snippet': info[5], 'timestamp': msg_ts, 'date': date, 'friendly_date': _friendly_date(days_ago, date), 'tag_ids': info[7], 'url': urlmap.url_thread(info[0]) } if info[8]: expl['editing_url'] = urlmap.url_compose(info[0]) return expl
def _explain_msg_summary(self, info): msg_ts = long(info[6], 36) days_ago = (time.time() - msg_ts) / (24*3600) msg_date = datetime.datetime.fromtimestamp(msg_ts) date = msg_date.strftime("%Y-%m-%d") urlmap = UrlMap(self.session) expl = { 'mid': info[0], 'id': info[1], 'from': info[2], 'to': info[3], 'subject': info[4], 'snippet': info[5], 'timestamp': msg_ts, 'shorttime': msg_date.strftime("%H:%M"), 'date': date, 'tag_ids': info[7], 'url': urlmap.url_thread(info[0]) } if info[8]: expl['editing_url'] = urlmap.url_edit(info[0]) return expl
def _explain_msg_summary(self, info): msg_ts = long(info[6], 36) days_ago = (time.time() - msg_ts) / (24 * 3600) msg_date = datetime.datetime.fromtimestamp(msg_ts) date = msg_date.strftime("%Y-%m-%d") urlmap = UrlMap(self.session) expl = { "mid": info[0], "id": info[1], "from": info[2], "from_email": ", ".join(ExtractEmails(info[2])), "to": info[3], "subject": info[4], "snippet": info[5], "timestamp": msg_ts, "shorttime": msg_date.strftime("%H:%M"), "date": date, "tag_ids": info[7], "url": urlmap.url_thread(info[0]), } if info[8]: expl["editing_url"] = urlmap.url_edit(info[0]) return expl
def _explain_msg_summary(self, info): msg_ts = long(info[6], 36) days_ago = (time.time() - msg_ts) / (24 * 3600) msg_date = datetime.datetime.fromtimestamp(msg_ts) date = msg_date.strftime("%Y-%m-%d") urlmap = UrlMap(self.session) expl = { 'mid': info[0], 'id': info[1], 'from': info[2], 'from_email': ', '.join(ExtractEmails(info[2])), 'to': info[3], 'subject': info[4], 'snippet': info[5], 'timestamp': msg_ts, 'shorttime': msg_date.strftime("%H:%M"), 'date': date, 'tag_ids': info[7], 'url': urlmap.url_thread(info[0]) } if info[8]: expl['editing_url'] = urlmap.url_edit(info[0]) return expl
def GetTagInfo(cfg, tn, stats=False, unread=None): tag = GetTag(cfg, tn) tid = tag._key info = { 'tid': tid, 'url': UrlMap(config=cfg).url_tag(tid), } for k in tag.all_keys(): if k not in INFO_HIDES_TAG_METADATA: info[k] = tag[k] if stats and (unread is not None): stats_all = len(cfg.index.TAGS.get(tid, [])) info['stats'] = { 'all': stats_all, 'new': len(cfg.index.TAGS.get(tid, set()) & unread), 'not': len(cfg.index.INDEX) - stats_all } return info
def __init__(self, session, idx, results=None, start=0, end=None, num=None, emails=None, view_pairs=None, people=None, suppress_data=False, full_threads=True): dict.__init__(self) self.session = session self.people = people self.emails = emails or [] self.view_pairs = view_pairs or {} self.idx = idx self.urlmap = UrlMap(self.session) results = self.results = results or session.results or [] num = num or session.config.prefs.num_results if end: start = end - num if start > len(results): start = len(results) if start < 0: start = 0 try: threads = [b36(r) for r in results[start:start + num]] except TypeError: results = threads = [] start = end = 0 self.session.ui.mark( _('Parsing metadata for %d results ' '(full_threads=%s)') % (len(threads), full_threads)) self.update({ 'summary': _('Search: %s') % ' '.join(session.searched), 'stats': { 'count': len(threads), 'start': start + 1, 'end': start + min(num, len(results) - start), 'total': len(results), }, 'search_terms': session.searched, 'address_ids': [], 'message_ids': [], 'view_pairs': view_pairs, 'thread_ids': threads, }) if 'tags' in self.session.config: search_tags = [ idx.config.get_tag(t.split(':')[1], {}) for t in session.searched if t.startswith('in:') or t.startswith('tag:') ] search_tag_ids = [t._key for t in search_tags if t] self.update({ 'search_tag_ids': search_tag_ids, }) if search_tag_ids: self['summary'] = ' & '.join( [t.name for t in search_tags if t]) else: search_tag_ids = [] if suppress_data or (not results and not emails): return self.update({ 'data': { 'addresses': {}, 'metadata': {}, 'messages': {}, 'threads': {} } }) if 'tags' in self.session.config: th = self['data']['tags'] = {} for tid in search_tag_ids: if tid not in th: th[tid] = self._tag(tid, {'searched': True}) idxs = results[start:start + num] for e in emails or []: self.add_email(e, idxs) done_idxs = set() while idxs: idxs = list(set(idxs) - done_idxs) for idx_pos in idxs: done_idxs.add(idx_pos) msg_info = idx.get_msg_at_idx_pos(idx_pos) self.add_msg_info(b36(idx_pos), msg_info, full_threads=full_threads, idxs=idxs) if emails and len(emails) == 1: self['summary'] = emails[0].get_msg_info(MailIndex.MSG_SUBJECT)
def _process_manifest_pass_two(self, full_name, manifest=None, plugin_path=None): """ Pass two of processing the manifest data. This maps templates and data to API commands and links registers classes and methods as hooks here and there. As these things depend both on configuration and the URL map, this happens as a second phase. """ if not manifest: return manifest_path = lambda *p: self._mf_path(manifest, *p) manifest_iteritems = lambda *p: self._mf_iteritems(manifest, *p) # Register javascript classes for fn in manifest.get('code', {}).get('javascript', []): class_name = fn.replace('/', '.').rsplit('.', 1)[0] # FIXME: Is this a good idea? if full_name.endswith('.'+class_name): parent, class_name = full_name.rsplit('.', 1) else: parent = full_name self.register_js(parent, class_name, os.path.join(plugin_path, fn)) # Register CSS files for fn in manifest.get('code', {}).get('css', []): file_name = fn.replace('/', '.').rsplit('.', 1)[0] self.register_css(full_name, file_name, os.path.join(plugin_path, fn)) # Register web assets if plugin_path: from mailpile.urlmap import UrlMap um = UrlMap(session=self.session, config=self.config) for url, info in manifest_iteritems('routes'): filename = os.path.join(plugin_path, info['file']) # Short-cut for static content if url.startswith('/static/'): self.register_web_asset(full_name, url[8:], filename, mimetype=info.get('mimetype', None)) continue # Finds the right command class and register asset in # the right place for that particular command. commands = [] if (not url.startswith('/api/')) and 'api' in info: url = '/api/%d%s' % (info['api'], url) if url[-1] == '/': url += 'as.html' for method in ('GET', 'POST', 'PUT', 'UPDATE', 'DELETE'): try: commands = um.map(None, method, url, {}, {}) break except UsageError: pass output = [o.get_render_mode() for o in commands if hasattr(o, 'get_render_mode')] output = output and output[-1] or 'html' if commands: command = commands[-1] tpath = command.template_path(output.split('.')[-1], template=output) self.register_web_asset(full_name, 'html/' + tpath, filename) else: print 'FIXME: Un-routable URL in manifest %s' % url # Register email content/crypto hooks s = self for which, reg in ( ('outgoing_content', s.register_outgoing_email_content_transform), ('outgoing_crypto', s.register_outgoing_email_crypto_transform), ('incoming_crypto', s.register_incoming_email_crypto_transform), ('incoming_content', s.register_incoming_email_content_transform) ): for item in manifest_path('email_transforms', which): name = '%3.3d_%s' % (int(item.get('priority', 999)), full_name) reg(name, self._get_class(full_name, item['class'])) # Register search keyword extractors s = self for which, reg in ( ('meta', s.register_meta_kw_extractor), ('text', s.register_text_kw_extractor), ('data', s.register_data_kw_extractor) ): for item in manifest_path('keyword_extractors', which): reg('%s.%s' % (full_name, item), self._get_class(full_name, item)) # Register contact/vcard hooks for which, reg in ( ('importers', self.register_vcard_importers), ('exporters', self.register_contact_exporters), ('context', self.register_contact_context_providers) ): for item in manifest_path('contacts', which): reg(self._get_class(full_name, item)) # Register periodic jobs def reg_job(info, spd, register): interval, cls = info['interval'], info['class'] callback = self._get_class(full_name, cls) register('%s.%s/%s-%s' % (full_name, cls, spd, interval), interval, callback) for info in manifest_path('periodic_jobs', 'fast'): reg_job(info, 'fast', self.register_fast_periodic_job) for info in manifest_path('periodic_jobs', 'slow'): reg_job(info, 'slow', self.register_slow_periodic_job) ucfull_name = full_name.capitalize() for ui_type, elems in manifest.get('user_interface', {}).iteritems(): for hook in elems: if 'javascript_setup' in hook: js = hook['javascript_setup'] if not js.startswith('Mailpile.'): hook['javascript_setup'] = '%s.%s' % (ucfull_name, js) if 'javascript_events' in hook: for event, call in hook['javascript_events'].iteritems(): if not call.startswith('Mailpile.'): hook['javascript_events'][event] = '%s.%s' \ % (ucfull_name, call) self.register_ui_element(ui_type, **hook)
def _use_data_view(self, view_name, result): self._debug('use_data_view(%s, ...)' % (view_name)) dv = UrlMap(self.env.session).map(None, 'GET', view_name, {}, {})[-1] return dv.view(result)
def _process_manifest_pass_two(self, full_name, manifest=None, plugin_path=None): """ Pass two of processing the manifest data. This maps templates and data to API commands and links registers classes and methods as hooks here and there. As these things depend both on configuration and the URL map, this happens as a second phase. """ if not manifest: return manifest_path = lambda *p: self._mf_path(manifest, *p) manifest_iteritems = lambda *p: self._mf_iteritems(manifest, *p) # Register javascript classes for fn in manifest.get("code", {}).get("javascript", []): class_name = fn.replace("/", ".").rsplit(".", 1)[0] # FIXME: Is this a good idea? if full_name.endswith("." + class_name): parent, class_name = full_name.rsplit(".", 1) else: parent = full_name self.register_js(parent, class_name, os.path.join(plugin_path, fn)) # Register CSS files for fn in manifest.get("code", {}).get("css", []): file_name = fn.replace("/", ".").rsplit(".", 1)[0] self.register_css(full_name, file_name, os.path.join(plugin_path, fn)) # Register web assets if plugin_path: from mailpile.urlmap import UrlMap um = UrlMap(session=self.session, config=self.config) for url, info in manifest_iteritems("routes"): filename = os.path.join(plugin_path, info["file"]) # Short-cut for static content if url.startswith("/static/"): self.register_web_asset(full_name, url[8:], filename, mimetype=info.get("mimetype", None)) continue # Finds the right command class and register asset in # the right place for that particular command. commands = [] if (not url.startswith("/api/")) and "api" in info: url = "/api/%d%s" % (info["api"], url) if url[-1] == "/": url += "as.html" for method in ("GET", "POST", "PUT", "UPDATE", "DELETE"): try: commands = um.map(None, method, url, {}, {}) break except UsageError: pass output = [o.get_render_mode() for o in commands if hasattr(o, "get_render_mode")] output = output and output[-1] or "html" if commands: command = commands[-1] tpath = command.template_path(output.split(".")[-1], template=output) self.register_web_asset(full_name, "html/" + tpath, filename) else: print "FIXME: Un-routable URL in manifest %s" % url # Register email content/crypto hooks s = self for which, reg in ( ("outgoing_content", s.register_outgoing_email_content_transform), ("outgoing_crypto", s.register_outgoing_email_crypto_transform), ("incoming_crypto", s.register_incoming_email_crypto_transform), ("incoming_content", s.register_incoming_email_content_transform), ): for item in manifest_path("email_transforms", which): name = "%3.3d_%s" % (int(item.get("priority", 999)), full_name) reg(name, self._get_class(full_name, item["class"])) # Register search keyword extractors s = self for which, reg in ( ("meta", s.register_meta_kw_extractor), ("text", s.register_text_kw_extractor), ("data", s.register_data_kw_extractor), ): for item in manifest_path("keyword_extractors", which): reg("%s.%s" % (full_name, item), self._get_class(full_name, item)) # Register contact/vcard hooks for which, reg in ( ("importers", self.register_vcard_importers), ("exporters", self.register_contact_exporters), ("context", self.register_contact_context_providers), ): for item in manifest_path("contacts", which): reg(self._get_class(full_name, item)) # Register periodic jobs def reg_job(info, spd, register): interval, cls = info["interval"], info["class"] callback = self._get_class(full_name, cls) register("%s.%s/%s-%s" % (full_name, cls, spd, interval), interval, callback) for info in manifest_path("periodic_jobs", "fast"): reg_job(info, "fast", self.register_fast_periodic_job) for info in manifest_path("periodic_jobs", "slow"): reg_job(info, "slow", self.register_slow_periodic_job) ucfull_name = full_name.capitalize() for ui_type, elems in manifest.get("user_interface", {}).iteritems(): for hook in elems: if "javascript_setup" in hook: js = hook["javascript_setup"] if not js.startswith("Mailpile."): hook["javascript_setup"] = "%s.%s" % (ucfull_name, js) if "javascript_events" in hook: for event, call in hook["javascript_events"].iteritems(): if not call.startswith("Mailpile."): hook["javascript_events"][event] = "%s.%s" % (ucfull_name, call) self.register_ui_element(ui_type, **hook)
def _use_data_view(self, view_name, result): dv = UrlMap(self.env.session).map(None, 'GET', view_name, {}, {})[-1] return dv.view(result)
def _process_manifest_pass_two(self, full_name, manifest=None, plugin_path=None): """ Pass two of processing the manifest data. This maps templates and data to API commands and links registers classes and methods as hooks here and there. As these things depend both on configuration and the URL map, this happens as a second phase. """ if not manifest: return manifest_path = lambda *p: self._mf_path(manifest, *p) manifest_iteritems = lambda *p: self._mf_iteritems(manifest, *p) # Register contact/vcard hooks for importer in manifest_path('contacts', 'importers'): self.register_vcard_importers(self._get_class(full_name, importer)) for exporter in manifest_path('contacts', 'exporters'): self.register_contact_exporters( self._get_class(full_name, exporter)) for context in manifest_path('contacts', 'context'): self.register_contact_context_providers( self._get_class(full_name, context)) # Register javascript classes for fn in manifest.get('code', {}).get('javascript', []): class_name = fn.replace('/', '.').rsplit('.', 1)[0] self.register_js(full_name, class_name, os.path.join(plugin_path, fn)) # Register CSS files for fn in manifest.get('code', {}).get('css', []): file_name = fn.replace('/', '.').rsplit('.', 1)[0] self.register_css(full_name, file_name, os.path.join(plugin_path, fn)) # Register web assets if plugin_path: try: from mailpile.urlmap import UrlMap um = UrlMap(session=self.session, config=self.config) for url, info in manifest_iteritems('routes'): filename = os.path.join(plugin_path, info['file']) # Short-cut for static content if url.startswith('/static/'): self.register_web_asset(full_name, url[8:], filename, mimetype=info.get( 'mimetype', None)) continue # Finds the right command class and register asset in # the right place for that particular command. commands = [] if (not url.startswith('/api/')) and 'api' in info: url = '/api/%d%s' % (info['api'], url) if url[-1] == '/': url += 'as.html' for method in ('GET', 'POST', 'PUT', 'UPDATE', 'DELETE'): try: commands = um.map(None, method, url, {}, {}) break except UsageError: pass output = [ o.get_render_mode() for o in commands if hasattr(o, 'get_render_mode') ] output = output and output[-1] or 'html' if commands: command = commands[-1] tpath = command.template_path(output.split('.')[-1], template=output) self.register_web_asset(full_name, 'html/' + tpath, filename) else: print 'FIXME: Un-routable URL in manifest %s' % url except: import traceback traceback.print_exc()
def _process_manifest_pass_two(self, full_name, manifest=None, plugin_path=None): """ Pass two of processing the manifest data. This maps templates and data to API commands and links registers classes and methods as hooks here and there. As these things depend both on configuration and the URL map, this happens as a second phase. """ if not manifest: return manifest_path = lambda *p: self._mf_path(manifest, *p) manifest_iteritems = lambda *p: self._mf_iteritems(manifest, *p) # Register contact/vcard hooks for importer in manifest_path('contacts', 'importers'): self.register_vcard_importers(self._get_class(full_name, importer)) for exporter in manifest_path('contacts', 'exporters'): self.register_contact_exporters(self._get_class(full_name, exporter)) for context in manifest_path('contacts', 'context'): self.register_contact_context_providers(self._get_class(full_name, context)) # Register javascript classes for fn in manifest.get('code', {}).get('javascript', []): class_name = fn.replace('/', '.').rsplit('.', 1)[0] self.register_js(full_name, class_name, os.path.join(plugin_path, fn)) # Register CSS files for fn in manifest.get('code', {}).get('css', []): file_name = fn.replace('/', '.').rsplit('.', 1)[0] self.register_css(full_name, file_name, os.path.join(plugin_path, fn)) # Register web assets if plugin_path: try: from mailpile.urlmap import UrlMap um = UrlMap(session=self.session, config=self.config) for url, info in manifest_iteritems('routes'): filename = os.path.join(plugin_path, info['file']) # Short-cut for static content if url.startswith('/static/'): self.register_web_asset(full_name, url[8:], filename, mimetype=info.get('mimetype', None)) continue # Finds the right command class and register asset in # the right place for that particular command. commands = [] if (not url.startswith('/api/')) and 'api' in info: url = '/api/%d%s' % (info['api'], url) if url[-1] == '/': url += 'as.html' for method in ('GET', 'POST', 'PUT', 'UPDATE', 'DELETE'): try: commands = um.map(None, method, url, {}, {}) break except UsageError: pass output = [o.get_render_mode() for o in commands if hasattr(o, 'get_render_mode')] output = output and output[-1] or 'html' if commands: command = commands[-1] tpath = command.template_path(output.split('.')[-1], template=output) self.register_web_asset(full_name, 'html/' + tpath, filename) else: print 'FIXME: Un-routable URL in manifest %s' % url except: import traceback traceback.print_exc()
def _real_do_GET(self, post_data={}, suppress_body=False, method='GET'): (scheme, netloc, path, params, query, frag) = urlparse(self.path) query_data = parse_qs(query) opath = path = unquote(path) # HTTP is stateless, so we create a new session for each request. self.session, config = self.server.session, self.server.session.config server_session = self.server.session # Debugging... if 'httpdata' in config.sys.debug: self.wfile = DebugFileWrapper(sys.stderr, self.wfile) # Path manipulation... if path == '/favicon.ico': path = '%s/static/favicon.ico' % (config.sys.http_path or '') if config.sys.http_path: if not path.startswith(config.sys.http_path): self.send_full_response(_("File not found (invalid path)"), code=404, mimetype='text/plain') return None path = path[len(config.sys.http_path):] if path.startswith('/_/'): path = path[2:] for static in ('/static/', '/bower_components/'): if path.startswith(static): return self.send_file(config, path[len(static):], suppress_body=suppress_body) self.session = session = Session(config) session.ui = HttpUserInteraction(self, config, log_parent=server_session.ui) if 'context' in post_data: session.load_context(post_data['context'][0]) elif 'context' in query_data: session.load_context(query_data['context'][0]) mark_name = 'Processing HTTP API request at %s' % time.time() session.ui.start_command(mark_name, [], {}) if 'http' in config.sys.debug: session.ui.warning = server_session.ui.warning session.ui.notify = server_session.ui.notify session.ui.error = server_session.ui.error session.ui.debug = server_session.ui.debug session.ui.debug('%s: %s qs = %s post = %s' % (method, opath, query_data, post_data)) idx = session.config.index if session.config.loaded_config: name = session.config.get_profile().get('name', 'Chelsea Manning') else: name = 'Chelsea Manning' http_headers = [] http_session = self.http_session() csrf_token = security.make_csrf_token(self.server.secret, http_session) session.ui.html_variables = { 'csrf_token': csrf_token, 'csrf_field': ('<input type="hidden" name="csrf" value="%s">' % csrf_token), 'http_host': self.headers.get('host', 'localhost'), 'http_hostname': self.http_host(), 'http_method': method, 'http_session': http_session, 'http_request': self, 'http_response_headers': http_headers, 'message_count': (idx and len(idx.INDEX) or 0), 'name': name, 'title': 'Mailpile dummy title', 'url_protocol': self.headers.get('x-forwarded-proto', 'http'), 'mailpile_size': idx and len(idx.INDEX) or 0 } session.ui.valid_csrf_token = lambda token: security.valid_csrf_token( self.server.secret, http_session, token) try: try: need_auth = not (mailpile.util.TESTING or session.config.sys.http_no_auth) commands = UrlMap(session).map(self, method, path, query_data, post_data, authenticate=need_auth) except UsageError: if (not path.endswith('/') and not session.config.sys.debug and method == 'GET'): commands = UrlMap(session).map(self, method, path + '/', query_data, post_data) url = quote(path) + '/' if query: url += '?' + query return self.send_http_redirect(url) else: raise cachectrl = None if 'http' not in config.sys.debug: etag_data = [] max_ages = [] have_ed = 0 for c in commands: max_ages.append(c.max_age()) ed = c.etag_data() have_ed += 1 if ed else 0 etag_data.extend(ed) if have_ed == len(commands): etag = self._mk_etag(*etag_data) conditional = self.headers.get('if-none-match') if conditional == etag: self.send_full_response('OK', code=304, msg='Unmodified') return None else: http_headers.append(('ETag', etag)) max_age = min(max_ages) if max_ages else 10 if max_age: cachectrl = 'must-revalidate, max-age=%d' % max_age else: cachectrl = 'must-revalidate, no-store, max-age=0' global LIVE_HTTP_REQUESTS hang_fix = 1 if ([1 for c in commands if c.IS_HANGING_ACTIVITY]) else 0 try: LIVE_HTTP_REQUESTS -= hang_fix session.ui.mark('Running %d commands' % len(commands)) results = [cmd.run() for cmd in commands] session.ui.mark('Displaying final result') session.ui.display_result(results[-1]) finally: LIVE_HTTP_REQUESTS += hang_fix session.ui.mark('Rendering response') mimetype, content = session.ui.render_response(session.config) session.ui.mark('Sending response') self.send_full_response(content, mimetype=mimetype, header_list=http_headers, cachectrl=cachectrl) except UrlRedirectException as e: return self.send_http_redirect(e.url) except SuppressHtmlOutput: return None except AccessError: self.send_full_response(_('Access Denied'), code=403, mimetype='text/plain') return None except: e = traceback.format_exc() session.ui.debug(e) if not session.config.sys.debug: e = _('Internal Error') self.send_full_response(e, code=500, mimetype='text/plain') return None finally: session.ui.report_marks( details=('timing' in session.config.sys.debug)) session.ui.finish_command(mark_name)