#==============================================================================#
# ez_api.py #
#==============================================================================#
#============#
# Includes #
#============#
import cPickle as pickle
from ez_process_base import ez_process_base, p2pCommand
# adding the eZchat path to search directory
import sys, os
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)),
os.pardir))
import ez_user as eu
import ez_message as em
import ez_preferences as ep
#==============================================================================#
# class ez_api #
#==============================================================================#
[docs]class ez_api(ez_process_base):
"""
Retrieving user information and handling commands.
Class ez_api brings along abunch of methods intended to be called directly by
the user. The class is inherited by the client via ez_process which makes the
methods available for the UI. It also takes care of the user and message
database.
Class variable *name*, declareing the user_id, is required for
initiating ez_api and is specified by the keyword ``'name'``
Class variable *UserDatabase* represents the Database where contact
information is stored, i.e. Usernames, Database user ids (UIDs), possibly
current IP and latest IPs and the Public key.
Class variable *MsgDatabase* represents the Database storing messages.
Messages cannot be read unless a valid private key is available.
"""
def __init__(self, **kwargs):
super(ez_api, self).__init__(**kwargs)
assert('name' in kwargs)
self.name = kwargs['name']
# TODO: (bcn 2014-10-19) @JNicL:
# This would give every client a fresh database.
#user_db_name = 'sqlite:///:memory:'
#msg_db_name = 'sqlite:///:memory:'
# Doesn't work, however, as you seem to be relying on files. There
# is also no need for seperate user and msg databases.
user_db_name = 'sqlite:///' + ep.join(ep.location['db'], self.name) + '_contacts'
msg_db_name = 'sqlite:///' + ep.join(ep.location['db'], self.name) + '_messages'
# uncomment here -> problem with private/public keys
# TODO: (bcn 2014-10-19) It is peculiar that it works in the cli when both
# share the SAME user database which is local/ez.db. The tests work with
# both versions. We should craft a test that is closer to the implementation
# used in ez_process and find the problem.
#self.UserDatabase = eu.UserDatabase(localdb=user_db_name)
self.UserDatabase = eu.user_database
self.MsgDatabase = em.MessageDatabase(localdb=msg_db_name)
if not self.UserDatabase.in_DB(name=self.name):
self.myself = eu.User(name=self.name, current_ip = '127.0.0.1:222')
self.UserDatabase.add_entry(self.myself)
else:
self.myself = self.UserDatabase.get_entry(name=self.name)
[docs] def cmd_close(self):
""" Client shutdown """
self.enableCLI = False
self.commandQueue.put(p2pCommand('shutdown'))
return
#def cmd_get_online_users(self):
##return [(user, self.ips[user]) for user in self.ips]
#print [(user, self.ips[user]) for user in self.ips]
[docs] def cmd_ping(self, user_id):
"""Ping a user given his ID."""
try:
cmd_dct = {'user_id': user_id}
self.commandQueue.put(p2pCommand('ping_request', cmd_dct))
except:
self.replyQueue.put(self.error("Syntax error in ping"))
[docs] def cmd_add(self, user_id, host, port):
"""
Add user IP to clients IP list.
:param user_id: id specifying the username
:type user_id: string
:param host: hosts IP
:type host: string
:param port: hosts port
:type port: integer
"""
try:
cmd_dct = {'user_id': user_id, 'host': host, 'port':port}
self.add_client(**cmd_dct)
self.commandQueue.put(p2pCommand('ping_request', cmd_dct))
except:
self.replyQueue.put(self.error("Syntax error in user"))
[docs] def cmd_servermode(self, host, port):
"""
Switch the client to servermode enabling to connect other users.
A users in the network can use the method
:py:meth:`ez_process.ez_api.ez_api.cmd_ips` to ask the client for
connection. The client then relays the request to other users and connects
them which each other. The connection process is described by the class
:py:class:`ez_process.ez_relay.ez_relay`
:param host: hosts IP
:type host: string
:param port: port on which to listen to
:type port: integer
"""
try:
cmd_dct = {'host': host, 'port': port}
self.commandQueue.put(p2pCommand('servermode', cmd_dct))
except:
self.replyQueue.put(self.error("Syntax error in servermode"))
[docs] def cmd_authentificate(self, host, port):
"""
Connect to a server with authentification.
A connection to a server enables the use of
:py:meth:`ez_process.ez_api.ez_api.cmd_ips`.
:param host: server IP
:type host: string
:param port: server port
:type port: integer
"""
cmd_dct = {'host': host, 'port':int(port)}
try:
self.commandQueue.put(p2pCommand('authentification_request', cmd_dct))
cmd_dct['user_id'] = 'server'
self.add_client(**cmd_dct)
self.replyQueue.put(self.success("Started cmd_authentificate"))
except:
self.replyQueue.put(self.error("Syntax error in cmd_authentificate"))
[docs] def cmd_connect(self, host, port):
"""
Connect to a server.
A connection to a server enables the use of
:py:meth:`ez_process.ez_api.ez_api.cmd_ips`.
:param host: server IP
:type host: string
:param port: server port
:type port: integer
"""
#master = (host, int(port))
cmd_dct = {'host': host, 'port':int(port)}
try:
self.commandQueue.put(p2pCommand('connect_server', cmd_dct))
cmd_dct['user_id'] = 'server'
self.add_client(**cmd_dct)
except:
self.replyQueue.put(self.error("Syntax error in connect"))
[docs] def cmd_bg(self):
""" Show background processes """
try:
print ("background_processes:", self.background_processes)
except:
self.replyQueue.put(self.error("Syntax error in bp"))
[docs] def cmd_sync(self, user_id):
"""
Initiate mesage database sync request.
:param user_id: the user with thom to sync
:type user_id: string
"""
try:
cmd_dct = {'user_id': user_id}
self.commandQueue.put(p2pCommand('db_sync_request_out', cmd_dct))
except:
self.replyQueue.put(self.error("Syntax error in cmd_sync"))
[docs] def cmd_passive_sync(self):
"""
Initiate passive mesage database syncing. The frequency is determined in
ez_process_preferences.
"""
try:
self.commandQueue.put(p2pCommand('db_sync_background'))
except:
self.replyQueue.put(self.error("Error in cmd_passive_sync"))
[docs] def cmd_ips(self, user_id):
"""
Request IPs from a user in servermode.
:param user_id: clients username
:type user_id: string
"""
try:
assert(user_id in self.ips)
cmd_dct = {'user_id': user_id}
self.commandQueue.put(p2pCommand('ips_request', cmd_dct))
except:
self.replyQueue.put(self.error("User_id not in known Ips"))
[docs] def cmd_key(self, user_id):
"""
Public key request.
:param user_id: clients username
:type user_id: string
"""
try:
cmd_dct = {'user_id': user_id}
self.commandQueue.put(p2pCommand('contact_request_out', cmd_dct))
except:
self.replyQueue.put(self.error("Syntax error in key"))
[docs] def cmd_send_msg(self, user_id, msg):
"""
Send an encrypted message.
The method requires the the target client to be online. The encryption can
be done only if a valid public key of the targets client is available.
:param user_id: clients username
:type user_id: string
:param msg: message
:type msg: string
"""
try:
if not self.UserDatabase.in_DB(name=user_id):
# raise error instead
self.replyQueue.put(self.error("User not in DB"))
return
# store msg in db
# TODO: nick Sa 04 Okt 2014 15:06:36 CEST
# apparently crypto does not allow unicode
mx = em.Message(self.name, user_id, str(msg))
self.replyQueue.put(self.success('Put UID: ' + str(mx.UID) +
' to the msg database'))
self.MsgDatabase.add_entry(mx)
if not user_id in self.ips:
# Strategy: send msg to random guys
return
data = pickle.dumps(mx)
cmd_data = {'user_id': user_id, 'data':data}
self.commandQueue.put(p2pCommand('send', cmd_data))
except:
self.replyQueue.put(self.error("Syntax error in command"))
[docs] def cmd_ping_background(self):
""" start background ping process """
self.commandQueue.put(p2pCommand('ping_background'))