534 lines
14 KiB
Python
534 lines
14 KiB
Python
# -*- coding: utf-8 -*-
|
|
# vi:si:et:sw=4:sts=4:ts=4
|
|
|
|
|
|
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(':Public')
|
|
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 != 'Public':
|
|
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)
|
|
i.queue_download()
|
|
i.update()
|
|
elif data['list'] and (data['list'].startswith(':') or data['list'].endswith(':Public')):
|
|
l = models.List.get_or_create(data['list'])
|
|
if l:
|
|
if l.name == 'Public' 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 = ['Public']
|
|
for id in data['ids']:
|
|
l = models.List.get(id)
|
|
l.index_ = n
|
|
n += 1
|
|
if l.type == 'static':
|
|
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', '')) != 16:
|
|
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.queue(u.id, 'peering', 'requestPeering')
|
|
return {}
|
|
actions.register(requestPeering, cache=False)
|
|
|
|
|
|
def acceptPeering(data):
|
|
'''
|
|
takes {
|
|
id
|
|
message
|
|
}
|
|
'''
|
|
if len(data.get('id', '')) != 16:
|
|
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.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):
|
|
logger.debug('invalid user id')
|
|
return {}
|
|
u = models.User.get_or_create(data['id'])
|
|
u.info['message'] = data.get('message', '')
|
|
u.update_peering(False)
|
|
state.nodes.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):
|
|
logger.debug('invalid user id')
|
|
return {}
|
|
u = models.User.get_or_create(data['id'])
|
|
u.info['message'] = data.get('message', '')
|
|
u.update_peering(False)
|
|
state.nodes.queue(u.id, 'peering', 'removePeering')
|
|
return {}
|
|
actions.register(removePeering, cache=False)
|
|
|
|
|
|
def cancelPeering(data):
|
|
'''
|
|
takes {
|
|
}
|
|
'''
|
|
if len(data.get('id', '')) != 16:
|
|
logger.debug('invalid user id')
|
|
return {}
|
|
u = models.User.get_or_create(data['id'])
|
|
u.info['message'] = data.get('message', '')
|
|
u.update_peering(False)
|
|
state.nodes.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)
|