# -*- coding: utf-8 -*- from copy import deepcopy import json import os import ox from changelog import add_record from oxtornado import actions from utils import update_dict, user_sort_key from . import models import settings import state import tor_request import logging logger = logging.getLogger(__name__) def init(data): ''' takes { } returns { config user preferences ui } ''' response = {} if os.path.exists(settings.oml_data_path): with open(settings.oml_data_path) as fd: config = json.load(fd) if not settings.FULLTEXT_SUPPORT: config['itemKeys'] = [k for k in config['itemKeys'] if k['id'] != 'fulltext'] else: config = {} response['config'] = config response['user'] = deepcopy(config['user']) response['version'] = settings.MINOR_VERSION if settings.preferences: response['user']['preferences'] = settings.preferences response['user']['id'] = settings.USER_ID response['user']['online'] = state.online if settings.ui: for f in settings.ui['filters']: if f['id'] == 'classification': f['id'] = 'categories' settings.ui._save() response['user']['ui'] = settings.ui return response actions.register(init) def public_init(data): response = init(data) name = response['user']['preferences']['username'] response['user'] = response['config']['user'] response['user']['preferences']['username'] = name response['user']['ui']['page'] = '' response['user']['ui']['showFolder'] = {'': True} response['readOnly'] = True for page in response['config']['pages']: if page['id'] == 'preferences': #page['parts'] = [p for p in page['parts'] if p['id'] in ('appearance', 'extensions')] page['parts'] = [p for p in page['parts'] if p['id'] in ('appearance',)] return response actions.register(public_init, action='init', version='public') def setPreferences(data): ''' takes { key: value, 'sub.key': value } ''' change_contact = 'contact' in data and \ data['contact'] != settings.preferences['contact'] change_username = 'username' in data and \ data['username'] != settings.preferences['username'] change_autostart = 'autostart' in data and \ data['autostart'] != settings.preferences['autostart'] if 'libraryPath' in data and \ data['libraryPath'] != settings.preferences['libraryPath']: change_path = [settings.preferences['libraryPath'], data['libraryPath']] else: change_path = False update_dict(settings.preferences, data) if 'username' in data: u = state.user() u.update_name() u.save() if change_username: add_record('editusername', data['username']) if state.nodes and state.nodes.local: state.nodes.local._update_if_ip_changed() if change_contact: add_record('editcontact', data['contact']) if change_path: state.tasks.queue('changelibrarypath', change_path) if change_autostart: import integration if settings.preferences['autostart']: integration.install_autostart() else: integration.uninstall_autostart() return settings.preferences actions.register(setPreferences, cache=False) def resetUI(data): ''' takes { } ''' ui_json = os.path.join(settings.data_path, 'ui.json') if os.path.exists(ui_json): os.unlink(ui_json) settings.ui = settings.pdict(ui_json, settings.config['user']['ui']) return settings.ui actions.register(resetUI, cache=False) def setUI(data): ''' takes { key: value, 'sub.key': value } ''' update_dict(settings.ui, data) #return settings.ui return {} actions.register(setUI, cache=False) def getUsers(data): ''' returns { users: [] } ''' users = [] ids = set() local = set() for u in models.User.query.filter(models.User.id != settings.USER_ID).all(): users.append(u.json()) ids.add(u.id) if state.nodes: for id in state.nodes.local: if id not in ids: n = state.nodes.local[id].copy() n['online'] = True n['name'] = n['username'] users.append(n) local.add(id) for n in users: n['local'] = n['id'] in local users.sort(key=user_sort_key) return { "users": users } actions.register(getUsers) def getUsersPublic(data): return { 'users': [] } actions.register(getUsersPublic, 'getUsers', version='public') def getLists(data): ''' returns { lists: [] } ''' from item.models import Item from user.models import List lists = [] lists.append({ 'id': '', 'items': Item.query.count(), 'name': 'Libraries', 'type': 'libraries', 'user': None, }) List.get_or_create(':Inbox') for u in models.User.query.filter((models.User.peered==True)|(models.User.id==settings.USER_ID)): lists += u.lists_json() return { 'lists': lists } actions.register(getLists) def getListsPublic(data): ''' returns { lists: [] } ''' from item.models import Item from sqlalchemy.sql import operators user = state.user() lists = [] lists.append({ 'id': '', 'items': user.items.count(), 'name': 'Libraries', 'type': 'libraries', 'user': None, }) lists += user.lists_json() return { 'lists': lists } actions.register(getListsPublic, 'getLists', version='public') def validate_query(query): validate_conditions(query['conditions']) def validate_conditions(conditions): for c in conditions: if 'conditions' in c: if list(sorted(c.keys())) != ['conditions', 'operator']: raise Exception('invalid query condition', c) validate_conditions(c['conditions']) else: if list(sorted(c.keys())) != ['key', 'operator', 'value']: raise Exception('invalid query condition', c) if c['key'] == 'list' and ':' not in c['value']: raise Exception('invalid query condition', c) def addList(data): ''' takes { name items query } ''' logger.debug('addList %s', data) user_id = settings.USER_ID if 'query' in data: validate_query(data['query']) if data['name']: l = models.List.create(user_id, data['name'], data.get('query')) if 'items' in data: l.add_items(data['items']) return l.json() else: raise Exception('name not set') return {} actions.register(addList, cache=False) def editList(data): ''' takes { id name query } ''' logger.debug('editList %s', data) l = models.List.get_or_create(data['id']) name = l.name if 'name' in data: l.name = data['name'] if 'query' in data and l.type != 'smart': raise Exception('query only for smart lists') if 'query' in data and l.type == 'smart': validate_query(data['query']) l._query = data['query'] if l.type == 'static' and name != l.name: add_record('editlist', name, {'name': l.name}) l.save() return l.json() actions.register(editList, cache=False) def removeList(data): ''' takes { id } ''' l = models.List.get(data['id']) if l and l.name != 'Inbox': l.remove() return {} actions.register(removeList, cache=False) def addListItems(data): ''' takes { list items } ''' if data['list'] == ':': from item.models import Item for item_id in data['items']: i = Item.get(item_id, for_update=True) i.queue_download() i.update() elif data['list'] and (data['list'].startswith(':') or data['list'].endswith(':')): l = models.List.get_or_create(data['list']) if l: if l.name in ('Inbox', '') and l.user_id != settings.USER_ID: state.tasks.queue('upload', { 'user': l.user_id, 'items': data['items'] }) else: l.add_items(data['items']) state.cache.clear('group:') return l.json() return {} actions.register(addListItems, cache=False) def removeListItems(data): ''' takes { list items } ''' l = models.List.get(data['list']) if l: l.remove_items(data['items']) state.cache.clear('group:') return l.json() return {} actions.register(removeListItems, cache=False) def sortLists(data): ''' takes { ids } ''' n = 0 logger.debug('sortLists %s', data) lists = [] for id in data['ids']: l = models.List.get(id) l.index_ = n n += 1 if l.type == 'static' and l.name not in ('', 'Inbox'): lists.append(l.name) state.db.session.add(l) state.db.session.commit() if lists: add_record('orderlists', lists) return {} actions.register(sortLists, cache=False) def editUser(data): ''' takes { id nickname } ''' if 'nickname' in data: p = models.User.get_or_create(data['id']) if data['nickname']: p.info['nickname'] = data['nickname'] elif 'nickname' in p.info: del p.info['nickname'] old = p.nickname p.update_name() if old != p.nickname: models.List.rename_user(old, p.nickname) p.save() return p.json() return {} actions.register(editUser, cache=False) def sortUsers(data): ''' takes { ids } ''' n = 0 for id in data['ids']: u = models.User.get(id) u.info['index'] = n n += 1 state.db.session.add(u) state.db.session.commit() if state.tasks: state.tasks.queue('syncmetadata') return {} actions.register(sortUsers, cache=False) def requestPeering(data): ''' takes { id message nickname (optional) } ''' if len(data.get('id', '')) != settings.ID_LENGTH: logger.debug('invalid user id') return {} u = models.User.get_or_create(data['id']) u.pending = 'sent' u.queued = True u.info['message'] = data.get('message', '') if data.get('nickname'): u.info['nickname'] = data.get('nickname', '') u.update_name() u.save() state.nodes.peer_queue(u.id, 'peering', 'requestPeering') return {} actions.register(requestPeering, cache=False) def acceptPeering(data): ''' takes { id message } ''' if len(data.get('id', '')) != settings.ID_LENGTH: logger.debug('invalid user id') return {} logger.debug('acceptPeering... %s', data) u = models.User.get_or_create(data['id']) u.info['message'] = data.get('message', '') u.update_peering(True) state.nodes.peer_queue(u.id, 'peering', 'acceptPeering') return {} actions.register(acceptPeering, cache=False) def rejectPeering(data): ''' takes { id message } ''' if len(data.get('id', '')) not in (16, 43, 56): logger.debug('invalid user id: %s', data) return {} u = models.User.get_or_create(data['id']) u.info['message'] = data.get('message', '') u.update_peering(False) state.nodes.peer_queue(u.id, 'peering', 'rejectPeering') return {} actions.register(rejectPeering, cache=False) def removePeering(data): ''' takes { id message } ''' if len(data.get('id', '')) not in (16, 43, 56): logger.debug('invalid user id: %s', data) return {} u = models.User.get(data['id'], for_update=True) if u: u.info['message'] = data.get('message', '') u.update_peering(False) state.nodes.peer_queue(u.id, 'peering', 'removePeering') return {} actions.register(removePeering, cache=False) def cancelPeering(data): ''' takes { } ''' if len(data.get('id', '')) != settings.ID_LENGTH: logger.debug('invalid user id: %s', data) return {} u = models.User.get_or_create(data['id']) u.info['message'] = data.get('message', '') u.update_peering(False) state.nodes.peer_queue(u.id, 'peering', 'cancelPeering') return {} actions.register(cancelPeering, cache=False) def getActivity(data): ''' return { activity progress } ''' return state.activity actions.register(getActivity, cache=False) def contact(data): ''' return { } ''' response = {} url = 'http://rnogx24drkbnrxa3.onion/contact' headers = { 'User-Agent': settings.USER_AGENT, } try: data = json.dumps(data).encode() opener = tor_request.get_opener() opener.addheaders = list(zip(headers.keys(), headers.values())) r = opener.open(url, data) error = r.status != 200 except: logger.debug('failed to send contact', exc_info=True) error = True if error: response = {'error': True} return response actions.register(contact, cache=False)