Browse Source

a few code style fixes

Natenom/support-murmur-13-1446181288462
Michael Ziegler 15 years ago
parent
commit
5a6747c83a
  1. 505
      muco.py
  2. 2
      mumble-django.wsgi
  3. 69
      munin.py
  4. 2
      pyweb/manage.py
  5. 231
      pyweb/mucli.py
  6. 567
      pyweb/mumble/MumbleCtlDbus.py
  7. 1107
      pyweb/mumble/MumbleCtlIce.py
  8. 39
      pyweb/mumble/__init__.py
  9. 169
      pyweb/mumble/admin.py
  10. 455
      pyweb/mumble/forms.py
  11. 43
      pyweb/mumble/management/__init__.py
  12. 1
      pyweb/mumble/management/commands/__init__.py
  13. 321
      pyweb/mumble/management/commands/checkenv.py
  14. 71
      pyweb/mumble/management/commands/getslice.py
  15. 7
      pyweb/mumble/management/commands/mmrunserver.py
  16. 7
      pyweb/mumble/management/commands/mmshell.py
  17. 7
      pyweb/mumble/management/commands/mmsyncdb.py
  18. 279
      pyweb/mumble/management/server_detect.py
  19. 107
      pyweb/mumble/management/update_schema.py
  20. 59
      pyweb/mumble/mctl.py
  21. 591
      pyweb/mumble/mmobjects.py
  22. 1457
      pyweb/mumble/models.py
  23. 303
      pyweb/mumble/murmurenvutils.py
  24. 41
      pyweb/mumble/templatetags/mumble_extras.py
  25. 109
      pyweb/mumble/testrunner.py
  26. 299
      pyweb/mumble/tests.py
  27. 23
      pyweb/mumble/urls.py
  28. 27
      pyweb/mumble/utils.py
  29. 690
      pyweb/mumble/views.py
  30. 9
      pyweb/processors.py
  31. 103
      pyweb/settings.py
  32. 37
      pyweb/urls.py
  33. 37
      pyweb/views.py

505
muco.py

@ -1,5 +1,6 @@
#!/usr/bin/python
#!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright (C) 2009, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright (C) 2009, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -16,7 +17,7 @@
""" """
# Set this to the same path you used in settings.py, or None for auto-detection. # Set this to the same path you used in settings.py, or None for auto-detection.
MUMBLE_DJANGO_ROOT = None;
MUMBLE_DJANGO_ROOT = None
### DO NOT CHANGE ANYTHING BELOW THIS LINE ### ### DO NOT CHANGE ANYTHING BELOW THIS LINE ###
@ -25,7 +26,7 @@ from os.path import join, dirname, abspath, exists
# Path auto-detection # Path auto-detection
if not MUMBLE_DJANGO_ROOT or not exists( MUMBLE_DJANGO_ROOT ): if not MUMBLE_DJANGO_ROOT or not exists( MUMBLE_DJANGO_ROOT ):
MUMBLE_DJANGO_ROOT = dirname(abspath(__file__));
MUMBLE_DJANGO_ROOT = dirname(abspath(__file__))
# environment variables # environment variables
sys.path.append( MUMBLE_DJANGO_ROOT ) sys.path.append( MUMBLE_DJANGO_ROOT )
@ -39,329 +40,313 @@ os.environ['DJANGO_SETTINGS_MODULE'] = 'pyweb.settings'
# Uncomment this line to point the egg cache to /tmp. # Uncomment this line to point the egg cache to /tmp.
#os.environ['PYTHON_EGG_CACHE'] = '/tmp/pyeggs' #os.environ['PYTHON_EGG_CACHE'] = '/tmp/pyeggs'
import locale
import curses
from curses.textpad import Textbox
import locale
import curses
from curses.textpad import Textbox
from django.core.exceptions import ValidationError
from django.db.models.fields.related import ForeignKey
from django.db import models
from django.core.exceptions import ValidationError
from django.db.models.fields.related import ForeignKey
from django.db import models
from mumble.models import *
from mumble.forms import *
from mumble.models import *
from mumble.forms import *
locale.setlocale(locale.LC_ALL, '') locale.setlocale(locale.LC_ALL, '')
class BaseWindow( object ): class BaseWindow( object ):
tabName = "tehBasez";
tabName = "tehBasez"
def __init__( self, parentwin, mumble, pos_x, pos_y ):
self.pos = ( pos_x, pos_y );
self.win = parentwin.subwin( pos_y, pos_x );
self.mm = mumble;
def __init__( self, parentwin, mumble, pos_x, pos_y ):
self.pos = ( pos_x, pos_y )
self.win = parentwin.subwin( pos_y, pos_x )
self.mm = mumble
self.win.keypad(1);
self.win.keypad(1)
def draw( self ):
self.win.addstr( 1, 1, self.tabName );
def draw( self ):
self.win.addstr( 1, 1, self.tabName )
def border( self ):
self.win.border();
def border( self ):
self.win.border()
def enter( self ):
while( True ):
key = self.win.getch();
if key == curses.KEY_UP:
return;
def enter( self ):
while( True ):
key = self.win.getch()
if key == curses.KEY_UP:
return
class WndChannels( BaseWindow ): class WndChannels( BaseWindow ):
tabName = 'Channels';
tabName = 'Channels'
def printItem( self, item, level ):
namestr = "";
if item.is_server or item.is_channel:
namestr = "%s (Channel)" % item.name;
else:
namestr = "%s (Player)" % item.name
def printItem( self, item, level ):
namestr = ""
if item.is_server or item.is_channel:
namestr = "%s (Channel)" % item.name
else:
namestr = "%s (Player)" % item.name
self.win.addstr( self.curr_y, 4*level+1, namestr.encode(locale.getpreferredencoding()) )
self.curr_y += 1;
self.win.addstr( self.curr_y, 4*level+1, namestr.encode(locale.getpreferredencoding()) )
self.curr_y += 1
def draw( self ):
self.curr_y = 1;
self.mm.rootchan.visit( self.printItem );
def draw( self ):
self.curr_y = 1
self.mm.rootchan.visit( self.printItem )
class WndSettings( BaseWindow ): class WndSettings( BaseWindow ):
tabName = 'Server settings';
blacklist = ( 'id', 'sslkey', 'sslcrt' );
def __init__( self, parentwin, mumble, pos_x, pos_y ):
BaseWindow.__init__( self, parentwin, mumble, pos_x, pos_y );
self.form = MumbleAdminForm( instance=mumble );
self.editors = {};
self.fields = [ mf for mf in mumble._meta.fields if mf.name not in self.blacklist ];
def getFieldHeight( self, field ):
if isinstance( field, models.TextField ):
return 10;
return 1;
def getFieldYPos( self, field ):
ypos = 3;
for curr_field in self.fields:
if curr_field is field:
return ypos;
ypos += self.getFieldHeight( curr_field );
raise ReferenceError( "Field not found!" );
def draw( self ):
curr_y = 3;
for field in self.fields:
value = unicode( getattr( self.mm, field.name ) );
self.win.addstr( curr_y, 1, field.verbose_name.encode(locale.getpreferredencoding()) );
height = self.getFieldHeight( field );
winsize = self.win.getmaxyx();
editwin = self.win.subwin( height, winsize[1]-31, self.pos[1] + curr_y, self.pos[0] + 30 );
editwin.keypad(1);
editwin.addstr( value.encode(locale.getpreferredencoding()) );
editbox = Textbox( editwin );
self.editors[field.name] = ( editwin, editbox );
curr_y += height;
def enter( self ):
self.selIdx = 0;
self.selMax = len( self.fields ) - 1;
while( True ):
# Highlight selected field label
field = self.fields[self.selIdx];
self.win.addstr(
self.getFieldYPos(field), 1,
field.verbose_name.encode(locale.getpreferredencoding()),
curses.A_STANDOUT
);
self.win.refresh();
key = self.win.getch();
if key == curses.KEY_UP:
if self.selIdx > 0:
self.selIdx -= 1;
else:
return;
elif key == curses.KEY_DOWN and self.selIdx < self.selMax:
self.selIdx += 1;
elif key in ( ord('q'), ord('Q') ):
return;
elif key in ( ord('s'), ord('S') ):
try:
self.mm.save();
except Exception, instance:
msg = unicode( instance );
else:
msg = "Your settings have been saved.";
self.win.addstr( 1, 5, msg.encode(locale.getpreferredencoding()) );
elif key in ( curses.KEY_RIGHT, curses.KEY_ENTER, ord('\n') ):
valid = False;
while not valid:
self.editors[field.name][1].edit();
try:
setattr( self.mm, field.name,
field.to_python( self.editors[field.name][1].gather().strip() )
);
except ValidationError, instance:
msg = unicode( instance );
self.win.addstr( 1, 5, msg.encode(locale.getpreferredencoding()), curses.A_STANDOUT );
self.win.refresh();
else:
valid = True;
self.win.move( 1, 5 );
self.win.clrtoeol();
self.editors[field.name][0].refresh();
self.win.addstr(
self.getFieldYPos(field), 1,
field.verbose_name.encode(locale.getpreferredencoding())
);
tabName = 'Server settings'
blacklist = ( 'id', 'sslkey', 'sslcrt' )
def __init__( self, parentwin, mumble, pos_x, pos_y ):
BaseWindow.__init__( self, parentwin, mumble, pos_x, pos_y )
self.form = MumbleAdminForm( instance=mumble )
self.editors = {}
self.fields = [ mf for mf in mumble._meta.fields if mf.name not in self.blacklist ]
def getFieldHeight( self, field ):
if isinstance( field, models.TextField ):
return 10
return 1
def getFieldYPos( self, field ):
ypos = 3
for curr_field in self.fields:
if curr_field is field:
return ypos
ypos += self.getFieldHeight( curr_field )
raise ReferenceError( "Field not found!" )
def draw( self ):
curr_y = 3
for field in self.fields:
value = unicode( getattr( self.mm, field.name ) )
self.win.addstr( curr_y, 1, field.verbose_name.encode(locale.getpreferredencoding()) )
height = self.getFieldHeight( field )
winsize = self.win.getmaxyx()
editwin = self.win.subwin( height, winsize[1]-31, self.pos[1] + curr_y, self.pos[0] + 30 )
editwin.keypad(1)
editwin.addstr( value.encode(locale.getpreferredencoding()) )
editbox = Textbox( editwin )
self.editors[field.name] = ( editwin, editbox )
curr_y += height
def enter( self ):
self.selIdx = 0
self.selMax = len( self.fields ) - 1
while( True ):
# Highlight selected field label
field = self.fields[self.selIdx]
self.win.addstr(
self.getFieldYPos(field), 1,
field.verbose_name.encode(locale.getpreferredencoding()),
curses.A_STANDOUT
)
self.win.refresh()
key = self.win.getch()
if key == curses.KEY_UP:
if self.selIdx > 0:
self.selIdx -= 1
else:
return
elif key == curses.KEY_DOWN and self.selIdx < self.selMax:
self.selIdx += 1
elif key in ( ord('q'), ord('Q') ):
return
elif key in ( ord('s'), ord('S') ):
try:
self.mm.save()
except Exception, instance:
msg = unicode( instance )
else:
msg = "Your settings have been saved."
self.win.addstr( 1, 5, msg.encode(locale.getpreferredencoding()) )
elif key in ( curses.KEY_RIGHT, curses.KEY_ENTER, ord('\n') ):
valid = False
while not valid:
self.editors[field.name][1].edit()
try:
setattr( self.mm, field.name,
field.to_python( self.editors[field.name][1].gather().strip() )
)
except ValidationError, instance:
msg = unicode( instance )
self.win.addstr( 1, 5, msg.encode(locale.getpreferredencoding()), curses.A_STANDOUT )
self.win.refresh()
else:
valid = True
self.win.move( 1, 5 )
self.win.clrtoeol()
self.editors[field.name][0].refresh()
self.win.addstr(
self.getFieldYPos(field), 1,
field.verbose_name.encode(locale.getpreferredencoding())
)
class WndUsers( BaseWindow ): class WndUsers( BaseWindow ):
tabName = 'Registered users';
tabName = 'Registered users'
def draw( self ):
curr_y = 3;
def draw( self ):
curr_y = 3
for acc in self.mm.mumbleuser_set.all():
self.win.addstr( curr_y, 1, acc.name.encode(locale.getpreferredencoding()) );
curr_y += 1;
for acc in self.mm.mumbleuser_set.all():
self.win.addstr( curr_y, 1, acc.name.encode(locale.getpreferredencoding()) )
curr_y += 1
class MumbleForm( object ): class MumbleForm( object ):
def __init__( self, parentwin, mumble, pos_x, pos_y ):
self.pos = ( pos_x, pos_y );
self.win = parentwin.subwin( pos_y, pos_x );
self.mm = mumble;
def __init__( self, parentwin, mumble, pos_x, pos_y ):
self.pos = ( pos_x, pos_y )
self.win = parentwin.subwin( pos_y, pos_x )
self.mm = mumble
self.win.keypad(1);
self.win.keypad(1)
self.windows = (
WndChannels( self.win, mumble, pos_x + 2, pos_y + 2 ),
WndSettings( self.win, mumble, pos_x + 2, pos_y + 2 ),
WndUsers( self.win, mumble, pos_x + 2, pos_y + 2 ),
);
self.windows = (
WndChannels( self.win, mumble, pos_x + 2, pos_y + 2 ),
WndSettings( self.win, mumble, pos_x + 2, pos_y + 2 ),
WndUsers( self.win, mumble, pos_x + 2, pos_y + 2 ),
)
self.curridx = 0;
self.currmax = len( self.windows ) - 1;
self.curridx = 0
self.currmax = len( self.windows ) - 1
currwin = property( lambda self: self.windows[self.curridx], None );
currwin = property( lambda self: self.windows[self.curridx], None )
def mvwin( self, pos_x, pos_y ):
self.win.mvwin( pos_y, pos_x );
def mvwin( self, pos_x, pos_y ):
self.win.mvwin( pos_y, pos_x )
def mvdefault( self ):
self.win.mvwin( self.pos[1], self.pos[0] );
def mvdefault( self ):
self.win.mvwin( self.pos[1], self.pos[0] )
def draw( self ):
self.win.addstr( 0, 0, self.mm.name.encode(locale.getpreferredencoding()) );
def draw( self ):
self.win.addstr( 0, 0, self.mm.name.encode(locale.getpreferredencoding()) )
def drawTabs( self ):
first = True;
for subwin in self.windows:
flags = 0;
if subwin is self.currwin: flags |= curses.A_STANDOUT;
def drawTabs( self ):
first = True
for subwin in self.windows:
flags = 0
if subwin is self.currwin: flags |= curses.A_STANDOUT
if first:
self.win.addstr( 1, 2, "%-20s" % subwin.tabName, flags );
first = False;
else:
self.win.addstr( "%-20s" % subwin.tabName, flags );
if first:
self.win.addstr( 1, 2, "%-20s" % subwin.tabName, flags )
first = False
else:
self.win.addstr( "%-20s" % subwin.tabName, flags )
def enter( self ):
self.drawTabs();
self.currwin.draw();
self.currwin.border();
def enter( self ):
self.drawTabs()
self.currwin.draw()
self.currwin.border()
while( True ):
key = self.win.getch();
while( True ):
key = self.win.getch()
if key == curses.KEY_LEFT and self.curridx > 0:
self.curridx -= 1;
if key == curses.KEY_LEFT and self.curridx > 0:
self.curridx -= 1
elif key == curses.KEY_RIGHT and self.curridx < self.currmax:
self.curridx += 1;
elif key == curses.KEY_RIGHT and self.curridx < self.currmax:
self.curridx += 1
elif key in ( ord('q'), ord('Q'), curses.KEY_UP ):
return;
elif key in ( ord('q'), ord('Q'), curses.KEY_UP ):
return
elif key in ( curses.KEY_ENTER, curses.KEY_DOWN, ord('\n') ):
self.currwin.enter();
elif key in ( curses.KEY_ENTER, curses.KEY_DOWN, ord('\n') ):
self.currwin.enter()
self.win.clear();
self.draw();
self.drawTabs();
self.currwin.draw();
self.currwin.border();
self.win.refresh();
self.win.clear()
self.draw()
self.drawTabs()
self.currwin.draw()
self.currwin.border()
self.win.refresh()
def main( stdscr ): def main( stdscr ):
first_y = 3;
curr_y = first_y;
first_y = 3
curr_y = first_y
mumbles = list();
mumbles = list()
for mm in Mumble.objects.all().order_by( "name", "id" ):
mwin = MumbleForm( stdscr, mm, pos_x=5, pos_y=curr_y );
mumbles.append( mwin );
mwin.draw();
curr_y += 1;
for mm in Mumble.objects.all().order_by( "name", "id" ):
mwin = MumbleForm( stdscr, mm, pos_x=5, pos_y=curr_y )
mumbles.append( mwin )
mwin.draw()
curr_y += 1
selectedIdx = 0;
selectedMax = len(mumbles) - 1;
selectedIdx = 0
selectedMax = len(mumbles) - 1
myname = "Mumble Commander";
myname = "Mumble Commander"
while( True ):
selectedObj = mumbles[selectedIdx];
while( True ):
selectedObj = mumbles[selectedIdx]
maxyx = stdscr.getmaxyx();
stdscr.addstr( 0, maxyx[1] / 2 - len(myname)/2, myname, curses.A_UNDERLINE );
maxyx = stdscr.getmaxyx()
stdscr.addstr( 0, maxyx[1] / 2 - len(myname)/2, myname, curses.A_UNDERLINE )
# Draw selection marker
stdscr.addstr( first_y + selectedIdx, 3, '*' );
stdscr.refresh();
# Draw selection marker
stdscr.addstr( first_y + selectedIdx, 3, '*' )
stdscr.refresh()
key = stdscr.getch();
if key == curses.KEY_UP:
stdscr.addstr( first_y + selectedIdx, 3, ' ' );
selectedIdx -= 1;
key = stdscr.getch()
if key == curses.KEY_UP:
stdscr.addstr( first_y + selectedIdx, 3, ' ' )
selectedIdx -= 1
elif key == curses.KEY_DOWN:
stdscr.addstr( first_y + selectedIdx, 3, ' ' );
selectedIdx += 1;
elif key == curses.KEY_DOWN:
stdscr.addstr( first_y + selectedIdx, 3, ' ' )
selectedIdx += 1
elif key in ( curses.KEY_RIGHT, curses.KEY_ENTER, ord('\n') ):
stdscr.clear();
stdscr.addstr( 0, maxyx[1] / 2 - len(myname)/2, myname, curses.A_UNDERLINE );
selectedObj.mvwin( 5, first_y );
selectedObj.draw();
stdscr.refresh();
elif key in ( curses.KEY_RIGHT, curses.KEY_ENTER, ord('\n') ):
stdscr.clear()
stdscr.addstr( 0, maxyx[1] / 2 - len(myname)/2, myname, curses.A_UNDERLINE )
selectedObj.mvwin( 5, first_y )
selectedObj.draw()
stdscr.refresh()
selectedObj.enter();
stdscr.clear();
selectedObj.mvdefault();
for mwin in mumbles:
mwin.draw();
elif key in ( ord('q'), ord('Q') ):
return;
if selectedIdx < 0: selectedIdx = 0;
elif selectedIdx > selectedMax: selectedIdx = selectedMax;
selectedObj.enter()
stdscr.clear()
selectedObj.mvdefault()
for mwin in mumbles:
mwin.draw()
elif key in ( ord('q'), ord('Q') ):
return
if selectedIdx < 0: selectedIdx = 0
elif selectedIdx > selectedMax: selectedIdx = selectedMax
if __name__ == '__main__': if __name__ == '__main__':
#parser = OptionParser();
#parser.add_option( "-v", "--verbose", help="verbose output messages", default=False, action="store_true" );
#parser.add_option( "-n", "--num", help="size of the Matrix", default=4, type = 'int' );
#parser.add_option( "-s", "--sure", help="don't prompt if num >= 10", default=False, action="store_true" );
#options, args = parser.parse_args();
curses.wrapper( main );
#parser = OptionParser()
#parser.add_option( "-v", "--verbose", help="verbose output messages", default=False, action="store_true" )
#parser.add_option( "-n", "--num", help="size of the Matrix", default=4, type = 'int' )
#parser.add_option( "-s", "--sure", help="don't prompt if num >= 10", default=False, action="store_true" )
#options, args = parser.parse_args()
curses.wrapper( main )

2
mumble-django.wsgi

@ -9,7 +9,7 @@ from os.path import join, dirname, abspath, exists
# Path auto-detection # Path auto-detection
if not MUMBLE_DJANGO_ROOT or not exists( MUMBLE_DJANGO_ROOT ): if not MUMBLE_DJANGO_ROOT or not exists( MUMBLE_DJANGO_ROOT ):
MUMBLE_DJANGO_ROOT = dirname(abspath(__file__));
MUMBLE_DJANGO_ROOT = dirname(abspath(__file__))
# environment variables # environment variables
sys.path.append( MUMBLE_DJANGO_ROOT ) sys.path.append( MUMBLE_DJANGO_ROOT )

69
munin.py

@ -1,5 +1,6 @@
#!/usr/bin/python
#!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright (C) 2009, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright (C) 2009, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -16,7 +17,7 @@
""" """
# Set this to the same path you used in settings.py, or None for auto-detection. # Set this to the same path you used in settings.py, or None for auto-detection.
MUMBLE_DJANGO_ROOT = None;
MUMBLE_DJANGO_ROOT = None
### DO NOT CHANGE ANYTHING BELOW THIS LINE ### ### DO NOT CHANGE ANYTHING BELOW THIS LINE ###
@ -25,7 +26,7 @@ from os.path import join, dirname, abspath, exists
# Path auto-detection # Path auto-detection
if not MUMBLE_DJANGO_ROOT or not exists( MUMBLE_DJANGO_ROOT ): if not MUMBLE_DJANGO_ROOT or not exists( MUMBLE_DJANGO_ROOT ):
MUMBLE_DJANGO_ROOT = dirname(abspath(__file__));
MUMBLE_DJANGO_ROOT = dirname(abspath(__file__))
# environment variables # environment variables
sys.path.append( MUMBLE_DJANGO_ROOT ) sys.path.append( MUMBLE_DJANGO_ROOT )
@ -50,46 +51,46 @@ categ = getattr( settings, "MUNIN_CATEGORY", "network" )
def get_running_instances(): def get_running_instances():
for server in MumbleServer.objects.all():
if not server.online:
continue
runinst = server.ctl.getBootedServers()
for inst in server.mumble_set.order_by("srvid").filter( srvid__in=runinst ):
yield inst
for server in MumbleServer.objects.all():
if not server.online:
continue
runinst = server.ctl.getBootedServers()
for inst in server.mumble_set.order_by("srvid").filter( srvid__in=runinst ):
yield inst
if sys.argv[-1] == 'config': if sys.argv[-1] == 'config':
prefenc = locale.getpreferredencoding()
prefenc = locale.getpreferredencoding()
print "graph_vlabel Users"
print "graph_args --base 1000"
print "graph_title", title
print "graph_category", categ
print "graph_vlabel Users"
print "graph_args --base 1000"
print "graph_title", title
print "graph_category", categ
for mumble in get_running_instances():
print "srv%d.label %s" % ( mumble.id, mumble.name.replace('#', '').encode(prefenc, "replace") );
if mumble.connecturl:
print "srv%d.info %s" % ( mumble.id, mumble.connecturl );
if mumble.users:
print "srv%d.warning %d" % ( mumble.id, int( mumble.users * warn ) );
print "srv%d.critical %d" % ( mumble.id, int( mumble.users * crit ) );
for mumble in get_running_instances():
print "srv%d.label %s" % ( mumble.id, mumble.name.replace('#', '').encode(prefenc, "replace") )
if mumble.connecturl:
print "srv%d.info %s" % ( mumble.id, mumble.connecturl )
if mumble.users:
print "srv%d.warning %d" % ( mumble.id, int( mumble.users * warn ) )
print "srv%d.critical %d" % ( mumble.id, int( mumble.users * crit ) )
elif sys.argv[-1] == 'autoconf': elif sys.argv[-1] == 'autoconf':
if Mumble.objects.count() == 0:
print "no (no servers configured)";
else:
# check if connecting works
try:
for mumble in get_running_instances():
mumble.ctl
except Exception, instance:
print "no (can't connect to server %s: %s)" % ( mumble.name, instance );
else:
print "yes";
if Mumble.objects.count() == 0:
print "no (no servers configured)"
else:
# check if connecting works
try:
for mumble in get_running_instances():
mumble.ctl
except Exception, instance:
print "no (can't connect to server %s: %s)" % ( mumble.name, instance )
else:
print "yes"
else: else:
for mumble in get_running_instances():
print "srv%d.value %d" % ( mumble.id, len( mumble.ctl.getPlayers( mumble.srvid ) ) );
for mumble in get_running_instances():
print "srv%d.value %d" % ( mumble.id, len( mumble.ctl.getPlayers( mumble.srvid ) ) )

2
pyweb/manage.py

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """

231
pyweb/mucli.py

@ -1,5 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright (C) 2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright (C) 2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -22,8 +23,8 @@ import os, sys
import inspect import inspect
import getpass import getpass
from optparse import OptionParser
from mumble.mctl import MumbleCtlBase
from optparse import OptionParser
from mumble.mctl import MumbleCtlBase
usage = """Usage: %prog [options] [<method name>] [<method arguments>] usage = """Usage: %prog [options] [<method name>] [<method arguments>]
@ -39,153 +40,153 @@ as False.
parser = OptionParser(usage=usage) parser = OptionParser(usage=usage)
parser.add_option( "-d", "--django-settings", parser.add_option( "-d", "--django-settings",
help="if specified, get connstring and slice defaults from the given Django "
"settings module. Default: empty.",
default=None
)
help="if specified, get connstring and slice defaults from the given Django "
"settings module. Default: empty.",
default=None
)
parser.add_option( "-c", "--connstring", parser.add_option( "-c", "--connstring",
help="connection string to use. Default is '%s'." % DEFAULT_CONNSTRING,
default=None
)
help="connection string to use. Default is '%s'." % DEFAULT_CONNSTRING,
default=None
)
parser.add_option( "-i", "--icesecret", parser.add_option( "-i", "--icesecret",
help="Ice secret to use in the connection. Also see --asksecret.",
default=None
)
help="Ice secret to use in the connection. Also see --asksecret.",
default=None
)
parser.add_option( "-a", "--asksecret", parser.add_option( "-a", "--asksecret",
help="Ask for the Ice secret on the shell instead of taking it from the command line.",
action="store_true", default=False
)
help="Ask for the Ice secret on the shell instead of taking it from the command line.",
action="store_true", default=False
)
parser.add_option( "-s", "--slice", parser.add_option( "-s", "--slice",
help="path to the slice file. Default is '%s'." % DEFAULT_SLICEFILE,
default=None
)
help="path to the slice file. Default is '%s'." % DEFAULT_SLICEFILE,
default=None
)
parser.add_option( "-e", "--encoding", parser.add_option( "-e", "--encoding",
help="Character set arguments are encoded in. Default: Read from LANG env variable with fallback to UTF-8.",
default=None
)
help="Character set arguments are encoded in. Default: Read from LANG env variable with fallback to UTF-8.",
default=None
)
parser.add_option( parser.add_option(
"-v", "--verbose",
help="Show verbose messages on stderr",
default=False,
action="store_true"
)
"-v", "--verbose",
help="Show verbose messages on stderr",
default=False,
action="store_true"
)
options, progargs = parser.parse_args() options, progargs = parser.parse_args()
if options.django_settings is not None: if options.django_settings is not None:
if options.verbose:
print >> sys.stderr, "Reading settings from module '%s'." % options.django_settings
if options.verbose:
print >> sys.stderr, "Reading settings from module '%s'." % options.django_settings
os.environ['DJANGO_SETTINGS_MODULE'] = options.django_settings
from django.conf import settings
os.environ['DJANGO_SETTINGS_MODULE'] = options.django_settings
from django.conf import settings
if options.connstring is None:
if options.verbose:
print >> sys.stderr, "Setting connstring from settings module"
options.connstring = settings.DEFAULT_CONN
if options.connstring is None:
if options.verbose:
print >> sys.stderr, "Setting connstring from settings module"
options.connstring = settings.DEFAULT_CONN
if options.slice is None:
if options.verbose:
print >> sys.stderr, "Setting slice from settings module"
options.slice = settings.SLICE
if options.slice is None:
if options.verbose:
print >> sys.stderr, "Setting slice from settings module"
options.slice = settings.SLICE
else: else:
if options.connstring is None:
if options.verbose:
print >> sys.stderr, "Setting default connstring"
options.connstring = DEFAULT_CONNSTRING
if options.connstring is None:
if options.verbose:
print >> sys.stderr, "Setting default connstring"
options.connstring = DEFAULT_CONNSTRING
if options.slice is None:
if options.verbose:
print >> sys.stderr, "Setting default slice"
options.slice = DEFAULT_SLICEFILE
if options.slice is None:
if options.verbose:
print >> sys.stderr, "Setting default slice"
options.slice = DEFAULT_SLICEFILE
if options.encoding is None: if options.encoding is None:
try:
locale = os.environ['LANG']
_, options.encoding = locale.split('.')
except (KeyError, ValueError):
options.encoding = "UTF-8"
try:
locale = os.environ['LANG']
_, options.encoding = locale.split('.')
except (KeyError, ValueError):
options.encoding = "UTF-8"
if options.verbose: if options.verbose:
print >> sys.stderr, "Connection info:"
print >> sys.stderr, " Connstring: %s" % options.connstring
print >> sys.stderr, " Slice: %s" % options.slice
print >> sys.stderr, "Encoding: %s" % options.encoding
print >> sys.stderr, "Connection info:"
print >> sys.stderr, " Connstring: %s" % options.connstring
print >> sys.stderr, " Slice: %s" % options.slice
print >> sys.stderr, "Encoding: %s" % options.encoding
if options.asksecret or options.icesecret == '': if options.asksecret or options.icesecret == '':
options.icesecret = getpass.getpass( "Ice secret: " )
options.icesecret = getpass.getpass( "Ice secret: " )
ctl = MumbleCtlBase.newInstance( options.connstring, options.slice, options.icesecret ) ctl = MumbleCtlBase.newInstance( options.connstring, options.slice, options.icesecret )
if not progargs: if not progargs:
# Print available methods.
for method in inspect.getmembers( ctl ):
if method[0][0] == '_' or not callable( method[1] ):
continue
if hasattr( method[1], "innerfunc" ):
args = inspect.getargspec( method[1].innerfunc )[0]
else:
args = inspect.getargspec( method[1] )[0]
if len( args ) > 1:
if args[0] == 'self':
print "%s( %s )" % ( method[0], ', '.join( args[1:] ) )
else:
print "%s()" % method[0]
# Print available methods.
for method in inspect.getmembers( ctl ):
if method[0][0] == '_' or not callable( method[1] ):
continue
if hasattr( method[1], "innerfunc" ):
args = inspect.getargspec( method[1].innerfunc )[0]
else:
args = inspect.getargspec( method[1] )[0]
if len( args ) > 1:
if args[0] == 'self':
print "%s( %s )" % ( method[0], ', '.join( args[1:] ) )
else:
print "%s()" % method[0]
else: else:
# function name given. check if its args matches ours, if yes call it, if not print usage
if options.verbose:
print >> sys.stderr, "Method name: %s" % progargs[0]
method = getattr( ctl, progargs[0] )
if hasattr( method, "innerfunc" ):
method = method.innerfunc
args = inspect.getargspec( method )[0]
if len(progargs) == len(args) and args[0] == 'self':
if len(args) == 1:
print method(ctl)
else:
cleanargs = []
for param in progargs[1:]:
try:
argtype, argval = param.split(':', 1)
except ValueError:
cleanargs.append( param.decode(options.encoding) )
else:
cleanval = {
'bool': lambda val: val in ('True', 'true', '1', 'Yes', 'yes'),
'int': int,
'float': float,
'string': str
}[ argtype ]( argval )
if argtype == 'string':
cleanval = cleanval.decode(options.encoding)
cleanargs.append(cleanval)
if options.verbose:
print >> sys.stderr, "Call arguments: %s" % repr(cleanargs)
print method( ctl, *cleanargs )
elif len(args) == 1:
print >> sys.stderr, "Method '%s' does not take any arguments." % progargs[0]
print >> sys.stderr, "Expected %s()" % progargs[0]
else:
print >> sys.stderr, "Invalid arguments for method '%s': %s" % ( progargs[0], ', '.join( progargs[1:] ) )
print >> sys.stderr, "Expected %s( %s )" % ( progargs[0], ', '.join( args[1:] ) )
# function name given. check if its args matches ours, if yes call it, if not print usage
if options.verbose:
print >> sys.stderr, "Method name: %s" % progargs[0]
method = getattr( ctl, progargs[0] )
if hasattr( method, "innerfunc" ):
method = method.innerfunc
args = inspect.getargspec( method )[0]
if len(progargs) == len(args) and args[0] == 'self':
if len(args) == 1:
print method(ctl)
else:
cleanargs = []
for param in progargs[1:]:
try:
argtype, argval = param.split(':', 1)
except ValueError:
cleanargs.append( param.decode(options.encoding) )
else:
cleanval = {
'bool': lambda val: val in ('True', 'true', '1', 'Yes', 'yes'),
'int': int,
'float': float,
'string': str
}[ argtype ]( argval )
if argtype == 'string':
cleanval = cleanval.decode(options.encoding)
cleanargs.append(cleanval)
if options.verbose:
print >> sys.stderr, "Call arguments: %s" % repr(cleanargs)
print method( ctl, *cleanargs )
elif len(args) == 1:
print >> sys.stderr, "Method '%s' does not take any arguments." % progargs[0]
print >> sys.stderr, "Expected %s()" % progargs[0]
else:
print >> sys.stderr, "Invalid arguments for method '%s': %s" % ( progargs[0], ', '.join( progargs[1:] ) )
print >> sys.stderr, "Expected %s( %s )" % ( progargs[0], ', '.join( args[1:] ) )

567
pyweb/mumble/MumbleCtlDbus.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009, withgod <withgod@sourceforge.net> * Copyright © 2009, withgod <withgod@sourceforge.net>
@ -15,316 +16,316 @@
* GNU General Public License for more details. * GNU General Public License for more details.
""" """
from PIL import Image
from struct import pack, unpack
from zlib import compress, decompress
from PIL import Image
from struct import pack, unpack
from zlib import compress, decompress
from mctl import MumbleCtlBase
from utils import ObjectInfo
from mctl import MumbleCtlBase
from utils import ObjectInfo
import dbus import dbus
from dbus.exceptions import DBusException from dbus.exceptions import DBusException
def MumbleCtlDbus( connstring ): def MumbleCtlDbus( connstring ):
""" Choose the correct DBus handler (1.1.8 or legacy) to use. """
""" Choose the correct DBus handler (1.1.8 or legacy) to use. """
meta = dbus.Interface( dbus.SystemBus().get_object( connstring, '/' ), 'net.sourceforge.mumble.Meta' );
meta = dbus.Interface( dbus.SystemBus().get_object( connstring, '/' ), 'net.sourceforge.mumble.Meta' )
try:
meta.getVersion();
except DBusException:
return MumbleCtlDbus_Legacy( connstring, meta );
else:
return MumbleCtlDbus_118( connstring, meta );
try:
meta.getVersion()
except DBusException:
return MumbleCtlDbus_Legacy( connstring, meta )
else:
return MumbleCtlDbus_118( connstring, meta )
class MumbleCtlDbus_118(MumbleCtlBase): class MumbleCtlDbus_118(MumbleCtlBase):
method = "DBus";
method = "DBus"
def __init__( self, connstring, meta ):
self.dbus_base = connstring;
self.meta = meta;
def __init__( self, connstring, meta ):
self.dbus_base = connstring
self.meta = meta
def _getDbusMeta( self ):
return self.meta;
def _getDbusMeta( self ):
return self.meta
def _getDbusServerObject( self, srvid):
if srvid not in self.getBootedServers():
raise SystemError, 'No murmur process with the given server ID (%d) is running and attached to system dbus under %s.' % ( srvid, self.meta );
def _getDbusServerObject( self, srvid):
if srvid not in self.getBootedServers():
raise SystemError, 'No murmur process with the given server ID (%d) is running and attached to system dbus under %s.' % ( srvid, self.meta )
return dbus.Interface( dbus.SystemBus().get_object( self.dbus_base, '/%d' % srvid ), 'net.sourceforge.mumble.Murmur' );
return dbus.Interface( dbus.SystemBus().get_object( self.dbus_base, '/%d' % srvid ), 'net.sourceforge.mumble.Murmur' )
def getVersion( self ):
return MumbleCtlDbus_118.convertDbusTypeToNative( self.meta.getVersion() );
def getVersion( self ):
return MumbleCtlDbus_118.convertDbusTypeToNative( self.meta.getVersion() )
def getAllConf(self, srvid):
conf = self.meta.getAllConf(dbus.Int32(srvid))
def getAllConf(self, srvid):
conf = self.meta.getAllConf(dbus.Int32(srvid))
info = {};
for key in conf:
if key == "playername":
info['username'] = conf[key];
else:
info[str(key)] = conf[key];
return info;
info = {}
for key in conf:
if key == "playername":
info['username'] = conf[key]
else:
info[str(key)] = conf[key]
return info
def getConf(self, srvid, key):
if key == "username":
key = "playername";
def getConf(self, srvid, key):
if key == "username":
key = "playername"
return self.meta.getConf(dbus.Int32( srvid ), key)
return self.meta.getConf(dbus.Int32( srvid ), key)
def setConf(self, srvid, key, value):
if key == "username":
key = "playername";
def setConf(self, srvid, key, value):
if key == "username":
key = "playername"
self.meta.setConf(dbus.Int32( srvid ), key, value)
def getDefaultConf(self):
conf = self.meta.getDefaultConf()
info = {};
for key in conf:
if key == "playername":
info['username'] = conf[key];
else:
info[str(key)] = conf[key];
return info;
def start( self, srvid ):
self.meta.start( srvid );
def stop( self, srvid ):
self.meta.stop( srvid );
def isBooted( self, srvid ):
return bool( self.meta.isBooted( srvid ) );
def deleteServer( self, srvid ):
srvid = dbus.Int32( srvid )
if self.meta.isBooted( srvid ):
self.meta.stop( srvid )
self.meta.deleteServer( srvid )
def newServer(self):
return self.meta.newServer()
def registerPlayer(self, srvid, name, email, password):
mumbleid = int( self._getDbusServerObject(srvid).registerPlayer(name) );
self.setRegistration( srvid, mumbleid, name, email, password );
return mumbleid;
def unregisterPlayer(self, srvid, mumbleid):
self._getDbusServerObject(srvid).unregisterPlayer(dbus.Int32( mumbleid ))
def getChannels(self, srvid):
chans = self._getDbusServerObject(srvid).getChannels()
ret = {};
for channel in chans:
print channel;
ret[ channel[0] ] = ObjectInfo(
id = int(channel[0]),
name = unicode(channel[1]),
parent = int(channel[2]),
links = [ int(lnk) for lnk in channel[3] ],
);
return ret;
def getPlayers(self, srvid):
players = self._getDbusServerObject(srvid).getPlayers();
ret = {};
for playerObj in players:
ret[ int(playerObj[0]) ] = ObjectInfo(
session = int( playerObj[0] ),
mute = bool( playerObj[1] ),
deaf = bool( playerObj[2] ),
suppress = bool( playerObj[3] ),
selfMute = bool( playerObj[4] ),
selfDeaf = bool( playerObj[5] ),
channel = int( playerObj[6] ),
userid = int( playerObj[7] ),
name = unicode( playerObj[8] ),
onlinesecs = int( playerObj[9] ),
bytespersec = int( playerObj[10] )
);
return ret;
def getRegisteredPlayers(self, srvid, filter = ''):
users = self._getDbusServerObject(srvid).getRegisteredPlayers( filter );
ret = {};
for user in users:
ret[int(user[0])] = ObjectInfo(
userid = int( user[0] ),
name = unicode( user[1] ),
email = unicode( user[2] ),
pw = unicode( user[3] )
);
return ret
def getACL(self, srvid, channelid):
raw_acls, raw_groups, raw_inherit = self._getDbusServerObject(srvid).getACL(channelid)
acls = [ ObjectInfo(
applyHere = bool(rule[0]),
applySubs = bool(rule[1]),
inherited = bool(rule[2]),
userid = int(rule[3]),
group = unicode(rule[4]),
allow = int(rule[5]),
deny = int(rule[6]),
)
for rule in raw_acls
];
groups = [ ObjectInfo(
name = unicode(group[0]),
inherited = bool(group[1]),
inherit = bool(group[2]),
inheritable = bool(group[3]),
add = [ int(usrid) for usrid in group[4] ],
remove = [ int(usrid) for usrid in group[5] ],
members = [ int(usrid) for usrid in group[6] ],
)
for group in raw_groups
];
return acls, groups, bool(raw_inherit);
def setACL(self, srvid, channelid, acls, groups, inherit):
# Pack acl ObjectInfo into a tuple and send that over dbus
dbus_acls = [
( rule.applyHere, rule.applySubs, rule.inherited, rule.userid, rule.group, rule.allow, rule.deny )
for rule in acls
];
dbus_groups = [
( group.name, group.inherited, group.inherit, group.inheritable, group.add, group.remove, group.members )
for group in groups
];
return self._getDbusServerObject(srvid).setACL( channelid, dbus_acls, dbus_groups, inherit );
def getBootedServers(self):
return MumbleCtlDbus_118.convertDbusTypeToNative(self.meta.getBootedServers())
def getAllServers(self):
return MumbleCtlDbus_118.convertDbusTypeToNative(self.meta.getAllServers())
def setSuperUserPassword(self, srvid, value):
self.meta.setSuperUserPassword(dbus.Int32(srvid), value)
def getRegistration(self, srvid, mumbleid):
user = self._getDbusServerObject(srvid).getRegistration(dbus.Int32(mumbleid))
return ObjectInfo(
userid = mumbleid,
name = unicode(user[1]),
email = unicode(user[2]),
pw = '',
);
def setRegistration(self, srvid, mumbleid, name, email, password):
return MumbleCtlDbus_118.convertDbusTypeToNative(
self._getDbusServerObject(srvid).setRegistration(dbus.Int32(mumbleid), name, email, password)
)
def getTexture(self, srvid, mumbleid):
texture = self._getDbusServerObject(srvid).getTexture(dbus.Int32(mumbleid));
if len(texture) == 0:
raise ValueError( "No Texture has been set." );
# this returns a list of bytes.
# first 4 bytes: Length of uncompressed string, rest: compressed data
orig_len = ( texture[0] << 24 ) | ( texture[1] << 16 ) | ( texture[2] << 8 ) | ( texture[3] );
# convert rest to string and run decompress
bytestr = "";
for byte in texture[4:]:
bytestr += pack( "B", int(byte) );
decompressed = decompress( bytestr );
# iterate over 4 byte chunks of the string
imgdata = "";
for idx in range( 0, orig_len, 4 ):
# read 4 bytes = BGRA and convert to RGBA
bgra = unpack( "4B", decompressed[idx:idx+4] );
imgdata += pack( "4B", bgra[2], bgra[1], bgra[0], bgra[3] );
# return an 600x60 RGBA image object created from the data
return Image.fromstring( "RGBA", ( 600, 60 ), imgdata);
def setTexture(self, srvid, mumbleid, infile):
# open image, convert to RGBA, and resize to 600x60
img = infile.convert( "RGBA" ).transform( ( 600, 60 ), Image.EXTENT, ( 0, 0, 600, 60 ) );
# iterate over the list and pack everything into a string
bgrastring = "";
for ent in list( img.getdata() ):
# ent is in RGBA format, but Murmur wants BGRA (ARGB inverse), so stuff needs
# to be reordered when passed to pack()
bgrastring += pack( "4B", ent[2], ent[1], ent[0], ent[3] );
# compress using zlib
compressed = compress( bgrastring );
# pack the original length in 4 byte big endian, and concat the compressed
# data to it to emulate qCompress().
texture = pack( ">L", len(bgrastring) ) + compressed;
# finally call murmur and set the texture
self._getDbusServerObject(srvid).setTexture(dbus.Int32( mumbleid ), texture)
def verifyPassword( self, srvid, username, password ):
player = self.getRegisteredPlayers( srvid, username );
if not player:
return -2;
ok = MumbleCtlDbus_118.convertDbusTypeToNative(
self._getDbusServerObject(srvid).verifyPassword( dbus.Int32( player[0].userid ), password )
);
if ok:
return player[0].userid;
else:
return -1;
@staticmethod
def convertDbusTypeToNative(data):
#i know dbus.* type is extends python native type.
#but dbus.* type is not native type. it's not good transparent for using Ice/Dbus.
ret = None
if isinstance(data, tuple) or type(data) is data.__class__ is dbus.Array or data.__class__ is dbus.Struct:
ret = []
for x in data:
ret.append(MumbleCtlDbus_118.convertDbusTypeToNative(x))
elif data.__class__ is dbus.Dictionary:
ret = {}
for x in data.items():
ret[MumbleCtlDbus_118.convertDbusTypeToNative(x[0])] = MumbleCtlDbus_118.convertDbusTypeToNative(x[1])
else:
if data.__class__ is dbus.Boolean:
ret = bool(data)
elif data.__class__ is dbus.String:
ret = unicode(data)
elif data.__class__ is dbus.Int32 or data.__class__ is dbus.UInt32:
ret = int(data)
elif data.__class__ is dbus.Byte:
ret = int(data)
return ret
self.meta.setConf(dbus.Int32( srvid ), key, value)
def getDefaultConf(self):
conf = self.meta.getDefaultConf()
info = {}
for key in conf:
if key == "playername":
info['username'] = conf[key]
else:
info[str(key)] = conf[key]
return info
def start( self, srvid ):
self.meta.start( srvid )
def stop( self, srvid ):
self.meta.stop( srvid )
def isBooted( self, srvid ):
return bool( self.meta.isBooted( srvid ) )
def deleteServer( self, srvid ):
srvid = dbus.Int32( srvid )
if self.meta.isBooted( srvid ):
self.meta.stop( srvid )
self.meta.deleteServer( srvid )
def newServer(self):
return self.meta.newServer()
def registerPlayer(self, srvid, name, email, password):
mumbleid = int( self._getDbusServerObject(srvid).registerPlayer(name) )
self.setRegistration( srvid, mumbleid, name, email, password )
return mumbleid
def unregisterPlayer(self, srvid, mumbleid):
self._getDbusServerObject(srvid).unregisterPlayer(dbus.Int32( mumbleid ))
def getChannels(self, srvid):
chans = self._getDbusServerObject(srvid).getChannels()
ret = {}
for channel in chans:
print channel
ret[ channel[0] ] = ObjectInfo(
id = int(channel[0]),
name = unicode(channel[1]),
parent = int(channel[2]),
links = [ int(lnk) for lnk in channel[3] ],
)
return ret
def getPlayers(self, srvid):
players = self._getDbusServerObject(srvid).getPlayers()
ret = {}
for playerObj in players:
ret[ int(playerObj[0]) ] = ObjectInfo(
session = int( playerObj[0] ),
mute = bool( playerObj[1] ),
deaf = bool( playerObj[2] ),
suppress = bool( playerObj[3] ),
selfMute = bool( playerObj[4] ),
selfDeaf = bool( playerObj[5] ),
channel = int( playerObj[6] ),
userid = int( playerObj[7] ),
name = unicode( playerObj[8] ),
onlinesecs = int( playerObj[9] ),
bytespersec = int( playerObj[10] )
)
return ret
def getRegisteredPlayers(self, srvid, filter = ''):
users = self._getDbusServerObject(srvid).getRegisteredPlayers( filter )
ret = {}
for user in users:
ret[int(user[0])] = ObjectInfo(
userid = int( user[0] ),
name = unicode( user[1] ),
email = unicode( user[2] ),
pw = unicode( user[3] )
)
return ret
def getACL(self, srvid, channelid):
raw_acls, raw_groups, raw_inherit = self._getDbusServerObject(srvid).getACL(channelid)
acls = [ ObjectInfo(
applyHere = bool(rule[0]),
applySubs = bool(rule[1]),
inherited = bool(rule[2]),
userid = int(rule[3]),
group = unicode(rule[4]),
allow = int(rule[5]),
deny = int(rule[6]),
)
for rule in raw_acls
]
groups = [ ObjectInfo(
name = unicode(group[0]),
inherited = bool(group[1]),
inherit = bool(group[2]),
inheritable = bool(group[3]),
add = [ int(usrid) for usrid in group[4] ],
remove = [ int(usrid) for usrid in group[5] ],
members = [ int(usrid) for usrid in group[6] ],
)
for group in raw_groups
]
return acls, groups, bool(raw_inherit)
def setACL(self, srvid, channelid, acls, groups, inherit):
# Pack acl ObjectInfo into a tuple and send that over dbus
dbus_acls = [
( rule.applyHere, rule.applySubs, rule.inherited, rule.userid, rule.group, rule.allow, rule.deny )
for rule in acls
]
dbus_groups = [
( group.name, group.inherited, group.inherit, group.inheritable, group.add, group.remove, group.members )
for group in groups
]
return self._getDbusServerObject(srvid).setACL( channelid, dbus_acls, dbus_groups, inherit )
def getBootedServers(self):
return MumbleCtlDbus_118.convertDbusTypeToNative(self.meta.getBootedServers())
def getAllServers(self):
return MumbleCtlDbus_118.convertDbusTypeToNative(self.meta.getAllServers())
def setSuperUserPassword(self, srvid, value):
self.meta.setSuperUserPassword(dbus.Int32(srvid), value)
def getRegistration(self, srvid, mumbleid):
user = self._getDbusServerObject(srvid).getRegistration(dbus.Int32(mumbleid))
return ObjectInfo(
userid = mumbleid,
name = unicode(user[1]),
email = unicode(user[2]),
pw = '',
)
def setRegistration(self, srvid, mumbleid, name, email, password):
return MumbleCtlDbus_118.convertDbusTypeToNative(
self._getDbusServerObject(srvid).setRegistration(dbus.Int32(mumbleid), name, email, password)
)
def getTexture(self, srvid, mumbleid):
texture = self._getDbusServerObject(srvid).getTexture(dbus.Int32(mumbleid))
if len(texture) == 0:
raise ValueError( "No Texture has been set." )
# this returns a list of bytes.
# first 4 bytes: Length of uncompressed string, rest: compressed data
orig_len = ( texture[0] << 24 ) | ( texture[1] << 16 ) | ( texture[2] << 8 ) | ( texture[3] )
# convert rest to string and run decompress
bytestr = ""
for byte in texture[4:]:
bytestr += pack( "B", int(byte) )
decompressed = decompress( bytestr )
# iterate over 4 byte chunks of the string
imgdata = ""
for idx in range( 0, orig_len, 4 ):
# read 4 bytes = BGRA and convert to RGBA
bgra = unpack( "4B", decompressed[idx:idx+4] )
imgdata += pack( "4B", bgra[2], bgra[1], bgra[0], bgra[3] )
# return an 600x60 RGBA image object created from the data
return Image.fromstring( "RGBA", ( 600, 60 ), imgdata)
def setTexture(self, srvid, mumbleid, infile):
# open image, convert to RGBA, and resize to 600x60
img = infile.convert( "RGBA" ).transform( ( 600, 60 ), Image.EXTENT, ( 0, 0, 600, 60 ) )
# iterate over the list and pack everything into a string
bgrastring = ""
for ent in list( img.getdata() ):
# ent is in RGBA format, but Murmur wants BGRA (ARGB inverse), so stuff needs
# to be reordered when passed to pack()
bgrastring += pack( "4B", ent[2], ent[1], ent[0], ent[3] )
# compress using zlib
compressed = compress( bgrastring )
# pack the original length in 4 byte big endian, and concat the compressed
# data to it to emulate qCompress().
texture = pack( ">L", len(bgrastring) ) + compressed
# finally call murmur and set the texture
self._getDbusServerObject(srvid).setTexture(dbus.Int32( mumbleid ), texture)
def verifyPassword( self, srvid, username, password ):
player = self.getRegisteredPlayers( srvid, username )
if not player:
return -2
ok = MumbleCtlDbus_118.convertDbusTypeToNative(
self._getDbusServerObject(srvid).verifyPassword( dbus.Int32( player[0].userid ), password )
)
if ok:
return player[0].userid
else:
return -1
@staticmethod
def convertDbusTypeToNative(data):
#i know dbus.* type is extends python native type.
#but dbus.* type is not native type. it's not good transparent for using Ice/Dbus.
ret = None
if isinstance(data, tuple) or type(data) is data.__class__ is dbus.Array or data.__class__ is dbus.Struct:
ret = []
for x in data:
ret.append(MumbleCtlDbus_118.convertDbusTypeToNative(x))
elif data.__class__ is dbus.Dictionary:
ret = {}
for x in data.items():
ret[MumbleCtlDbus_118.convertDbusTypeToNative(x[0])] = MumbleCtlDbus_118.convertDbusTypeToNative(x[1])
else:
if data.__class__ is dbus.Boolean:
ret = bool(data)
elif data.__class__ is dbus.String:
ret = unicode(data)
elif data.__class__ is dbus.Int32 or data.__class__ is dbus.UInt32:
ret = int(data)
elif data.__class__ is dbus.Byte:
ret = int(data)
return ret
class MumbleCtlDbus_Legacy( MumbleCtlDbus_118 ): class MumbleCtlDbus_Legacy( MumbleCtlDbus_118 ):
def getVersion( self ):
return ( 1, 1, 4, u"1.1.4" );
def getVersion( self ):
return ( 1, 1, 4, u"1.1.4" )
def setRegistration(self, srvid, mumbleid, name, email, password):
return MumbleCtlDbus_118.convertDbusTypeToNative(
self._getDbusServerObject(srvid).updateRegistration( ( dbus.Int32(mumbleid), name, email, password ) )
)
def setRegistration(self, srvid, mumbleid, name, email, password):
return MumbleCtlDbus_118.convertDbusTypeToNative(
self._getDbusServerObject(srvid).updateRegistration( ( dbus.Int32(mumbleid), name, email, password ) )
)

1107
pyweb/mumble/MumbleCtlIce.py
File diff suppressed because it is too large
View File

39
pyweb/mumble/__init__.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
* *
@ -16,29 +17,29 @@
version = { 'major': 2, 'minor': 2, 'beta': None } version = { 'major': 2, 'minor': 2, 'beta': None }
if version['beta']: if version['beta']:
version_str = "v%(major)d.%(minor)dbeta%(beta)d" % version
version_str = "v%(major)d.%(minor)dbeta%(beta)d" % version
else: else:
version_str = "v%(major)d.%(minor)d" % version
version_str = "v%(major)d.%(minor)d" % version
def getVersions(): def getVersions():
""" Generator that yields all available upstream versions. """
url = 'http://bitbucket.org/Svedrin/mumble-django/raw/tip/.hgtags'
from urllib2 import urlopen
webtags = urlopen(url)
try:
while True:
line = webtags.readline().strip()
if not line:
raise StopIteration
_, version = line.split(' ')
yield version
finally:
webtags.close()
""" Generator that yields all available upstream versions. """
url = 'http://bitbucket.org/Svedrin/mumble-django/raw/tip/.hgtags'
from urllib2 import urlopen
webtags = urlopen(url)
try:
while True:
line = webtags.readline().strip()
if not line:
raise StopIteration
_, version = line.split(' ')
yield version
finally:
webtags.close()
def getLatestUpstreamVersion(): def getLatestUpstreamVersion():
""" Return the latest version available upstream. """
return max(getVersions())
""" Return the latest version available upstream. """
return max(getVersions())
def isUptodate(): def isUptodate():
""" Check if this version of Mumble-Django is the latest available. """
return version_str >= getLatestUpstreamVersion()
""" Check if this version of Mumble-Django is the latest available. """
return version_str >= getLatestUpstreamVersion()

169
pyweb/mumble/admin.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -22,124 +23,124 @@ from mumble.forms import MumbleServerForm, MumbleAdminForm, MumbleUserAdminForm
from mumble.models import MumbleServer, Mumble, MumbleUser from mumble.models import MumbleServer, Mumble, MumbleUser
class MumbleServerAdmin(admin.ModelAdmin): class MumbleServerAdmin(admin.ModelAdmin):
list_display = [ 'dbus', 'get_murmur_online', 'get_murmur_version' ]
search_fields = [ 'dbus' ]
ordering = [ 'dbus' ]
list_display = [ 'dbus', 'get_murmur_online', 'get_murmur_version' ]
search_fields = [ 'dbus' ]
ordering = [ 'dbus' ]
form = MumbleServerForm
form = MumbleServerForm
def get_murmur_online( self, obj ):
return obj.online
def get_murmur_online( self, obj ):
return obj.online
get_murmur_online.short_description = _('Master is running')
get_murmur_online.boolean = True
get_murmur_online.short_description = _('Master is running')
get_murmur_online.boolean = True
def get_murmur_version( self, obj ):
if obj.online:
return obj.prettyversion
return "?"
def get_murmur_version( self, obj ):
if obj.online:
return obj.prettyversion
return "?"
get_murmur_version.short_description = _("Server version")
get_murmur_version.short_description = _("Server version")
class MumbleAdmin(admin.ModelAdmin): class MumbleAdmin(admin.ModelAdmin):
""" Specification for the "Server administration" admin section. """
""" Specification for the "Server administration" admin section. """
list_display = [ 'name', 'srvid', 'get_addr', 'get_port', 'get_murmur_online', 'get_booted',
'get_is_public', 'get_users_regged', 'get_users_online', 'get_channel_count' ];
list_filter = [ 'addr', 'server' ];
search_fields = [ 'name', 'addr', 'port' ];
ordering = [ 'name' ];
form = MumbleAdminForm;
list_display = [ 'name', 'srvid', 'get_addr', 'get_port', 'get_murmur_online', 'get_booted',
'get_is_public', 'get_users_regged', 'get_users_online', 'get_channel_count' ]
list_filter = [ 'addr', 'server' ]
search_fields = [ 'name', 'addr', 'port' ]
ordering = [ 'name' ]
form = MumbleAdminForm
def get_murmur_online( self, obj ):
return obj.server.online
def get_murmur_online( self, obj ):
return obj.server.online
get_murmur_online.short_description = _('Master is running')
get_murmur_online.boolean = True
get_murmur_online.short_description = _('Master is running')
get_murmur_online.boolean = True
def get_addr( self, obj ):
if not obj.addr:
return "*"
return obj.addr
def get_addr( self, obj ):
if not obj.addr:
return "*"
return obj.addr
get_addr.short_description = _('Server Address')
get_addr.short_description = _('Server Address')
def get_port( self, obj ):
if not obj.port:
return '< %d >' % (obj.server.defaultPort + obj.srvid - 1)
return obj.port
def get_port( self, obj ):
if not obj.port:
return '< %d >' % (obj.server.defaultPort + obj.srvid - 1)
return obj.port
get_port.short_description = _('Server Port')
get_port.short_description = _('Server Port')
def get_booted( self, obj ):
return obj.booted
def get_booted( self, obj ):
return obj.booted
get_booted.short_description = _('Instance is running')
get_booted.boolean = True
get_booted.short_description = _('Instance is running')
get_booted.boolean = True
def get_users_regged( self, obj ):
""" Populates the "Registered users" column. """
if obj.booted:
return obj.users_regged;
else:
return '-';
def get_users_regged( self, obj ):
""" Populates the "Registered users" column. """
if obj.booted:
return obj.users_regged
else:
return '-'
get_users_regged.short_description = _( 'Registered users' );
get_users_regged.short_description = _( 'Registered users' )
def get_users_online( self, obj ):
""" Populates the "Online users" column. """
if obj.booted:
return obj.users_online;
else:
return '-';
def get_users_online( self, obj ):
""" Populates the "Online users" column. """
if obj.booted:
return obj.users_online
else:
return '-'
get_users_online.short_description = _( 'Online users' );
get_users_online.short_description = _( 'Online users' )
def get_channel_count( self, obj ):
""" Populates the "Channel Count" column. """
if obj.booted:
return obj.channel_cnt;
else:
return '-';
def get_channel_count( self, obj ):
""" Populates the "Channel Count" column. """
if obj.booted:
return obj.channel_cnt
else:
return '-'
get_channel_count.short_description = _( 'Channel count' );
get_channel_count.short_description = _( 'Channel count' )
def get_is_public( self, obj ):
""" Populates the "Public" column. """
if obj.booted:
if obj.is_public:
return _( 'Yes' );
else:
return _( 'No' );
else:
return '-';
def get_is_public( self, obj ):
""" Populates the "Public" column. """
if obj.booted:
if obj.is_public:
return _( 'Yes' )
else:
return _( 'No' )
else:
return '-'
get_is_public.short_description = _( 'Public' );
get_is_public.short_description = _( 'Public' )
class MumbleUserAdmin(admin.ModelAdmin): class MumbleUserAdmin(admin.ModelAdmin):
""" Specification for the "Registered users" admin section. """
""" Specification for the "Registered users" admin section. """
list_display = [ 'owner', 'server', 'name', 'mumbleid', 'get_acl_admin' ];
list_filter = [ 'server' ];
search_fields = [ 'owner__username', 'name' ];
ordering = [ 'owner__username' ];
list_display = [ 'owner', 'server', 'name', 'mumbleid', 'get_acl_admin' ]
list_filter = [ 'server' ]
search_fields = [ 'owner__username', 'name' ]
ordering = [ 'owner__username' ]
form = MumbleUserAdminForm
form = MumbleUserAdminForm
def get_acl_admin( self, obj ):
if obj.server.booted:
return obj.aclAdmin
return None
def get_acl_admin( self, obj ):
if obj.server.booted:
return obj.aclAdmin
return None
get_acl_admin.short_description = _('Admin on root channel')
get_acl_admin.boolean = True
get_acl_admin.short_description = _('Admin on root channel')
get_acl_admin.boolean = True
admin.site.register( MumbleServer, MumbleServerAdmin );
admin.site.register( Mumble, MumbleAdmin );
admin.site.register( MumbleUser, MumbleUserAdmin );
admin.site.register( MumbleServer, MumbleServerAdmin )
admin.site.register( Mumble, MumbleAdmin )
admin.site.register( MumbleUser, MumbleUserAdmin )

455
pyweb/mumble/forms.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -17,309 +18,309 @@
import socket import socket
import re import re
from django import forms
from django.conf import settings
from django.forms import Form, ModelForm
from django.utils.translation import ugettext_lazy as _
from django import forms
from django.conf import settings
from django.forms import Form, ModelForm
from django.utils.translation import ugettext_lazy as _
from mumble.models import MumbleServer, Mumble, MumbleUser
from mumble.models import MumbleServer, Mumble, MumbleUser
class PropertyModelForm( ModelForm ): class PropertyModelForm( ModelForm ):
""" ModelForm that gets/sets fields that are not within the model's
fields as model attributes. Necessary to get forms that manipulate
properties.
"""
""" ModelForm that gets/sets fields that are not within the model's
fields as model attributes. Necessary to get forms that manipulate
properties.
"""
def __init__( self, *args, **kwargs ):
ModelForm.__init__( self, *args, **kwargs );
def __init__( self, *args, **kwargs ):
ModelForm.__init__( self, *args, **kwargs )
if self.instance:
instfields = self.instance._meta.get_all_field_names()
for fldname in self.fields:
if fldname in instfields:
continue
self.fields[fldname].initial = getattr( self.instance, fldname )
prop = getattr( self.instance.__class__, fldname )
if prop.__doc__:
self.fields[fldname].label = _(prop.__doc__)
if self.instance:
instfields = self.instance._meta.get_all_field_names()
for fldname in self.fields:
if fldname in instfields:
continue
self.fields[fldname].initial = getattr( self.instance, fldname )
prop = getattr( self.instance.__class__, fldname )
if prop.__doc__:
self.fields[fldname].label = _(prop.__doc__)
def save( self, commit=True ):
inst = ModelForm.save( self, commit=commit )
def save( self, commit=True ):
inst = ModelForm.save( self, commit=commit )
if commit:
self.save_to_model( inst )
else:
# Update when the model has been saved.
from django.db.models import signals
self._update_inst = inst
signals.post_save.connect( self.save_listener, sender=inst.__class__ )
if commit:
self.save_to_model( inst )
else:
# Update when the model has been saved.
from django.db.models import signals
self._update_inst = inst
signals.post_save.connect( self.save_listener, sender=inst.__class__ )
return inst
return inst
def save_listener( self, **kwargs ):
if kwargs['instance'] is self._update_inst:
self.save_to_model( self._update_inst )
def save_listener( self, **kwargs ):
if kwargs['instance'] is self._update_inst:
self.save_to_model( self._update_inst )
def save_to_model( self, inst ):
instfields = inst._meta.get_all_field_names()
def save_to_model( self, inst ):
instfields = inst._meta.get_all_field_names()
for fldname in self.fields:
if fldname not in instfields:
setattr( inst, fldname, self.cleaned_data[fldname] )
for fldname in self.fields:
if fldname not in instfields:
setattr( inst, fldname, self.cleaned_data[fldname] )
class MumbleForm( PropertyModelForm ): class MumbleForm( PropertyModelForm ):
""" The Mumble Server admin form that allows to configure settings which do
not necessarily have to be reserved to the server hoster.
Server hosters are expected to use the Django admin application instead,
where everything can be configured freely.
"""
url = forms.CharField( required=False )
motd = forms.CharField( required=False, widget=forms.Textarea )
passwd = forms.CharField( required=False, help_text=_(
"Password required to join. Leave empty for public servers.") )
supw = forms.CharField( required=False, widget=forms.PasswordInput )
obfsc = forms.BooleanField( required=False, help_text=_(
"If on, IP adresses of the clients are not logged.") )
player = forms.CharField( required=False )
channel = forms.CharField( required=False )
defchan = forms.TypedChoiceField( choices=(), coerce=int, required=False )
timeout = forms.IntegerField( required=False )
certreq = forms.BooleanField( required=False )
textlen = forms.IntegerField( required=False )
html = forms.BooleanField( required=False )
def __init__( self, *args, **kwargs ):
PropertyModelForm.__init__( self, *args, **kwargs )
# Populate the `default channel' field's choices
choices = [ ('', '----------') ]
if self.instance and self.instance.srvid is not None:
if self.instance.booted:
def add_item( item, level ):
if item.is_server or item.is_channel:
choices.append( ( item.chanid, ( "-"*level + " " + item.name ) ) )
self.instance.rootchan.visit(add_item)
else:
current = self.instance.defchan
if current is not None:
choices.append( ( current, "Current value: %d" % current ) )
self.fields['defchan'].choices = choices
class Meta:
model = Mumble;
fields = ['name'];
""" The Mumble Server admin form that allows to configure settings which do
not necessarily have to be reserved to the server hoster.
Server hosters are expected to use the Django admin application instead,
where everything can be configured freely.
"""
url = forms.CharField( required=False )
motd = forms.CharField( required=False, widget=forms.Textarea )
passwd = forms.CharField( required=False, help_text=_(
"Password required to join. Leave empty for public servers.") )
supw = forms.CharField( required=False, widget=forms.PasswordInput )
obfsc = forms.BooleanField( required=False, help_text=_(
"If on, IP adresses of the clients are not logged.") )
player = forms.CharField( required=False )
channel = forms.CharField( required=False )
defchan = forms.TypedChoiceField( choices=(), coerce=int, required=False )
timeout = forms.IntegerField( required=False )
certreq = forms.BooleanField( required=False )
textlen = forms.IntegerField( required=False )
html = forms.BooleanField( required=False )
def __init__( self, *args, **kwargs ):
PropertyModelForm.__init__( self, *args, **kwargs )
# Populate the `default channel' field's choices
choices = [ ('', '----------') ]
if self.instance and self.instance.srvid is not None:
if self.instance.booted:
def add_item( item, level ):
if item.is_server or item.is_channel:
choices.append( ( item.chanid, ( "-"*level + " " + item.name ) ) )
self.instance.rootchan.visit(add_item)
else:
current = self.instance.defchan
if current is not None:
choices.append( ( current, "Current value: %d" % current ) )
self.fields['defchan'].choices = choices
class Meta:
model = Mumble
fields = ['name']
class MumbleAdminForm( MumbleForm ): class MumbleAdminForm( MumbleForm ):
""" A Mumble Server admin form intended to be used by the server hoster. """
""" A Mumble Server admin form intended to be used by the server hoster. """
users = forms.IntegerField( required=False )
bwidth = forms.IntegerField( required=False )
sslcrt = forms.CharField( required=False, widget=forms.Textarea )
sslkey = forms.CharField( required=False, widget=forms.Textarea )
booted = forms.BooleanField( required=False )
autoboot = forms.BooleanField( required=False )
bonjour = forms.BooleanField( required=False )
users = forms.IntegerField( required=False )
bwidth = forms.IntegerField( required=False )
sslcrt = forms.CharField( required=False, widget=forms.Textarea )
sslkey = forms.CharField( required=False, widget=forms.Textarea )
booted = forms.BooleanField( required=False )
autoboot = forms.BooleanField( required=False )
bonjour = forms.BooleanField( required=False )
class Meta:
fields = None
exclude = None
class Meta:
fields = None
exclude = None
def clean_port( self ):
""" Check if the port number is valid. """
def clean_port( self ):
""" Check if the port number is valid. """
port = self.cleaned_data['port'];
port = self.cleaned_data['port']
if port is not None and port != '':
if port < 1 or port >= 2**16:
raise forms.ValidationError(
_("Port number %(portno)d is not within the allowed range %(minrange)d - %(maxrange)d") % {
'portno': port,
'minrange': 1,
'maxrange': 2**16,
});
return port;
return None
if port is not None and port != '':
if port < 1 or port >= 2**16:
raise forms.ValidationError(
_("Port number %(portno)d is not within the allowed range %(minrange)d - %(maxrange)d") % {
'portno': port,
'minrange': 1,
'maxrange': 2**16,
})
return port
return None
class MumbleServerForm( ModelForm ): class MumbleServerForm( ModelForm ):
defaultconf = forms.CharField( label=_("Default config"), required=False, widget=forms.Textarea )
defaultconf = forms.CharField( label=_("Default config"), required=False, widget=forms.Textarea )
def __init__( self, *args, **kwargs ):
ModelForm.__init__( self, *args, **kwargs )
def __init__( self, *args, **kwargs ):
ModelForm.__init__( self, *args, **kwargs )
if self.instance and self.instance.id:
if self.instance.online:
confstr = ""
conf = self.instance.defaultconf
for field in conf:
confstr += "%s: %s\n" % ( field, conf[field] )
self.fields["defaultconf"].initial = confstr
else:
self.fields["defaultconf"].initial = _("This server is currently offline.")
if self.instance and self.instance.id:
if self.instance.online:
confstr = ""
conf = self.instance.defaultconf
for field in conf:
confstr += "%s: %s\n" % ( field, conf[field] )
self.fields["defaultconf"].initial = confstr
else:
self.fields["defaultconf"].initial = _("This server is currently offline.")
class Meta:
model = MumbleServer
class Meta:
model = MumbleServer
class MumbleUserForm( ModelForm ): class MumbleUserForm( ModelForm ):
""" The user registration form used to register an account. """
""" The user registration form used to register an account. """
password = forms.CharField( widget=forms.PasswordInput, required=False )
password = forms.CharField( widget=forms.PasswordInput, required=False )
def __init__( self, *args, **kwargs ):
ModelForm.__init__( self, *args, **kwargs );
self.server = None;
def __init__( self, *args, **kwargs ):
ModelForm.__init__( self, *args, **kwargs )
self.server = None
def clean_name( self ):
""" Check if the desired name is forbidden or taken. """
def clean_name( self ):
""" Check if the desired name is forbidden or taken. """
name = self.cleaned_data['name'];
name = self.cleaned_data['name']
if self.server is None:
raise AttributeError( "You need to set the form's server attribute to the server instance "
"for validation to work." );
if self.server is None:
raise AttributeError( "You need to set the form's server attribute to the server instance "
"for validation to work." )
if self.server.player and re.compile( self.server.player ).match( name ) is None:
raise forms.ValidationError( _( "That name is forbidden by the server." ) );
if self.server.player and re.compile( self.server.player ).match( name ) is None:
raise forms.ValidationError( _( "That name is forbidden by the server." ) )
if not self.instance.id and len( self.server.ctl.getRegisteredPlayers( self.server.srvid, name ) ) > 0:
raise forms.ValidationError( _( "Another player already registered that name." ) );
if not self.instance.id and len( self.server.ctl.getRegisteredPlayers( self.server.srvid, name ) ) > 0:
raise forms.ValidationError( _( "Another player already registered that name." ) )
return name;
return name
def clean_password( self ):
""" Verify a password has been given. """
passwd = self.cleaned_data['password'];
if not passwd and ( not self.instance or self.instance.mumbleid == -1 ):
raise forms.ValidationError( _( "Cannot register player without a password!" ) );
return passwd;
def clean_password( self ):
""" Verify a password has been given. """
passwd = self.cleaned_data['password']
if not passwd and ( not self.instance or self.instance.mumbleid == -1 ):
raise forms.ValidationError( _( "Cannot register player without a password!" ) )
return passwd
class Meta:
model = MumbleUser;
fields = ( 'name', 'password' );
class Meta:
model = MumbleUser
fields = ( 'name', 'password' )
class MumbleUserPasswordForm( MumbleUserForm ): class MumbleUserPasswordForm( MumbleUserForm ):
""" The user registration form used to register an account on a private server in protected mode. """
""" The user registration form used to register an account on a private server in protected mode. """
serverpw = forms.CharField(
label=_('Server Password'),
help_text=_('This server is private and protected mode is active. Please enter the server password.'),
widget=forms.PasswordInput(render_value=False)
);
serverpw = forms.CharField(
label=_('Server Password'),
help_text=_('This server is private and protected mode is active. Please enter the server password.'),
widget=forms.PasswordInput(render_value=False)
)
def clean_serverpw( self ):
""" Validate the password """
serverpw = self.cleaned_data['serverpw'];
if self.server.passwd != serverpw:
raise forms.ValidationError( _( "The password you entered is incorrect." ) );
return serverpw;
def clean_serverpw( self ):
""" Validate the password """
serverpw = self.cleaned_data['serverpw']
if self.server.passwd != serverpw:
raise forms.ValidationError( _( "The password you entered is incorrect." ) )
return serverpw
def clean( self ):
""" prevent save() from trying to store the password in the Model instance. """
# clean() will be called after clean_serverpw(), so it has already been validated here.
if 'serverpw' in self.cleaned_data:
del( self.cleaned_data['serverpw'] );
return self.cleaned_data;
def clean( self ):
""" prevent save() from trying to store the password in the Model instance. """
# clean() will be called after clean_serverpw(), so it has already been validated here.
if 'serverpw' in self.cleaned_data:
del( self.cleaned_data['serverpw'] )
return self.cleaned_data
class MumbleUserLinkForm( MumbleUserForm ): class MumbleUserLinkForm( MumbleUserForm ):
""" Special registration form to either register or link an account. """
""" Special registration form to either register or link an account. """
linkacc = forms.BooleanField(
label=_('Link account'),
help_text=_('The account already exists and belongs to me, just link it instead of creating.'),
required=False,
);
linkacc = forms.BooleanField(
label=_('Link account'),
help_text=_('The account already exists and belongs to me, just link it instead of creating.'),
required=False,
)
def __init__( self, *args, **kwargs ):
MumbleUserForm.__init__( self, *args, **kwargs );
self.mumbleid = None;
def __init__( self, *args, **kwargs ):
MumbleUserForm.__init__( self, *args, **kwargs )
self.mumbleid = None
def clean_name( self ):
""" Check if the target account exists in Murmur. """
if 'linkacc' not in self.data:
return MumbleUserForm.clean_name( self );
def clean_name( self ):
""" Check if the target account exists in Murmur. """
if 'linkacc' not in self.data:
return MumbleUserForm.clean_name( self )
# Check if user exists
name = self.cleaned_data['name'];
# Check if user exists
name = self.cleaned_data['name']
if len( self.server.ctl.getRegisteredPlayers( self.server.srvid, name ) ) != 1:
raise forms.ValidationError( _( "No such user found." ) );
if len( self.server.ctl.getRegisteredPlayers( self.server.srvid, name ) ) != 1:
raise forms.ValidationError( _( "No such user found." ) )
return name;
return name
def clean_password( self ):
""" Verify that the password is correct. """
if 'linkacc' not in self.data:
return MumbleUserForm.clean_password( self );
def clean_password( self ):
""" Verify that the password is correct. """
if 'linkacc' not in self.data:
return MumbleUserForm.clean_password( self )
if 'name' not in self.cleaned_data:
# keep clean() from trying to find a user that CAN'T exist
self.mumbleid = -10;
return '';
if 'name' not in self.cleaned_data:
# keep clean() from trying to find a user that CAN'T exist
self.mumbleid = -10
return ''
# Validate password with Murmur
passwd = self.cleaned_data['password'];
# Validate password with Murmur
passwd = self.cleaned_data['password']
self.mumbleid = self.server.ctl.verifyPassword( self.server.srvid, self.cleaned_data['name'], passwd )
if self.mumbleid <= 0:
raise forms.ValidationError( _( "The password you entered is incorrect." ) );
self.mumbleid = self.server.ctl.verifyPassword( self.server.srvid, self.cleaned_data['name'], passwd )
if self.mumbleid <= 0:
raise forms.ValidationError( _( "The password you entered is incorrect." ) )
return passwd;
return passwd
def clean( self ):
""" Create the MumbleUser instance to save in. """
if 'linkacc' not in self.data or self.mumbleid <= 0:
return self.cleaned_data;
def clean( self ):
""" Create the MumbleUser instance to save in. """
if 'linkacc' not in self.data or self.mumbleid <= 0:
return self.cleaned_data
try:
m_user = MumbleUser.objects.get( server=self.server, mumbleid=self.mumbleid );
except MumbleUser.DoesNotExist:
m_user = MumbleUser( server=self.server, name=self.cleaned_data['name'], mumbleid=self.mumbleid );
m_user.save( dontConfigureMurmur=True );
else:
if m_user.owner is not None:
raise forms.ValidationError( _( "That account belongs to someone else." ) );
try:
m_user = MumbleUser.objects.get( server=self.server, mumbleid=self.mumbleid )
except MumbleUser.DoesNotExist:
m_user = MumbleUser( server=self.server, name=self.cleaned_data['name'], mumbleid=self.mumbleid )
m_user.save( dontConfigureMurmur=True )
else:
if m_user.owner is not None:
raise forms.ValidationError( _( "That account belongs to someone else." ) )
if m_user.getAdmin() and not settings.ALLOW_ACCOUNT_LINKING_ADMINS:
raise forms.ValidationError( _( "Linking Admin accounts is not allowed." ) );
self.instance = m_user;
if m_user.getAdmin() and not settings.ALLOW_ACCOUNT_LINKING_ADMINS:
raise forms.ValidationError( _( "Linking Admin accounts is not allowed." ) )
self.instance = m_user
return self.cleaned_data;
return self.cleaned_data
class MumbleUserAdminForm( PropertyModelForm ): class MumbleUserAdminForm( PropertyModelForm ):
aclAdmin = forms.BooleanField( required=False );
password = forms.CharField( widget=forms.PasswordInput, required=False )
aclAdmin = forms.BooleanField( required=False )
password = forms.CharField( widget=forms.PasswordInput, required=False )
def clean_password( self ):
""" Verify a password has been given. """
passwd = self.cleaned_data['password'];
if not passwd and ( not self.instance or self.instance.mumbleid == -1 ):
raise forms.ValidationError( _( "Cannot register player without a password!" ) );
return passwd;
def clean_password( self ):
""" Verify a password has been given. """
passwd = self.cleaned_data['password']
if not passwd and ( not self.instance or self.instance.mumbleid == -1 ):
raise forms.ValidationError( _( "Cannot register player without a password!" ) )
return passwd
class Meta:
model = Mumble;
class Meta:
model = Mumble
class MumbleKickForm( Form ): class MumbleKickForm( Form ):
session = forms.IntegerField();
ban = forms.BooleanField( required=False );
reason = forms.CharField( required=False );
session = forms.IntegerField()
ban = forms.BooleanField( required=False )
reason = forms.CharField( required=False )
class MumbleTextureForm( Form ): class MumbleTextureForm( Form ):
""" The form used to upload a new image to be set as texture. """
usegravatar = forms.BooleanField( required=False, label=_("Use my Gravatar as my Texture") );
texturefile = forms.ImageField( required=False, label=_("User Texture") );
""" The form used to upload a new image to be set as texture. """
usegravatar = forms.BooleanField( required=False, label=_("Use my Gravatar as my Texture") )
texturefile = forms.ImageField( required=False, label=_("User Texture") )

43
pyweb/mumble/management/__init__.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -14,24 +15,24 @@
* GNU General Public License for more details. * GNU General Public License for more details.
""" """
from shutil import copy, move
from os.path import exists, join
from shutil import copy, move
from os.path import exists, join
from django.conf import settings
from django.db import connection
from django.db.models import signals
from django.conf import settings
from django.db import connection
from django.db.models import signals
from mumble import models
from mumble import models
from update_schema import update_schema
from server_detect import find_existing_instances
from update_schema import update_schema
from server_detect import find_existing_instances
if settings.DATABASE_ENGINE == "sqlite3": if settings.DATABASE_ENGINE == "sqlite3":
# Move the DB to the db subdirectory if necessary.
oldpath = join( settings.MUMBLE_DJANGO_ROOT, "mumble-django.db3" )
if not exists( settings.DATABASE_NAME ) and exists( oldpath ):
move( oldpath, settings.DATABASE_NAME )
# Move the DB to the db subdirectory if necessary.
oldpath = join( settings.MUMBLE_DJANGO_ROOT, "mumble-django.db3" )
if not exists( settings.DATABASE_NAME ) and exists( oldpath ):
move( oldpath, settings.DATABASE_NAME )
cursor = connection.cursor() cursor = connection.cursor()
@ -39,18 +40,18 @@ cursor = connection.cursor()
tablename = models.Mumble._meta.db_table tablename = models.Mumble._meta.db_table
if tablename in connection.introspection.get_table_list(cursor): if tablename in connection.introspection.get_table_list(cursor):
fields = connection.introspection.get_table_description(cursor, tablename)
uptodate = "server_id" in [ entry[0] for entry in fields ]
fields = connection.introspection.get_table_description(cursor, tablename)
uptodate = "server_id" in [ entry[0] for entry in fields ]
else: else:
# Table doesn't yet exist, so syncdb will create it properly
uptodate = True
# Table doesn't yet exist, so syncdb will create it properly
uptodate = True
if not uptodate: if not uptodate:
if settings.DATABASE_ENGINE == "sqlite3":
# backup the db before the conversion.
copy( settings.DATABASE_NAME, settings.DATABASE_NAME+".bak" )
signals.post_syncdb.connect( update_schema, sender=models );
if settings.DATABASE_ENGINE == "sqlite3":
# backup the db before the conversion.
copy( settings.DATABASE_NAME, settings.DATABASE_NAME+".bak" )
signals.post_syncdb.connect( update_schema, sender=models )
else: else:
signals.post_syncdb.connect( find_existing_instances, sender=models );
signals.post_syncdb.connect( find_existing_instances, sender=models )

1
pyweb/mumble/management/commands/__init__.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>

321
pyweb/mumble/management/commands/checkenv.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -16,184 +17,184 @@
import os import os
from django.core.management.base import BaseCommand
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.conf import settings
from django.core.management.base import BaseCommand
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.conf import settings
from mumble.models import Mumble
from mumble.models import Mumble
class TestFailed( Exception ): class TestFailed( Exception ):
pass;
pass
class Command( BaseCommand ): class Command( BaseCommand ):
help = "Run a few tests on Mumble-Django's setup."
def handle(self, **options):
try:
import Ice
except ImportError:
pass
else:
self.check_slice();
self.check_rootdir();
self.check_dbase();
self.check_sites();
self.check_mumbles();
self.check_admins();
self.check_secret_key();
def check_slice( self ):
print "Checking slice file...",
if settings.SLICE is None:
raise TestFailed( "You don't have set the SLICE variable in settings.py." )
if " " in settings.SLICE:
raise TestFailed( "You have a space char in your Slice path. This will confuse Ice, please check." )
if not settings.SLICE.endswith( ".ice" ):
raise TestFailed( "The slice file name MUST end with '.ice'." )
try:
fd = open( settings.SLICE, "rb" )
slice = fd.read()
fd.close()
except IOError, err:
raise TestFailed( "Failed opening the slice file: %s" % err )
help = "Run a few tests on Mumble-Django's setup."
def handle(self, **options):
try:
import Ice
except ImportError:
pass
else:
self.check_slice()
self.check_rootdir()
self.check_dbase()
self.check_sites()
self.check_mumbles()
self.check_admins()
self.check_secret_key()
def check_slice( self ):
print "Checking slice file...",
if settings.SLICE is None:
raise TestFailed( "You don't have set the SLICE variable in settings.py." )
if " " in settings.SLICE:
raise TestFailed( "You have a space char in your Slice path. This will confuse Ice, please check." )
if not settings.SLICE.endswith( ".ice" ):
raise TestFailed( "The slice file name MUST end with '.ice'." )
try:
fd = open( settings.SLICE, "rb" )
slice = fd.read()
fd.close()
except IOError, err:
raise TestFailed( "Failed opening the slice file: %s" % err )
import Ice
Ice.loadSlice( settings.SLICE )
import Ice
Ice.loadSlice( settings.SLICE )
print "[ OK ]"
print "[ OK ]"
def check_rootdir( self ):
print "Checking root directory access...",
if not os.path.exists( settings.MUMBLE_DJANGO_ROOT ):
raise TestFailed( "The mumble-django root directory does not exist." );
def check_rootdir( self ):
print "Checking root directory access...",
if not os.path.exists( settings.MUMBLE_DJANGO_ROOT ):
raise TestFailed( "The mumble-django root directory does not exist." )
elif settings.DATABASE_ENGINE != "sqlite3":
print "not using sqlite [ OK ]"
else:
statinfo = os.stat( settings.MUMBLE_DJANGO_ROOT );
if statinfo.st_uid == 0:
raise TestFailed(
"The mumble-django root directory belongs to user root. This is "
"most certainly not what you want because it will prevent your "
"web server from being able to write to the database. Please check." );
elif not os.access( settings.MUMBLE_DJANGO_ROOT, os.W_OK ):
raise TestFailed( "The mumble-django root directory is not writable." );
else:
print "[ OK ]";
def check_dbase( self ):
print "Checking database access...",
if settings.DATABASE_ENGINE == "sqlite3":
if not os.path.exists( settings.DATABASE_NAME ):
raise TestFailed( "database does not exist. Have you run syncdb yet?" );
else:
statinfo = os.stat( settings.DATABASE_NAME );
if statinfo.st_uid == 0:
raise TestFailed(
"the database file belongs to root. This is most certainly not what "
"you want because it will prevent your web server from being able "
"to write to it. Please check." );
elif not os.access( settings.DATABASE_NAME, os.W_OK ):
raise TestFailed( "database file is not writable." );
else:
print "[ OK ]";
else:
print "not using sqlite, so I can't check.";
def check_sites( self ):
print "Checking URL configuration...",
try:
site = Site.objects.get_current();
except Site.DoesNotExist:
try:
sid = settings.SITE_ID
except AttributeError:
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured(
"You're using the Django \"sites framework\" without having set the SITE_ID "
"setting. Create a site in your database and rerun this command to fix this error.")
else:
print( "none set.\n"
"Please enter the domain where Mumble-Django is reachable." );
dom = raw_input( "> " ).strip();
site = Site( id=sid, name=dom, domain=dom );
site.save();
if site.domain == 'example.com':
print( "still the default.\n"
"The domain is configured as example.com, which is the default but does not make sense. "
"Please enter the domain where Mumble-Django is reachable." );
site.domain = raw_input( "> " ).strip();
site.save();
print site.domain, "[ OK ]";
elif settings.DATABASE_ENGINE != "sqlite3":
print "not using sqlite [ OK ]"
else:
statinfo = os.stat( settings.MUMBLE_DJANGO_ROOT )
if statinfo.st_uid == 0:
raise TestFailed(
"The mumble-django root directory belongs to user root. This is "
"most certainly not what you want because it will prevent your "
"web server from being able to write to the database. Please check." )
elif not os.access( settings.MUMBLE_DJANGO_ROOT, os.W_OK ):
raise TestFailed( "The mumble-django root directory is not writable." )
else:
print "[ OK ]"
def check_dbase( self ):
print "Checking database access...",
if settings.DATABASE_ENGINE == "sqlite3":
if not os.path.exists( settings.DATABASE_NAME ):
raise TestFailed( "database does not exist. Have you run syncdb yet?" )
else:
statinfo = os.stat( settings.DATABASE_NAME )
if statinfo.st_uid == 0:
raise TestFailed(
"the database file belongs to root. This is most certainly not what "
"you want because it will prevent your web server from being able "
"to write to it. Please check." )
elif not os.access( settings.DATABASE_NAME, os.W_OK ):
raise TestFailed( "database file is not writable." )
else:
print "[ OK ]"
else:
print "not using sqlite, so I can't check."
def check_sites( self ):
print "Checking URL configuration...",
try:
site = Site.objects.get_current()
except Site.DoesNotExist:
try:
sid = settings.SITE_ID
except AttributeError:
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured(
"You're using the Django \"sites framework\" without having set the SITE_ID "
"setting. Create a site in your database and rerun this command to fix this error.")
else:
print( "none set.\n"
"Please enter the domain where Mumble-Django is reachable." )
dom = raw_input( "> " ).strip()
site = Site( id=sid, name=dom, domain=dom )
site.save()
if site.domain == 'example.com':
print( "still the default.\n"
"The domain is configured as example.com, which is the default but does not make sense. "
"Please enter the domain where Mumble-Django is reachable." )
site.domain = raw_input( "> " ).strip()
site.save()
print site.domain, "[ OK ]"
def check_admins( self ):
print "Checking if an Admin user exists...",
for user in User.objects.all():
if user.is_superuser:
print "[ OK ]";
return;
def check_admins( self ):
print "Checking if an Admin user exists...",
for user in User.objects.all():
if user.is_superuser:
print "[ OK ]"
return
raise TestFailed( ""
"No admin user exists, so you won't be able to log in to the admin system. You "
"should run `./manage.py createsuperuser` to create one." );
raise TestFailed( ""
"No admin user exists, so you won't be able to log in to the admin system. You "
"should run `./manage.py createsuperuser` to create one." )
def check_mumbles( self ):
print "Checking Murmur instances...",
def check_mumbles( self ):
print "Checking Murmur instances...",
mm = Mumble.objects.all();
mm = Mumble.objects.all()
if mm.count() == 0:
raise TestFailed(
"no Mumble servers are configured, you might want to run "
"`./manage.py syncdb` to run an auto detection." );
else:
for mumble in mm:
try:
mumble.ctl
except Exception, err:
raise TestFailed(
"Connecting to Murmur `%s` (%s) failed: %s" % ( mumble.name, mumble.server, err )
);
print "[ OK ]";
def check_secret_key( self ):
print "Checking SECRET_KEY...",
if mm.count() == 0:
raise TestFailed(
"no Mumble servers are configured, you might want to run "
"`./manage.py syncdb` to run an auto detection." )
else:
for mumble in mm:
try:
mumble.ctl
except Exception, err:
raise TestFailed(
"Connecting to Murmur `%s` (%s) failed: %s" % ( mumble.name, mumble.server, err )
)
print "[ OK ]"
def check_secret_key( self ):
print "Checking SECRET_KEY...",
blacklist = ( 'u-mp185msk#z4%s(do2^5405)y5d!9adbn92)apu_p^qvqh10v', );
if settings.SECRET_KEY in blacklist:
raise TestFailed(
"Your SECRET_KEY setting matches one of the keys that were put in the settings.py "
"file shipped with Mumble-Django, which means your SECRET_KEY is all but secret. "
"You should change the setting, or run gen_secret_key.sh to do it for you."
);
else:
print "[ OK ]";
blacklist = ( 'u-mp185msk#z4%s(do2^5405)y5d!9adbn92)apu_p^qvqh10v', )
if settings.SECRET_KEY in blacklist:
raise TestFailed(
"Your SECRET_KEY setting matches one of the keys that were put in the settings.py "
"file shipped with Mumble-Django, which means your SECRET_KEY is all but secret. "
"You should change the setting, or run gen_secret_key.sh to do it for you."
)
else:
print "[ OK ]"

71
pyweb/mumble/management/commands/getslice.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -17,41 +18,41 @@
import Ice, IcePy, os, getpass import Ice, IcePy, os, getpass
from sys import stderr from sys import stderr
from django.core.management.base import BaseCommand
from django.core.management.base import BaseCommand
from mumble.models import MumbleServer
from mumble.models import MumbleServer
class Command( BaseCommand ): class Command( BaseCommand ):
help = "Check if the known servers support getSlice."
def handle(self, **options):
prop = Ice.createProperties([])
prop.setProperty("Ice.ImplicitContext", "Shared")
idd = Ice.InitializationData()
idd.properties = prop
ice = Ice.initialize(idd)
for serv in MumbleServer.objects.all():
print >>stderr, "Probing server at '%s'..." % serv.dbus
if serv.secret:
ice.getImplicitContext().put( "secret", serv.secret.encode("utf-8") )
prx = ice.stringToProxy( serv.dbus.encode("utf-8") )
# Try loading the Slice from Murmur directly via its getSlice method.
try:
slice = IcePy.Operation( 'getSlice',
Ice.OperationMode.Idempotent, Ice.OperationMode.Idempotent,
True, (), (), (), IcePy._t_string, ()
).invoke(prx, ((), None))
except TypeError, err:
print >>stderr, " Received TypeError:", err
print >>stderr, " It seems your version of IcePy is incompatible."
except Ice.OperationNotExistException:
print >>stderr, " Your version of Murmur does not support getSlice."
else:
print slice
print >>stderr, " Successfully received the slice (length: %d bytes.)" % len(slice)
help = "Check if the known servers support getSlice."
def handle(self, **options):
prop = Ice.createProperties([])
prop.setProperty("Ice.ImplicitContext", "Shared")
idd = Ice.InitializationData()
idd.properties = prop
ice = Ice.initialize(idd)
for serv in MumbleServer.objects.all():
print >>stderr, "Probing server at '%s'..." % serv.dbus
if serv.secret:
ice.getImplicitContext().put( "secret", serv.secret.encode("utf-8") )
prx = ice.stringToProxy( serv.dbus.encode("utf-8") )
# Try loading the Slice from Murmur directly via its getSlice method.
try:
slice = IcePy.Operation( 'getSlice',
Ice.OperationMode.Idempotent, Ice.OperationMode.Idempotent,
True, (), (), (), IcePy._t_string, ()
).invoke(prx, ((), None))
except TypeError, err:
print >>stderr, " Received TypeError:", err
print >>stderr, " It seems your version of IcePy is incompatible."
except Ice.OperationNotExistException:
print >>stderr, " Your version of Murmur does not support getSlice."
else:
print slice
print >>stderr, " Successfully received the slice (length: %d bytes.)" % len(slice)

7
pyweb/mumble/management/commands/mmrunserver.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -14,8 +15,8 @@
* GNU General Public License for more details. * GNU General Public License for more details.
""" """
from django.core.management.commands.runserver import Command as OrigCommand
from mumble.murmurenvutils import MumbleCommandWrapper
from django.core.management.commands.runserver import Command as OrigCommand
from mumble.murmurenvutils import MumbleCommandWrapper
class Command( MumbleCommandWrapper, OrigCommand ): class Command( MumbleCommandWrapper, OrigCommand ):
pass
pass

7
pyweb/mumble/management/commands/mmshell.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -14,8 +15,8 @@
* GNU General Public License for more details. * GNU General Public License for more details.
""" """
from django.core.management.commands.shell import Command as OrigCommand
from mumble.murmurenvutils import MumbleCommandWrapper_noargs
from django.core.management.commands.shell import Command as OrigCommand
from mumble.murmurenvutils import MumbleCommandWrapper_noargs
class Command( MumbleCommandWrapper_noargs, OrigCommand ): class Command( MumbleCommandWrapper_noargs, OrigCommand ):
pass
pass

7
pyweb/mumble/management/commands/mmsyncdb.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -14,8 +15,8 @@
* GNU General Public License for more details. * GNU General Public License for more details.
""" """
from django.core.management.commands.syncdb import Command as OrigCommand
from mumble.murmurenvutils import MumbleCommandWrapper_noargs
from django.core.management.commands.syncdb import Command as OrigCommand
from mumble.murmurenvutils import MumbleCommandWrapper_noargs
class Command( MumbleCommandWrapper_noargs, OrigCommand ): class Command( MumbleCommandWrapper_noargs, OrigCommand ):
pass
pass

279
pyweb/mumble/management/server_detect.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -16,154 +17,154 @@
import os, getpass import os, getpass
from django.db import DatabaseError
from django.conf import settings
from django.db import DatabaseError
from django.conf import settings
from mumble.models import MumbleServer, Mumble
from mumble.mctl import MumbleCtlBase
from mumble.models import MumbleServer, Mumble
from mumble.mctl import MumbleCtlBase
def find_in_dicts( keys, conf, default, valueIfNotFound=None ): def find_in_dicts( keys, conf, default, valueIfNotFound=None ):
if not isinstance( keys, tuple ):
keys = ( keys, );
if not isinstance( keys, tuple ):
keys = ( keys, )
for keyword in keys:
if keyword in conf:
return conf[keyword];
for keyword in keys:
if keyword in conf:
return conf[keyword]
for keyword in keys:
keyword = keyword.lower();
if keyword in default:
return default[keyword];
for keyword in keys:
keyword = keyword.lower()
if keyword in default:
return default[keyword]
return valueIfNotFound;
return valueIfNotFound
def find_existing_instances( **kwargs ): def find_existing_instances( **kwargs ):
if "verbosity" in kwargs:
v = kwargs['verbosity'];
else:
v = 1;
if v > 1:
print "Starting Mumble servers and players detection now.";
triedEnviron = False;
online = False;
while not online:
if not triedEnviron and 'MURMUR_CONNSTR' in os.environ:
dbusName = os.environ['MURMUR_CONNSTR'];
triedEnviron = True;
if v > 1:
print "Trying environment setting", dbusName;
else:
print "--- Murmur connection info ---"
print " 1) DBus -- net.sourceforge.mumble.murmur"
print " 2) ICE -- Meta:tcp -h 127.0.0.1 -p 6502"
print "Enter 1 or 2 for the defaults above, nothing to skip Server detection,"
print "and if the defaults do not fit your needs, enter the correct string."
print "Whether to use DBus or Ice will be detected automatically from the"
print "string's format."
print
dbusName = raw_input( "Service string: " ).strip();
if not dbusName:
if v:
print 'Be sure to run "python manage.py syncdb" with Murmur running before'
print "trying to use this app! Otherwise, existing Murmur servers won't be"
print 'configurable!';
return False;
elif dbusName == "1":
dbusName = "net.sourceforge.mumble.murmur";
elif dbusName == "2":
dbusName = "Meta:tcp -h 127.0.0.1 -p 6502";
icesecret = getpass.getpass("Please enter the Ice secret (if any): ");
try:
ctl = MumbleCtlBase.newInstance( dbusName, settings.SLICE, icesecret );
except Exception, instance:
if v:
print "Unable to connect using name %s. The error was:" % dbusName;
print instance;
print
else:
online = True;
if v > 1:
print "Successfully connected to Murmur via connection string %s, using %s." % ( dbusName, ctl.method );
servIDs = ctl.getAllServers();
try:
meta = MumbleServer.objects.get( dbus=dbusName );
except MumbleServer.DoesNotExist:
meta = MumbleServer( dbus=dbusName );
finally:
meta.secret = icesecret;
meta.save();
for id in servIDs:
if v > 1:
print "Checking Murmur instance with id %d." % id;
# first check that the server has not yet been inserted into the DB
try:
instance = Mumble.objects.get( server=meta, srvid=id );
except Mumble.DoesNotExist:
values = {
"server": meta,
"srvid": id,
}
if v:
print "Found new Murmur instance %d on bus '%s'... " % ( id, dbusName )
# now create a model for the record set.
instance = Mumble( **values );
else:
if v:
print "Syncing Murmur instance %d: '%s'... " % ( instance.id, instance.name )
try:
instance.configureFromMurmur();
except DatabaseError, err:
try:
# Find instances with the same address/port
dup = Mumble.objects.get( addr=instance.addr, port=instance.port )
except Mumble.DoesNotExist:
# None exist - this must've been something else.
print "Server ID / Name: %d / %s" % ( instance.srvid, instance.name )
raise err
else:
print "ERROR: There is already another server instance registered"
print " on the same address and port."
print " -------------"
print " New Server ID:", instance.srvid,
print " New Server Name:", instance.name
print " Address:", instance.addr
print " Port:", instance.port
print " Connection string:", instance.server.dbus
print " -------------"
print " Duplicate Server ID:", dup.srvid,
print "Duplicate Server Name:", dup.name
print " Address:", dup.addr
print " Port:", dup.port
print " Connection string:", dup.server.dbus
return False
except Exception, err:
print "Server ID / Name: %d / %s" % ( instance.srvid, instance.name )
raise err
# Now search for players on this server that have not yet been registered
if instance.booted:
if v > 1:
print "Looking for registered Players on Server id %d." % id;
instance.readUsersFromMurmur( verbose=v );
elif v:
print "This server is not running, can't sync players.";
if v > 1:
print "Successfully finished Servers and Players detection.";
return True;
if "verbosity" in kwargs:
v = kwargs['verbosity']
else:
v = 1
if v > 1:
print "Starting Mumble servers and players detection now."
triedEnviron = False
online = False
while not online:
if not triedEnviron and 'MURMUR_CONNSTR' in os.environ:
dbusName = os.environ['MURMUR_CONNSTR']
triedEnviron = True
if v > 1:
print "Trying environment setting", dbusName
else:
print "--- Murmur connection info ---"
print " 1) DBus -- net.sourceforge.mumble.murmur"
print " 2) ICE -- Meta:tcp -h 127.0.0.1 -p 6502"
print "Enter 1 or 2 for the defaults above, nothing to skip Server detection,"
print "and if the defaults do not fit your needs, enter the correct string."
print "Whether to use DBus or Ice will be detected automatically from the"
print "string's format."
print
dbusName = raw_input( "Service string: " ).strip()
if not dbusName:
if v:
print 'Be sure to run "python manage.py syncdb" with Murmur running before'
print "trying to use this app! Otherwise, existing Murmur servers won't be"
print 'configurable!'
return False
elif dbusName == "1":
dbusName = "net.sourceforge.mumble.murmur"
elif dbusName == "2":
dbusName = "Meta:tcp -h 127.0.0.1 -p 6502"
icesecret = getpass.getpass("Please enter the Ice secret (if any): ")
try:
ctl = MumbleCtlBase.newInstance( dbusName, settings.SLICE, icesecret )
except Exception, instance:
if v:
print "Unable to connect using name %s. The error was:" % dbusName
print instance
print
else:
online = True
if v > 1:
print "Successfully connected to Murmur via connection string %s, using %s." % ( dbusName, ctl.method )
servIDs = ctl.getAllServers()
try:
meta = MumbleServer.objects.get( dbus=dbusName )
except MumbleServer.DoesNotExist:
meta = MumbleServer( dbus=dbusName )
finally:
meta.secret = icesecret
meta.save()
for id in servIDs:
if v > 1:
print "Checking Murmur instance with id %d." % id
# first check that the server has not yet been inserted into the DB
try:
instance = Mumble.objects.get( server=meta, srvid=id )
except Mumble.DoesNotExist:
values = {
"server": meta,
"srvid": id,
}
if v:
print "Found new Murmur instance %d on bus '%s'... " % ( id, dbusName )
# now create a model for the record set.
instance = Mumble( **values )
else:
if v:
print "Syncing Murmur instance %d: '%s'... " % ( instance.id, instance.name )
try:
instance.configureFromMurmur()
except DatabaseError, err:
try:
# Find instances with the same address/port
dup = Mumble.objects.get( addr=instance.addr, port=instance.port )
except Mumble.DoesNotExist:
# None exist - this must've been something else.
print "Server ID / Name: %d / %s" % ( instance.srvid, instance.name )
raise err
else:
print "ERROR: There is already another server instance registered"
print " on the same address and port."
print " -------------"
print " New Server ID:", instance.srvid,
print " New Server Name:", instance.name
print " Address:", instance.addr
print " Port:", instance.port
print " Connection string:", instance.server.dbus
print " -------------"
print " Duplicate Server ID:", dup.srvid,
print "Duplicate Server Name:", dup.name
print " Address:", dup.addr
print " Port:", dup.port
print " Connection string:", dup.server.dbus
return False
except Exception, err:
print "Server ID / Name: %d / %s" % ( instance.srvid, instance.name )
raise err
# Now search for players on this server that have not yet been registered
if instance.booted:
if v > 1:
print "Looking for registered Players on Server id %d." % id
instance.readUsersFromMurmur( verbose=v )
elif v:
print "This server is not running, can't sync players."
if v > 1:
print "Successfully finished Servers and Players detection."
return True

107
pyweb/mumble/management/update_schema.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -15,62 +16,62 @@
""" """
import os import os
from os.path import join
from os.path import join
from django.db import connection, transaction
from django.db.models import signals
from django.conf import settings
from django.db import connection, transaction
from django.db.models import signals
from django.conf import settings
from mumble import models
from mumble import models
from mumble.management.server_detect import find_existing_instances from mumble.management.server_detect import find_existing_instances
def update_schema( **kwargs ): def update_schema( **kwargs ):
if "verbosity" in kwargs:
v = kwargs['verbosity'];
else:
v = 1;
if v:
print "Migrating Database schema for Mumble-Django 2.0 now."
scriptdir = join( settings.CONVERSIONSQL_ROOT, {
'postgresql_psycopg2': 'pgsql',
'postgresql': 'pgsql',
'mysql': 'mysql',
'sqlite3': 'sqlite',
}[settings.DATABASE_ENGINE] )
if v > 1:
print "Reading migration scripts for %s from '%s'" % ( settings.DATABASE_ENGINE, scriptdir )
scripts = [ filename for filename in os.listdir( scriptdir ) if filename.endswith( ".sql" ) ]
scripts.sort()
for filename in scripts:
cursor = connection.cursor()
scriptpath = os.path.join( scriptdir, filename )
scriptfile = open( scriptpath, "r" )
try:
if v > 1:
print "Running migration script '%s'..." % scriptpath
stmt = scriptfile.read()
cursor.execute( stmt )
except IOError, err:
print "Error reading file '%s':" % filename
print err
except cursor.db.connection.Error, err:
print "Error executing file '%s':" % filename
print err
finally:
scriptfile.close()
cursor.close()
if v:
print "Database migration finished successfully."
find_existing_instances( **kwargs )
if "verbosity" in kwargs:
v = kwargs['verbosity']
else:
v = 1
if v:
print "Migrating Database schema for Mumble-Django 2.0 now."
scriptdir = join( settings.CONVERSIONSQL_ROOT, {
'postgresql_psycopg2': 'pgsql',
'postgresql': 'pgsql',
'mysql': 'mysql',
'sqlite3': 'sqlite',
}[settings.DATABASE_ENGINE] )
if v > 1:
print "Reading migration scripts for %s from '%s'" % ( settings.DATABASE_ENGINE, scriptdir )
scripts = [ filename for filename in os.listdir( scriptdir ) if filename.endswith( ".sql" ) ]
scripts.sort()
for filename in scripts:
cursor = connection.cursor()
scriptpath = os.path.join( scriptdir, filename )
scriptfile = open( scriptpath, "r" )
try:
if v > 1:
print "Running migration script '%s'..." % scriptpath
stmt = scriptfile.read()
cursor.execute( stmt )
except IOError, err:
print "Error reading file '%s':" % filename
print err
except cursor.db.connection.Error, err:
print "Error executing file '%s':" % filename
print err
finally:
scriptfile.close()
cursor.close()
if v:
print "Database migration finished successfully."
find_existing_instances( **kwargs )

59
pyweb/mumble/mctl.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009, withgod <withgod@sourceforge.net> * Copyright © 2009, withgod <withgod@sourceforge.net>
@ -18,41 +19,41 @@
import re import re
class MumbleCtlBase(object): class MumbleCtlBase(object):
""" This class defines the base interface that the Mumble model expects. """
""" This class defines the base interface that the Mumble model expects. """
cache = {};
cache = {}
@staticmethod
def newInstance( connstring, slicefile=None, icesecret=None ):
""" Create a new CTL object for the given connstring.
@staticmethod
def newInstance( connstring, slicefile=None, icesecret=None ):
""" Create a new CTL object for the given connstring.
Optional parameters are the path to the slice file and the
Ice secret necessary to authenticate to Murmur.
Optional parameters are the path to the slice file and the
Ice secret necessary to authenticate to Murmur.
The path can be omitted only if using DBus or running Murmur
1.2.3 or later, which exports a getSlice method to retrieve
the Slice from.
"""
The path can be omitted only if using DBus or running Murmur
1.2.3 or later, which exports a getSlice method to retrieve
the Slice from.
"""
# check cache
if connstring in MumbleCtlBase.cache:
return MumbleCtlBase.cache[connstring];
# check cache
if connstring in MumbleCtlBase.cache:
return MumbleCtlBase.cache[connstring]
# connstring defines whether to connect via ICE or DBus.
# Dbus service names: some.words.divided.by.periods
# ICE specs are WAY more complex, so if DBus doesn't match, use ICE.
rd = re.compile( r'^(\w+\.)*\w+$' );
# connstring defines whether to connect via ICE or DBus.
# Dbus service names: some.words.divided.by.periods
# ICE specs are WAY more complex, so if DBus doesn't match, use ICE.
rd = re.compile( r'^(\w+\.)*\w+$' )
if rd.match( connstring ):
from MumbleCtlDbus import MumbleCtlDbus
ctl = MumbleCtlDbus( connstring )
else:
from MumbleCtlIce import MumbleCtlIce
ctl = MumbleCtlIce( connstring, slicefile, icesecret )
if rd.match( connstring ):
from MumbleCtlDbus import MumbleCtlDbus
ctl = MumbleCtlDbus( connstring )
else:
from MumbleCtlIce import MumbleCtlIce
ctl = MumbleCtlIce( connstring, slicefile, icesecret )
MumbleCtlBase.cache[connstring] = ctl;
return ctl;
MumbleCtlBase.cache[connstring] = ctl
return ctl
@staticmethod
def clearCache():
MumbleCtlBase.cache = {};
@staticmethod
def clearCache():
MumbleCtlBase.cache = {}

591
pyweb/mumble/mmobjects.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -17,334 +18,334 @@
import socket import socket
import datetime import datetime
import re import re
from time import time
from time import time
from django.utils.http import urlquote
from django.conf import settings
from django.utils.http import urlquote
from django.conf import settings
def cmp_channels( left, rite ): def cmp_channels( left, rite ):
""" Compare two channels, first by position, and if that equals, by name. """
if hasattr( left, "position" ) and hasattr( rite, "position" ):
byorder = cmp( left.position, rite.position );
if byorder != 0:
return byorder;
return cmp_names( left, rite );
""" Compare two channels, first by position, and if that equals, by name. """
if hasattr( left, "position" ) and hasattr( rite, "position" ):
byorder = cmp( left.position, rite.position )
if byorder != 0:
return byorder
return cmp_names( left, rite )
def cmp_names( left, rite ): def cmp_names( left, rite ):
""" Compare two objects by their name property. """
return cmp( left.name, rite.name );
""" Compare two objects by their name property. """
return cmp( left.name, rite.name )
class mmChannel( object ): class mmChannel( object ):
""" Represents a channel in Murmur. """
""" Represents a channel in Murmur. """
def __init__( self, server, channel_obj, parent_chan = None ):
self.server = server;
self.players = list();
self.subchans = list();
self.linked = list();
self.channel_obj = channel_obj;
self.chanid = channel_obj.id;
def __init__( self, server, channel_obj, parent_chan = None ):
self.server = server
self.players = list()
self.subchans = list()
self.linked = list()
self.channel_obj = channel_obj
self.chanid = channel_obj.id
self.parent = parent_chan;
if self.parent is not None:
self.parent.subchans.append( self );
self.parent = parent_chan
if self.parent is not None:
self.parent.subchans.append( self )
self._acl = None;
# Lookup unknown attributes in self.channel_obj to automatically include Murmur's fields
def __getattr__( self, key ):
if hasattr( self.channel_obj, key ):
return getattr( self.channel_obj, key );
else:
raise AttributeError( "'%s' object has no attribute '%s'" % ( self.__class__.__name__, key ) );
def parent_channels( self ):
""" Return the names of this channel's parents in the channel tree. """
if self.parent is None or self.parent.is_server or self.parent.chanid == 0:
return [];
return self.parent.parent_channels() + [self.parent.name];
def getACL( self ):
""" Retrieve the ACL for this channel. """
if not self._acl:
self._acl = mmACL( self, self.server.ctl.getACL( self.server.srvid, self.chanid ) );
return self._acl;
acl = property( getACL );
is_server = False;
is_channel = True;
is_player = False;
playerCount = property(
lambda self: len( self.players ) + sum( [ chan.playerCount for chan in self.subchans ] ),
doc="The number of players in this channel."
);
id = property(
lambda self: "channel_%d"%self.chanid,
doc="A string ready to be used in an id property of an HTML tag."
);
top_or_not_empty = property(
lambda self: self.parent is None or self.parent.chanid == 0 or self.playerCount > 0,
doc="True if this channel needs to be shown because it is root, a child of root, or has players."
);
show = property( lambda self: settings.SHOW_EMPTY_SUBCHANS or self.top_or_not_empty );
def __str__( self ):
return '<Channel "%s" (%d)>' % ( self.name, self.chanid );
def sort( self ):
""" Sort my subchannels and players, and then iterate over them and sort them recursively. """
self.subchans.sort( cmp_channels );
self.players.sort( cmp_names );
for subc in self.subchans:
subc.sort();
def visit( self, callback, lvl = 0 ):
""" Call callback on myself, then visit my subchans, then my players. """
callback( self, lvl );
for subc in self.subchans:
subc.visit( callback, lvl + 1 );
for plr in self.players:
plr.visit( callback, lvl + 1 );
def getURL( self, for_user = None ):
""" Create an URL to connect to this channel. The URL is of the form
mumble://username@host:port/parentchans/self.name
"""
from urlparse import urlunsplit
versionstr = "version=%s" % self.server.prettyversion;
if self.parent is not None:
chanlist = self.parent_channels() + [self.name];
chanlist = [ urlquote( chan ) for chan in chanlist ];
urlpath = "/".join( chanlist );
else:
urlpath = "";
if for_user is not None:
netloc = "%s@%s" % ( for_user.name, self.server.netloc );
return urlunsplit(( "mumble", netloc, urlpath, versionstr, "" ))
else:
return urlunsplit(( "mumble", self.server.netloc, urlpath, versionstr, "" ))
connecturl = property( getURL );
def setDefault( self ):
""" Make this the server's default channel. """
self.server.defchan = self.chanid;
self.server.save();
is_default = property(
lambda self: self.server.defchan == self.chanid,
doc="True if this channel is the server's default channel."
);
def asDict( self ):
chandata = self.channel_obj.__dict__.copy();
chandata['players'] = [ pl.asDict() for pl in self.players ];
chandata['subchans'] = [ sc.asDict() for sc in self.subchans ];
return chandata;
def asMvXml( self, parentnode ):
""" Return an XML tree for this channel suitable for MumbleViewer-ng. """
from xml.etree.cElementTree import SubElement
me = SubElement( parentnode, "item" , id=self.id, rel='channel' )
content = SubElement( me, "content" )
name = SubElement( content , "name" )
name.text = self.name
for sc in self.subchans:
sc.asMvXml(me)
for pl in self.players:
pl.asMvXml(me)
def asMvJson( self ):
""" Return a Dict for this channel suitable for MumbleViewer-ng. """
return {
"attributes": {
"href": self.connecturl,
"id": self.id,
"rel": "channel",
},
"data": self.name,
"children": [ sc.asMvJson() for sc in self.subchans ] + \
[ pl.asMvJson() for pl in self.players ],
"state": { False: "closed", True: "open" }[self.top_or_not_empty],
}
self._acl = None
# Lookup unknown attributes in self.channel_obj to automatically include Murmur's fields
def __getattr__( self, key ):
if hasattr( self.channel_obj, key ):
return getattr( self.channel_obj, key )
else:
raise AttributeError( "'%s' object has no attribute '%s'" % ( self.__class__.__name__, key ) )
def parent_channels( self ):
""" Return the names of this channel's parents in the channel tree. """
if self.parent is None or self.parent.is_server or self.parent.chanid == 0:
return []
return self.parent.parent_channels() + [self.parent.name]
def getACL( self ):
""" Retrieve the ACL for this channel. """
if not self._acl:
self._acl = mmACL( self, self.server.ctl.getACL( self.server.srvid, self.chanid ) )
return self._acl
acl = property( getACL )
is_server = False
is_channel = True
is_player = False
playerCount = property(
lambda self: len( self.players ) + sum( [ chan.playerCount for chan in self.subchans ] ),
doc="The number of players in this channel."
)
id = property(
lambda self: "channel_%d"%self.chanid,
doc="A string ready to be used in an id property of an HTML tag."
)
top_or_not_empty = property(
lambda self: self.parent is None or self.parent.chanid == 0 or self.playerCount > 0,
doc="True if this channel needs to be shown because it is root, a child of root, or has players."
)
show = property( lambda self: settings.SHOW_EMPTY_SUBCHANS or self.top_or_not_empty )
def __str__( self ):
return '<Channel "%s" (%d)>' % ( self.name, self.chanid )
def sort( self ):
""" Sort my subchannels and players, and then iterate over them and sort them recursively. """
self.subchans.sort( cmp_channels )
self.players.sort( cmp_names )
for subc in self.subchans:
subc.sort()
def visit( self, callback, lvl = 0 ):
""" Call callback on myself, then visit my subchans, then my players. """
callback( self, lvl )
for subc in self.subchans:
subc.visit( callback, lvl + 1 )
for plr in self.players:
plr.visit( callback, lvl + 1 )
def getURL( self, for_user = None ):
""" Create an URL to connect to this channel. The URL is of the form
mumble://username@host:port/parentchans/self.name
"""
from urlparse import urlunsplit
versionstr = "version=%s" % self.server.prettyversion
if self.parent is not None:
chanlist = self.parent_channels() + [self.name]
chanlist = [ urlquote( chan ) for chan in chanlist ]
urlpath = "/".join( chanlist )
else:
urlpath = ""
if for_user is not None:
netloc = "%s@%s" % ( for_user.name, self.server.netloc )
return urlunsplit(( "mumble", netloc, urlpath, versionstr, "" ))
else:
return urlunsplit(( "mumble", self.server.netloc, urlpath, versionstr, "" ))
connecturl = property( getURL )
def setDefault( self ):
""" Make this the server's default channel. """
self.server.defchan = self.chanid
self.server.save()
is_default = property(
lambda self: self.server.defchan == self.chanid,
doc="True if this channel is the server's default channel."
)
def asDict( self ):
chandata = self.channel_obj.__dict__.copy()
chandata['players'] = [ pl.asDict() for pl in self.players ]
chandata['subchans'] = [ sc.asDict() for sc in self.subchans ]
return chandata
def asMvXml( self, parentnode ):
""" Return an XML tree for this channel suitable for MumbleViewer-ng. """
from xml.etree.cElementTree import SubElement
me = SubElement( parentnode, "item" , id=self.id, rel='channel' )
content = SubElement( me, "content" )
name = SubElement( content , "name" )
name.text = self.name
for sc in self.subchans:
sc.asMvXml(me)
for pl in self.players:
pl.asMvXml(me)
def asMvJson( self ):
""" Return a Dict for this channel suitable for MumbleViewer-ng. """
return {
"attributes": {
"href": self.connecturl,
"id": self.id,
"rel": "channel",
},
"data": self.name,
"children": [ sc.asMvJson() for sc in self.subchans ] + \
[ pl.asMvJson() for pl in self.players ],
"state": { False: "closed", True: "open" }[self.top_or_not_empty],
}
class mmPlayer( object ): class mmPlayer( object ):
""" Represents a Player in Murmur. """
def __init__( self, server, player_obj, player_chan ):
self.player_obj = player_obj;
self.onlinesince = datetime.datetime.fromtimestamp( float( time() - player_obj.onlinesecs ) );
self.channel = player_chan;
self.channel.players.append( self );
if self.isAuthed:
from mumble.models import MumbleUser
try:
self.mumbleuser = MumbleUser.objects.get( mumbleid=self.userid, server=server );
except MumbleUser.DoesNotExist:
self.mumbleuser = None;
else:
self.mumbleuser = None;
# Lookup unknown attributes in self.player_obj to automatically include Murmur's fields
def __getattr__( self, key ):
if hasattr( self.player_obj, key ):
return getattr( self.player_obj, key );
else:
raise AttributeError( "'%s' object has no attribute '%s'" % ( self.__class__.__name__, key ) );
def __str__( self ):
return '<Player "%s" (%d, %d)>' % ( self.name, self.session, self.userid );
hasComment = property(
lambda self: hasattr( self.player_obj, "comment" ) and bool(self.player_obj.comment),
doc="True if this player has a comment set."
);
isAuthed = property(
lambda self: self.userid != -1,
doc="True if this player is authenticated (+A)."
);
isAdmin = property(
lambda self: self.mumbleuser and self.mumbleuser.getAdmin(),
doc="True if this player is in the Admin group in the ACL."
);
is_server = False;
is_channel = False;
is_player = True;
def getIpAsString( self ):
""" Get the client's IPv4 or IPv6 address, in a pretty format. """
addr = self.player_obj.address;
if max( addr[:10] ) == 0 and addr[10:12] == (255, 255):
return "%d.%d.%d.%d" % tuple( addr[12:] );
ip6addr = [(hi << 8 | lo) for (hi, lo) in zip(addr[0::2], addr[1::2])]
# colon-separated string:
ipstr = ':'.join([ ("%x" % part) for part in ip6addr ]);
# 0:0:0 -> ::
return re.sub( "((^|:)(0:){2,})", '::', ipstr, 1 );
ipaddress = property( getIpAsString );
fqdn = property( lambda self: socket.getfqdn( self.ipaddress ),
doc="The fully qualified domain name of the user's host." );
# kept for compatibility to mmChannel (useful for traversal funcs)
playerCount = property( lambda self: -1, doc="Exists only for compatibility to mmChannel." );
id = property(
lambda self: "player_%d"%self.session,
doc="A string ready to be used in an id property of an HTML tag."
);
def visit( self, callback, lvl = 0 ):
""" Call callback on myself. """
callback( self, lvl );
def asDict( self ):
pldata = self.player_obj.__dict__.copy();
if self.mumbleuser:
if self.mumbleuser.hasTexture():
pldata['texture'] = self.mumbleuser.textureUrl;
return pldata;
def asMvXml( self, parentnode ):
""" Return an XML node for this player suitable for MumbleViewer-ng. """
from xml.etree.cElementTree import SubElement
me = SubElement( parentnode, "item" , id=self.id, rel='user' )
content = SubElement( me, "content" )
name = SubElement( content , "name" )
name.text = self.name
def asMvJson( self ):
""" Return a Dict for this player suitable for MumbleViewer-ng. """
return {
"attributes": {
"id": self.id,
"rel": "user",
},
'data': self.name,
}
""" Represents a Player in Murmur. """
def __init__( self, server, player_obj, player_chan ):
self.player_obj = player_obj
self.onlinesince = datetime.datetime.fromtimestamp( float( time() - player_obj.onlinesecs ) )
self.channel = player_chan
self.channel.players.append( self )
if self.isAuthed:
from mumble.models import MumbleUser
try:
self.mumbleuser = MumbleUser.objects.get( mumbleid=self.userid, server=server )
except MumbleUser.DoesNotExist:
self.mumbleuser = None
else:
self.mumbleuser = None
# Lookup unknown attributes in self.player_obj to automatically include Murmur's fields
def __getattr__( self, key ):
if hasattr( self.player_obj, key ):
return getattr( self.player_obj, key )
else:
raise AttributeError( "'%s' object has no attribute '%s'" % ( self.__class__.__name__, key ) )
def __str__( self ):
return '<Player "%s" (%d, %d)>' % ( self.name, self.session, self.userid )
hasComment = property(
lambda self: hasattr( self.player_obj, "comment" ) and bool(self.player_obj.comment),
doc="True if this player has a comment set."
)
isAuthed = property(
lambda self: self.userid != -1,
doc="True if this player is authenticated (+A)."
)
isAdmin = property(
lambda self: self.mumbleuser and self.mumbleuser.getAdmin(),
doc="True if this player is in the Admin group in the ACL."
)
is_server = False
is_channel = False
is_player = True
def getIpAsString( self ):
""" Get the client's IPv4 or IPv6 address, in a pretty format. """
addr = self.player_obj.address
if max( addr[:10] ) == 0 and addr[10:12] == (255, 255):
return "%d.%d.%d.%d" % tuple( addr[12:] )
ip6addr = [(hi << 8 | lo) for (hi, lo) in zip(addr[0::2], addr[1::2])]
# colon-separated string:
ipstr = ':'.join([ ("%x" % part) for part in ip6addr ])
# 0:0:0 -> ::
return re.sub( "((^|:)(0:){2,})", '::', ipstr, 1 )
ipaddress = property( getIpAsString )
fqdn = property( lambda self: socket.getfqdn( self.ipaddress ),
doc="The fully qualified domain name of the user's host." )
# kept for compatibility to mmChannel (useful for traversal funcs)
playerCount = property( lambda self: -1, doc="Exists only for compatibility to mmChannel." )
id = property(
lambda self: "player_%d"%self.session,
doc="A string ready to be used in an id property of an HTML tag."
)
def visit( self, callback, lvl = 0 ):
""" Call callback on myself. """
callback( self, lvl )
def asDict( self ):
pldata = self.player_obj.__dict__.copy()
if self.mumbleuser:
if self.mumbleuser.hasTexture():
pldata['texture'] = self.mumbleuser.textureUrl
return pldata
def asMvXml( self, parentnode ):
""" Return an XML node for this player suitable for MumbleViewer-ng. """
from xml.etree.cElementTree import SubElement
me = SubElement( parentnode, "item" , id=self.id, rel='user' )
content = SubElement( me, "content" )
name = SubElement( content , "name" )
name.text = self.name
def asMvJson( self ):
""" Return a Dict for this player suitable for MumbleViewer-ng. """
return {
"attributes": {
"id": self.id,
"rel": "user",
},
'data': self.name,
}
class mmACL( object ): class mmACL( object ):
""" Represents an ACL for a certain channel. """
""" Represents an ACL for a certain channel. """
def __init__( self, channel, acl_obj ):
self.channel = channel;
self.acls, self.groups, self.inherit = acl_obj;
def __init__( self, channel, acl_obj ):
self.channel = channel
self.acls, self.groups, self.inherit = acl_obj
self.groups_dict = {};
self.groups_dict = {}
for group in self.groups:
self.groups_dict[ group.name ] = group;
for group in self.groups:
self.groups_dict[ group.name ] = group
def group_has_member( self, name, userid ):
""" Checks if the given userid is a member of the given group in this channel. """
if name not in self.groups_dict:
raise ReferenceError( "No such group '%s'" % name );
def group_has_member( self, name, userid ):
""" Checks if the given userid is a member of the given group in this channel. """
if name not in self.groups_dict:
raise ReferenceError( "No such group '%s'" % name )
return userid in self.groups_dict[name].add or userid in self.groups_dict[name].members;
return userid in self.groups_dict[name].add or userid in self.groups_dict[name].members
def group_add_member( self, name, userid ):
""" Make sure this userid is a member of the group in this channel (and subs). """
if name not in self.groups_dict:
raise ReferenceError( "No such group '%s'" % name );
def group_add_member( self, name, userid ):
""" Make sure this userid is a member of the group in this channel (and subs). """
if name not in self.groups_dict:
raise ReferenceError( "No such group '%s'" % name )
group = self.groups_dict[name];
group = self.groups_dict[name]
# if neither inherited nor to be added, add
if userid not in group.members and userid not in group.add:
group.add.append( userid );
# if neither inherited nor to be added, add
if userid not in group.members and userid not in group.add:
group.add.append( userid )
# if to be removed, unremove
if userid in group.remove:
group.remove.remove( userid );
# if to be removed, unremove
if userid in group.remove:
group.remove.remove( userid )
def group_remove_member( self, name, userid ):
""" Make sure this userid is NOT a member of the group in this channel (and subs). """
if name not in self.groups_dict:
raise ReferenceError( "No such group '%s'" % name );
def group_remove_member( self, name, userid ):
""" Make sure this userid is NOT a member of the group in this channel (and subs). """
if name not in self.groups_dict:
raise ReferenceError( "No such group '%s'" % name )
group = self.groups_dict[name];
group = self.groups_dict[name]
# if added here, unadd
if userid in group.add:
group.add.remove( userid );
# if member and not in remove, add to remove
elif userid in group.members and userid not in group.remove:
group.remove.append( userid );
# if added here, unadd
if userid in group.add:
group.add.remove( userid )
# if member and not in remove, add to remove
elif userid in group.members and userid not in group.remove:
group.remove.append( userid )
def save( self ):
""" Send this ACL to Murmur. """
return self.channel.server.ctl.setACL(
self.channel.server.srvid,
self.channel.chanid,
self.acls, self.groups, self.inherit
);
def save( self ):
""" Send this ACL to Murmur. """
return self.channel.server.ctl.setACL(
self.channel.server.srvid,
self.channel.chanid,
self.acls, self.groups, self.inherit
)

1457
pyweb/mumble/models.py
File diff suppressed because it is too large
View File

303
pyweb/mumble/murmurenvutils.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -15,230 +16,230 @@
""" """
import os, subprocess, signal import os, subprocess, signal
from select import select
from os.path import join, exists
from shutil import copyfile
from select import select
from os.path import join, exists
from shutil import copyfile
from django.conf import settings
from django.conf import settings
from utils import ObjectInfo
from utils import ObjectInfo
def get_available_versions(): def get_available_versions():
""" Return murmur versions installed inside the LAB_DIR. """
dirs = os.listdir( settings.TEST_MURMUR_LAB_DIR );
dirs.sort();
return dirs;
""" Return murmur versions installed inside the LAB_DIR. """
dirs = os.listdir( settings.TEST_MURMUR_LAB_DIR )
dirs.sort()
return dirs
def run_callback( version, callback, *args, **kwargs ): def run_callback( version, callback, *args, **kwargs ):
""" Initialize the database and run murmur, then call the callback.
After the callback has returned, kill murmur.
""" Initialize the database and run murmur, then call the callback.
After the callback has returned, kill murmur.
The callback will be passed the Popen object that wraps murmur,
and any arguments that were passed to run_callback.
The callback will be passed the Popen object that wraps murmur,
and any arguments that were passed to run_callback.
If the callback raises an exception, murmur will still be properly
shutdown and the exception will be reraised.
If the callback raises an exception, murmur will still be properly
shutdown and the exception will be reraised.
The callback can either return an arbitrary value, or a tuple.
If it returns a tuple, it must be of the form:
The callback can either return an arbitrary value, or a tuple.
If it returns a tuple, it must be of the form:
( <any> intended_return_value, <bool> call_update_dbase )
( <any> intended_return_value, <bool> call_update_dbase )
That means: If the second value evaluates to True, update_dbase
will be called; the first value will be returned by run_callback.
That means: If the second value evaluates to True, update_dbase
will be called; the first value will be returned by run_callback.
If the callback returns anything other than a tuple, that value
will be returned directly.
If the callback returns anything other than a tuple, that value
will be returned directly.
So, If run_callback should return a tuple, you will need to return
the tuple form mentioned above in the callback, and put your tuple
into the first parameter.
"""
So, If run_callback should return a tuple, you will need to return
the tuple form mentioned above in the callback, and put your tuple
into the first parameter.
"""
murmur_root = join( settings.TEST_MURMUR_LAB_DIR, version );
if not exists( murmur_root ):
raise EnvironmentError( "This version could not be found: '%s' does not exist!" % murmur_root );
murmur_root = join( settings.TEST_MURMUR_LAB_DIR, version )
if not exists( murmur_root ):
raise EnvironmentError( "This version could not be found: '%s' does not exist!" % murmur_root )
init_dbase( version );
init_dbase( version )
process = run_murmur( version );
process = run_murmur( version )
try:
result = callback( process, *args, **kwargs );
if type(result) == tuple:
if result[1]:
update_dbase( version );
return result[0];
else:
return result;
finally:
kill_murmur( process );
try:
result = callback( process, *args, **kwargs )
if type(result) == tuple:
if result[1]:
update_dbase( version )
return result[0]
else:
return result
finally:
kill_murmur( process )
def init_dbase( version ): def init_dbase( version ):
""" Initialize Murmur's database by copying the one from FILES_DIR. """
dbasefile = join( settings.TEST_MURMUR_FILES_DIR, "murmur-%s.db3" % version );
if not exists( dbasefile ):
raise EnvironmentError( "This version could not be found: '%s' does not exist!" % dbasefile );
murmurfile = join( settings.TEST_MURMUR_LAB_DIR, version, "murmur.sqlite" );
copyfile( dbasefile, murmurfile );
""" Initialize Murmur's database by copying the one from FILES_DIR. """
dbasefile = join( settings.TEST_MURMUR_FILES_DIR, "murmur-%s.db3" % version )
if not exists( dbasefile ):
raise EnvironmentError( "This version could not be found: '%s' does not exist!" % dbasefile )
murmurfile = join( settings.TEST_MURMUR_LAB_DIR, version, "murmur.sqlite" )
copyfile( dbasefile, murmurfile )
def update_dbase( version ): def update_dbase( version ):
""" Copy Murmur's database to FILES_DIR (the inverse of init_dbase). """
murmurfile = join( settings.TEST_MURMUR_LAB_DIR, version, "murmur.sqlite" );
if not exists( murmurfile ):
raise EnvironmentError( "Murmur's database could not be found: '%s' does not exist!" % murmurfile );
dbasefile = join( settings.TEST_MURMUR_FILES_DIR, "murmur-%s.db3" % version );
copyfile( murmurfile, dbasefile );
""" Copy Murmur's database to FILES_DIR (the inverse of init_dbase). """
murmurfile = join( settings.TEST_MURMUR_LAB_DIR, version, "murmur.sqlite" )
if not exists( murmurfile ):
raise EnvironmentError( "Murmur's database could not be found: '%s' does not exist!" % murmurfile )
dbasefile = join( settings.TEST_MURMUR_FILES_DIR, "murmur-%s.db3" % version )
copyfile( murmurfile, dbasefile )
def run_murmur( version ): def run_murmur( version ):
""" Run the given Murmur version as a subprocess.
""" Run the given Murmur version as a subprocess.
Either returns a Popen object or raises an EnvironmentError.
"""
Either returns a Popen object or raises an EnvironmentError.
"""
murmur_root = join( settings.TEST_MURMUR_LAB_DIR, version );
if not exists( murmur_root ):
raise EnvironmentError( "This version could not be found: '%s' does not exist!" % murmur_root );
murmur_root = join( settings.TEST_MURMUR_LAB_DIR, version )
if not exists( murmur_root ):
raise EnvironmentError( "This version could not be found: '%s' does not exist!" % murmur_root )
binary_candidates = ( 'murmur.64', 'murmur.x86', 'murmurd' );
binary_candidates = ( 'murmur.64', 'murmur.x86', 'murmurd' )
for binname in binary_candidates:
if exists( join( murmur_root, binname ) ):
process = subprocess.Popen(
( join( murmur_root, binname ), '-fg' ),
stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
cwd=murmur_root
);
for binname in binary_candidates:
if exists( join( murmur_root, binname ) ):
process = subprocess.Popen(
( join( murmur_root, binname ), '-fg' ),
stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
cwd=murmur_root
)
# Check capabilities by waiting for certain lines to show up.
capa = ObjectInfo( has_dbus=False, has_ice=False, has_instance=False, has_users=False );
# Check capabilities by waiting for certain lines to show up.
capa = ObjectInfo( has_dbus=False, has_ice=False, has_instance=False, has_users=False )
def canRead( self, timeout=1 ):
rdy_read, rdy_write, rdy_other = select( [self.stdout], [], [], timeout );
return self.stdout in rdy_read;
def canRead( self, timeout=1 ):
rdy_read, rdy_write, rdy_other = select( [self.stdout], [], [], timeout )
return self.stdout in rdy_read
setattr(subprocess.Popen, 'canRead', canRead)
setattr(subprocess.Popen, 'canRead', canRead)
while process.canRead(0.5):
line = process.stdout.readline();
#print "read line:", line
if line == 'DBus registration succeeded\n':
capa.has_dbus = True;
elif line == 'MurmurIce: Endpoint "tcp -h 127.0.0.1 -p 6502" running\n':
capa.has_ice = True;
elif line == '1 => Server listening on 0.0.0.0:64738\n':
capa.has_instance = True;
elif "> Authenticated\n" in line:
capa.has_users = True;
while process.canRead(0.5):
line = process.stdout.readline()
#print "read line:", line
if line == 'DBus registration succeeded\n':
capa.has_dbus = True
elif line == 'MurmurIce: Endpoint "tcp -h 127.0.0.1 -p 6502" running\n':
capa.has_ice = True
elif line == '1 => Server listening on 0.0.0.0:64738\n':
capa.has_instance = True
elif "> Authenticated\n" in line:
capa.has_users = True
process.capabilities = capa;
process.capabilities = capa
return process;
return process
raise EnvironmentError( "Murmur binary not found. (Tried %s)" % unicode(binary_candidates) );
raise EnvironmentError( "Murmur binary not found. (Tried %s)" % unicode(binary_candidates) )
def wait_for_user( process, timeout=1 ): def wait_for_user( process, timeout=1 ):
""" Wait for a user to connect. This call will consume any output from murmur
until a line indicating a user's attempt to connect has been found.
""" Wait for a user to connect. This call will consume any output from murmur
until a line indicating a user's attempt to connect has been found.
The timeout parameter specifies how long (in seconds) to wait for input.
It defaults to 1 second. If you set this to 0 it will return at the end
of input (and thereby tell you if a player has already connected). If
you set this to None, the call will block until a player has connected.
The timeout parameter specifies how long (in seconds) to wait for input.
It defaults to 1 second. If you set this to 0 it will return at the end
of input (and thereby tell you if a player has already connected). If
you set this to None, the call will block until a player has connected.
Returns True if a user has connected before the timeout has been hit,
False otherwise.
"""
while process.canRead( timeout ):
line = process.stdout.readline();
if "> Authenticated\n" in line:
process.capabilities.has_users = True;
return True;
return False;
Returns True if a user has connected before the timeout has been hit,
False otherwise.
"""
while process.canRead( timeout ):
line = process.stdout.readline()
if "> Authenticated\n" in line:
process.capabilities.has_users = True
return True
return False
def kill_murmur( process ): def kill_murmur( process ):
""" Send a sigterm to the given process. """
return os.kill( process.pid, signal.SIGTERM );
""" Send a sigterm to the given process. """
return os.kill( process.pid, signal.SIGTERM )
class MumbleCommandWrapper_noargs( object ): class MumbleCommandWrapper_noargs( object ):
""" Mixin used to run a standard Django command inside MurmurEnvUtils.
""" Mixin used to run a standard Django command inside MurmurEnvUtils.
To modify a standard Django command for MEU, you will need to create
a new command and derive its Command class from the wrapper, and the
Command class of the original command:
To modify a standard Django command for MEU, you will need to create
a new command and derive its Command class from the wrapper, and the
Command class of the original command:
from django.core.management.commands.shell import Command as ShellCommand
from mumble.murmurenvutils import MumbleCommandWrapper
from django.core.management.commands.shell import Command as ShellCommand
from mumble.murmurenvutils import MumbleCommandWrapper
class Command( MumbleCommandWrapper, ShellCommand ):
pass
class Command( MumbleCommandWrapper, ShellCommand ):
pass
That will run the original command, after the user has had the chance to
select the version of Murmur to run.
"""
That will run the original command, after the user has had the chance to
select the version of Murmur to run.
"""
def _choose_version( self ):
print "Choose version:";
def _choose_version( self ):
print "Choose version:"
vv = get_available_versions();
for idx in range(len(vv)):
print " #%d %s" % ( idx, vv[idx] );
vv = get_available_versions()
for idx in range(len(vv)):
print " #%d %s" % ( idx, vv[idx] )
chosen = int( raw_input("#> ") );
chosen = int( raw_input("#> ") )
return vv[chosen];
return vv[chosen]
def handle_noargs( self, **options ):
self.origOpts = options;
def handle_noargs( self, **options ):
self.origOpts = options
run_callback( self._choose_version(), self.runOrig );
run_callback( self._choose_version(), self.runOrig )
def runOrig( self, proc ):
super( MumbleCommandWrapper_noargs, self ).handle_noargs( **self.origOpts );
def runOrig( self, proc ):
super( MumbleCommandWrapper_noargs, self ).handle_noargs( **self.origOpts )
class MumbleCommandWrapper( object ): class MumbleCommandWrapper( object ):
""" Mixin used to run a standard Django command inside MurmurEnvUtils.
""" Mixin used to run a standard Django command inside MurmurEnvUtils.
To modify a standard Django command for MEU, you will need to create
a new command and derive its Command class from the wrapper, and the
Command class of the original command:
To modify a standard Django command for MEU, you will need to create
a new command and derive its Command class from the wrapper, and the
Command class of the original command:
from django.core.management.commands.shell import Command as ShellCommand
from mumble.murmurenvutils import MumbleCommandWrapper
from django.core.management.commands.shell import Command as ShellCommand
from mumble.murmurenvutils import MumbleCommandWrapper
class Command( MumbleCommandWrapper, ShellCommand ):
pass
class Command( MumbleCommandWrapper, ShellCommand ):
pass
That will run the original command, after the user has had the chance to
select the version of Murmur to run.
"""
That will run the original command, after the user has had the chance to
select the version of Murmur to run.
"""
def _choose_version( self ):
print "Choose version:";
def _choose_version( self ):
print "Choose version:"
vv = get_available_versions();
for idx in range(len(vv)):
print " #%d %s" % ( idx, vv[idx] );
vv = get_available_versions()
for idx in range(len(vv)):
print " #%d %s" % ( idx, vv[idx] )
chosen = int( raw_input("#> ") );
chosen = int( raw_input("#> ") )
return vv[chosen];
return vv[chosen]
def handle( self, *args, **options ):
self.origArgs = args;
self.origOpts = options;
def handle( self, *args, **options ):
self.origArgs = args
self.origOpts = options
run_callback( self._choose_version(), self.runOrig );
run_callback( self._choose_version(), self.runOrig )
def runOrig( self, proc ):
super( MumbleCommandWrapper, self ).handle( *self.origArgs, **self.origOpts );
def runOrig( self, proc ):
super( MumbleCommandWrapper, self ).handle( *self.origArgs, **self.origOpts )

41
pyweb/mumble/templatetags/mumble_extras.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -19,44 +20,44 @@ from django.template.loader import render_to_string
from django.conf import settings from django.conf import settings
register = template.Library();
register = template.Library()
@register.filter @register.filter
def trunc( string, maxlen = 50 ): def trunc( string, maxlen = 50 ):
""" converts "a very very extaordinary long text" to "a very very extra... """
if len(string) < maxlen:
return string;
return string[:(maxlen - 3)] + "";
""" converts "a very very extaordinary long text" to "a very very extra... """
if len(string) < maxlen:
return string
return string[:(maxlen - 3)] + ""
@register.filter @register.filter
def chanview( obj, user = None ): def chanview( obj, user = None ):
""" renders an mmChannel / mmPlayer object with the correct template """
if obj.is_server:
return render_to_string( 'mumble/server.html', { 'Server': obj, 'MumbleAccount': user, 'MEDIA_URL': settings.MEDIA_URL } );
elif obj.is_channel:
return render_to_string( 'mumble/channel.html', { 'Channel': obj, 'MumbleAccount': user, 'MEDIA_URL': settings.MEDIA_URL } );
elif obj.is_player:
return render_to_string( 'mumble/player.html', { 'Player': obj, 'MumbleAccount': user, 'MEDIA_URL': settings.MEDIA_URL } );
""" renders an mmChannel / mmPlayer object with the correct template """
if obj.is_server:
return render_to_string( 'mumble/server.html', { 'Server': obj, 'MumbleAccount': user, 'MEDIA_URL': settings.MEDIA_URL } )
elif obj.is_channel:
return render_to_string( 'mumble/channel.html', { 'Channel': obj, 'MumbleAccount': user, 'MEDIA_URL': settings.MEDIA_URL } )
elif obj.is_player:
return render_to_string( 'mumble/player.html', { 'Player': obj, 'MumbleAccount': user, 'MEDIA_URL': settings.MEDIA_URL } )
@register.filter @register.filter
def chanurl( obj, user ): def chanurl( obj, user ):
""" create a connection URL and takes the user's login into account """
return obj.getURL( user );
""" create a connection URL and takes the user's login into account """
return obj.getURL( user )
@register.filter @register.filter
def mmversion_lt( obj, version ): def mmversion_lt( obj, version ):
""" return True if the given Server's version is less than the given version. """
return tuple(obj.version[:3]) < tuple([int(v) for v in version.split('.')])
""" return True if the given Server's version is less than the given version. """
return tuple(obj.version[:3]) < tuple([int(v) for v in version.split('.')])
@register.filter @register.filter
def mmversion_eq( obj, version ): def mmversion_eq( obj, version ):
""" return True if the given Server's version equals the given version. """
return tuple(obj.version[:3]) == tuple([int(v) for v in version.split('.')])
""" return True if the given Server's version equals the given version. """
return tuple(obj.version[:3]) == tuple([int(v) for v in version.split('.')])
@register.filter @register.filter
def mmversion_gt( obj, version ): def mmversion_gt( obj, version ):
""" return True if the given Server's version is greater than the given version. """
return tuple(obj.version[:3]) > tuple([int(v) for v in version.split('.')])
""" return True if the given Server's version is greater than the given version. """
return tuple(obj.version[:3]) > tuple([int(v) for v in version.split('.')])

109
pyweb/mumble/testrunner.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -16,84 +17,84 @@
import os import os
from django.test.simple import run_tests as django_run_tests
from django.conf import settings
from django.test.simple import run_tests as django_run_tests
from django.conf import settings
from murmurenvutils import get_available_versions, run_callback, wait_for_user
from murmurenvutils import get_available_versions, run_callback, wait_for_user
def run_tests( test_labels, verbosity=1, interactive=True, extra_tests=[] ): def run_tests( test_labels, verbosity=1, interactive=True, extra_tests=[] ):
""" Run the Django built in testing framework, but before testing the mumble
app, allow Murmur to be set up correctly.
"""
""" Run the Django built in testing framework, but before testing the mumble
app, allow Murmur to be set up correctly.
"""
if not test_labels:
test_labels = [ appname.split('.')[-1] for appname in settings.INSTALLED_APPS ];
if not test_labels:
test_labels = [ appname.split('.')[-1] for appname in settings.INSTALLED_APPS ]
# No need to sync any murmur servers for the other apps
os.environ['MURMUR_CONNSTR'] = '';
# No need to sync any murmur servers for the other apps
os.environ['MURMUR_CONNSTR'] = ''
# The easy way: mumble is not being tested.
if "mumble" not in test_labels:
return django_run_tests( test_labels, verbosity, interactive, extra_tests );
# The easy way: mumble is not being tested.
if "mumble" not in test_labels:
return django_run_tests( test_labels, verbosity, interactive, extra_tests )
# First run everything apart from mumble. mumble will be tested separately, so Murmur
# can be set up properly first.
failed_tests = 0;
# First run everything apart from mumble. mumble will be tested separately, so Murmur
# can be set up properly first.
failed_tests = 0
if len(test_labels) > 1:
# only run others if mumble is not the only app to be tested
test_labels = list(test_labels);
test_labels.remove( "mumble" );
failed_tests += django_run_tests( test_labels, verbosity, interactive, extra_tests );
if len(test_labels) > 1:
# only run others if mumble is not the only app to be tested
test_labels = list(test_labels)
test_labels.remove( "mumble" )
failed_tests += django_run_tests( test_labels, verbosity, interactive, extra_tests )
failed_tests += run_mumble_tests( verbosity, interactive );
failed_tests += run_mumble_tests( verbosity, interactive )
return failed_tests;
return failed_tests
def run_mumble_tests( verbosity=1, interactive=True ): def run_mumble_tests( verbosity=1, interactive=True ):
connstrings = {
'DBus': 'net.sourceforge.mumble.murmur',
'Ice': 'Meta:tcp -h 127.0.0.1 -p 6502',
};
connstrings = {
'DBus': 'net.sourceforge.mumble.murmur',
'Ice': 'Meta:tcp -h 127.0.0.1 -p 6502',
}
def django_run_tests_wrapper( process, version ):
wr_failed_tests = 0;
def django_run_tests_wrapper( process, version ):
wr_failed_tests = 0
for method in connstrings:
# Check if this server is ready to be used with the current method
if getattr( process.capabilities, ("has_%s" % method.lower()), False ):
print "Testing mumble %s via %s" % ( version, method );
for method in connstrings:
# Check if this server is ready to be used with the current method
if getattr( process.capabilities, ("has_%s" % method.lower()), False ):
print "Testing mumble %s via %s" % ( version, method )
os.environ['MURMUR_CONNSTR'] = connstrings[method];
settings.DEFAULT_CONN = connstrings[method];
settings.SLICE_VERSION = [ int(dgt) for dgt in version.split('.') ];
os.environ['MURMUR_CONNSTR'] = connstrings[method]
settings.DEFAULT_CONN = connstrings[method]
settings.SLICE_VERSION = [ int(dgt) for dgt in version.split('.') ]
print "MURMUR_CONNSTR:", os.environ['MURMUR_CONNSTR'];
print "DEFAULT_CONN: ", settings.DEFAULT_CONN;
print "SLICE_VERSION: ", settings.SLICE_VERSION;
print "MURMUR_CONNSTR:", os.environ['MURMUR_CONNSTR']
print "DEFAULT_CONN: ", settings.DEFAULT_CONN
print "SLICE_VERSION: ", settings.SLICE_VERSION
if not process.capabilities.has_users:
print "Waiting for user to connect (60 seconds)."
wait_for_user( process, timeout=60 );
if not process.capabilities.has_users:
print "Waiting for user to connect (60 seconds)."
wait_for_user( process, timeout=60 )
wr_failed_tests += django_run_tests( ('mumble',), verbosity, interactive, [] );
else:
print "Mumble %s does not support Method %s" % ( version, method );
wr_failed_tests += django_run_tests( ('mumble',), verbosity, interactive, [] )
else:
print "Mumble %s does not support Method %s" % ( version, method )
return wr_failed_tests;
return wr_failed_tests
failed_tests = 0;
failed_tests = 0
from mctl import MumbleCtlBase
from mctl import MumbleCtlBase
for version in get_available_versions():
MumbleCtlBase.clearCache();
for version in get_available_versions():
MumbleCtlBase.clearCache()
run = raw_input( "Run tests for %s? [Y/n] " % version );
if run in ('Y', 'y', ''):
failed_tests += run_callback( version, django_run_tests_wrapper, version );
run = raw_input( "Run tests for %s? [Y/n] " % version )
if run in ('Y', 'y', ''):
failed_tests += run_callback( version, django_run_tests_wrapper, version )
return failed_tests;
return failed_tests

299
pyweb/mumble/tests.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -14,195 +15,195 @@
* GNU General Public License for more details. * GNU General Public License for more details.
""" """
from django.conf import settings
from django.test import TestCase
from django.conf import settings
from django.test import TestCase
from models import Mumble
from utils import ObjectInfo
from models import Mumble
from utils import ObjectInfo
class InstancesHandling( TestCase ): class InstancesHandling( TestCase ):
""" Tests creation, editing and removing of vserver instances. """
def setUp( self ):
# Make sure we always start with a FRESH murmur instance, checking for left-over instances
# and deleting them before creating ours.
try:
self.murmur = Mumble.objects.get( addr="0.0.0.0", port=31337 );
except Mumble.DoesNotExist:
pass
else:
self.murmur.delete();
finally:
self.murmur = Mumble( name="#unit testing instance#", addr="0.0.0.0", port=31337 );
self.murmur.save();
def testDefaultConf( self ):
conf = self.murmur.ctl.getAllConf( self.murmur.srvid );
self.assert_( type(conf) == dict );
self.assert_( "host" in conf );
self.assert_( "port" in conf );
self.assert_( "certificate" in conf );
self.assert_( "key" in conf );
self.assert_( "registerhostname" in conf );
self.assert_( "registername" in conf );
self.assert_( "channelname" in conf );
self.assert_( "username" in conf );
self.assert_( "obfuscate" in conf );
self.assert_( "defaultchannel" in conf );
def testAddrPortUnique( self ):
try:
duplicate = Mumble(
name="#another unit testing instance#",
addr=self.murmur.addr, port=self.murmur.port,
dbus=settings.DEFAULT_CONN
);
if duplicate.ctl.method == "ICE":
import Murmur
self.assertRaises( Murmur.ServerFailureException, duplicate.save );
elif self.murmur.version[:2] == [ 1, 2 ]:
from dbus import DBusException
self.assertRaises( DBusException, duplicate.save );
else:
from sqlite3 import IntegrityError
self.assertRaises( IntegrityError, duplicate.save );
finally:
# make sure the duplicate is removed
duplicate.ctl.deleteServer( duplicate.srvid );
def tearDown( self ):
self.murmur.delete();
""" Tests creation, editing and removing of vserver instances. """
def setUp( self ):
# Make sure we always start with a FRESH murmur instance, checking for left-over instances
# and deleting them before creating ours.
try:
self.murmur = Mumble.objects.get( addr="0.0.0.0", port=31337 )
except Mumble.DoesNotExist:
pass
else:
self.murmur.delete()
finally:
self.murmur = Mumble( name="#unit testing instance#", addr="0.0.0.0", port=31337 )
self.murmur.save()
def testDefaultConf( self ):
conf = self.murmur.ctl.getAllConf( self.murmur.srvid )
self.assert_( type(conf) == dict )
self.assert_( "host" in conf )
self.assert_( "port" in conf )
self.assert_( "certificate" in conf )
self.assert_( "key" in conf )
self.assert_( "registerhostname" in conf )
self.assert_( "registername" in conf )
self.assert_( "channelname" in conf )
self.assert_( "username" in conf )
self.assert_( "obfuscate" in conf )
self.assert_( "defaultchannel" in conf )
def testAddrPortUnique( self ):
try:
duplicate = Mumble(
name="#another unit testing instance#",
addr=self.murmur.addr, port=self.murmur.port,
dbus=settings.DEFAULT_CONN
)
if duplicate.ctl.method == "ICE":
import Murmur
self.assertRaises( Murmur.ServerFailureException, duplicate.save )
elif self.murmur.version[:2] == [ 1, 2 ]:
from dbus import DBusException
self.assertRaises( DBusException, duplicate.save )
else:
from sqlite3 import IntegrityError
self.assertRaises( IntegrityError, duplicate.save )
finally:
# make sure the duplicate is removed
duplicate.ctl.deleteServer( duplicate.srvid )
def tearDown( self ):
self.murmur.delete()
class DataReading( TestCase ): class DataReading( TestCase ):
""" Tests reading data from murmur using the low-level CTL methods. """
""" Tests reading data from murmur using the low-level CTL methods. """
def setUp( self ):
# BIG FAT WARNING: This sucks ass, because it assumes the tester has a
# Murmur database like the one I have.
# I definitely need to prepare Murmur somehow before running these tests.
# Just don't yet know how.
self.murmur = Mumble.objects.get(id=1);
def setUp( self ):
# BIG FAT WARNING: This sucks ass, because it assumes the tester has a
# Murmur database like the one I have.
# I definitely need to prepare Murmur somehow before running these tests.
# Just don't yet know how.
self.murmur = Mumble.objects.get(id=1)
def testCtlGetChannels( self ):
""" Test getChannels() """
def testCtlGetChannels( self ):
""" Test getChannels() """
channels = self.murmur.ctl.getChannels( self.murmur.srvid );
channels = self.murmur.ctl.getChannels( self.murmur.srvid )
if self.murmur.ctl.method == "ICE":
import Murmur
self.assertEquals( type( channels[0] ), Murmur.Channel );
else:
self.assertEquals( type( channels[0] ), ObjectInfo );
if self.murmur.ctl.method == "ICE":
import Murmur
self.assertEquals( type( channels[0] ), Murmur.Channel )
else:
self.assertEquals( type( channels[0] ), ObjectInfo )
self.assert_( hasattr( channels[0], "id" ) );
self.assert_( hasattr( channels[0], "name" ) );
self.assert_( hasattr( channels[0], "parent" ) );
self.assert_( hasattr( channels[0], "links" ) );
self.assert_( hasattr( channels[0], "id" ) )
self.assert_( hasattr( channels[0], "name" ) )
self.assert_( hasattr( channels[0], "parent" ) )
self.assert_( hasattr( channels[0], "links" ) )
def testCtlGetPlayers( self ):
""" Test getPlayers() """
def testCtlGetPlayers( self ):
""" Test getPlayers() """
players = self.murmur.ctl.getPlayers( self.murmur.srvid );
players = self.murmur.ctl.getPlayers( self.murmur.srvid )
self.assert_( len(players) > 0 );
self.assert_( len(players) > 0 )
self.assertEquals( type(players), dict );
self.assertEquals( type(players), dict )
for plidx in players:
player = players[plidx];
for plidx in players:
player = players[plidx]
if self.murmur.ctl.method == "ICE" and self.murmur.version[:2] == ( 1, 2 ):
import Murmur
self.assertEquals( type( player ), Murmur.User );
else:
self.assertEquals( type( player ), ObjectInfo );
if self.murmur.ctl.method == "ICE" and self.murmur.version[:2] == ( 1, 2 ):
import Murmur
self.assertEquals( type( player ), Murmur.User )
else:
self.assertEquals( type( player ), ObjectInfo )
self.assert_( hasattr( player, "session" ) );
self.assert_( hasattr( player, "mute" ) );
self.assert_( hasattr( player, "deaf" ) );
self.assert_( hasattr( player, "selfMute" ) );
self.assert_( hasattr( player, "selfDeaf" ) );
self.assert_( hasattr( player, "channel" ) );
self.assert_( hasattr( player, "userid" ) );
self.assert_( hasattr( player, "name" ) );
self.assert_( hasattr( player, "onlinesecs" ) );
self.assert_( hasattr( player, "bytespersec" ) );
self.assert_( hasattr( player, "session" ) )
self.assert_( hasattr( player, "mute" ) )
self.assert_( hasattr( player, "deaf" ) )
self.assert_( hasattr( player, "selfMute" ) )
self.assert_( hasattr( player, "selfDeaf" ) )
self.assert_( hasattr( player, "channel" ) )
self.assert_( hasattr( player, "userid" ) )
self.assert_( hasattr( player, "name" ) )
self.assert_( hasattr( player, "onlinesecs" ) )
self.assert_( hasattr( player, "bytespersec" ) )
def testCtlGetRegisteredPlayers( self ):
""" Test getRegistredPlayers() and getRegistration() """
def testCtlGetRegisteredPlayers( self ):
""" Test getRegistredPlayers() and getRegistration() """
players = self.murmur.ctl.getRegisteredPlayers( self.murmur.srvid );
players = self.murmur.ctl.getRegisteredPlayers( self.murmur.srvid )
self.assert_( len(players) > 0 );
self.assert_( len(players) > 0 )
self.assertEquals( type(players), dict );
self.assertEquals( type(players), dict )
for plidx in players:
player = players[plidx];
for plidx in players:
player = players[plidx]
self.assertEquals( type( player ), ObjectInfo );
self.assertEquals( type( player ), ObjectInfo )
self.assert_( hasattr( player, "userid" ) );
self.assert_( hasattr( player, "name" ) );
self.assert_( hasattr( player, "email" ) );
self.assert_( hasattr( player, "pw" ) );
self.assert_( hasattr( player, "userid" ) )
self.assert_( hasattr( player, "name" ) )
self.assert_( hasattr( player, "email" ) )
self.assert_( hasattr( player, "pw" ) )
# compare with getRegistration result
reg = self.murmur.ctl.getRegistration( self.murmur.srvid, player.userid );
# compare with getRegistration result
reg = self.murmur.ctl.getRegistration( self.murmur.srvid, player.userid )
self.assertEquals( type( reg ), ObjectInfo );
self.assertEquals( type( reg ), ObjectInfo )
self.assert_( hasattr( reg, "userid" ) );
self.assert_( hasattr( reg, "name" ) );
self.assert_( hasattr( reg, "email" ) );
self.assert_( hasattr( reg, "pw" ) );
self.assert_( hasattr( reg, "userid" ) )
self.assert_( hasattr( reg, "name" ) )
self.assert_( hasattr( reg, "email" ) )
self.assert_( hasattr( reg, "pw" ) )
self.assertEquals( player.userid, reg.userid );
self.assertEquals( player.name, reg.name );
self.assertEquals( player.email, reg.email );
self.assertEquals( player.pw, reg.pw );
self.assertEquals( player.userid, reg.userid )
self.assertEquals( player.name, reg.name )
self.assertEquals( player.email, reg.email )
self.assertEquals( player.pw, reg.pw )
def testCtlGetAcl( self ):
""" Test getACL() for the root channel """
def testCtlGetAcl( self ):
""" Test getACL() for the root channel """
acls, groups, inherit = self.murmur.ctl.getACL( self.murmur.srvid, 0 );
acls, groups, inherit = self.murmur.ctl.getACL( self.murmur.srvid, 0 )
for rule in acls:
if self.murmur.ctl.method == "ICE" and self.murmur.version[:2] == ( 1, 2 ):
import Murmur
self.assertEquals( type( rule ), Murmur.ACL );
else:
self.assertEquals( type( rule ), ObjectInfo );
for rule in acls:
if self.murmur.ctl.method == "ICE" and self.murmur.version[:2] == ( 1, 2 ):
import Murmur
self.assertEquals( type( rule ), Murmur.ACL )
else:
self.assertEquals( type( rule ), ObjectInfo )
self.assert_( hasattr( rule, "applyHere" ) );
self.assert_( hasattr( rule, "applySubs" ) );
self.assert_( hasattr( rule, "inherited" ) );
self.assert_( hasattr( rule, "userid" ) );
self.assert_( hasattr( rule, "group" ) );
self.assert_( hasattr( rule, "allow" ) );
self.assert_( hasattr( rule, "deny" ) );
self.assert_( hasattr( rule, "applyHere" ) )
self.assert_( hasattr( rule, "applySubs" ) )
self.assert_( hasattr( rule, "inherited" ) )
self.assert_( hasattr( rule, "userid" ) )
self.assert_( hasattr( rule, "group" ) )
self.assert_( hasattr( rule, "allow" ) )
self.assert_( hasattr( rule, "deny" ) )
for grp in groups:
if self.murmur.ctl.method == "ICE":
import Murmur
self.assertEquals( type( grp ), Murmur.Group );
else:
self.assertEquals( type( grp ), ObjectInfo );
for grp in groups:
if self.murmur.ctl.method == "ICE":
import Murmur
self.assertEquals( type( grp ), Murmur.Group )
else:
self.assertEquals( type( grp ), ObjectInfo )
self.assert_( hasattr( grp, "name" ) );
self.assert_( hasattr( grp, "inherited" ) );
self.assert_( hasattr( grp, "inherit" ) );
self.assert_( hasattr( grp, "inheritable" ) );
self.assert_( hasattr( grp, "add" ) );
self.assert_( hasattr( grp, "remove" ) );
self.assert_( hasattr( grp, "members" ) );
self.assert_( hasattr( grp, "name" ) )
self.assert_( hasattr( grp, "inherited" ) )
self.assert_( hasattr( grp, "inherit" ) )
self.assert_( hasattr( grp, "inheritable" ) )
self.assert_( hasattr( grp, "add" ) )
self.assert_( hasattr( grp, "remove" ) )
self.assert_( hasattr( grp, "members" ) )

23
pyweb/mumble/urls.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -17,19 +18,19 @@
from django.conf.urls.defaults import patterns from django.conf.urls.defaults import patterns
urlpatterns = patterns( urlpatterns = patterns(
'mumble.views',
( r'djangousers', 'djangousers' ),
( r'(?P<server>\d+)/users', 'users' ),
'mumble.views',
( r'djangousers', 'djangousers' ),
( r'(?P<server>\d+)/users', 'users' ),
( r'(?P<server>\d+)/(?P<userid>\d+)/texture.png', 'showTexture' ),
( r'(?P<server>\d+)/(?P<userid>\d+)/texture.png', 'showTexture' ),
( r'murmur/tree/(?P<server>\d+)', 'mmng_tree' ),
( r'mumbleviewer/(?P<server>\d+).xml', 'mumbleviewer_tree_xml' ),
( r'mumbleviewer/(?P<server>\d+).json', 'mumbleviewer_tree_json'),
( r'murmur/tree/(?P<server>\d+)', 'mmng_tree' ),
( r'mumbleviewer/(?P<server>\d+).xml', 'mumbleviewer_tree_xml' ),
( r'mumbleviewer/(?P<server>\d+).json', 'mumbleviewer_tree_json'),
( r'mobile/(?P<server>\d+)', 'mobile_show' ),
( r'mobile/?$', 'mobile_mumbles' ),
( r'mobile/(?P<server>\d+)', 'mobile_show' ),
( r'mobile/?$', 'mobile_mumbles' ),
( r'(?P<server>\d+)', 'show' ),
( r'$', 'mumbles' ),
( r'(?P<server>\d+)', 'show' ),
( r'$', 'mumbles' ),
) )

27
pyweb/mumble/utils.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -15,22 +16,22 @@
""" """
class ObjectInfo( object ): class ObjectInfo( object ):
""" Wraps arbitrary information to be easily accessed. """
""" Wraps arbitrary information to be easily accessed. """
def __init__( self, **kwargs ):
self.__dict__ = kwargs;
def __init__( self, **kwargs ):
self.__dict__ = kwargs
def __str__( self ):
return unicode( self );
def __str__( self ):
return unicode( self )
def __repr__( self ):
return unicode( self );
def __repr__( self ):
return unicode( self )
def __unicode__( self ):
return unicode( self.__dict__ );
def __unicode__( self ):
return unicode( self.__dict__ )
def __contains__( self, name ):
return name in self.__dict__;
def __contains__( self, name ):
return name in self.__dict__
def __getitem__( self, name ):
return self.__dict__[name];
def __getitem__( self, name ):
return self.__dict__[name]

690
pyweb/mumble/views.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -15,398 +16,387 @@
""" """
import simplejson import simplejson
from StringIO import StringIO
from PIL import Image
from StringIO import StringIO
from PIL import Image
from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404
from django.template import RequestContext
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.conf import settings
from django.core.urlresolvers import reverse
from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404
from django.template import RequestContext
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.contrib.auth import views as auth_views
from django.core.urlresolvers import reverse
from django.contrib.auth import views as auth_views
from models import Mumble, MumbleUser
from forms import MumbleForm, MumbleUserForm, MumbleUserPasswordForm
from forms import MumbleUserLinkForm, MumbleTextureForm, MumbleKickForm
from models import Mumble, MumbleUser
from forms import MumbleForm, MumbleUserForm, MumbleUserPasswordForm
from forms import MumbleUserLinkForm, MumbleTextureForm, MumbleKickForm
def redir( request ): def redir( request ):
""" Redirect to the servers list. """
if request.META['HTTP_USER_AGENT'].startswith( 'BlackBerry' ) or \
"Opera Mobi" in request.META['HTTP_USER_AGENT'] or \
"Opera Mini" in request.META['HTTP_USER_AGENT'] or \
"Windows CE" in request.META['HTTP_USER_AGENT'] or \
"MIDP" in request.META['HTTP_USER_AGENT'] or \
"Palm" in request.META['HTTP_USER_AGENT'] or \
"NetFront" in request.META['HTTP_USER_AGENT'] or \
"Nokia" in request.META['HTTP_USER_AGENT'] or \
"Symbian" in request.META['HTTP_USER_AGENT'] or \
"UP.Browser" in request.META['HTTP_USER_AGENT'] or \
"UP.Link" in request.META['HTTP_USER_AGENT'] or \
"WinWAP" in request.META['HTTP_USER_AGENT'] or \
"Android" in request.META['HTTP_USER_AGENT'] or \
"DoCoMo" in request.META['HTTP_USER_AGENT'] or \
"KDDI-" in request.META['HTTP_USER_AGENT'] or \
"Softbank" in request.META['HTTP_USER_AGENT'] or \
"J-Phone" in request.META['HTTP_USER_AGENT'] or \
"IEMobile" in request.META['HTTP_USER_AGENT'] or \
"iPod" in request.META['HTTP_USER_AGENT'] or \
"iPhone" in request.META['HTTP_USER_AGENT']:
return HttpResponseRedirect( reverse( mobile_mumbles ) );
else:
return HttpResponseRedirect( reverse( mumbles ) );
""" Redirect to the servers list. """
if request.META['HTTP_USER_AGENT'].startswith( 'BlackBerry' ) or \
"Opera Mobi" in request.META['HTTP_USER_AGENT'] or \
"Opera Mini" in request.META['HTTP_USER_AGENT'] or \
"Windows CE" in request.META['HTTP_USER_AGENT'] or \
"MIDP" in request.META['HTTP_USER_AGENT'] or \
"Palm" in request.META['HTTP_USER_AGENT'] or \
"NetFront" in request.META['HTTP_USER_AGENT'] or \
"Nokia" in request.META['HTTP_USER_AGENT'] or \
"Symbian" in request.META['HTTP_USER_AGENT'] or \
"UP.Browser" in request.META['HTTP_USER_AGENT'] or \
"UP.Link" in request.META['HTTP_USER_AGENT'] or \
"WinWAP" in request.META['HTTP_USER_AGENT'] or \
"Android" in request.META['HTTP_USER_AGENT'] or \
"DoCoMo" in request.META['HTTP_USER_AGENT'] or \
"KDDI-" in request.META['HTTP_USER_AGENT'] or \
"Softbank" in request.META['HTTP_USER_AGENT'] or \
"J-Phone" in request.META['HTTP_USER_AGENT'] or \
"IEMobile" in request.META['HTTP_USER_AGENT'] or \
"iPod" in request.META['HTTP_USER_AGENT'] or \
"iPhone" in request.META['HTTP_USER_AGENT']:
return HttpResponseRedirect( reverse( mobile_mumbles ) )
else:
return HttpResponseRedirect( reverse( mumbles ) )
def mobile_mumbles( request ): def mobile_mumbles( request ):
return mumbles( request, mobile=True );
return mumbles( request, mobile=True )
def mumbles( request, mobile=False ): def mumbles( request, mobile=False ):
""" Display a list of all configured Mumble servers, or redirect if only one configured. """
mms = Mumble.objects.all().order_by( "name" );
""" Display a list of all configured Mumble servers, or redirect if only one configured. """
mms = Mumble.objects.all().order_by( "name" )
if len(mms) == 1:
return HttpResponseRedirect( reverse(
{ False: show, True: mobile_show }[mobile],
kwargs={ 'server': mms[0].id, }
) );
if len(mms) == 1:
return HttpResponseRedirect( reverse(
{ False: show, True: mobile_show }[mobile],
kwargs={ 'server': mms[0].id, }
) )
return render_to_response(
'mumble/%s.html' % { False: 'list', True: 'mobile_list' }[mobile],
{ 'MumbleObjects': mms,
'MumbleActive': True,
},
context_instance = RequestContext(request)
);
return render_to_response(
'mumble/%s.html' % { False: 'list', True: 'mobile_list' }[mobile],
{ 'MumbleObjects': mms,
'MumbleActive': True,
},
context_instance = RequestContext(request)
)
def show( request, server ): def show( request, server ):
""" Display the channel list for the given Server ID.
This includes not only the channel list itself, but indeed the user registration,
server admin and user texture form as well. The template then uses JavaScript
to display these forms integrated into the Channel viewer.
"""
srv = get_object_or_404( Mumble, id=server );
if not srv.booted:
return render_to_response(
'mumble/offline.html',
{ 'DBaseObject': srv,
'MumbleActive': True,
}, context_instance = RequestContext(request) );
isAdmin = srv.isUserAdmin( request.user );
# The tab to start on.
displayTab = 0;
if isAdmin:
if request.method == 'POST' and "mode" in request.POST and request.POST['mode'] == 'admin':
adminform = MumbleForm( request.POST, instance=srv );
if adminform.is_valid():
adminform.save();
return HttpResponseRedirect( reverse( show, kwargs={ 'server': int(server), } ) );
else:
displayTab = 2;
else:
adminform = MumbleForm( instance=srv );
else:
adminform = None;
registered = False;
user = None;
if request.user.is_authenticated():
# Unregistered users may or may not need a password to register.
if settings.PROTECTED_MODE and srv.passwd:
unregged_user_form = MumbleUserPasswordForm;
# Unregistered users may or may not want to link an existing account
elif settings.ALLOW_ACCOUNT_LINKING:
unregged_user_form = MumbleUserLinkForm;
else:
unregged_user_form = MumbleUserForm;
if request.method == 'POST' and 'mode' in request.POST and request.POST['mode'] == 'reg':
try:
user = MumbleUser.objects.get( server=srv, owner=request.user );
except MumbleUser.DoesNotExist:
regform = unregged_user_form( request.POST );
regform.server = srv;
if regform.is_valid():
model = regform.save( commit=False );
model.owner = request.user;
model.server = srv;
# If we're linking accounts, the change is local only.
model.save( dontConfigureMurmur=( "linkacc" in regform.data ) );
return HttpResponseRedirect( reverse( show, kwargs={ 'server': int(server), } ) );
else:
displayTab = 1;
else:
regform = MumbleUserForm( request.POST, instance=user );
regform.server = srv;
if regform.is_valid():
regform.save();
return HttpResponseRedirect( reverse( show, kwargs={ 'server': int(server), } ) );
else:
displayTab = 1;
else:
try:
user = MumbleUser.objects.get( server=srv, owner=request.user );
except MumbleUser.DoesNotExist:
regform = unregged_user_form();
else:
regform = MumbleUserForm( instance=user );
registered = True;
if request.method == 'POST' and 'mode' in request.POST and request.POST['mode'] == 'texture' and registered:
textureform = MumbleTextureForm( request.POST, request.FILES );
if textureform.is_valid():
if 'usegravatar' in textureform.cleaned_data and textureform.cleaned_data['usegravatar'] and user.gravatar:
user.setTextureFromUrl( user.gravatar );
elif 'texturefile' in request.FILES:
user.setTexture( Image.open( request.FILES['texturefile'] ) );
return HttpResponseRedirect( reverse( show, kwargs={ 'server': int(server), } ) );
else:
textureform = MumbleTextureForm();
else:
regform = None;
textureform = None;
if isAdmin:
if request.method == 'POST' and 'mode' in request.POST:
if request.POST['mode'] == 'kick':
kickform = MumbleKickForm( request.POST );
if kickform.is_valid():
if kickform.cleaned_data["ban"]:
srv.banUser( kickform.cleaned_data['session'], kickform.cleaned_data['reason'] );
srv.kickUser( kickform.cleaned_data['session'], kickform.cleaned_data['reason'] );
# ChannelTable is a somewhat misleading name, as it actually contains channels and players.
channelTable = [];
for cid in srv.channels:
if cid != 0 and srv.channels[cid].show:
channelTable.append( srv.channels[cid] );
for pid in srv.players:
channelTable.append( srv.players[pid] );
show_url = reverse( show, kwargs={ 'server': srv.id } );
login_url = reverse( auth_views.login );
return render_to_response(
'mumble/mumble.html',
{
'login_url': "%s?next=%s" % ( login_url, show_url ),
'DBaseObject': srv,
'ChannelTable': channelTable,
'CurrentUserIsAdmin': isAdmin,
'AdminForm': adminform,
'RegForm': regform,
'TextureForm': textureform,
'Registered': registered,
'DisplayTab': displayTab,
'MumbleActive': True,
'MumbleAccount':user,
},
context_instance = RequestContext(request)
);
""" Display the channel list for the given Server ID.
This includes not only the channel list itself, but indeed the user registration,
server admin and user texture form as well. The template then uses JavaScript
to display these forms integrated into the Channel viewer.
"""
srv = get_object_or_404( Mumble, id=server )
if not srv.booted:
return render_to_response(
'mumble/offline.html',
{ 'DBaseObject': srv,
'MumbleActive': True,
}, context_instance = RequestContext(request) )
isAdmin = srv.isUserAdmin( request.user )
# The tab to start on.
displayTab = 0
if isAdmin:
if request.method == 'POST' and "mode" in request.POST and request.POST['mode'] == 'admin':
adminform = MumbleForm( request.POST, instance=srv )
if adminform.is_valid():
adminform.save()
return HttpResponseRedirect( reverse( show, kwargs={ 'server': int(server), } ) )
else:
displayTab = 2
else:
adminform = MumbleForm( instance=srv )
else:
adminform = None
registered = False
user = None
if request.user.is_authenticated():
# Unregistered users may or may not need a password to register.
if settings.PROTECTED_MODE and srv.passwd:
unregged_user_form = MumbleUserPasswordForm
# Unregistered users may or may not want to link an existing account
elif settings.ALLOW_ACCOUNT_LINKING:
unregged_user_form = MumbleUserLinkForm
else:
unregged_user_form = MumbleUserForm
if request.method == 'POST' and 'mode' in request.POST and request.POST['mode'] == 'reg':
try:
user = MumbleUser.objects.get( server=srv, owner=request.user )
except MumbleUser.DoesNotExist:
regform = unregged_user_form( request.POST )
regform.server = srv
if regform.is_valid():
model = regform.save( commit=False )
model.owner = request.user
model.server = srv
# If we're linking accounts, the change is local only.
model.save( dontConfigureMurmur=( "linkacc" in regform.data ) )
return HttpResponseRedirect( reverse( show, kwargs={ 'server': int(server), } ) )
else:
displayTab = 1
else:
regform = MumbleUserForm( request.POST, instance=user )
regform.server = srv
if regform.is_valid():
regform.save()
return HttpResponseRedirect( reverse( show, kwargs={ 'server': int(server), } ) )
else:
displayTab = 1
else:
try:
user = MumbleUser.objects.get( server=srv, owner=request.user )
except MumbleUser.DoesNotExist:
regform = unregged_user_form()
else:
regform = MumbleUserForm( instance=user )
registered = True
if request.method == 'POST' and 'mode' in request.POST and request.POST['mode'] == 'texture' and registered:
textureform = MumbleTextureForm( request.POST, request.FILES )
if textureform.is_valid():
if 'usegravatar' in textureform.cleaned_data and textureform.cleaned_data['usegravatar'] and user.gravatar:
user.setTextureFromUrl( user.gravatar )
elif 'texturefile' in request.FILES:
user.setTexture( Image.open( request.FILES['texturefile'] ) )
return HttpResponseRedirect( reverse( show, kwargs={ 'server': int(server), } ) )
else:
textureform = MumbleTextureForm()
else:
regform = None
textureform = None
if isAdmin:
if request.method == 'POST' and 'mode' in request.POST:
if request.POST['mode'] == 'kick':
kickform = MumbleKickForm( request.POST )
if kickform.is_valid():
if kickform.cleaned_data["ban"]:
srv.banUser( kickform.cleaned_data['session'], kickform.cleaned_data['reason'] )
srv.kickUser( kickform.cleaned_data['session'], kickform.cleaned_data['reason'] )
# ChannelTable is a somewhat misleading name, as it actually contains channels and players.
channelTable = []
for cid in srv.channels:
if cid != 0 and srv.channels[cid].show:
channelTable.append( srv.channels[cid] )
for pid in srv.players:
channelTable.append( srv.players[pid] )
show_url = reverse( show, kwargs={ 'server': srv.id } )
login_url = reverse( auth_views.login )
return render_to_response( 'mumble/mumble.html', {
'login_url': "%s?next=%s" % ( login_url, show_url ),
'DBaseObject': srv,
'ChannelTable': channelTable,
'CurrentUserIsAdmin': isAdmin,
'AdminForm': adminform,
'RegForm': regform,
'TextureForm': textureform,
'Registered': registered,
'DisplayTab': displayTab,
'MumbleActive': True,
'MumbleAccount':user,
}, context_instance = RequestContext(request) )
def mobile_show( request, server ): def mobile_show( request, server ):
""" Display the channel list for the given Server ID. """
srv = get_object_or_404( Mumble, id=server );
user = None;
if request.user.is_authenticated():
try:
user = MumbleUser.objects.get( server=srv, owner=request.user );
except MumbleUser.DoesNotExist:
pass;
""" Display the channel list for the given Server ID. """
return render_to_response(
'mumble/mobile_mumble.html',
{
'DBaseObject': srv,
'MumbleActive': True,
'MumbleAccount':user,
},
context_instance = RequestContext(request)
);
srv = get_object_or_404( Mumble, id=server )
user = None
if request.user.is_authenticated():
try:
user = MumbleUser.objects.get( server=srv, owner=request.user )
except MumbleUser.DoesNotExist:
pass
return render_to_response( 'mumble/mobile_mumble.html', {
'DBaseObject': srv,
'MumbleActive': True,
'MumbleAccount':user,
}, context_instance = RequestContext(request) )
def showTexture( request, server, userid ): def showTexture( request, server, userid ):
""" Pack the given user's texture into an HttpResponse.
""" Pack the given user's texture into an HttpResponse.
If userid is none, use the currently logged in User.
"""
If userid is none, use the currently logged in User.
"""
srv = get_object_or_404( Mumble, id=int(server) );
user = get_object_or_404( MumbleUser, server=srv, id=int(userid) );
srv = get_object_or_404( Mumble, id=int(server) )
user = get_object_or_404( MumbleUser, server=srv, id=int(userid) )
try:
img = user.getTexture();
except ValueError:
raise Http404();
else:
buf = StringIO();
img.save( buf, "PNG" );
return HttpResponse( buf.getvalue(), "image/png" );
try:
img = user.getTexture()
except ValueError:
raise Http404()
else:
buf = StringIO()
img.save( buf, "PNG" )
return HttpResponse( buf.getvalue(), "image/png" )
@login_required @login_required
def users( request, server ): def users( request, server ):
""" Create a list of MumbleUsers for a given server serialized as a JSON object.
If the request has a "data" field, evaluate that and update the user records.
"""
srv = get_object_or_404( Mumble, id=int(server) );
if "resync" in request.POST and request.POST['resync'] == "true":
srv.readUsersFromMurmur();
if not srv.isUserAdmin( request.user ):
return HttpResponse(
simplejson.dumps({ 'success': False, 'objects': [], 'errormsg': 'Access denied' }),
mimetype='text/javascript'
);
if request.method == 'POST':
data = simplejson.loads( request.POST['data'] );
for record in data:
if record['id'] == -1:
if record['delete']:
continue;
mu = MumbleUser( server=srv );
else:
mu = MumbleUser.objects.get( id=record['id'] );
if record['delete']:
mu.delete();
continue;
mu.name = record['name'];
mu.password = record['password'];
if record['owner']:
mu.owner = User.objects.get( id=int(record['owner']) );
mu.save();
mu.aclAdmin = record['admin'];
users = [];
for mu in srv.mumbleuser_set.all():
owner = None;
if mu.owner is not None:
owner = mu.owner.id
users.append( {
'id': mu.id,
'name': mu.name,
'password': None,
'owner': owner,
'admin': mu.aclAdmin,
} );
return HttpResponse(
simplejson.dumps( { 'success': True, 'objects': users } ),
mimetype='text/javascript'
);
""" Create a list of MumbleUsers for a given server serialized as a JSON object.
If the request has a "data" field, evaluate that and update the user records.
"""
srv = get_object_or_404( Mumble, id=int(server) )
if "resync" in request.POST and request.POST['resync'] == "true":
srv.readUsersFromMurmur()
if not srv.isUserAdmin( request.user ):
return HttpResponse(
simplejson.dumps({ 'success': False, 'objects': [], 'errormsg': 'Access denied' }),
mimetype='text/javascript'
)
if request.method == 'POST':
data = simplejson.loads( request.POST['data'] )
for record in data:
if record['id'] == -1:
if record['delete']:
continue
mu = MumbleUser( server=srv )
else:
mu = MumbleUser.objects.get( id=record['id'] )
if record['delete']:
mu.delete()
continue
mu.name = record['name']
mu.password = record['password']
if record['owner']:
mu.owner = User.objects.get( id=int(record['owner']) )
mu.save()
mu.aclAdmin = record['admin']
users = []
for mu in srv.mumbleuser_set.all():
owner = None
if mu.owner is not None:
owner = mu.owner.id
users.append( {
'id': mu.id,
'name': mu.name,
'password': None,
'owner': owner,
'admin': mu.aclAdmin,
} )
return HttpResponse(
simplejson.dumps( { 'success': True, 'objects': users } ),
mimetype='text/javascript'
)
@login_required @login_required
def djangousers( request ): def djangousers( request ):
""" Return a list of all Django users' names and IDs. """
""" Return a list of all Django users' names and IDs. """
users = [ { 'uid': '', 'uname': '------' } ];
for du in User.objects.all().order_by( 'username' ):
users.append( {
'uid': du.id,
'uname': unicode( du ),
} );
users = [ { 'uid': '', 'uname': '------' } ]
for du in User.objects.all().order_by( 'username' ):
users.append( {
'uid': du.id,
'uname': unicode( du ),
} )
return HttpResponse(
simplejson.dumps( { 'success': True, 'objects': users } ),
mimetype='text/javascript'
);
return HttpResponse(
simplejson.dumps( { 'success': True, 'objects': users } ),
mimetype='text/javascript'
)
def mmng_tree( request, server ): def mmng_tree( request, server ):
""" Return a JSON representation of the channel tree suitable for
Murmur Manager:
http://github.com/cheald/murmur-manager/tree/master/widget/
To make the client widget query this view, set the URL attribute
to "http://<mumble-django base URL>/mumble"
"""
srv = get_object_or_404( Mumble, id=int(server) );
chanlist = []
userlist = []
for chanid in srv.channels:
channel = srv.channels[chanid]
if channel.parent is not None:
parent = channel.parent.chanid
else:
parent = -1
chanlist.append({
"type": "channel",
"id": channel.chanid,
"name": channel.name,
"parent": parent,
"position": channel.position,
"state": channel.temporary and "temporary" or "permanent"
})
for sessionid in srv.players:
user = srv.players[sessionid]
userlist.append({
"type": "player",
"name": user.name,
"channel": user.channel.chanid,
"mute": user.mute or user.selfMute or user.suppress,
"deaf": user.deaf or user.selfDeaf,
"online": user.onlinesecs,
"state": "online"
})
if "callback" in request.GET:
prefix = request.GET["callback"]
else:
prefix = ""
return HttpResponse(
prefix + "(" + simplejson.dumps( { 'channels': chanlist, 'users': userlist } ) + ")",
mimetype='text/javascript'
);
""" Return a JSON representation of the channel tree suitable for
Murmur Manager:
http://github.com/cheald/murmur-manager/tree/master/widget/
To make the client widget query this view, set the URL attribute
to "http://<mumble-django base URL>/mumble"
"""
srv = get_object_or_404( Mumble, id=int(server) )
chanlist = []
userlist = []
for chanid in srv.channels:
channel = srv.channels[chanid]
if channel.parent is not None:
parent = channel.parent.chanid
else:
parent = -1
chanlist.append({
"type": "channel",
"id": channel.chanid,
"name": channel.name,
"parent": parent,
"position": channel.position,
"state": channel.temporary and "temporary" or "permanent"
})
for sessionid in srv.players:
user = srv.players[sessionid]
userlist.append({
"type": "player",
"name": user.name,
"channel": user.channel.chanid,
"mute": user.mute or user.selfMute or user.suppress,
"deaf": user.deaf or user.selfDeaf,
"online": user.onlinesecs,
"state": "online"
})
if "callback" in request.GET:
prefix = request.GET["callback"]
else:
prefix = ""
return HttpResponse(
prefix + "(" + simplejson.dumps( { 'channels': chanlist, 'users': userlist } ) + ")",
mimetype='text/javascript'
)
def mumbleviewer_tree_xml( request, server ): def mumbleviewer_tree_xml( request, server ):
""" Get the XML tree from the server and serialize it to the client. """
from xml.etree.cElementTree import tostring as xml_to_string
srv = get_object_or_404( Mumble, id=int(server) );
return HttpResponse(
xml_to_string( srv.asMvXml(), encoding='utf-8' ),
mimetype='text/xml'
);
""" Get the XML tree from the server and serialize it to the client. """
from xml.etree.cElementTree import tostring as xml_to_string
srv = get_object_or_404( Mumble, id=int(server) )
return HttpResponse(
xml_to_string( srv.asMvXml(), encoding='utf-8' ),
mimetype='text/xml'
)
def mumbleviewer_tree_json( request, server ): def mumbleviewer_tree_json( request, server ):
""" Get the Dict from the server and serialize it as JSON to the client. """
srv = get_object_or_404( Mumble, id=int(server) );
if "jsonp_callback" in request.GET:
prefix = request.GET["jsonp_callback"]
else:
prefix = ""
return HttpResponse(
prefix + "(" + simplejson.dumps( srv.asMvJson() ) + ")",
mimetype='text/javascript'
);
""" Get the Dict from the server and serialize it as JSON to the client. """
srv = get_object_or_404( Mumble, id=int(server) )
if "jsonp_callback" in request.GET:
prefix = request.GET["jsonp_callback"]
else:
prefix = ""
return HttpResponse(
prefix + "(" + simplejson.dumps( srv.asMvJson() ) + ")",
mimetype='text/javascript'
)

9
pyweb/processors.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -15,9 +16,9 @@
""" """
def installed_apps(request): def installed_apps(request):
from django.conf import settings
return { 'ROSETTA_INSTALLED': "rosetta" in settings.INSTALLED_APPS }
from django.conf import settings
return { 'ROSETTA_INSTALLED': "rosetta" in settings.INSTALLED_APPS }
def mumble_version(request): def mumble_version(request):
from mumble import version_str
return { 'CURRENTVERSION': version_str }
from mumble import version_str
return { 'CURRENTVERSION': version_str }

103
pyweb/settings.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
# Django settings for mumble_django project. # Django settings for mumble_django project.
""" """
@ -56,7 +57,7 @@ MUMBLE_DJANGO_ROOT = None ##
from os.path import join, dirname, abspath, exists from os.path import join, dirname, abspath, exists
if not MUMBLE_DJANGO_ROOT or not exists( MUMBLE_DJANGO_ROOT ): if not MUMBLE_DJANGO_ROOT or not exists( MUMBLE_DJANGO_ROOT ):
MUMBLE_DJANGO_ROOT = dirname(dirname(abspath(__file__)));
MUMBLE_DJANGO_ROOT = dirname(dirname(abspath(__file__)))
# URL Template for constructing Gravatars. # URL Template for constructing Gravatars.
GRAVATAR_URL = 'http://www.gravatar.com/avatar/%(hash)s.jpg?d=monsterid&s=%(size)d' GRAVATAR_URL = 'http://www.gravatar.com/avatar/%(hash)s.jpg?d=monsterid&s=%(size)d'
@ -95,8 +96,8 @@ PROTECTED_MODE = False
# stolen they could easily take over the server. (So make sure the password can't be easily # stolen they could easily take over the server. (So make sure the password can't be easily
# guessed, use at least over 9000 letters, blah blah.) # guessed, use at least over 9000 letters, blah blah.)
# This feature is only available if PROTECTED_MODE is not active. # This feature is only available if PROTECTED_MODE is not active.
ALLOW_ACCOUNT_LINKING = True # Allow linking in general?
ALLOW_ACCOUNT_LINKING_ADMINS = False # Allow linking for Admin accounts?
ALLOW_ACCOUNT_LINKING = True # Allow linking in general?
ALLOW_ACCOUNT_LINKING_ADMINS = False # Allow linking for Admin accounts?
# Warning and Critical levels for the Munin plugin. These will be multiplied with the # Warning and Critical levels for the Munin plugin. These will be multiplied with the
# server instance's slot count to calculate the real levels. # server instance's slot count to calculate the real levels.
@ -155,85 +156,85 @@ MEDIA_URL = MUMBLE_DJANGO_URL+'static/'
ADMIN_MEDIA_PREFIX = MUMBLE_DJANGO_URL+'media/' ADMIN_MEDIA_PREFIX = MUMBLE_DJANGO_URL+'media/'
# URL to the login view # URL to the login view
LOGIN_URL = MUMBLE_DJANGO_URL + 'accounts/login';
LOGIN_REDIRECT_URL = MUMBLE_DJANGO_URL + 'accounts/profile';
LOGIN_URL = MUMBLE_DJANGO_URL + 'accounts/login'
LOGIN_REDIRECT_URL = MUMBLE_DJANGO_URL + 'accounts/profile'
# Automatically generate a .secret.txt file containing the SECRET_KEY. # Automatically generate a .secret.txt file containing the SECRET_KEY.
# Shamelessly stolen from ByteFlow: <http://www.byteflow.su> # Shamelessly stolen from ByteFlow: <http://www.byteflow.su>
try: try:
SECRET_KEY
SECRET_KEY
except NameError: except NameError:
SECRET_FILE = join(MUMBLE_DJANGO_ROOT, '.secret.txt')
try:
SECRET_KEY = open(SECRET_FILE).read().strip()
except IOError:
try:
from random import choice
SECRET_KEY = ''.join([choice('abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)') for i in range(50)])
secret = file(SECRET_FILE, 'w')
secret.write(SECRET_KEY)
secret.close()
except IOError:
Exception('Please create a %s file with random characters to generate your secret key!' % SECRET_FILE)
SECRET_FILE = join(MUMBLE_DJANGO_ROOT, '.secret.txt')
try:
SECRET_KEY = open(SECRET_FILE).read().strip()
except IOError:
try:
from random import choice
SECRET_KEY = ''.join([choice('abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)') for i in range(50)])
secret = file(SECRET_FILE, 'w')
secret.write(SECRET_KEY)
secret.close()
except IOError:
Exception('Please create a %s file with random characters to generate your secret key!' % SECRET_FILE)
# List of callables that know how to import templates from various sources. # List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = ( TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
) )
MIDDLEWARE_CLASSES = ( MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
) )
ROOT_URLCONF = 'pyweb.urls' ROOT_URLCONF = 'pyweb.urls'
TEMPLATE_DIRS = ( TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
join( MUMBLE_DJANGO_ROOT, 'pyweb', 'templates' ),
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
join( MUMBLE_DJANGO_ROOT, 'pyweb', 'templates' ),
) )
TEMPLATE_CONTEXT_PROCESSORS = ( TEMPLATE_CONTEXT_PROCESSORS = (
"django.core.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
'processors.installed_apps',
'processors.mumble_version',
"django.core.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
'processors.installed_apps',
'processors.mumble_version',
) )
TEST_RUNNER = 'mumble.testrunner.run_tests' TEST_RUNNER = 'mumble.testrunner.run_tests'
TEST_MURMUR_LAB_DIR = join( dirname(MUMBLE_DJANGO_ROOT), 'murmur' );
TEST_MURMUR_FILES_DIR = join( MUMBLE_DJANGO_ROOT, 'testdata' );
TEST_MURMUR_LAB_DIR = join( dirname(MUMBLE_DJANGO_ROOT), 'murmur' )
TEST_MURMUR_FILES_DIR = join( MUMBLE_DJANGO_ROOT, 'testdata' )
CONVERSIONSQL_ROOT = join( MUMBLE_DJANGO_ROOT, "pyweb", "mumble", "conversionsql" );
CONVERSIONSQL_ROOT = join( MUMBLE_DJANGO_ROOT, "pyweb", "mumble", "conversionsql" )
INSTALLED_APPS = [ INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.admin',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'registration',
'mumble',
'django.contrib.auth',
'django.contrib.admin',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'registration',
'mumble',
] ]
def modprobe( name ): def modprobe( name ):
""" Try to import the named module, and if that works add it to INSTALLED_APPS. """
try:
__import__( name )
except ImportError:
pass
else:
INSTALLED_APPS.append( name )
""" Try to import the named module, and if that works add it to INSTALLED_APPS. """
try:
__import__( name )
except ImportError:
pass
else:
INSTALLED_APPS.append( name )
# Check if rosetta is available. # Check if rosetta is available.
# http://code.google.com/p/django-rosetta # http://code.google.com/p/django-rosetta

37
pyweb/urls.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -26,32 +27,32 @@ handler404 = 'django.views.defaults.page_not_found'
handler500 = 'django.views.defaults.server_error' handler500 = 'django.views.defaults.server_error'
urlpatterns = patterns('', urlpatterns = patterns('',
(r'^/?$', 'mumble.views.redir' ),
(r'^/?$', 'mumble.views.redir' ),
# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
# to INSTALLED_APPS to enable admin documentation:
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
# to INSTALLED_APPS to enable admin documentation:
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
(r'^accounts/profile/', 'views.profile' ),
(r'^accounts/imprint/', 'views.imprint' ),
(r'^accounts/', include('registration.urls') ),
(r'^accounts/profile/', 'views.profile' ),
(r'^accounts/imprint/', 'views.imprint' ),
(r'^accounts/', include('registration.urls') ),
(r'^mumble/', include('mumble.urls')),
(r'^mumble/', include('mumble.urls')),
# Uncomment the next line to enable the admin:
(r'^admin/', admin.site.urls),
# Uncomment the next line to enable the admin:
(r'^admin/', admin.site.urls),
) )
if "rosetta" in settings.INSTALLED_APPS: if "rosetta" in settings.INSTALLED_APPS:
urlpatterns += patterns( '',
( r'rosetta/', include( 'rosetta.urls' ) )
)
urlpatterns += patterns( '',
( r'rosetta/', include( 'rosetta.urls' ) )
)
# Development stuff # Development stuff
if settings.DEBUG or True: if settings.DEBUG or True:
urlpatterns += patterns('',
(r'^%s(?P<path>.*)$' % settings.MEDIA_URL[1:],
'django.views.static.serve',
{'document_root': settings.MEDIA_ROOT, 'show_indexes': True} ),
)
urlpatterns += patterns('',
(r'^%s(?P<path>.*)$' % settings.MEDIA_URL[1:],
'django.views.static.serve',
{'document_root': settings.MEDIA_ROOT, 'show_indexes': True} ),
)

37
pyweb/views.py

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# kate: space-indent on; indent-width 4; replace-tabs on;
""" """
* Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net> * Copyright © 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
@ -14,29 +15,29 @@
* GNU General Public License for more details. * GNU General Public License for more details.
""" """
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.contrib.auth.decorators import login_required
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.contrib.auth.decorators import login_required
from mumble.models import MumbleUser
from mumble.models import MumbleUser
@login_required @login_required
def profile( request ): def profile( request ):
userdata = {
"ProfileActive": True,
"mumbleaccs": MumbleUser.objects.filter( owner = request.user ),
};
userdata = {
"ProfileActive": True,
"mumbleaccs": MumbleUser.objects.filter( owner = request.user ),
}
return render_to_response(
'registration/profile.html',
userdata,
context_instance = RequestContext(request)
);
return render_to_response(
'registration/profile.html',
userdata,
context_instance = RequestContext(request)
)
def imprint( request ): def imprint( request ):
import mumble
return render_to_response(
'registration/imprint.html',
{ 'upstreamversion': mumble.getLatestUpstreamVersion() },
context_instance = RequestContext(request) );
import mumble
return render_to_response(
'registration/imprint.html',
{ 'upstreamversion': mumble.getLatestUpstreamVersion() },
context_instance = RequestContext(request) )
Loading…
Cancel
Save