• Facebook
  • Twitter
  • Reddit
  • StumbleUpon
  • Digg
  • email

#! coding: utf-8
# pylint: disable-msg=W0311
from uuid import uuid4
from time import time
from random import randint
from hashlib import md5, sha1
from urllib import urlopen, quote_plus
from xml.etree import ElementTree
from redis import Redis
 
import settings
 
class XmlListConfig(list):
  def __init__(self, aList):
    for element in aList:
      if element:
        # treat like dict
        if len(element) == 1 or element[0].tag != element[1].tag:
          self.append(XmlDictConfig(element))
        # treat like list
        elif element[0].tag == element[1].tag:
          self.append(XmlListConfig(element))
      elif element.text:
        text = element.text.strip()
        if text:
          self.append(text)
 
 
class XmlDictConfig(dict):
  '''
  Example usage:
 
  >>> tree = ElementTree.parse('your_file.xml')
  >>> root = tree.getroot()
  >>> xmldict = XmlDictConfig(root)
 
  Or, if you want to use an XML string:
 
  >>> root = ElementTree.XML(xml_string)
  >>> xmldict = XmlDictConfig(root)
 
  And then use xmldict for what it is... a dict.
  '''
  def __init__(self, parent_element):
    if parent_element.items():
      self.update(dict(parent_element.items()))
    for element in parent_element:
      if element:
        # treat like dict - we assume that if the first two tags
        # in a series are different, then they are all different.
        if len(element) == 1 or element[0].tag != element[1].tag:
          aDict = XmlDictConfig(element)
        # treat like list - we assume that if the first two tags
        # in a series are the same, then the rest are the same.
        else:
          # here, we put the list in dictionary; the key is the
          # tag name the list elements all share in common, and
          # the value is the list itself 
          aDict = {element[0].tag: XmlListConfig(element)}
        # if the tag has attributes, add those to the dict
        if element.items():
          aDict.update(dict(element.items()))
        self.update({element.tag: aDict})
      # this assumes that if you've got an attribute in a tag,
      # you won't be having any text. This may or may not be a 
      # good idea -- time will tell. It works for the way we are
      # currently doing XML configuration files...
      elif element.items():
        self.update({element.tag: dict(element.items())})
      # finally, if there are no child tags and no attributes, extract
      # the text
      else:
        self.update({element.tag: element.text})
 
def xml2dict(xml_string):
  root = ElementTree.XML(xml_string)
  xmldict = XmlDictConfig(root)
  return xmldict
 
 
api_prefix = "http://%s/bigbluebutton/api/" % settings.server_address
db = Redis(host=settings.redis_host, port=settings.redis_port)
 
def get_secure_uri(api_uri):
  uri = api_uri.replace("?", "")
  checksum = sha1(uri + settings.security_salt).hexdigest()
  return api_uri + "&checksum=%s" % checksum
 
def _create(name, meeting_id, attendee_pw, moderator_pw):
  query = "name=%s&meetingID=%s&attendeePW=%s&moderatorPW=%s&logoutURL=%s" % \
          (quote_plus(name), meeting_id, attendee_pw, moderator_pw, settings.logout_url)
  api_uri = "create?%s" % query
  uri = get_secure_uri(api_uri)
  url = api_prefix + uri
  return_code = xml2dict(urlopen(url).read()).get("returncode")
  if return_code == "SUCCESS":
    return True
  return False
 
def _join(full_name, meeting_id, password):
  api_uri = "join?fullName=%s&meetingID=%s&password=%s" % \
            (full_name, meeting_id, password)
  uri = get_secure_uri(api_uri)
  url = api_prefix + uri
  return url
 
def create_meeting(name, attendee_users, moderator_users):
  attendee_pw = str(uuid4())
  moderator_pw = str(uuid4())
  while True: # get unique id
    meeting_id = randint(10000, 99999)#str(uuid4())
    key = "meeting:%s" % meeting_id
    if not db.get(key):
      break
  ok = _create(name, meeting_id, attendee_pw, moderator_pw)
  if ok:
    info = {"name": name,
            "id": meeting_id,
            "create_at": time(),
            "attendee_users": attendee_users,
            "moderator_users": moderator_users,
            "attendee_password": attendee_pw,
            "moderator_password": moderator_pw}
    key = "meeting:%s" % meeting_id
    db.set(key, str(info))
 
    key = "meeting_name:%s" % name
    db.set(key, 1)
    return meeting_id
  return False
 
def is_valid(name):
  key = "meeting_name:%s" % name
  if db.get(key):
    return False
  return True
 
def join_meeting(username, password, meeting_id):
  ok = check(username, password)
  username = username.lower()
  if ok:
    key = "meeting:%s" % meeting_id
    info = db.get(key)
    if info:
      info = eval(info)
      info = dict(info) # for hide error mark
      if username in info.get("moderator_users"):
        password = info.get("moderator_password")
      elif username in info.get("attendee_users"):
        password = info.get("attendee_password")
      else:
        return False
 
    url = _join(username, meeting_id, password)
    message = urlopen(url).read()
    if "FAILED" in message: # expired, recreate
      info = get_meeting_info(meeting_id)
      if not info:
        return False
      name = info.get("name")
      attendee_users = info.get("attendee_users")
      moderator_users = info.get("moderator_users")
      remove(meeting_id)       # remove old
      meeting_id = create_meeting(name, attendee_users, moderator_users)  # re-create     
      key = "meeting:%s" % meeting_id
      info = db.get(key)
      info = eval(info)
      info = dict(info) # for hide error mark
      if username in info.get("attendee_users"):
        password = info.get("attendee_password")
      elif username in info.get("moderator_users"):
        password = info.get("moderator_password")
      url = _join(username, meeting_id, password)
    return url
  return False
 
def get_meeting_info(meeting_id):
  key = "meeting:%s" % meeting_id
  info = db.get(key)
  if info:
    info = eval(info)
    return info
 
def remove(meeting_id):
  key = "meeting:%s" % meeting_id
  info = db.get(key)
  if key:
    name = eval(info).get("name")
    db.delete("meeting_name:%s" % name)
  return db.delete(key)
 
def update(username, meeting_id, name, attendee_users):
  key = "meeting:%s" % meeting_id
  info = db.get(key)
  if info:
    info = eval(info)
    info['name'] = name
 
    username = username.lower()
    if username not in info['moderator_users']:
      info['moderator_users'].append(username)
 
    info['attendee_users'] = attendee_users
    db.set(key, info)
    return True
  return False
 
def meeting_list():
  key = "meeting:*"
  keys = db.keys(key)
  if keys:
    meetings = []
    for key in keys:
      meetings.append(eval(db.get(key)))
    sorted(meetings, key=lambda k: k["create_at"], reverse=True)
    return meetings
  return None
 
def is_running(meeting_id):
  api_uri = "isMeetingRunning?meetingID=%s" % meeting_id
  api_uri = get_secure_uri(api_uri)
  url = api_prefix + api_uri
  xml_string = urlopen(url).read()
  params = xml2dict(xml_string)
  if params.get("returncode") == "SUCCESS":
    if params.get("running") == "true":
      return True
    return False
  return None
 
def add_user(username, password=''):
  key = "passwd:%s" % username.lower()
  if not db.get(key):
    if not password:
      password = str(uuid4())
      auto_passwd = password
      password = md5(password).hexdigest()
      db.set(key, password)
      return auto_passwd
    else:
      password = md5(password).hexdigest()
      db.set(key, password)
      return True
  return False
 
def remove_user(username):
  return db.delete("passwd:%s" % username.lower())
 
def change_password(username, new_password):
  key = "passwd:%s" % username.lower()
  password = md5(new_password).hexdigest()
  db.set(key, password)
  return True
 
 
def check(username, password):
  if not username or not password:
    return False
  password = md5(password).hexdigest()
  key = "passwd:%s" % username.lower()
  store_pw = db.get(key)
  if store_pw == password:
    return True
  return False
 
def suggest(keyword):
  key = "passwd:%s*" % keyword.lower()
  keys = db.keys(key)
  keys = [key.replace("passwd:", "") for key in keys]
  return list(set(keys))
 
 
if __name__ == "__main__":
  pass