#!/usr/bin/env python # Copyright (C) 2012 Aleksey Lim # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os import re import sys import json import shlex import locale from os.path import join import active_document as ad from sugar_network import local, sugar, toolkit, IPCClient from sugar_network.resources.volume import Volume, Request from sugar_network.local.mountset import Mountset from sugar_network.local.mounts import HomeMount, RemoteMount from sugar_network.local.ipc_client import Router from active_toolkit.options import Option from active_toolkit import printf, application, coroutine, enforce anonymous = Option( 'use anonymous user to access to Sugar Network server; ' 'only read-only operations are available in this mode', default=False, type_cast=Option.bool_cast, action='store_true', name='anonymous') porcelain = Option( 'give the output in an easy-to-parse format for scripts', default=False, type_cast=Option.bool_cast, action='store_true', name='porcelain') _ESCAPE_VALUE_RE = re.compile('([^\[\]\{\}0-9][^\]\[\{\}]+)') class Application(application.Application): def __init__(self, **kwargs): application.Application.__init__(self, **kwargs) application.rundir.value = join(local.local_root.value, 'run') @application.command( 'send POST API request') def POST(self): self._call('POST', sys.stdin.read()) @application.command( 'send PUT API request') def PUT(self): self._call('PUT', sys.stdin.read()) @application.command( 'send GET API request') def GET(self): self._call('GET', None) def _call(self, method, content=None): if content: try: content = json.loads(content) except Exception, error: printf.info('Cannot parse JSON from stdin: %s', error) return request = Request(method=method) request.content = content response = ad.Response() reply = [] if self.args and self.args[0].startswith('/'): path = self.args.pop(0).strip('/').split('/') request['document'] = path.pop(0) if path: request['guid'] = path.pop(0) if path: request['prop'] = path.pop(0) for arg in self.args: arg = shlex.split(arg) if not arg: continue arg = arg[0] if '=' not in arg: reply.append(arg) continue arg, value = arg.split('=', 1) arg = arg.strip() enforce(arg, 'No argument name in %r expression', arg) if arg in request: if isinstance(request[arg], basestring): request[arg] = [request[arg]] request[arg].append(value) else: request[arg] = value pid_path = None server = None mountset = None try: if not self.check_for_instance(): pid_path = self.new_instance() if anonymous.value: sugar.uid = lambda: 'anonymous' sugar.nickname = lambda: 'anonymous' sugar.color = lambda: '#000000,#000000' sugar.privkey_path = lambda: '/fake/path' else: toolkit.ensure_dsa_pubkey(sugar.profile_path('owner.key')) volume = Volume(local.db_path()) mountset = Mountset(volume) mountset['~'] = HomeMount(volume) mountset['/'] = RemoteMount(volume) mountset['/'].mounted.wait() server = coroutine.WSGIServer( ('localhost', local.ipc_port.value), Router(mountset)) coroutine.spawn(server.serve_forever) coroutine.dispatch() result = IPCClient().call(request, response) finally: if server is not None: server.close() if mountset is not None: mountset.close() if pid_path: os.unlink(pid_path) if result: if porcelain.value: if type(result) in (list, tuple): for i in result: # TODO print i else: # TODO print i elif reply: for key in reply: key = _ESCAPE_VALUE_RE.sub("'\\1'", key) print eval('result%s' % key) else: print json.dumps(result, indent=2) # New defaults application.debug.value = sugar.logger_level() Option.seek('main', [application.debug, anonymous, porcelain]) Option.seek('local', [local.api_url, local.layers, local.ipc_port]) locale.setlocale(locale.LC_ALL, '') app = Application( name='sugar-network', description='Sugar Network client utility', epilog='See http://wiki.sugarlabs.org/go/Sugar_Network ' \ 'for details.', config_files=[ '/etc/sweets.conf', '~/.config/sweets/config', sugar.profile_path('sweets.conf'), ], stop_args=['launch']) app.start()