class SanickioskScreensaverPrefs(SectionPlugin): default_classconfig = { 'xscreensaver_enable': True, 'xscreensaver_idle': '0:01:30', 'glslideshow_duration': '', 'glslideshow_pan': '', 'glslideshow_fade': '3', 'glslideshow_zoom': '100', 'glslideshow_clip': True, } classconfig_root = True def init(self): self.title = 'Screensaver' self.icon = 'picture' self.category = 'Sanickiosk' self.append(self.ui.inflate('sanickiosk_screensaver:main')) self.binder = Binder(self, self) self.binder.populate() @on('save', 'click') def save(self): self.binder.update() self.save_classconfig() self.context.notify( 'info', _('Saved. Please restart Sanickiosk for changes to take effect.')) self.binder.populate() all_vars = '\n'.join( [k + '="' + str(v) + '"' for k, v in self.classconfig.iteritems()]) open('/home/kiosk/.sanickiosk/screensaver.cfg', 'w').write(all_vars) #save
class TestPlugin (SectionPlugin): def init(self): self.title = 'GeT3' # those are not class attributes and can be only set in or after init() self.icon = 'question' self.category = 'Software' """ UI Inflater searches for the named XML layout and inflates it into an UIElement object tree """ settings = filter(lambda x: '_settings.py' in x , os.listdir("/home/django/get3/GeT3/settings/")) self.instances = [GetIstance(a) for a in settings] self.append(self.ui.inflate('ajenti_get:main')) self.binder = Binder(self, self.find('bindroot')) self.refresh() self.binder.populate() def refresh(self): """ Changing element properties automatically results in an UI updated being issued to client """ #self.find('counter-label').text = 'Counter: %i' % self.counter #@on('increment-button', 'click') #def on_button(self): """ This method is called every time a child element with ID 'increment-button' fires a 'click' event """ #self.counter += 1 #self.refresh()
class SimpleDemo(SectionPlugin): def init(self): self.title = "Binder" self.icon = "question" self.category = "Demo" self.append(self.ui.inflate("test:binder-main")) self.data = [Person("Alice", "123"), Person("Bob", "234")] self.dict = {"a": 1, "b": 2} self.find("data").text = repr(self.data) self.binder = Binder(self, self.find("bindroot")) @on("populate", "click") def on_populate(self): self.binder.populate() @on("unpopulate", "click") def on_unpopulate(self): self.binder.unpopulate() @on("update", "click") def on_update(self): self.binder.update() self.find("data").text = repr(self.data)
class SimpleDemo (SectionPlugin): def init(self): self.title = 'Binder' self.icon = 'question' self.category = 'Demo' self.append(self.ui.inflate('test:binder-main')) self.data = [ Person('Alice', '123'), Person('Bob', '234'), ] self.find('data').text = repr(self.data) self.binder = Binder(self, self.find('bindroot')) @on('populate', 'click') def on_populate(self): self.binder.populate() @on('unpopulate', 'click') def on_unpopulate(self): self.binder.unpopulate() @on('update', 'click') def on_update(self): self.binder.update() self.find('data').text = repr(self.data)
class TestPlugin(SectionPlugin): def init(self): self.title = 'GeT3' # those are not class attributes and can be only set in or after init() self.icon = 'question' self.category = 'Software' """ UI Inflater searches for the named XML layout and inflates it into an UIElement object tree """ settings = filter(lambda x: '_settings.py' in x, os.listdir("/home/django/get3/GeT3/settings/")) self.instances = [GetIstance(a) for a in settings] self.append(self.ui.inflate('ajenti_get:main')) self.binder = Binder(self, self.find('bindroot')) self.refresh() self.binder.populate() def refresh(self): """ Changing element properties automatically results in an UI updated being issued to client """ #self.find('counter-label').text = 'Counter: %i' % self.counter #@on('increment-button', 'click') #def on_button(self): """
class SanickioskScreensaverPrefs (SectionPlugin): default_classconfig = { 'xscreensaver_enable': True, 'xscreensaver_idle': '0:01:30', 'glslideshow_duration': '', 'glslideshow_pan': '', 'glslideshow_fade': '3', 'glslideshow_zoom': '100', 'glslideshow_clip': True, } classconfig_root = True def init(self): self.title = 'Screensaver' self.icon = 'picture' self.category = 'Sanickiosk' self.append(self.ui.inflate('sanickiosk_screensaver:main')) self.binder = Binder(self, self) self.binder.populate() @on('save', 'click') def save(self): self.binder.update() self.save_classconfig() self.context.notify('info', _('Saved. Please restart Sanickiosk for changes to take effect.')) self.binder.populate() all_vars = '\n'.join([k + '="' + str(v) + '"' for k,v in self.classconfig.iteritems()]) open('/home/kiosk/.sanickiosk/screensaver.cfg', 'w').write(all_vars) #save
class Teamspeak(SectionPlugin): def init(self): self.title = 'Teamspeak' self.icon = 'comments' self.category = _('Software') self.append(self.ui.inflate('teamspeak3:main')) self.backend = TeamspeakBackend.get() self.users = [] self.servers = [] self.banlist = [] self.binder = Binder(self, self) def post_item_bind_users(object, collection, item, ui): ui.find('poke').on('click', self.on_poke, item) ui.find('kick').on('click', self.on_kick, item) ui.find('ban').on('click', self.on_ban, item) def post_item_bind_banlist(object, collection, item, ui): ui.find('unban').on('click', self.on_unban, item) self.find('users').post_item_bind = post_item_bind_users self.find('banlist').post_item_bind = post_item_bind_banlist def on_poke(self, item): self.backend.clientpoke(item.uid) self.context.notify('info', 'Client has been poked!') def on_kick(self, item): self.backend.clientkick(item.uid) self.refresh() self.context.notify('info', 'Client has been kicked from the server!') def on_ban(self, item): self.backend.clientban(item.username) # @todo Change this to IP Address! self.refresh() self.context.notify('info', 'Client has been banned from the server!') def on_unban(self, item): self.backend.clientunban(item.banid) self.refresh() self.context.notify('info', 'Client has been unbanned from the server!') def on_page_load(self): self.refresh() def refresh(self): try: self.backend.initialize() except Exception as err: self.context.notify('error', err.message) self.context.launch('configure-plugin', plugin=self.backend) return self.binder.update() self.users = self.backend.clientlist() self.servers = self.backend.serverinfo() self.banlist = self.backend.banlist() self.binder.populate()
class SanickioskScreensaverPrefs (SectionPlugin): default_classconfig = { 'enable_videos': False, 'video_volume': 50 } classconfig_root = True def init(self): self.title = 'Режим Видео' self.icon = 'facetime-video' self.category = 'Kiosk' self.append(self.ui.inflate('kiosk_videos:main')) self.binder = Binder(self, self) self.binder.populate() @on('save', 'click') def save(self): self.binder.update() self.save_classconfig() self.context.notify('info', _('Настройките са запаметени. Моля, рестартирайте киоска.')) self.binder.populate() #all_vars = '\n'.join([k + '="' + str(v) + '"' for k,v in self.classconfig.iteritems()]) for k,v in self.classconfig.iteritems(): if k == 'enable_videos': enable_videos = "X-GNOME-Autostart-enabled=%s" % str(v).lower() if k == 'video_volume': video_volume = v.lower() if enable_videos == "X-GNOME-Autostart-enabled=true": ajenti_config = "/etc/ajenti/config.json" #Disable Browser Mode subprocess.call(['sed', '-i', r's/\\"enable_browser\\": true/\\"enable_browser\\": false/g', ajenti_config]) subprocess.call(['sed', '-i', r's/X-GNOME-Autostart-enabled=true/X-GNOME-Autostart-enabled=false/g', "/home/kiosk/.config/autostart/2-browser.desktop"]) #Disable Photo Mode subprocess.call(['sed', '-i', r's/\\"photos_enable\\": true/\\"photos_enable\\": false/g', ajenti_config]) subprocess.call(['sed', '-i', r's/X-GNOME-Autostart-enabled=true/X-GNOME-Autostart-enabled=false/g', "/home/kiosk/.config/autostart/2-photos.desktop"]) cfg = ("[Desktop Entry]\n" "Type=Application\n" "Exec=/home/kiosk/.kiosk/videos.sh %s\n" "Hidden=false\n" "NoDisplay=false\n" "%s\n" "Name[en_US]=2-videos\n" "Name=2-videos\n" "Comment[en_US]=\n" "Comment=") % (video_volume, enable_videos) open('/home/kiosk/.config/autostart/2-videos.desktop', 'w').write(cfg) #save
class SanickioskScreensaverPrefs (SectionPlugin): default_classconfig = { 'enable_browser': False, 'home_url': 'http://*****:*****@on('save', 'click') def save(self): self.binder.update() self.save_classconfig() self.context.notify('info', _('Настройките са запаметени. Моля, рестартирайте киоска.')) self.binder.populate() #all_vars = '\n'.join([k + '="' + str(v) + '"' for k,v in self.classconfig.iteritems()]) for k,v in self.classconfig.iteritems(): if k == 'enable_browser': enable_browser = "X-GNOME-Autostart-enabled=%s" % str(v).lower() if k == 'home_url': home_url = v.lower() if enable_browser == "X-GNOME-Autostart-enabled=true": ajenti_config = "/etc/ajenti/config.json" #Disable Video Mode subprocess.call(['sed', '-i', r's/\\"enable_videos\\": true/\\"enable_videos\\": false/g', ajenti_config]) subprocess.call(['sed', '-i', r's/X-GNOME-Autostart-enabled=true/X-GNOME-Autostart-enabled=false/g', "/home/kiosk/.config/autostart/2-videos.desktop"]) #Disable Photo Mode subprocess.call(['sed', '-i', r's/\\"photos_enable\\": true/\\"photos_enable\\": false/g', ajenti_config]) subprocess.call(['sed', '-i', r's/X-GNOME-Autostart-enabled=true/X-GNOME-Autostart-enabled=false/g', "/home/kiosk/.config/autostart/2-photos.desktop"]) cfg = ("[Desktop Entry]\n" "Type=Application\n" "Exec=chromium-browser --kiosk --no-first-run --disable-infobars --disable-session-crashed-bubble %s\n" "Hidden=false\n" "NoDisplay=false\n" "%s\n" "Name[en_US]=2-browser\n" "Name=2-browser\n" "Comment[en_US]=\n" "Comment=") % (home_url, enable_browser) open('/home/kiosk/.config/autostart/2-browser.desktop', 'w').write(cfg) #save
class SanickioskScreensaverPrefs (SectionPlugin): default_classconfig = { 'photos_enable': False, } classconfig_root = True def init(self): self.title = 'Режим Снимки' self.icon = 'picture' self.category = 'Kiosk' self.append(self.ui.inflate('kiosk_photos:main')) self.binder = Binder(self, self) self.binder.populate() @on('save', 'click') def save(self): self.binder.update() self.save_classconfig() self.context.notify('info', _('Настройките са запаметени. Моля, рестартирайте киоска.')) self.binder.populate() #all_vars = '\n'.join([k + '="' + str(v) + '"' for k,v in self.classconfig.iteritems()]) for k,v in self.classconfig.iteritems(): if k == 'photos_enable': photos_enable = "X-GNOME-Autostart-enabled=%s" % str(v).lower() if photos_enable == "X-GNOME-Autostart-enabled=true": ajenti_config = "/etc/ajenti/config.json" #Disable Browser Mode subprocess.call(['sed', '-i', r's/\\"enable_browser\\": true/\\"enable_browser\\": false/g', ajenti_config]) subprocess.call(['sed', '-i', r's/X-GNOME-Autostart-enabled=true/X-GNOME-Autostart-enabled=false/g', "/home/kiosk/.config/autostart/2-browser.desktop"]) #Disable Video Mode subprocess.call(['sed', '-i', r's/\\"enable_videos\\": true/\\"enable_videos\\": false/g', ajenti_config]) subprocess.call(['sed', '-i', r's/X-GNOME-Autostart-enabled=true/X-GNOME-Autostart-enabled=false/g', "/home/kiosk/.config/autostart/2-videos.desktop"]) cfg = ("[Desktop Entry]\n" "Type=Application\n" "Exec=feh --recursive --quiet --randomize --full-screen --borderless --reload 300 --slideshow-delay 10 --no-menus --auto-zoom --hide-pointer /home/kiosk/Photos/\n" "Hidden=false\n" "NoDisplay=false\n" "%s\n" "Name[en_US]=2-photos\n" "Name=2-photos\n" "Comment[en_US]=\n" "Comment=") % (photos_enable) open('/home/kiosk/.config/autostart/2-photos.desktop', 'w').write(cfg) #save
class Dnsmasq(SectionPlugin): def init(self): self.icon = 'globe' self.title = 'Dnsmasq' self.category = _('Software') self.append(self.ui.inflate('dnsmasq:main')) self.leases = DnsmasqLeases() self.binder = Binder(self.leases, self.find('main')) self.binder.populate() @on('refresh', 'click') def refresh(self): self.leases.update() self.binder.populate()
class MailPlugin(SectionPlugin): def init(self): # meta-data self.title = _('Mail') self.icon = 'envelope' self.category = _("Software") self.append(self.ui.inflate('mail:main')) def on_first_page_load(self): username = pwd.getpwuid(os.getuid()).pw_name self.mbox = mailbox.mbox('/var/mail/%s' % username, factory=message_factory(mailbox.mboxMessage)) self.binder = Binder(self, self.find('main')) self.binder.populate() @on('refresh', 'click') def refresh(self): self.binder.populate()
class LetsEncryptPlugin (SectionPlugin): pwd = os.path.join(os.path.dirname(os.path.realpath(__file__)), '') nginx_config_dir = platform_select( debian='/etc/nginx.custom.d', centos='/etc/nginx.custom.d', mageia='/etc/nginx.custom.d', freebsd='/usr/local/etc/nginx.custom.d', arch='/etc/nginx/sites-available', osx='/opt/local/etc/nginx', ) crontab_dir = platform_select( debian='/etc/cron.d', centos='/etc/cron.d', mageia='/etc/cron.d', freebsd='/usr/local/etc/cron.d', arch='/etc/cron.d', osx='/opt/local/etc/cron.d', ) has_domains = False def init(self): self.title = 'LetsEncrypt' # those are not class attributes and can be only set in or after init() self.icon = 'lock' self.category = 'Security' """ UI Inflater searches for the named XML layout and inflates it into an UIElement object tree """ self.append(self.ui.inflate('letsencrypt:main')) self.settings = Settings() self.binder = Binder(self.settings, self) self.binder.populate() def on_page_load(self): filepath = self.settings.basedir + self.settings.domainfile domains = '' if os.path.isfile(filepath): domains = os.linesep.join(self.read_domain_file()) cron = self.check_cron() self.find('domains').value = str(domains) self.find('cronjob').value = cron def write_domain_file(self): filepath = self.settings.basedir + self.settings.domainfile if not self.find('domains').value: self.context.notify('info', 'No domains specified') self.has_domains = False return file = open(filepath, 'w') if file.write(self.find('domains').value) is None: self.has_domains = True else: self.context.notify('error', 'Domain file write error') file.close() def read_domain_file(self): filepath = self.settings.basedir + self.settings.domainfile if not open(filepath): self.context.notify('error', 'Domain file could not be read') file = open(filepath) with file as f: lines = f.readlines() return lines def create_folders(self): uid = pwd.getpwnam("www-data").pw_uid gid = grp.getgrnam("www-data").gr_gid if not os.path.exists(self.settings.basedir): os.makedirs(self.settings.basedir) os.chown(self.settings.basedir, uid, gid) if not os.path.exists(self.settings.wellknown): os.makedirs(self.settings.wellknown) os.chown(self.settings.wellknown, uid, gid) def create_custom_config(self): template = """ BASEDIR=$basedir WELLKNOWN=$wellknown """ dict = { 'basedir': self.settings.basedir, 'wellknown': self.settings.wellknown } filename = 'config' filepath = self.settings.basedir + filename file = open(filepath, 'w') src = Template( template ) if file.write(src.safe_substitute(dict)) is not None: self.context.notify('info', 'Letsencrypt error') file.close() def create_wellknown(self): if not self.check_nginx_custom_dir(): return False template = """ server { server_name $domains; listen *:80; location $location { alias $alias; } } """ dict = { 'location': '/.well-known/acme-challenge', 'alias': self.settings.wellknown, 'domains': " ".join(self.read_domain_file()) } filepath = self.nginx_config_dir + '/' + self.settings.nginx_config file = open(filepath, 'w') src = Template( template ) if file.write(src.safe_substitute(dict)) is not None: self.context.notify('info', 'WELLKNOWN config write error') file.close() def create_cron(self): file = open(self.crontab_dir + '/' + self.settings.cronfile, 'w') template = "0 0 1 * * " + self.pwd + 'libs/letsencrypt.sh/letsencrypt.sh -c' if not file.write(template): self.context.notify('info', 'Cron job error') file.close() def remove_cron(self): if os.path.isfile(self.crontab_dir + '/' + self.settings.cronfile): if os.remove(self.crontab_dir + '/' + self.settings.cronfile): return True else: self.context.notify('info', 'Cron remove error') return False def check_cron(self): if os.path.isfile(self.crontab_dir + '/' + self.settings.cronfile): return True return False def check_nginx_custom_dir(self): if not os.path.isdir(self.nginx_config_dir): if os.makedirs(self.nginx_config_dir): return True else: self.context.notify('error', 'NGINX custom dir write error') return False def request_certificates(self): params = [self.pwd + 'libs/letsencrypt.sh/letsencrypt.sh', '-c'] if self.find('renewal').value: params.append('--force') p = subprocess.Popen(params, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() if out: self.context.notify('info', 'OUT: ' + out) if err: self.context.notify('info', 'ERR: ' + err) def save(self): self.binder.update() self.binder.populate() self.create_folders() self.write_domain_file() if not self.has_domains: return self.create_custom_config() self.create_wellknown() if self.settings.cronjob: self.create_cron() else: self.remove_cron() @on('save', 'click') def save_button(self): self.save() @on('request', 'click') def request_button(self): self.save() self.request_certificates()
class DBPlugin (SectionPlugin): service_name = '' service_buttons = [] has_users = True def init(self): self.append(self.ui.inflate('db_common:main')) self.binder = Binder(None, self) self.find_type('servicebar').buttons = self.service_buttons def delete_db(db, c): self.query_drop(db) self.refresh() self.find('databases').delete_item = delete_db def delete_user(user, c): self.query_drop_user(user) self.refresh() self.find('users').delete_item = delete_user def on_page_load(self): self.refresh() @on('sql-run', 'click') def on_sql_run(self): try: result = self.query_sql(self.find('sql-db').value, self.find('sql-input').value) self.context.notify('info', _('Query finished')) except Exception as e: self.context.notify('error', str(e)) return tbl = self.find('sql-output') tbl.empty() if len(result) > 200: self.context.notify('info', _('Output cut from %i rows to 200') % len(result)) result = result[:200] for row in result: erow = self.ui.create('dtr') tbl.append(erow) for cell in row: ecell = self.ui.create('dtd') ecell.append(self.ui.create('label', text=str(cell))) erow.append(ecell) @on('add-db', 'click') def on_add_db(self): self.find('db-name-dialog').value = '' self.find('db-name-dialog').visible = True @on('add-user', 'click') def on_add_user(self): self.find('add-user-dialog').visible = True def refresh(self): self.binder.setup(self).populate() self.databases = [] self.users = [] try: self.databases = self.query_databases() if self.has_users: self.users = self.query_users() except Exception as e: import traceback; traceback.print_exc(); self.context.notify('error', str(e)) if hasattr(self, 'config_class'): self.context.launch('configure-plugin', plugin=self.config_class.get()) return self.binder.unpopulate() self.find('sql-db').labels = self.find('sql-db').values = [x.name for x in self.databases] self.binder.populate() self.find_type('servicebar').reload() @on('db-name-dialog', 'submit') def on_db_name_dialog_submit(self, value=None): try: self.query_create(value) except Exception as e: self.context.notify('error', str(e)) return self.refresh() @on('add-user-dialog', 'button') def on_add_user_dialog(self, button=None): d = self.find('add-user-dialog') d.visible = False if button == 'ok': u = User() u.name = d.find('name').value u.host = d.find('host').value u.password = d.find('password').value try: self.query_create_user(u) except Exception as e: self.context.notify('error', str(e)) return self.refresh() def query_sql(self, db, sql): raise NotImplementedError() def query_databases(self): raise NotImplementedError() def query_drop(self, db): raise NotImplementedError() def query_create(self, name): raise NotImplementedError() def query_users(self): raise NotImplementedError() def query_create_user(self, user): raise NotImplementedError() def query_drop_user(self, user): raise NotImplementedError()
class CSFSection(SectionPlugin): def init(self): self.title = _("CSF Firewall") self.icon = "fire" self.category = _("System") self.backend = CSFBackend.get() self.append(self.ui.inflate("csf:main")) self.config = CSFConfig(path="/etc/csf/csf.conf") self.list_allow = [] self.list_deny = [] self.list_tempallow = [] self.list_tempban = [] def delete_rule(csf_option, i): self.save() subprocess.call(["csf", csf_option, i.value.split("#")[0]]) self.refresh() self.find("list_allow").on_delete = lambda i, c: delete("-ar", i) self.find("list_deny").on_delete = lambda i, c: delete("-dr", i) self.find("list_tempallow").on_delete = lambda i, c: delete("-tr", i) self.find("list_tempban").on_delete = lambda i, c: delete("-tr", i) def add_rule(csf_option, address): self.save() p = subprocess.Popen(["csf", csf_option, address], stdout=subprocess.PIPE) o, e = p.communicate() self.context.notify("info", o) self.refresh() self.find("list_allow-add").on("click", lambda: add_rule("-a", self.find("permanent-lists-add-address").value)) self.find("list_deny-add").on("click", lambda: add_rule("-d", self.find("permanent-lists-add-address").value)) self.find("list_tempallow-add").on( "click", lambda: add_rule("-ta", self.find("temporary-lists-add-address").value) ) self.find("list_tempban-add").on( "click", lambda: add_rule("-td", self.find("temporary-lists-add-address").value) ) self.binder = Binder(None, self) self.binder_lists = Binder(self, self.find("lists")) def on_page_load(self): self.refresh() def refresh(self): self.config.load() self.list_allow = self.backend.read_list("allow") self.list_deny = self.backend.read_list("deny") self.list_tempallow = self.backend.read_list("tempallow") self.list_tempban = self.backend.read_list("tempban") self.binder.setup(self.config.tree).populate() self.binder_lists.populate() @on("apply", "click") def on_apply(self): self.backend.apply() self.context.notify("info", _("Applied")) @on("save", "click") def save(self): self.binder.update() self.config.save() self.backend.write_list("allow", self.list_allow) self.backend.write_list("deny", self.list_deny) self.backend.write_list("tempallow", self.list_tempallow) self.backend.write_list("tempban", self.list_tempban) self.binder.setup(self.config.tree).populate() self.context.notify("info", _("Saved")) try: self.backend.test_config() self.context.notify("info", _("Self-test OK")) except Exception, e: self.context.notify("error", str(e))
class FileManager (SectionPlugin): default_classconfig = {'root': '/'} classconfig_editor = FileManagerConfigEditor classconfig_root = True def init(self): self.title = _('File Manager') self.category = _('Tools') self.icon = 'folder-open' self.backend = FMBackend().get() self.append(self.ui.inflate('fm:main')) self.controller = Controller() def post_item_bind(object, collection, item, ui): ui.find('name').on('click', self.on_item_click, object, item) ui.find('edit').on('click', self.edit, item.fullpath) self.find('items').post_item_bind = post_item_bind def post_bc_bind(object, collection, item, ui): ui.find('name').on('click', self.on_bc_click, object, item) self.find('breadcrumbs').post_item_bind = post_bc_bind self.clipboard = [] self.tabs = self.find('tabs') self.find('dialog').buttons = [ {'id': 'save', 'text': _('Save')}, {'id': 'cancel', 'text': _('Cancel')}, ] def on_first_page_load(self): self.controller.new_tab(self.classconfig['root']) self.binder = Binder(self.controller, self.find('filemanager')).autodiscover().populate() self.binder_c = Binder(self, self.find('bind-clipboard')).autodiscover().populate() def refresh_clipboard(self): self.binder_c.reset().autodiscover().populate() @on('tabs', 'switch') def on_tab_switch(self): if self.tabs.active == (len(self.controller.tabs) - 1): self.controller.new_tab(self.classconfig['root']) self.refresh() @on('close', 'click') def on_tab_close(self): if len(self.controller.tabs) > 2: self.controller.tabs.pop(self.tabs.active) self.tabs.active = 0 self.refresh() @on('new-file', 'click') def on_new_file(self): open(os.path.join(self.controller.tabs[self.tabs.active].path, 'new file'), 'w').close() self.refresh() def upload(self, name, file): open(os.path.join(self.controller.tabs[self.tabs.active].path, name), 'w').write(file.read()) self.refresh() @on('new-dir', 'click') def on_new_directory(self): os.mkdir(os.path.join(self.controller.tabs[self.tabs.active].path, 'new directory')) self.refresh() @on('mass-cut', 'click') def on_cut(self): l = self._get_checked() for i in l: i.action = 'cut' self.clipboard += l self.refresh_clipboard() @on('mass-copy', 'click') def on_copy(self): l = self._get_checked() for i in l: i.action = 'copy' self.clipboard += l self.refresh_clipboard() @on('mass-delete', 'click') def on_delete(self): self.backend.remove(self._get_checked()) self.refresh() @on('paste', 'click') def on_paste(self): tab = self.controller.tabs[self.tabs.active] for_move = [] for_copy = [] for i in self.clipboard: if i.action == 'cut': for_move.append(i) else: for_copy.append(i) try: if for_move: self.backend.move(for_move, tab.path) if for_copy: self.backend.copy(for_copy, tab.path) self.clipboard = [] except Exception as e: self.context.notify('error', str(e)) self.refresh_clipboard() self.refresh() @on('select-all', 'click') def on_select_all(self): self.binder.update() tab = self.controller.tabs[self.tabs.active] for item in tab.items: item.checked = True self.binder.populate() self.context.notify('info', _('Selected %i items') % len(tab.items)) def _get_checked(self): self.binder.update() tab = self.controller.tabs[self.tabs.active] r = [] for item in tab.items: if item.checked: r.append(item) item.checked = False self.refresh() return r @on('clear-clipboard', 'click') def on_clear_clipboard(self): self.clipboard = [] self.refresh_clipboard() def on_item_click(self, tab, item): path = os.path.join(tab.path, item.name) if not os.path.isdir(path): self.edit(path) if not path.startswith(self.classconfig['root']): return tab.navigate(path) self.refresh() def edit(self, path): self.find('dialog').visible = True self.item = Item(path) self.item.read() self.binder_d = Binder(self.item, self.find('dialog')).autodiscover().populate() @on('dialog', 'button') def on_close_dialog(self, button): self.find('dialog').visible = False if button == 'save': self.binder_d.update() self.item.write() self.refresh() def on_bc_click(self, tab, item): if not item.path.startswith(self.classconfig['root']): return tab.navigate(item.path) self.refresh() def refresh(self): for tab in self.controller.tabs: tab.refresh() self.binder.populate()
class SanickioskScreensaverPrefs(SectionPlugin): default_classconfig = { 'home_url': 'http://sanickiosk.org', 'kioskspeeddial': False, 'kioskmode': True, 'fullscreen': True, 'browser_reset': True, 'browser_idle': '2', 'nokeys': False, 'nocontextmenu': True, 'nomenu': True, 'nodownload': True, 'noprint': True, 'nomaillinks': True, 'disable_config_url': True, 'empty_on_exit': True, 'accept_cookies_session_only': True, 'hide_toolbar': False, 'hide_home': False, 'hide_back': False, 'hide_forward': False, 'hide_reload': False, 'hide_addressbar': False, 'hide_find': False, 'hide_zoom': False, 'hide_ppreview': True, 'hide_print': True, 'hide_reset': False, 'custom_user_agent': '', } classconfig_root = True def init(self): self.title = 'Browser' self.icon = 'globe' self.category = 'Sanickiosk' self.append(self.ui.inflate('sanickiosk_browser:main')) self.binder = Binder(self, self) self.binder.populate() @on('save', 'click') def save(self): self.binder.update() self.save_classconfig() self.context.notify( 'info', _('Saved. Please restart Sanickiosk for changes to take effect.')) self.binder.populate() all_vars = '\n'.join( [k + '="' + str(v) + '"' for k, v in self.classconfig.iteritems()]) open('/home/kiosk/.sanickiosk/browser.cfg', 'w').write(all_vars) #save @on('speeddial_edit', 'click') def speeddial_edit(self): self.context.launch('notepad', path='/home/kiosk/.opera/speeddial.sav') @on('filters_edit', 'click') def filters_edit(self): self.context.launch('notepad', path='/home/kiosk/.opera/urlfilter.ini')
class Configurator(SectionPlugin): def init(self): self.title = 'Configure' self.icon = 'wrench' self.category = '' self.order = 50 self.append(self.ui.inflate('configurator:main')) self.binder = Binder(ajenti.config.tree, self.find('ajenti-config')) self.ccmgr = ClassConfigManager.get() self.classconfig_binding = Binder(self.ccmgr, self.find('classconfigs')) self.classconfig_rows = {} def post_classconfig_bind(object, collection, item, ui): self.classconfig_rows[item] = ui editor = item.classconfig_editor.new(self.ui) ui.find('container').append(editor) binder = DictAutoBinding(item, 'classconfig', editor.find('bind')) binder.populate() def save(): binder.update() item.save_classconfig() self.context.notify('info', 'Saved') ui.find('save').on('click', save) self.find('classconfigs').find( 'classes').post_item_bind = post_classconfig_bind self.find('users').new_item = lambda c: UserData() def post_user_bind(object, collection, item, ui): box = ui.find('permissions') box.empty() for prov in PermissionProvider.get_all(): line = self.ui.create('tab', title=prov.get_name()) box.append(line) for perm in prov.get_permissions(): line.append(self.ui.create('checkbox', id=perm[0], text=perm[1], \ value=(perm[0] in item.permissions))) self.find('users').post_item_bind = post_user_bind def post_user_update(object, collection, item, ui): box = ui.find('permissions') for prov in PermissionProvider.get_all(): for perm in prov.get_permissions(): has = box.find(perm[0]).value if has and not perm[0] in item.permissions: item.permissions.append(perm[0]) if not has and perm[0] in item.permissions: item.permissions.remove(perm[0]) self.find('users').post_item_update = post_user_update self.refresh() def on_page_load(self): self.refresh() def refresh(self): self.binder.reset().autodiscover().populate() self.ccmgr.reload() self.classconfig_binding.reset().autodiscover().populate() @on('save-button', 'click') @restrict('configurator:configure') def save(self): self.binder.update() for user in ajenti.config.tree.users.values(): if not '|' in user.password: user.password = UserManager.get().hash_password(user.password) self.binder.populate() ajenti.config.save() self.context.notify( 'info', 'Saved. Please restart Ajenti for changes to take effect.') @on('fake-ssl', 'click') def on_gen_ssl(self): host = self.find('fake-ssl-host').value path = self.find('fake-ssl-path').value if host == '': self.context.notify('error', 'Please supply hostname') elif not os.path.exists(path): self.context.notify('error', 'Please supply valid path') else: self.gen_ssl(host, path.rstrip('/')) @on('restart-button', 'click') def on_restart(self): ajenti.restart() @intent('configure-plugin') def configure_plugin(self, plugin=None): self.find('tabs').active = 1 self.refresh() #if plugin in self.classconfig_rows: # self.classconfig_rows[plugin].children[0].expanded = True # print self.classconfig_rows[plugin].children[0] if plugin: self.context.notify( 'info', 'Please configure %s plugin!' % plugin.classconfig_editor.title) self.activate() @intent('setup-fake-ssl') def gen_ssl(self, host, path): self.save() ajenti.config.tree.ssl.enable = True ajenti.config.tree.ssl.certificate_path = '%s/ajenti.pem' % path ajenti.config.save() self.refresh() script = """ echo '\n-> Generating key\n'; openssl genrsa -des3 -out /tmp/ajenti.key -passout pass:1234 2048; echo '\n-> Generating certificate request\n'; openssl req -new -key /tmp/ajenti.key -out /tmp/ajenti.csr -passin pass:1234 -subj /C=US/ST=NA/L=Nowhere/O=Acme\\ Inc/OU=IT/CN={0}/; echo '\n-> Removing passphrase\n'; cp /tmp/ajenti.key /tmp/ajenti.key.org; openssl rsa -in /tmp/ajenti.key.org -out /tmp/ajenti.key -passin pass:1234; echo '\n-> Generating certificate\n'; openssl x509 -req -days 365 -in /tmp/ajenti.csr -signkey /tmp/ajenti.key -out /tmp/ajenti.crt -passin pass:1234; cat /tmp/ajenti.key /tmp/ajenti.crt > {1}/ajenti.pem; rm /tmp/ajenti.*; echo '\n\n===================\nRestart Ajenti to apply changes!\n==================='; """.format(host, path) self.context.launch('terminal', command=script)
class LetsEncryptPlugin (SectionPlugin): pwd = os.path.join(os.path.dirname(os.path.realpath(__file__)), '') nginx_config_dir = platform_select( debian='/etc/nginx.custom.d', centos='/etc/nginx.custom.d', mageia='/etc/nginx.custom.d', freebsd='/usr/local/etc/nginx.custom.d', arch='/etc/nginx/sites-available', osx='/opt/local/etc/nginx', ) crontab_dir = platform_select( debian='/etc/cron.d', centos='/etc/cron.d', mageia='/etc/cron.d', freebsd='/usr/local/etc/cron.d', arch='/etc/cron.d', osx='/opt/local/etc/cron.d', ) has_domains = False def log(self, tolog): file = open(self.pwd+ '/' + "log.txt" , 'a') file.write('\n' + str(tolog) ) file.close() def init(self): self.title = 'LetsEncrypt' # those are not class attributes and can be only set in or after init() self.icon = 'lock' self.category = 'Security' """ UI Inflater searches for the named XML layout and inflates it into an UIElement object tree """ self.append(self.ui.inflate('letsencrypt:main')) self.settings = Settings() self.binder = Binder(self.settings, self) self.binder.populate() def on_page_load(self): filepath = self.settings.basedir + self.settings.domainfile domains = '' if os.path.isfile(filepath): domains = os.linesep.join(self.read_domain_file()) cron = self.check_cron() self.find('domains').value = str(domains) self.find('cronjob').value = cron def write_domain_file(self): filepath = self.settings.basedir + self.settings.domainfile if not self.find('domains').value: self.context.notify('info', 'No domains specified') self.has_domains = False return file = open(filepath, 'w') if file.write(self.find('domains').value) is None: self.has_domains = True else: self.context.notify('error', 'Domain file write error') file.close() def read_domain_file(self): filepath = self.settings.basedir + self.settings.domainfile if not open(filepath): self.context.notify('error', 'Domain file could not be read') file = open(filepath) with file as f: lines = f.readlines() return lines def create_folders(self): uid = pwd.getpwnam("www-data").pw_uid gid = grp.getgrnam("www-data").gr_gid if not os.path.exists(self.settings.basedir): os.makedirs(self.settings.basedir) os.chown(self.settings.basedir, uid, gid) if not os.path.exists(self.settings.wellknown): os.makedirs(self.settings.wellknown) os.chown(self.settings.wellknown, uid, gid) def create_custom_config(self): template = """ BASEDIR=$basedir WELLKNOWN=$wellknown """ dict = { 'basedir': self.settings.basedir, 'wellknown': self.settings.wellknown } filename = 'config' filepath = self.settings.basedir + filename file = open(filepath, 'w') src = Template( template ) if file.write(src.safe_substitute(dict)) is not None: self.context.notify('info', 'Letsencrypt error') file.close() def create_wellknown(self): if not self.check_nginx_custom_dir(): return False template = """ server { server_name $domains; listen *:80; location $location { alias $alias; } } """ dict = { 'location': '/.well-known/acme-challenge', 'alias': self.settings.wellknown, 'domains': " ".join(self.read_domain_file()) } filepath = self.nginx_config_dir + '/' + self.settings.nginx_config file = open(filepath, 'w') src = Template( template ) if file.write(src.safe_substitute(dict)) is not None: self.context.notify('info', 'WELLKNOWN config write error') file.close() def create_cron(self): file = open(self.crontab_dir + '/' + self.settings.cronfile, 'w') template = "0 0 1 * * " + self.pwd + 'libs/letsencrypt.sh/letsencrypt.sh -c' file.write(template) file.close() def remove_cron(self): if os.path.isfile(self.crontab_dir + '/' + self.settings.cronfile): if os.remove(self.crontab_dir + '/' + self.settings.cronfile): return True else: self.context.notify('info', 'Cron remove error') return False def check_cron(self): if os.path.isfile(self.crontab_dir + '/' + self.settings.cronfile): return True return False def check_nginx_custom_dir(self): if not os.path.isdir(self.nginx_config_dir): if os.makedirs(self.nginx_config_dir): return True else: self.context.notify('error', 'NGINX custom dir write error') return False def request_certificates(self): params = [ self.pwd + 'libs/letsencrypt.sh/dehydrated.sh', '-c'] self.log(params[0]) if self.find('renewal').value: params.append('--force') try: p = subprocess.Popen(params, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() except NameError as ex: self.log( str(type(ex)) ) self.log( str(ex.message) ) self.log( str(ex.args) ) self.log( str(ex) ) self.log( traceback.print_exc() ) self.context.notify('info', 'An error occured! Please check the logs') return if out: self.context.notify('info', 'OUT: ' + out) if err: self.log(err + '') self.context.notify('info', 'ERR: ' + err) def save(self): self.binder.update() self.binder.populate() self.create_folders() self.write_domain_file() if not self.has_domains: return self.create_custom_config() self.create_wellknown() if self.settings.cronjob: self.create_cron() else: self.remove_cron() @on('save', 'click') def save_button(self): self.save() @on('request', 'click') def request_button(self): self.save() self.request_certificates()
class FileManager (SectionPlugin): default_classconfig = {'root': '/', 'start': '/'} classconfig_editor = FileManagerConfigEditor classconfig_root = True def init(self): self.title = _('Файлов мениджър') self.category = _('Tools') self.icon = 'folder-open' self.backend = FMBackend().get() self.append(self.ui.inflate('fm:main')) self.controller = Controller() def post_item_bind(object, collection, item, ui): ui.find('name').on('click', self.on_item_click, object, item) ui.find('edit').on('click', self.edit, item.fullpath) self.find('items').post_item_bind = post_item_bind def post_bc_bind(object, collection, item, ui): ui.find('name').on('click', self.on_bc_click, object, item) self.find('breadcrumbs').post_item_bind = post_bc_bind self.clipboard = [] self.tabs = self.find('tabs') def on_first_page_load(self): self.new_tab() self.binder = Binder(self.controller, self.find('filemanager')).populate() self.binder_c = Binder(self, self.find('bind-clipboard')).populate() def on_page_load(self): self.refresh() def refresh_clipboard(self): self.binder_c.setup().populate() @on('tabs', 'switch') def on_tab_switch(self): if self.tabs.active == (len(self.controller.tabs) - 1): self.new_tab() self.refresh() @intent('fm:browse') def new_tab(self, path=None): dir = path or self.classconfig.get('start', None) or '/' if not dir.startswith(self.classconfig['root']): dir = self.classconfig['root'] self.controller.new_tab(dir) if not self.active: self.activate() @on('close', 'click') def on_tab_close(self): if len(self.controller.tabs) > 2: self.controller.tabs.pop(self.tabs.active) self.tabs.active = 0 self.refresh() @on('new-file', 'click') def on_new_file(self): destination = self.controller.tabs[self.tabs.active].path logging.info('[fm] new file in %s' % destination) path = os.path.join(destination, 'new file') try: open(path, 'w').close() self._chown_new(path) except OSError as e: self.context.notify('error', str(e)) self.refresh() @on('new-dir', 'click') def on_new_directory(self): destination = self.controller.tabs[self.tabs.active].path logging.info('[fm] new directory in %s' % destination) path = os.path.join(destination, 'new directory') if not os.path.exists(path): try: os.mkdir(path) os.chmod(path, 0755) self._chown_new(path) except OSError as e: self.context.notify('error', str(e)) self.refresh() def _chown_new(self, path): uid = self.classconfig.get('new_owner', 'root') or 'root' gid = self.classconfig.get('new_group', 'root') or 'root' try: uid = int(uid) except: uid = pwd.getpwnam(uid)[2] try: gid = int(gid) except: gid = grp.getgrnam(gid)[2] os.chown(path, uid, gid) def upload(self, name, file): destination = self.controller.tabs[self.tabs.active].path logging.info('[fm] uploading %s to %s' % (name, destination)) try: output = open(os.path.join(destination, name), 'w') while True: data = file.read(1024 * 1024) if not data: break gevent.sleep(0) output.write(data) output.close() except OSError as e: self.context.notify('error', str(e)) self.refresh() @on('mass-cut', 'click') def on_cut(self): l = self._get_checked() for i in l: i.action = 'cut' self.clipboard += l self.refresh_clipboard() @on('mass-copy', 'click') def on_copy(self): l = self._get_checked() for i in l: i.action = 'copy' self.clipboard += l self.refresh_clipboard() @on('mass-delete', 'click') def on_delete(self): def callback(task): self.context.notify('info', _('Files deleted')) self.refresh() self.backend.remove(self._get_checked(), cb=callback) @on('paste', 'click') def on_paste(self): tab = self.controller.tabs[self.tabs.active] for_move = [] for_copy = [] for i in self.clipboard: if i.action == 'cut': for_move.append(i) else: for_copy.append(i) try: if for_move: def callback(task): self.context.notify('info', _('Files moved')) self.refresh() self.backend.move(for_move, tab.path, callback) if for_copy: def callback(task): self.context.notify('info', _('Files copied')) self.refresh() self.backend.copy(for_copy, tab.path, callback) self.clipboard = [] except Exception as e: self.context.notify('error', str(e)) self.refresh_clipboard() @on('select-all', 'click') def on_select_all(self): self.binder.update() tab = self.controller.tabs[self.tabs.active] for item in tab.items: item.checked = not item.checked self.binder.populate() self.context.notify('info', _('Selected %i items') % len(tab.items)) def _get_checked(self): self.binder.update() tab = self.controller.tabs[self.tabs.active] r = [] for item in tab.items: if item.checked: r.append(item) item.checked = False self.refresh() return r @on('clear-clipboard', 'click') def on_clear_clipboard(self): self.clipboard = [] self.refresh_clipboard() def on_item_click(self, tab, item): path = os.path.join(tab.path, item.name) if not os.path.isdir(path): self.edit(path) if not path.startswith(self.classconfig['root']): return tab.navigate(path) self.refresh() def edit(self, path): self.find('dialog').visible = True self.item = Item(path) self.item.read() self.binder_d = Binder(self.item, self.find('dialog')).populate() # Unpack u = Unpacker.find(self.item.fullpath.lower()) unpack_btn = self.find('dialog').find('unpack') unpack_btn.visible = u is not None def cb(): self.context.notify('info', _('Unpacked')) self.refresh() def unpack(): u.unpack(self.item.fullpath, cb=cb) logging.info('[fm] unpacking %s' % self.item.fullpath) unpack_btn.on('click', lambda: unpack()) # Edit edit_btn = self.find('dialog').find('edit') if self.item.size > 1024 * 1024 * 5: edit_btn.visible = False def edit(): self.context.launch('notepad', path=self.item.fullpath) edit_btn.on('click', lambda: edit()) @on('dialog', 'button') def on_close_dialog(self, button): self.find('dialog').visible = False if button == 'save': self.binder_d.update() try: self.item.write() except Exception as e: self.context.notify('error', str(e)) self.refresh() if self.find('chmod-recursive').value: cmd = 'chown -Rv "%s:%s" "%s"; chmod -Rv %o "%s"' % ( self.item.owner, self.item.group, self.item.fullpath, self.item.mode, self.item.fullpath, ) self.context.launch('terminal', command=cmd) logging.info('[fm] modifying %s: %o %s:%s' % (self.item.fullpath, self.item.mode, self.item.owner, self.item.group)) def on_bc_click(self, tab, item): if not item.path.startswith(self.classconfig['root']): return tab.navigate(item.path) self.refresh() def refresh(self, _=None): for tab in self.controller.tabs: tab.refresh() self.binder.populate()
class Users(SectionPlugin): def init(self): self.title = _('Users') self.icon = 'group' self.category = _('System') self.append(self.ui.inflate('users:main')) def _filterOnlyUsers(x): u = int(x.uid) return u >= 500 def _filterOnlySystemUsers(x): u = int(x.uid) return u < 500 def _sorter(x): g = int(x.gid) if g >= 500: return g - 10000 return g self.find('users').filter = _filterOnlyUsers self.find('system-users').filter = _filterOnlySystemUsers self.find('groups').sorting = _sorter self.config = PasswdConfig(path='/etc/passwd') self.config_g = GroupConfig(path='/etc/group') self.binder = Binder(None, self.find('passwd-config')) self.binder_system = Binder(None, self.find('passwd-config-system')) self.binder_g = Binder(None, self.find('group-config')) self.mgr = UsersBackend.get() def post_item_bind(object, collection, item, ui): ui.find('change-password').on('click', self.change_password, item, ui) ui.find('remove-password').on('click', self.remove_password, item) if not os.path.exists(item.home): ui.find('create-home-dir').on('click', self.create_home_dir, item, ui) ui.find('create-home-dir').visible = True self.find('users').post_item_bind = post_item_bind self.find('system-users').post_item_bind = post_item_bind def on_page_load(self): self.refresh() def refresh(self): self.config.load() self.config_g.load() self.binder.setup(self.config.tree).populate() self.binder_system.setup(self.config.tree).populate() self.binder_g.setup(self.config_g.tree) self.find('group-members').labels = self.find( 'group-members').values = [x.name for x in self.config.tree.users] self.binder_g.populate() @on('add-user', 'click') def on_add_user(self): self.find('input-username').visible = True @on('input-username', 'submit') def on_add_user_done(self, value): self.mgr.add_user(value) self.refresh() @on('add-group', 'click') def on_add_group(self): self.find('input-groupname').visible = True @on('input-groupname', 'submit') def on_add_group_done(self, value): self.mgr.add_group(value) self.refresh() @on('save-users', 'click') def save_users(self): self.binder.update() self.config.save() @on('save-system-users', 'click') def save_system_users(self): self.binder_system.update() self.config.save() @on('save-groups', 'click') def save_groups(self): self.binder_g.update() self.config_g.save() def create_home_dir(self, user, ui): self.mgr.make_home_dir(user) self.context.notify('info', _('Home dir for %s was created') % user.name) ui.find('create-home-dir').visible = False def change_password(self, user, ui): new_password = ui.find('new-password').value if new_password: try: self.mgr.change_password(user, new_password) self.context.notify( 'info', _('Password for %s was changed') % user.name) ui.find('new-password').value = '' except Exception, e: self.context.notify('error', _('Error: "%s"') % e.message) else:
class MpdPlugin(SectionPlugin): default_classconfig = {'host': '127.0.0.1', 'port': 6600} classconfig_editor = MpdPluginConfigurator def bindui(self, ui_id, method_name): def decorator(method): setattr(self.find(ui_id), method_name, method) return method return decorator def init(self): self._mpd = MPD(**self.classconfig) # meta-data self.title = 'MPD' self.icon = 'music' self.category = _("Software") self.append(self.ui.inflate('mpd:main')) # data-bindings self.status = Status.EMPTY self.song = Song.EMPTY self.playlist = [] self.playlists = [] self.outputs = [] self.taxonomy = Taxonomy.EMPTY self.library = [] self.init_ui_bindings() def init_ui_bindings(self): @self.bindui('playlist', 'post_item_bind') def post_item_bind(obj, collection, item, ui): ui.find('play').on('click', self.play, item) @self.bindui('playlist', 'delete_item') def delete_item(item, collection): self.remove(item) @self.bindui('playlists', 'add_item') def add_playlist(item, collection): for i in count(1): try: self._mpd.do('save', 'Untitled %d' % i) except CommandError: continue else: break self.refresh() @self.bindui('playlists', 'delete_item') def delete_playlist(item, collection): self._mpd.do('rm', item.playlist) self.refresh() @self.bindui('playlists', 'post_item_bind') def post_playlist_bind(obj, collection, item, ui): ui.find('load').on('click', lambda *args: self._mpd.do(*args) or self.refresh(), 'load', item.playlist) #ui.find('clear').on('click', self._mpd.do, 'playlistclear', item.playlist) @self.bindui('outputs', 'post_item_bind') def post_output_bind(obj, collection, item, ui): ui.find('enabled').on('click', self.toggleoutput, item) @self.bindui('library', 'post_item_bind') def post_library_bind(obj, collection, item, ui): ui.find('add').on('click', self.add, item.file) @on('tabs', 'switch') def tab_switch(self): if self.find('tabs').active == 2: # library tab self.taxonomy = Taxonomy(izip( ('artists', 'albums', 'genres'), self._mpd.bulk_do([ ('list', 'artist'), ('list', 'album'), ('list', 'genre') ], defaults=([], [], [])))) self.binder.populate() @on('search', 'click') def search(self, add=False): self.binder.update() filter = [] for f in ('artist', 'album', 'genre'): if self.taxonomy[f] is not None: filter.append(f) filter.append(self.taxonomy[f]) if add and not filter: self.context.notify('error', _('Can not add all library! Select some filters first.')) return self.library = map(Song, (self._mpd.do('findadd' if add else 'find', *filter) or []) if filter else ifilter(lambda s: 'file' in s, self._mpd.do('listallinfo') or [])) self.binder.populate() @on('searchadd', 'click') def searchadd(self): self.search(add=True) self.refresh() @on('rename', 'click') def rename_playlists(self): old_names = map(lambda p: p.playlist, self.playlists) self.binder.update() new_names = map(lambda p: p.playlist, self.playlists) self._mpd.bulk_do( imap(lambda p: ('rename',) + p, ifilter(lambda p: p[0] != p[1], izip(old_names, new_names)))) self.refresh() def on_first_page_load(self): self.binder = Binder(self, self.find('mpd')) self.refresh() self.context.session.spawn(self.worker) def worker(self): waiter = MPD() connected = True while True: events = waiter.wait(( MPD.EV_QUEUE_CHANGED, MPD.EV_PLAYLIST_CHANGED, MPD.EV_VOLUME_CHANGED, MPD.EV_OPTIONS_CHANGED, MPD.EV_PLAYER_CHANGED, MPD.EV_OUTPUT_CHANGED)) if events is None: if connected: self.context.notify('error', 'Could not connect to MPD!') self.context.launch('configure-plugin', plugin=self) connected = False sleep(10) else: connected = True self.refresh(events) def add(self, *urls): if not urls: return def readfile(url): with closing(urlopen(url) if url.startswith(('http://', 'https://')) else open(url, 'r')) as f: return f.read().splitlines() clean_playlist = lambda lines: ifilter(None, imap(str.strip, imap(str, lines))) parse_null = lambda url: (url,) parsers = { '.m3u': lambda url: ifilter(lambda line: not line.startswith('#'), readfile(url)), '.pls': lambda url: imap(lambda line: line.split('=')[1], ifilter(lambda line: line.startswith('File'), readfile(url))), } urls = flatten(imap(lambda url: clean_playlist(parsers.get(os.path.splitext(url)[1], parse_null)(url)), urls)) cmds = imap(lambda url: ('addid', url), urls) try: self._mpd.bulk_do(cmds) except CommandError: self.context.notify('error', _('Songs not found')) else: self.refresh() @on('consume', 'click') def toggleconsume(self): self._mpd.do('consume', int(not self.status.consume)) self.refresh() @on('single', 'click') def togglesingle(self): self._mpd.do('single', int(not self.status.single)) self.refresh() @on('random', 'click') def togglerandom(self): self._mpd.do('random', int(not self.status.random)) self.refresh() @on('repeat', 'click') def togglerepeat(self): self._mpd.do('repeat', int(not self.status.repeat)) self.refresh() @on('add', 'click') def open_add_dialog(self): self.find('add_dialog').visible = True @on('add_dialog', 'button') def submit_add_dialog(self, button): dialog = self.find('add_dialog') dialog.visible = False if button == 'add': self.add(*dialog.find('new_song_url').value.strip().splitlines()) @on('refresh', 'click') def refresh(self, areas=()): if not areas: update = ['playlistinfo', 'status', 'currentsong', 'listplaylists', 'outputs'] defaults = [[], Status.EMPTY, Song.EMPTY, [], []] else: update = list(set(ifilter(bool, flatten( ('playlistinfo',) if area in (MPD.EV_QUEUE_CHANGED,) else ('listplaylists',) if area in (MPD.EV_PLAYLIST_CHANGED,) else ('status',) if area in (MPD.EV_VOLUME_CHANGED, MPD.EV_OPTIONS_CHANGED) else ('status', 'currentsong') if area in (MPD.EV_PLAYER_CHANGED,) else ('outputs',) if area in (MPD.EV_OUTPUT_CHANGED,) else None for area in areas)))) defaults = map(lambda n: {} if n in ('status', 'currentsong') else [], update) data = UpdateInfo(izip(update, self._mpd.bulk_do(*update, defaults=defaults))) for key in update: setattr(self, { 'playlistinfo': 'playlist', 'listplaylists': 'playlists', 'currentsong': 'song', }.get(key, key), getattr(data, key)) if 'status' in update: # reset icons if not all playlist is to be updated if 'playlistinfo' not in update: for song in self.playlist: song.icon = 'music' try: if self.status.state == 'play': self.find('play').visible = False self.find('pause').visible = True self.playlist[self.status.song].icon = 'play' else: self.find('play').visible = True self.find('pause').visible = False except AttributeError: pass self.binder.populate() def toggleoutput(self, output): self._mpd.do('disableoutput' if output.outputenabled else 'enableoutput', output.outputid) self.refresh() @on('voldown', 'click') def voldown(self, delta=10): self._mpd.do('volume', -delta) self.refresh() @on('volup', 'click') def volup(self, delta=10): self._mpd.do('volume', delta) self.refresh() _last_volume = 0 @on('mute', 'click') def mute(self): if self.status.volume == 0: self.volume(self._last_volume) else: self._last_volume = self.status.volume self.volume(0) def volume(self, value): self._mpd.do('setvol', value) self.refresh() def remove(self, song): self._mpd.do('deleteid', song.id) self.refresh() #@on('reorder', 'click') #def reorder(self): #self.binder.update() @on('update', 'click') def update(self): num = self._mpd.do('update') if num: self.context.notify('info', _('Update #%s started...') % num) @on('play', 'click') def play(self, song=None): if song is None: self._mpd.do('play') else: self._mpd.do('playid', song.id) self.refresh() @on('pause', 'click') def pause(self): self._mpd.do('pause') self.refresh() @on('stop', 'click') def stop(self): self._mpd.do('stop') self.refresh() @on('prev', 'click') def prev(self): self._mpd.do('previous') self.refresh() @on('next', 'click') def next(self): self._mpd.do('next') self.refresh() @on('clear', 'click') def clear(self): self._mpd.do('clear') self.refresh()
class Configurator (SectionPlugin): def init(self): self.title = 'Configure' self.icon = 'wrench' self.category = '' self.order = 50 self.append(self.ui.inflate('configurator:main')) self.binder = Binder(ajenti.config.tree, self.find('ajenti-config')) self.ccmgr = ClassConfigManager.get() self.classconfig_binding = Binder(self.ccmgr, self.find('classconfigs')) self.classconfig_rows = {} def post_classconfig_bind(object, collection, item, ui): self.classconfig_rows[item] = ui editor = item.classconfig_editor.new(self.ui) ui.find('container').append(editor) binder = DictAutoBinding(item, 'classconfig', editor.find('bind')) binder.populate() def save(): binder.update() item.save_classconfig() self.context.notify('info', 'Saved') ui.find('save').on('click', save) self.find('classconfigs').find('classes').post_item_bind = post_classconfig_bind self.find('users').new_item = lambda c: UserData() def post_user_bind(object, collection, item, ui): box = ui.find('permissions') box.empty() for prov in PermissionProvider.get_all(): line = self.ui.create('tab', title=prov.get_name()) box.append(line) for perm in prov.get_permissions(): line.append(self.ui.create('checkbox', id=perm[0], text=perm[1], \ value=(perm[0] in item.permissions))) self.find('users').post_item_bind = post_user_bind def post_user_update(object, collection, item, ui): box = ui.find('permissions') for prov in PermissionProvider.get_all(): for perm in prov.get_permissions(): has = box.find(perm[0]).value if has and not perm[0] in item.permissions: item.permissions.append(perm[0]) if not has and perm[0] in item.permissions: item.permissions.remove(perm[0]) self.find('users').post_item_update = post_user_update self.refresh() def on_page_load(self): self.refresh() def refresh(self): self.binder.reset().autodiscover().populate() self.ccmgr.reload() self.classconfig_binding.reset().autodiscover().populate() @on('save-button', 'click') @restrict('configurator:configure') def save(self): self.binder.update() for user in ajenti.config.tree.users.values(): if not '|' in user.password: user.password = UserManager.get().hash_password(user.password) self.binder.populate() ajenti.config.save() self.context.notify('info', 'Saved. Please restart Ajenti for changes to take effect.') @on('fake-ssl', 'click') def on_gen_ssl(self): host = self.find('fake-ssl-host').value path = self.find('fake-ssl-path').value if host == '': self.context.notify('error', 'Please supply hostname') elif not os.path.exists(path): self.context.notify('error', 'Please supply valid path') else: self.gen_ssl(host, path.rstrip('/')) @on('restart-button', 'click') def on_restart(self): ajenti.restart() @intent('configure-plugin') def configure_plugin(self, plugin=None): self.find('tabs').active = 1 self.refresh() #if plugin in self.classconfig_rows: # self.classconfig_rows[plugin].children[0].expanded = True # print self.classconfig_rows[plugin].children[0] if plugin: self.context.notify('info', 'Please configure %s plugin!' % plugin.classconfig_editor.title) self.activate() @intent('setup-fake-ssl') def gen_ssl(self, host, path): self.save() ajenti.config.tree.ssl.enable = True ajenti.config.tree.ssl.certificate_path = '%s/ajenti.pem' % path ajenti.config.save() self.refresh() script = """ echo '\n-> Generating key\n'; openssl genrsa -des3 -out /tmp/ajenti.key -passout pass:1234 2048; echo '\n-> Generating certificate request\n'; openssl req -new -key /tmp/ajenti.key -out /tmp/ajenti.csr -passin pass:1234 -subj /C=US/ST=NA/L=Nowhere/O=Acme\\ Inc/OU=IT/CN={0}/; echo '\n-> Removing passphrase\n'; cp /tmp/ajenti.key /tmp/ajenti.key.org; openssl rsa -in /tmp/ajenti.key.org -out /tmp/ajenti.key -passin pass:1234; echo '\n-> Generating certificate\n'; openssl x509 -req -days 365 -in /tmp/ajenti.csr -signkey /tmp/ajenti.key -out /tmp/ajenti.crt -passin pass:1234; cat /tmp/ajenti.key /tmp/ajenti.crt > {1}/ajenti.pem; rm /tmp/ajenti.*; echo '\n\n===================\nRestart Ajenti to apply changes!\n==================='; """.format(host, path) self.context.launch('terminal', command=script)
class FileManager (SectionPlugin): default_classconfig = {'root': '/', 'start': '/'} classconfig_editor = FileManagerConfigEditor classconfig_root = True def init(self): self.title = _('File Manager') self.category = _('Tools') self.icon = 'folder-open' self.backend = FMBackend().get() self.append(self.ui.inflate('fm:main')) self.controller = Controller() def post_item_bind(object, collection, item, ui): ui.find('name').on('click', self.on_item_click, object, item) ui.find('edit').on('click', self.edit, item.fullpath) self.find('items').post_item_bind = post_item_bind def post_bc_bind(object, collection, item, ui): ui.find('name').on('click', self.on_bc_click, object, item) self.find('breadcrumbs').post_item_bind = post_bc_bind self.clipboard = [] self.tabs = self.find('tabs') def on_first_page_load(self): self.new_tab() self.binder = Binder(self.controller, self.find('filemanager')).populate() self.binder_c = Binder(self, self.find('bind-clipboard')).populate() def on_page_load(self): self.refresh() def refresh_clipboard(self): self.binder_c.setup().populate() @on('tabs', 'switch') def on_tab_switch(self): if self.tabs.active == (len(self.controller.tabs) - 1): self.new_tab() self.refresh() @intent('fm:browse') def new_tab(self, path=None): dir = path or self.classconfig.get('start', None) or '/' if not dir.startswith(self.classconfig['root']): dir = self.classconfig['root'] self.controller.new_tab(dir) if not self.active: self.activate() @on('close', 'click') def on_tab_close(self): if len(self.controller.tabs) > 2: self.controller.tabs.pop(self.tabs.active) self.tabs.active = 0 self.refresh() @on('new-file', 'click') def on_new_file(self): destination = self.controller.tabs[self.tabs.active].path logging.info('[fm] new file in %s' % destination) path = os.path.join(destination, 'new file') try: open(path, 'w').close() self._chown_new(path) except OSError as e: self.context.notify('error', str(e)) self.refresh() @on('new-dir', 'click') def on_new_directory(self): destination = self.controller.tabs[self.tabs.active].path logging.info('[fm] new directory in %s' % destination) path = os.path.join(destination, 'new directory') if not os.path.exists(path): try: os.mkdir(path) os.chmod(path, 0755) self._chown_new(path) except OSError as e: self.context.notify('error', str(e)) self.refresh() def _chown_new(self, path): uid = self.classconfig.get('new_owner', 'root') or 'root' gid = self.classconfig.get('new_group', 'root') or 'root' try: uid = int(uid) except: uid = pwd.getpwnam(uid)[2] try: gid = int(gid) except: gid = grp.getgrnam(gid)[2] os.chown(path, uid, gid) def upload(self, name, file): destination = self.controller.tabs[self.tabs.active].path logging.info('[fm] uploading %s to %s' % (name, destination)) try: output = open(os.path.join(destination, name), 'w') while True: data = file.read(1024 * 1024) if not data: break gevent.sleep(0) output.write(data) output.close() except OSError as e: self.context.notify('error', str(e)) self.refresh() @on('mass-cut', 'click') def on_cut(self): l = self._get_checked() for i in l: i.action = 'cut' self.clipboard += l self.refresh_clipboard() @on('mass-copy', 'click') def on_copy(self): l = self._get_checked() for i in l: i.action = 'copy' self.clipboard += l self.refresh_clipboard() @on('mass-delete', 'click') def on_delete(self): def callback(task): self.context.notify('info', _('Files deleted')) self.refresh() self.backend.remove(self._get_checked(), cb=callback) @on('paste', 'click') def on_paste(self): tab = self.controller.tabs[self.tabs.active] for_move = [] for_copy = [] for i in self.clipboard: if i.action == 'cut': for_move.append(i) else: for_copy.append(i) try: if for_move: def callback(task): self.context.notify('info', _('Files moved')) self.refresh() self.backend.move(for_move, tab.path, callback) if for_copy: def callback(task): self.context.notify('info', _('Files copied')) self.refresh() self.backend.copy(for_copy, tab.path, callback) self.clipboard = [] except Exception as e: self.context.notify('error', str(e)) self.refresh_clipboard() @on('select-all', 'click') def on_select_all(self): self.binder.update() tab = self.controller.tabs[self.tabs.active] for item in tab.items: item.checked = not item.checked self.binder.populate() self.context.notify('info', _('Selected %i items') % len(tab.items)) def _get_checked(self): self.binder.update() tab = self.controller.tabs[self.tabs.active] r = [] for item in tab.items: if item.checked: r.append(item) item.checked = False self.refresh() return r @on('clear-clipboard', 'click') def on_clear_clipboard(self): self.clipboard = [] self.refresh_clipboard() def on_item_click(self, tab, item): path = os.path.join(tab.path, item.name) if not os.path.isdir(path): self.edit(path) if not path.startswith(self.classconfig['root']): return tab.navigate(path) self.refresh() def edit(self, path): self.find('dialog').visible = True self.item = Item(path) self.item.read() self.binder_d = Binder(self.item, self.find('dialog')).populate() # Unpack u = Unpacker.find(self.item.fullpath.lower()) unpack_btn = self.find('dialog').find('unpack') unpack_btn.visible = u is not None def cb(): self.context.notify('info', _('Unpacked')) self.refresh() def unpack(): u.unpack(self.item.fullpath, cb=cb) logging.info('[fm] unpacking %s' % self.item.fullpath) unpack_btn.on('click', lambda: unpack()) # Edit edit_btn = self.find('dialog').find('edit') if self.item.size > 1024 * 1024 * 5: edit_btn.visible = False def edit(): self.context.launch('notepad', path=self.item.fullpath) edit_btn.on('click', lambda: edit()) @on('dialog', 'button') def on_close_dialog(self, button): self.find('dialog').visible = False if button == 'save': self.binder_d.update() try: self.item.write() except Exception as e: self.context.notify('error', str(e)) self.refresh() if self.find('chmod-recursive').value: cmd = 'chown -Rv "%s:%s" "%s"; chmod -Rv %o "%s"' % ( self.item.owner, self.item.group, self.item.fullpath, self.item.mode, self.item.fullpath, ) self.context.launch('terminal', command=cmd) logging.info('[fm] modifying %s: %o %s:%s' % (self.item.fullpath, self.item.mode, self.item.owner, self.item.group)) def on_bc_click(self, tab, item): if not item.path.startswith(self.classconfig['root']): return tab.navigate(item.path) self.refresh() def refresh(self, _=None): for tab in self.controller.tabs: tab.refresh() self.binder.populate()
class MailPlugin (SectionPlugin): def init(self): self.title = _('Mail') self.icon = 'envelope' self.category = 'Web' self.manager = MailManager.get() if not self.manager.is_configured: self.append(self.ui.inflate('vh-mail:not-configured')) else: self.post_init() @on('initial-enable', 'click') def on_initial_enable(self): self.post_init() self.manager.save() self.refresh() def post_init(self): self.empty() self.append(self.ui.inflate('vh-mail:main')) self.binder = Binder(None, self) def post_mb_bind(object, collection, item, ui): ui.find('size').text = str_fsize(self.manager.get_usage(item)) def post_mb_update(object, collection, item, ui): if ui.find('password').value: item.password = ui.find('password').value self.find('mailboxes').post_item_bind = post_mb_bind self.find('mailboxes').post_item_update = post_mb_update self.find('mailboxes').filter = \ lambda mb: self.context.session.identity in ['root', mb.owner] self.find('targets').new_item = lambda c: ForwardingTarget.create() self.binder.setup(self.manager.config) def _fetch_new_mailbox_name(self, cls): mb = cls.create() mb.local = self.find('new-mailbox-local').value mb.domain = self.find('new-mailbox-domain').value or \ self.find('new-mailbox-domain-custom').value if not mb.local: self.context.notify('error', _('Invalid mailbox name')) return if not mb.domain: self.context.notify('error', _('Invalid mailbox domain')) return if cls == ForwardingMailbox: for existing in self.manager.config.forwarding_mailboxes: if existing.name == mb.name: self.context.notify( 'error', _('This address is already taken') ) return else: for existing in self.manager.config.mailboxes: if existing.name == mb.name: self.context.notify( 'error', _('This address is already taken') ) return self.find('new-mailbox-local').value = '' return mb @on('new-mailbox', 'click') def on_new_mailbox(self): self.binder.update() mb = self._fetch_new_mailbox_name(Mailbox) if not mb: return mb.owner = self.context.session.identity mb.password = '' self.manager.config.mailboxes.append(mb) self.manager.save() self.binder.populate() @on('new-forwarding-mailbox', 'click') def on_new_forwarding_mailbox(self): self.binder.update() mb = self._fetch_new_mailbox_name(ForwardingMailbox) if not mb: return mb.owner = self.context.session.identity self.manager.config.forwarding_mailboxes.append(mb) self.manager.save() self.binder.populate() def on_page_load(self): self.refresh() def refresh(self): if not self.manager.is_configured: return domains = [] for ws in VHManager.get().config.websites: if self.context.session.identity in ['root', ws.owner]: domains += [d.domain for d in ws.domains] domains = sorted(list(set(domains))) if self.find('new-mailbox-domain'): self.find('new-mailbox-domain').labels = \ domains + [_('Custom domain')] self.find('new-mailbox-domain').values = domains + [None] if self.manager.is_configured: self.binder.unpopulate().populate() if os.path.exists(self.manager.config.dkim_private_key): pubkey = subprocess.check_output([ 'openssl', 'rsa', '-in', self.manager.config.dkim_private_key, '-pubout' ]) pubkey = filter(None, pubkey.split('-'))[1].replace('\n', '') dns = '@\t\t\t\t10800 IN TXT "v=spf1 a -all"\n' dns += '_domainkey\t\t10800 IN TXT "o=~; r=postmaster@<domain>"\n' dns += '%s._domainkey\t10800 IN TXT "v=DKIM1; k=rsa; p=%s"\n' % ( self.manager.config.dkim_selector, pubkey ) dns += '_dmarc\t\t\t10800 IN TXT "v=DMARC1; p=quarantine"\n' self.find('dkim-domain-entry').value = dns else: self.find('dkim-domain-entry').value = _('No valid key exists') @on('generate-dkim-key', 'click') def on_generate_dkim_key(self): self.binder.update() self.manager.generate_dkim_key() self.binder.populate() self.save() @on('generate-tls-cert', 'click') def on_generate_tls_cert(self): self.binder.update() self.manager.generate_tls_cert() self.binder.populate() self.save() @on('save', 'click') def save(self): self.binder.update() self.manager.save() self.refresh() self.context.notify('info', _('Saved'))
class Hosts (SectionPlugin): def init(self): self.title = _('Instance Manager') self.icon = 'sitemap' self.category = _('System') self.append(self.ui.inflate('lxc:main')) def post_item_bind(object, collection, item, ui): ui.find('terminate').on('click', self.on_terminate, item.name) ui.find('start').on('click', self.on_start, item.name) ui.find('stop').on('click', self.on_stop, item.name) ui.find('freeze').on('click', self.on_freeze, item.name) ui.find('unfreeze').on('click', self.on_unfreeze, item.name) ui.find('terminal').on('click', self.on_terminal, item.name) ui.find('backup').on('click', self.on_backup, item.name) ui.find('config').on('click', self.on_config, item.name) ui.find('terminate').visible = not item.params['up'] ui.find('config').visible = not item.params['up'] ui.find('start').visible = not item.params['up'] ui.find('stop').visible = item.params['up'] and not item.params['fz'] ui.find('freeze').visible = not item.params['fz'] and item.params['up'] ui.find('unfreeze').visible = item.params['fz'] and item.params['up'] ui.find('terminal').visible = not item.params['fz'] and item.params['up'] ui.find('backup').visible = item.params['fz'] and item.params['up'] self.find('collection').post_item_bind = post_item_bind self.obj_collection = self.listg() self.binder = Binder(self,self) def on_page_load(self): self.refresh() @on('refresh', 'click') def on_refresh(self): self.refresh() @on('create', 'click') def on_create(self): self.find('input-create').visible = True @on('network', 'click') def on_network(self): self.context.launch('notepad', path='/etc/default/lxc-net') def on_terminate(self, value): self.destroy(value) self.refresh() def on_start(self, value): self.start(value) self.refresh() def on_stop(self, value): self.stop(value) self.refresh() def on_freeze(self, value): self.freeze(value) self.refresh() def on_unfreeze(self, value): self.unfreeze(value) self.refresh() def on_terminal(self, value): cmd = 'lxc-attach -qn {} '.format(value) self.context.launch('terminal',command=cmd) def on_backup(self, value): path = self.backup(value) #self.context.notify('Backup Completed', 'Your backup for {} instance is stored at {}'.format(value,path)) self.context.notify('Backup Completed',path) def on_config(self, value): configpath = '/var/lib/lxc/{}/config'.format(value) self.context.launch('notepad', path=configpath) @on('input-create', 'submit') def on_input_create(self, value): self.create(value) self.refresh() def refresh(self): self.obj_collection = self.listg() self.binder.update() self.binder.populate() return def listg(self): """ List all containers with status (Running, Frozen or Stopped) in a dict Same as lxc-list or lxc-ls --fancy (0.9) """ status_container = {} output_list = [] outcmd = self.run('lxc-ls --fancy | tail -n+3', output=True).splitlines() for line in outcmd: status_container[line.split()[0]] = line.split()[1:] for container in self.ls(): cont_mem = '{} MB'.format(self.memory_usage(container)) if status_container[container][0] == 'RUNNING': output_list.append(CContainer(container, status='Running',ip=status_container[container][1],mem=cont_mem,up=1,fz=0)) elif status_container[container][0] == 'STOPPED': output_list.append(CContainer(container, status='Stopped',ip='-',mem=cont_mem,up=0,fz=1)) elif status_container[container][0] == 'FROZEN': output_list.append(CContainer(container, status='Frozen',ip=status_container[container][1],mem=cont_mem,up=1,fz=1)) return output_list def memory_usage(self,name): """ returns memory usage in MB """ outcmd = self.run('lxc-cgroup -n {} memory.usage_in_bytes'.format(name), output=True) return int(int(outcmd) / 1024 / 1024) def run(self,cmd, output=False): """ To run command easier """ if output: try: out = subprocess.check_output('{}'.format(cmd), shell=True, close_fds=True) except subprocess.CalledProcessError: out = False return out return subprocess.check_call('{}'.format(cmd), shell=True, close_fds=True) # returns 0 for True def ls(self): """ List containers directory """ lxc_dir = self.lxcdir() ct_list = [] try: lsdir = os.listdir(lxc_dir) for i in lsdir: # ensure that we have a valid path and config file if os.path.isdir('{}/{}'.format(lxc_dir, i)) and os.path.isfile(('{}/{}/config'.format(lxc_dir, i))): ct_list.append(i) except OSError: ct_list = [] return sorted(ct_list) def lxcdir(self): return self.run('lxc-config lxc.lxcpath', output=True).strip() def list_status(self): """ List all containers with status (Running, Frozen or Stopped) in a dict """ containers = [] for container in self.ls(): state = self.ct_info(container)['state'] # TODO: figure out why pycharm thinks state is an int containers.append(dict(container=container, state=state.lower())) return containers def ct_info(self, container): if self.exists(container): return 0 output = self.run('lxc-info -qn {}'.format(container), output=True).splitlines() state = {'pid': 0} for val in output: state[val.split(':')[0].lower().strip().replace(" ", "_")] = val.split(':')[1].strip() return state def exists(self, container): """ Check if container exists """ if container in self.ls(): return True return False def create(self, container, template='ubuntu'): """ Create a container (without all options) Default template: Ubuntu """ if self.exists(container): return 0 command = 'lxc-create -n {}'.format(container) command += ' -t {}'.format(template) return self.run(command) def destroy(self,container): """ Destroys a container """ if not self.exists(container): return 0 return self.run('lxc-destroy -n {}'.format(container)) def start(self,container): """ Starts a container """ if not self.exists(container): return 0 return self.run('lxc-start -dn {}'.format(container)) def stop(self,container): """ Stops a container """ if not self.exists(container): return 0 return self.run('lxc-stop -n {}'.format(container)) def freeze(self,container): """ Freezes a container """ if not self.exists(container): return 0 return self.run('lxc-freeze -n {}'.format(container)) def unfreeze(self,container): """ Unfreezes a container """ if not self.exists(container): return 0 return self.run('lxc-unfreeze -n {}'.format(container)) def backup(self, container, destination='/var/lxc-backup/'): """ Backup container with tar to a storage repository (SR). If SR is localy then the path is /var/lxc-backup/ Returns path/filename of the backup instances """ prefix = time.strftime("%Y-%m-%d__%H-%M.tar.gz") filename = '{}/{}-{}'.format(destination, container, prefix) if not self.exists(container): return 0 if not os.path.isdir(destination): self.run('mkdir -p {}'.format(destination)) self.run('tar czf {} -C /var/lib/lxc {}'.format(filename, container)) return filename
class WebsiteEditorPlugin (SectionPlugin): def init(self): self.title = 'Website editor' self.icon = 'globe' self.category = 'Web' self.hidden = True self.manager = VHManager.get() self.binder = Binder(None, self) self.append(self.ui.inflate('vh:main-website')) self.find('domains').new_item = lambda c: WebsiteDomain.create('example.com') self.find('ports').new_item = lambda c: WebsitePort.create(80) def post_location_bind(object, collection, item, ui): ui.find('backend-params').empty() ui.find('backend-params').append(self.ui.inflate('vh:main-backend-params-%s' % item.backend.type)) item.backend.__binder = Binder(item.backend, ui.find('backend-params')) item.backend.__binder.populate() def post_location_update(object, collection, item, ui): item.backend.__binder.update() self.find('locations').post_item_bind = post_location_bind self.find('locations').post_item_update = post_location_update self.find('create-location-type').labels = [] self.find('create-location-type').values = [] for g in sorted(ApplicationGatewayComponent.get_classes(), key=lambda x: x.title): self.find('create-location-type').labels.append(g.title) self.find('create-location-type').values.append(g.id) @intent('v:manage-website') def on_launch(self, website=None): self.activate() self.website = website self.binder.setup(self.website) self.binder.populate() for ext in BaseExtension.get_classes(): ext.selftest() extensions = BaseExtension.get_classes() def create_location(): self.binder.update() t = self.find('create-location-type').value l = WebsiteLocation.create(self.website, template=t) l.backend.type = t self.website.locations.append(l) self.refresh() self.find('create-location').on('click', create_location) # Extensions for tab in list(self.find('tabs').children): if hasattr(tab, '-is-extension'): tab.delete() self.website.extensions = [] for ext in extensions: ext = ext.new(self.ui, self.website, config=self.website.extension_configs.get(ext.classname, None)) ext._ui_container = self.ui.create('tab', children=[ext], title=ext.name) setattr(ext._ui_container, '-is-extension', True) self.website.extensions.append(ext) self.find('tabs').append(ext._ui_container) # Root creator self.find('root-not-created').visible = not os.path.exists(self.website.root) def create_root(): self.binder.update() try: os.mkdir(self.website.root) except: pass subprocess.call(['chown', 'www-data', self.website.root]) self.save() self.find('create-root-directory').on('click', create_root) self.find('set-path').on('click', self.save) # Downloader def download(): url = self.find('download-url').value self.save() tmppath = '/tmp/ajenti-v-download' script = 'wget "%s" -O "%s" ' % (url, tmppath) if url.lower().endswith('.tar.gz') or url.lower().endswith('.tgz'): script += '&& tar xf "%s" -C "%s"' % (tmppath, self.website.root) elif url.lower().endswith('.zip'): script += '&& unzip "%s" -d "%s"' % (tmppath, self.website.root) script += ' && chown www-data -R "%s"' % self.website.root script += ' && find "%s" -type d -exec chmod 775 {} ";"' % self.website.root script += ' && find "%s" -type f -exec chmod 644 {} ";"' % self.website.root def callback(): self.save() self.activate() if os.path.exists(tmppath): os.unlink(tmppath) self.context.notify('info', _('Download complete')) self.context.launch('terminal', command=script, callback=callback) self.find('download').on('click', download) @on('go-back', 'click') def on_go_back(self): WebsitesPlugin.get().activate() @on('destroy', 'click') def on_destroy(self): for ext in self.website.extensions: try: ext.on_destroy() except Exception, e: logging.error(str(e)) self.manager.config.websites.remove(self.website) self.save() self.on_go_back()
class FileManager(SectionPlugin): default_classconfig = {"root": "/"} classconfig_editor = FileManagerConfigEditor classconfig_root = True def init(self): self.title = _("File Manager") self.category = _("Tools") self.icon = "folder-open" self.backend = FMBackend().get() self.append(self.ui.inflate("fm:main")) self.controller = Controller() self.controller.new_tab(self.classconfig["root"]) def post_item_bind(object, collection, item, ui): ui.find("name").on("click", self.on_item_click, object, item) ui.find("edit").on("click", self.edit, item.fullpath) self.find("items").post_item_bind = post_item_bind def post_bc_bind(object, collection, item, ui): ui.find("name").on("click", self.on_bc_click, object, item) self.find("breadcrumbs").post_item_bind = post_bc_bind self.binder = Binder(self.controller, self.find("filemanager")).autodiscover().populate() self.clipboard = [] self.binder_c = Binder(self, self.find("filemanager")).autodiscover().populate() self.tabs = self.find("tabs") self.find("dialog").buttons = [{"id": "save", "text": _("Save")}, {"id": "cancel", "text": _("Cancel")}] def refresh_clipboard(self): self.binder_c.reset().autodiscover().populate() @on("tabs", "switch") def on_tab_switch(self): if self.tabs.active == (len(self.controller.tabs) - 1): self.controller.new_tab(self.classconfig["root"]) self.refresh() @on("close", "click") def on_tab_close(self): if len(self.controller.tabs) > 2: self.controller.tabs.pop(self.tabs.active) self.tabs.active = 0 self.refresh() @on("new-file", "click") def on_new_file(self): open(os.path.join(self.controller.tabs[self.tabs.active].path, "new file"), "w").close() self.refresh() def upload(self, name, file): open(os.path.join(self.controller.tabs[self.tabs.active].path, name), "w").write(file.read()) self.refresh() @on("new-dir", "click") def on_new_directory(self): os.mkdir(os.path.join(self.controller.tabs[self.tabs.active].path, "new directory")) self.refresh() @on("mass-cut", "click") def on_cut(self): l = self._get_checked() for i in l: i.action = "cut" self.clipboard += l self.refresh_clipboard() @on("mass-copy", "click") def on_copy(self): l = self._get_checked() for i in l: i.action = "copy" self.clipboard += l self.refresh_clipboard() @on("mass-delete", "click") def on_delete(self): self.backend.remove(self._get_checked()) self.refresh() @on("paste", "click") def on_paste(self): tab = self.controller.tabs[self.tabs.active] for_move = [] for_copy = [] for i in self.clipboard: if i.action == "cut": for_move.append(i) else: for_copy.append(i) if for_move: self.backend.move(for_move, tab.path) if for_copy: self.backend.copy(for_copy, tab.path) self.clipboard = [] self.refresh_clipboard() self.refresh() def _get_checked(self): self.binder.update() tab = self.controller.tabs[self.tabs.active] r = [] for item in tab.items: if item.checked: r.append(item) item.checked = False self.refresh() return r @on("clear-clipboard", "click") def on_clear_clipboard(self): self.clipboard = [] self.refresh_clipboard() def on_item_click(self, tab, item): path = os.path.join(tab.path, item.name) if not os.path.isdir(path): self.edit(path) if not path.startswith(self.classconfig["root"]): return tab.navigate(path) self.refresh() def edit(self, path): self.find("dialog").visible = True self.item = Item(path) self.item.read() self.binder_d = Binder(self.item, self.find("dialog")).autodiscover().populate() @on("dialog", "button") def on_close_dialog(self, button): self.find("dialog").visible = False if button == "save": self.binder_d.update() self.item.write() self.refresh() def on_bc_click(self, tab, item): if not item.path.startswith(self.classconfig["root"]): return tab.navigate(item.path) self.refresh() def refresh(self): for tab in self.controller.tabs: tab.refresh() self.binder.populate()
class FileManager(SectionPlugin): default_classconfig = {"root": "/", "start": "/"} classconfig_editor = FileManagerConfigEditor classconfig_root = True def init(self): self.title = _("Файлов мениджър") self.category = _("Tools") self.icon = "folder-open" self.backend = FMBackend().get() self.append(self.ui.inflate("fm:main")) self.controller = Controller() def post_item_bind(object, collection, item, ui): ui.find("name").on("click", self.on_item_click, object, item) ui.find("edit").on("click", self.edit, item.fullpath) self.find("items").post_item_bind = post_item_bind def post_bc_bind(object, collection, item, ui): ui.find("name").on("click", self.on_bc_click, object, item) self.find("breadcrumbs").post_item_bind = post_bc_bind self.clipboard = [] self.tabs = self.find("tabs") def on_first_page_load(self): self.new_tab() self.binder = Binder(self.controller, self.find("filemanager")).populate() self.binder_c = Binder(self, self.find("bind-clipboard")).populate() def on_page_load(self): self.refresh() def refresh_clipboard(self): self.binder_c.setup().populate() @on("tabs", "switch") def on_tab_switch(self): if self.tabs.active == (len(self.controller.tabs) - 1): self.new_tab() self.refresh() @intent("fm:browse") def new_tab(self, path=None): dir = path or self.classconfig.get("start", None) or "/" if not dir.startswith(self.classconfig["root"]): dir = self.classconfig["root"] self.controller.new_tab(dir) if not self.active: self.activate() @on("close", "click") def on_tab_close(self): if len(self.controller.tabs) > 2: self.controller.tabs.pop(self.tabs.active) self.tabs.active = 0 self.refresh() @on("new-file", "click") def on_new_file(self): destination = self.controller.tabs[self.tabs.active].path logging.info("[fm] new file in %s" % destination) path = os.path.join(destination, "new file") try: open(path, "w").close() self._chown_new(path) except OSError as e: self.context.notify("error", str(e)) self.refresh() @on("new-dir", "click") def on_new_directory(self): destination = self.controller.tabs[self.tabs.active].path logging.info("[fm] new directory in %s" % destination) path = os.path.join(destination, "new directory") if not os.path.exists(path): try: os.mkdir(path) os.chmod(path, 0755) self._chown_new(path) except OSError as e: self.context.notify("error", str(e)) self.refresh() def _chown_new(self, path): uid = self.classconfig.get("new_owner", "root") or "root" gid = self.classconfig.get("new_group", "root") or "root" try: uid = int(uid) except: uid = pwd.getpwnam(uid)[2] try: gid = int(gid) except: gid = grp.getgrnam(gid)[2] os.chown(path, uid, gid) def upload(self, name, file): destination = self.controller.tabs[self.tabs.active].path logging.info("[fm] uploading %s to %s" % (name, destination)) try: output = open(os.path.join(destination, name), "w") while True: data = file.read(1024 * 1024) if not data: break gevent.sleep(0) output.write(data) output.close() except OSError as e: self.context.notify("error", str(e)) self.refresh() @on("mass-cut", "click") def on_cut(self): l = self._get_checked() for i in l: i.action = "cut" self.clipboard += l self.refresh_clipboard() @on("mass-copy", "click") def on_copy(self): l = self._get_checked() for i in l: i.action = "copy" self.clipboard += l self.refresh_clipboard() @on("mass-delete", "click") def on_delete(self): def callback(task): self.context.notify("info", _("Files deleted")) self.refresh() self.backend.remove(self._get_checked(), cb=callback) @on("paste", "click") def on_paste(self): tab = self.controller.tabs[self.tabs.active] for_move = [] for_copy = [] for i in self.clipboard: if i.action == "cut": for_move.append(i) else: for_copy.append(i) try: if for_move: def callback(task): self.context.notify("info", _("Files moved")) self.refresh() self.backend.move(for_move, tab.path, callback) if for_copy: def callback(task): self.context.notify("info", _("Files copied")) self.refresh() self.backend.copy(for_copy, tab.path, callback) self.clipboard = [] except Exception as e: self.context.notify("error", str(e)) self.refresh_clipboard() @on("select-all", "click") def on_select_all(self): self.binder.update() tab = self.controller.tabs[self.tabs.active] for item in tab.items: item.checked = not item.checked self.binder.populate() self.context.notify("info", _("Selected %i items") % len(tab.items)) def _get_checked(self): self.binder.update() tab = self.controller.tabs[self.tabs.active] r = [] for item in tab.items: if item.checked: r.append(item) item.checked = False self.refresh() return r @on("clear-clipboard", "click") def on_clear_clipboard(self): self.clipboard = [] self.refresh_clipboard() def on_item_click(self, tab, item): path = os.path.join(tab.path, item.name) if not os.path.isdir(path): self.edit(path) if not path.startswith(self.classconfig["root"]): return tab.navigate(path) self.refresh() def edit(self, path): self.find("dialog").visible = True self.item = Item(path) self.item.read() self.binder_d = Binder(self.item, self.find("dialog")).populate() # Unpack u = Unpacker.find(self.item.fullpath.lower()) unpack_btn = self.find("dialog").find("unpack") unpack_btn.visible = u is not None def cb(): self.context.notify("info", _("Unpacked")) self.refresh() def unpack(): u.unpack(self.item.fullpath, cb=cb) logging.info("[fm] unpacking %s" % self.item.fullpath) unpack_btn.on("click", lambda: unpack()) # Edit edit_btn = self.find("dialog").find("edit") if self.item.size > 1024 * 1024 * 5: edit_btn.visible = False def edit(): self.context.launch("notepad", path=self.item.fullpath) edit_btn.on("click", lambda: edit()) @on("dialog", "button") def on_close_dialog(self, button): self.find("dialog").visible = False if button == "save": self.binder_d.update() try: self.item.write() except Exception as e: self.context.notify("error", str(e)) self.refresh() if self.find("chmod-recursive").value: cmd = 'chown -Rv "%s:%s" "%s"; chmod -Rv %o "%s"' % ( self.item.owner, self.item.group, self.item.fullpath, self.item.mode, self.item.fullpath, ) self.context.launch("terminal", command=cmd) logging.info( "[fm] modifying %s: %o %s:%s" % (self.item.fullpath, self.item.mode, self.item.owner, self.item.group) ) def on_bc_click(self, tab, item): if not item.path.startswith(self.classconfig["root"]): return tab.navigate(item.path) self.refresh() def refresh(self, _=None): for tab in self.controller.tabs: tab.refresh() self.binder.populate()
class Firewall(SectionPlugin): def init(self): self.title = "Firewall" self.icon = "fire" self.category = "System" self.append(self.ui.inflate("iptables:main")) self.fw_mgr = FirewallManager.get() self.config = IPTablesConfig(path=self.fw_mgr.config_path) self.binder = Binder(None, self.find("config")) self.find("tables").new_item = lambda c: TableData() self.find("chains").new_item = lambda c: ChainData() self.find("rules").new_item = lambda c: RuleData() self.find("options").new_item = lambda c: OptionData() self.find("options").binding = OptionsBinding self.find("options").filter = lambda i: not i.name in ["j", "jump"] def post_rule_bind(o, c, i, u): u.find("add-option").on("change", self.on_add_option, c, i, u) actions = ["ACCEPT", "DROP", "REJECT", "LOG", "MASQUERADE", "DNAT", "SNAT"] + list( set(itertools.chain.from_iterable([[c.name for c in t.chains] for t in self.config.tree.tables])) ) u.find("action-select").labels = actions u.find("action-select").values = actions action = "" j_option = i.get_option("j", "jump") if j_option: action = j_option.arguments[0].value u.find("action").text = action u.find("action").style = "iptables-action iptables-%s" % action u.find("action-select").value = action def post_rule_update(o, c, i, u): action = u.find("action-select").value j_option = i.get_option("j", "jump") if j_option: j_option.arguments[0].value = action else: o = OptionData.create_destination() o.arguments[0].value = action i.options.append(o) self.find("rules").post_item_bind = post_rule_bind self.find("rules").post_item_update = post_rule_update self.find("add-option").values = self.find("add-option").labels = ["Add option"] + sorted( OptionData.templates.keys() ) def on_page_load(self): if not os.path.exists(self.fw_mgr.config_path): subprocess.call("iptables-save > %s" % self.fw_mgr.config_path, shell=True) self.config.load() self.refresh() def refresh(self): self.binder.reset(self.config.tree).autodiscover().populate() self.find("autostart").text = ("Disable" if self.fw_mgr.get_autostart_state() else "Enable") + " autostart" @on("autostart", "click") def on_autostart_change(self): self.fw_mgr.set_autostart_state(not self.fw_mgr.get_autostart_state()) self.refresh() def on_add_option(self, options, rule, ui): o = OptionData.create(ui.find("add-option").value) ui.find("add-option").value = "" rule.options.append(o) self.binder.populate() @on("save", "click") def save(self): self.binder.update() self.config.save() self.refresh() @on("edit", "click") def raw_edit(self): self.context.launch("notepad", path="/etc/iptables.up.rules") @on("apply", "click") def apply(self): self.save() cmd = "cat /etc/iptables.up.rules | iptables-restore" if subprocess.call(cmd, shell=True) != 0: self.context.launch("terminal", command=cmd) else: self.context.notify("info", "Saved")
class WebsitesPlugin(SectionPlugin): def init(self): self.title = _('Websites') self.icon = 'globe' self.category = 'Web' self.order = 1 self.manager = VHManager.get() if not self.manager.is_configured: from ajenti.plugins.vh import destroyed_configs self.append(self.ui.inflate('vh:not-configured')) self.find('destroyed-configs').text = ', '.join(destroyed_configs) else: self.post_init() @on('initial-enable', 'click') def on_initial_enable(self): self.post_init() self.manager.save() self.refresh() def post_init(self): self.empty() self.append(self.ui.inflate('vh:main')) self.binder = Binder(None, self) def post_ws_bind(object, collection, item, ui): def manage(): self.context.launch('v:manage-website', website=item) ui.find('manage').on('click', manage) self.find('websites').post_item_bind = post_ws_bind self.find( 'websites').filter = lambda ws: self.context.session.identity in [ 'root', ws.owner ] self.binder.setup(self.manager) @on('new-website', 'click') def on_new_website(self): self.binder.update() name = self.find('new-website-name').value self.find('new-website-name').value = '' if not name: name = '_' slug = slugify(name) slugs = [x.slug for x in self.manager.config.websites] while slug in slugs: slug += '_' w = Website.create(name) w.slug = slug w.owner = self.context.session.identity self.manager.config.websites.append(w) self.manager.save() self.binder.populate() def on_page_load(self): self.refresh() def refresh(self): if self.manager.is_configured: self.manager.config.websites = sorted(self.manager.config.websites, key=lambda x: x.name) self.binder.setup().populate() @on('recheck', 'click') def on_recheck(self): self.binder.update() self.context.endpoint.send_progress(_('Testing configuration')) try: self.manager.run_checks() finally: self.context.endpoint.send_progress(None) self.refresh() @on('save', 'click') def save(self): self.context.endpoint.send_progress(_('Saving changes')) self.binder.update() self.manager.save() self.context.endpoint.send_progress(_('Applying changes')) self.manager.update_configuration() self.context.endpoint.send_progress(_('Restarting web services')) self.manager.restart_services() gevent.spawn(self.on_recheck) self.refresh() self.context.notify('info', _('Saved'))
class Firewall (SectionPlugin): platforms = ['centos', 'debian'] def init(self): self.title = _('Firewall') self.icon = 'fire' self.category = _('System') self.append(self.ui.inflate('iptables:main')) self.fw_mgr = FirewallManager.get() self.config = IPTablesConfig(path=self.fw_mgr.config_path_ajenti) self.binder = Binder(None, self.find('config')) self.find('tables').new_item = lambda c: TableData() self.find('chains').new_item = lambda c: ChainData() self.find('rules').new_item = lambda c: RuleData() self.find('options').new_item = lambda c: OptionData() self.find('options').binding = OptionsBinding self.find('options').filter = lambda i: not i.name in ['j', 'jump'] def post_rule_bind(o, c, i, u): u.find('add-option').on('change', self.on_add_option, c, i, u) action = '' j_option = i.get_option('j', 'jump') if j_option: action = j_option.arguments[0].value u.find('action').text = action u.find('action').style = 'iptables-action iptables-%s' % action u.find('action-select').value = action u.find('title').text = i.comment if i.comment else i.summary def post_rule_update(o, c, i, u): action = u.find('action-select').value j_option = i.get_option('j', 'jump') if j_option: j_option.arguments[0].value = action else: if action: o = OptionData.create_destination() o.arguments[0].value = action i.options.append(o) self.find('rules').post_item_bind = post_rule_bind self.find('rules').post_item_update = post_rule_update self.find('add-option').values = self.find('add-option').labels = [_('Add option')] + sorted(OptionData.templates.keys()) def on_page_load(self): if not os.path.exists(self.fw_mgr.config_path_ajenti): if not os.path.exists(self.fw_mgr.config_path): open(self.fw_mgr.config_path, 'w').write(""" *mangle :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] COMMIT *nat :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] COMMIT *filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] COMMIT """) open(self.fw_mgr.config_path_ajenti, 'w').write(open(self.fw_mgr.config_path).read()) self.config.load() self.refresh() @on('load-current', 'click') def on_load_current(self): subprocess.call('iptables-save > %s' % self.fw_mgr.config_path, shell=True) self.config.load() self.refresh() def refresh(self): self.find('autostart').text = (_('Disable') if self.fw_mgr.get_autostart_state() else _('Enable')) + _(' autostart') self.binder.reset(self.config.tree) actions = ['ACCEPT', 'DROP', 'REJECT', 'LOG', 'MASQUERADE', 'DNAT', 'SNAT'] + \ list(set(itertools.chain.from_iterable([[c.name for c in t.chains] for t in self.config.tree.tables]))) self.find('action-select').labels = actions self.find('action-select').values = actions self.find('chain-action-select').labels = actions self.find('chain-action-select').values = actions self.binder.autodiscover().populate() @on('autostart', 'click') def on_autostart_change(self): self.fw_mgr.set_autostart_state(not self.fw_mgr.get_autostart_state()) self.refresh() def on_add_option(self, options, rule, ui): o = OptionData.create(ui.find('add-option').value) ui.find('add-option').value = '' rule.options.append(o) self.binder.populate() @on('save', 'click') def save(self): self.binder.update() for t in self.config.tree.tables: for c in t.chains: for r in c.rules: r.verify() self.config.save() open(self.fw_mgr.config_path, 'w').write( ''.join( l.split('#')[0] + '\n' for l in open(self.fw_mgr.config_path_ajenti).read().splitlines() ) ) self.refresh() self.context.notify('info', _('Saved')) @on('edit', 'click') def raw_edit(self): self.context.launch('notepad', path='/etc/iptables.up.rules') @on('apply', 'click') def apply(self): self.save() cmd = 'cat %s | iptables-restore' % self.fw_mgr.config_path if subprocess.call(cmd, shell=True) != 0: self.context.launch('terminal', command=cmd) else: self.context.notify('info', _('Applied successfully'))
class CSFSection(SectionPlugin): def init(self): self.title = _('CSF Firewall') self.icon = 'fire' self.category = _('System') self.backend = CSFBackend.get() self.append(self.ui.inflate('csf:main')) self.config = CSFConfig(path='/etc/csf/csf.conf') self.list_allow = [] self.list_deny = [] self.list_tempallow = [] self.list_tempban = [] def delete_rule(csf_option, i): self.save() subprocess.call(['csf', csf_option, i.value.split('#')[0]]) self.refresh() self.find('list_allow').on_delete = lambda i, c: delete('-ar', i) self.find('list_deny').on_delete = lambda i, c: delete('-dr', i) self.find('list_tempallow').on_delete = lambda i, c: delete('-tr', i) self.find('list_tempban').on_delete = lambda i, c: delete('-tr', i) def add_rule(csf_option, address): self.save() p = subprocess.Popen(['csf', csf_option, address], stdout=subprocess.PIPE) o, e = p.communicate() self.context.notify('info', o) self.refresh() self.find('list_allow-add').on( 'click', lambda: add_rule('-a', self.find('permanent-lists-add-address').value)) self.find('list_deny-add').on( 'click', lambda: add_rule('-d', self.find('permanent-lists-add-address').value)) self.find('list_tempallow-add').on( 'click', lambda: add_rule('-ta', self.find('temporary-lists-add-address').value)) self.find('list_tempban-add').on( 'click', lambda: add_rule('-td', self.find('temporary-lists-add-address').value)) self.binder = Binder(None, self) self.binder_lists = Binder(self, self.find('lists')) def on_page_load(self): self.refresh() def refresh(self): self.config.load() self.list_allow = self.backend.read_list('allow') self.list_deny = self.backend.read_list('deny') self.list_tempallow = self.backend.read_list('tempallow') self.list_tempban = self.backend.read_list('tempban') self.binder.setup(self.config.tree).populate() self.binder_lists.populate() @on('apply', 'click') def on_apply(self): self.backend.apply() self.context.notify('info', _('Applied')) @on('save', 'click') def save(self): self.binder.update() self.config.save() self.backend.write_list('allow', self.list_allow) self.backend.write_list('deny', self.list_deny) self.backend.write_list('tempallow', self.list_tempallow) self.backend.write_list('tempban', self.list_tempban) self.binder.setup(self.config.tree).populate() self.context.notify('info', _('Saved')) try: self.backend.test_config() self.context.notify('info', _('Self-test OK')) except Exception as e: self.context.notify('error', str(e))
class WebsitesPlugin (SectionPlugin): def init(self): self.title = _('Websites') self.icon = 'globe' self.category = 'Web' self.manager = VHManager.get() if not self.manager.is_configured: from ajenti.plugins.vh import destroyed_configs self.append(self.ui.inflate('vh:not-configured')) self.find('destroyed-configs').text = ', '.join(destroyed_configs) else: self.post_init() @on('initial-enable', 'click') def on_initial_enable(self): self.post_init() self.manager.save() self.refresh() def post_init(self): self.empty() self.append(self.ui.inflate('vh:main')) self.binder = Binder(None, self) def post_ws_bind(object, collection, item, ui): def manage(): self.context.launch('v:manage-website', website=item) ui.find('manage').on('click', manage) self.find('websites').post_item_bind = post_ws_bind self.find('websites').filter = lambda ws: self.context.session.identity in ['root', ws.owner] self.binder.setup(self.manager) @on('new-website', 'click') def on_new_website(self): self.binder.update() name = self.find('new-website-name').value self.find('new-website-name').value = '' if not name: name = '_' slug = slugify(name) slugs = [x.slug for x in self.manager.config.websites] while slug in slugs: slug += '_' w = Website.create(name) w.slug = slug w.owner = self.context.session.identity self.manager.config.websites.append(w) self.manager.save() self.binder.populate() def on_page_load(self): self.refresh() def refresh(self): if self.manager.is_configured: self.binder.setup().populate() @on('recheck', 'click') def on_recheck(self): self.context.endpoint.send_progress(_('Saving changes')) self.binder.update() self.manager.save() self.context.endpoint.send_progress(_('Testing configuration')) self.manager.run_checks() self.refresh() @on('save', 'click') def save(self): self.context.endpoint.send_progress(_('Saving changes')) self.binder.update() self.manager.save() self.context.endpoint.send_progress(_('Applying changes')) self.manager.update_configuration() self.context.endpoint.send_progress(_('Restarting web services')) self.manager.restart_services() self.context.endpoint.send_progress(_('Testing configuration')) self.manager.run_checks() self.refresh() self.context.notify('info', _('Saved'))
class MailPlugin(SectionPlugin): def init(self): self.title = _("Mail") self.icon = "envelope" self.category = "Web" self.manager = MailManager.get() if not self.manager.is_configured: self.append(self.ui.inflate("vh-mail:not-configured")) else: self.post_init() @on("initial-enable", "click") def on_initial_enable(self): self.post_init() self.manager.save() self.refresh() def post_init(self): self.empty() self.append(self.ui.inflate("vh-mail:main")) self.binder = Binder(None, self) def post_mb_bind(object, collection, item, ui): ui.find("size").text = str_fsize(self.manager.get_usage(item)) def post_mb_update(object, collection, item, ui): if ui.find("password").value: item.password = ui.find("password").value self.find("mailboxes").post_item_bind = post_mb_bind self.find("mailboxes").post_item_update = post_mb_update self.find("mailboxes").filter = lambda mb: self.context.session.identity in ["root", mb.owner] self.find("targets").new_item = lambda c: ForwardingTarget.create() self.binder.setup(self.manager.config) def _fetch_new_mailbox_name(self, cls): mb = cls.create() mb.local = self.find("new-mailbox-local").value mb.domain = self.find("new-mailbox-domain").value or self.find("new-mailbox-domain-custom").value if not mb.local: self.context.notify("error", _("Invalid mailbox name")) return if not mb.domain: self.context.notify("error", _("Invalid mailbox domain")) return for existing in self.manager.config.mailboxes: if existing.name == mb.name: self.context.notify("error", _("This address is already taken")) return self.find("new-mailbox-local").value = "" return mb @on("new-mailbox", "click") def on_new_mailbox(self): self.binder.update() mb = self._fetch_new_mailbox_name(Mailbox) if not mb: return mb.owner = self.context.session.identity mb.password = "" self.manager.config.mailboxes.append(mb) self.manager.save() self.binder.populate() @on("new-forwarding-mailbox", "click") def on_new_forwarding_mailbox(self): self.binder.update() mb = self._fetch_new_mailbox_name(ForwardingMailbox) if not mb: return mb.owner = self.context.session.identity self.manager.config.forwarding_mailboxes.append(mb) self.manager.save() self.binder.populate() def on_page_load(self): self.refresh() def refresh(self): if not self.manager.is_configured: return domains = [] for ws in VHManager.get().config.websites: if self.context.session.identity in ["root", ws.owner]: domains += [d.domain for d in ws.domains] domains = sorted(list(set(domains))) if self.find("new-mailbox-domain"): self.find("new-mailbox-domain").labels = domains + [_("Custom domain")] self.find("new-mailbox-domain").values = domains + [None] if self.manager.is_configured: self.binder.unpopulate().populate() if os.path.exists(self.manager.config.dkim_private_key): pubkey = subprocess.check_output(["openssl", "rsa", "-in", self.manager.config.dkim_private_key, "-pubout"]) pubkey = filter(None, pubkey.split("-"))[1].replace("\n", "") dns = '@\t\t\t\t10800 IN TXT "v=spf1 a -all"\n' dns += '_domainkey\t\t10800 IN TXT "o=~; r=postmaster@<domain>"\n' dns += '%s._domainkey\t10800 IN TXT "v=DKIM1; k=rsa; p="%s"\n' % (self.manager.config.dkim_selector, pubkey) dns += '_dmarc\t\t\t10800 IN TXT "v=DMARC1; p=quarantine; sp=r"\n' self.find("dkim-domain-entry").value = dns else: self.find("dkim-domain-entry").value = _("No valid key exists") @on("generate-dkim-key", "click") def on_generate_dkim_key(self): self.binder.update() self.manager.generate_dkim_key() self.binder.populate() self.save() @on("generate-tls-cert", "click") def on_generate_tls_cert(self): self.binder.update() self.manager.generate_tls_cert() self.binder.populate() self.save() @on("save", "click") def save(self): self.binder.update() self.manager.save() self.refresh() self.context.notify("info", _("Saved"))
class WebsitesWebsiteEditorPlugin(SectionPlugin): uses_access_permission_of = WebsitesPlugin def init(self): self.title = 'Website editor' self.icon = 'globe' self.category = 'Web' self.order = 2 self.hidden = True self.manager = VHManager.get() self.binder = Binder(None, self) self.append(self.ui.inflate('vh:main-website')) self.find( 'domains').new_item = lambda c: WebsiteDomain.create('example.com') self.find('ports').new_item = lambda c: WebsitePort.create(80) def post_location_bind(object, collection, item, ui): ui.find('backend-params').empty() ui.find('backend-params').append( self.ui.inflate('vh:main-backend-params-%s' % item.backend.type)) item.backend.__binder = Binder(item.backend, ui.find('backend-params')) item.backend.__binder.populate() def post_location_update(object, collection, item, ui): item.backend.__binder.update() self.find('locations').post_item_bind = post_location_bind self.find('locations').post_item_update = post_location_update self.find('create-location-type').labels = [] self.find('create-location-type').values = [] for g in sorted(ApplicationGatewayComponent.get_classes(), key=lambda x: x.title): self.find('create-location-type').labels.append(g.title) self.find('create-location-type').values.append(g.id) @intent('v:manage-website') def on_launch(self, website=None): self.activate() self.website = website self.binder.setup(self.website) self.binder.populate() for ext in BaseExtension.get_classes(): ext.selftest() extensions = BaseExtension.get_classes() def create_location(): self.binder.update() t = self.find('create-location-type').value l = WebsiteLocation.create(self.website, template=t) l.backend.type = t self.website.locations.append(l) self.refresh() self.find('create-location').on('click', create_location) # Extensions for tab in list(self.find('tabs').children): if hasattr(tab, '-is-extension'): tab.delete() self.website.extensions = [] for ext in extensions: ext = ext.new(self.ui, self.website, config=self.website.extension_configs.get( ext.classname, None)) ext.editor_ui = self ext._ui_container = self.ui.create('tab', children=[ext], title=ext.name) setattr(ext._ui_container, '-is-extension', True) self.website.extensions.append(ext) self.find('tabs').append(ext._ui_container) # Root creator self.find('root-not-created').visible = not os.path.exists( self.website.root) def create_root(): self.binder.update() if not os.path.exists(self.website.root): os.makedirs(self.website.root) subprocess.call(['chown', '-R', 'www-data:', self.website.root]) self.save() self.find('create-root-directory').on('click', create_root) self.find('fix-root-permissions').on('click', create_root) self.find('set-path').on('click', self.save) # Downloader def download(): url = self.find('download-url').value self.save() tmppath = '/tmp/ajenti-v-download' script = 'wget "%s" -O "%s" ' % (url, tmppath) if url.lower().endswith( ('tar', '.tar.gz', '.tgz', '.tar.bz2', '.tbz2')): script += '&& tar xf "%s" -C "%s"' % (tmppath, self.website.root) elif url.lower().endswith('.rar'): script += '&& unrar x "%s" "%s"' % (tmppath, self.website.root) elif url.lower().endswith('.zip'): script += '&& unzip "%s" -d "%s"' % (tmppath, self.website.root) script += ' && chown www-data: -R "%s"' % self.website.root script += ' && find "%s" -type d -exec chmod 775 {} ";"' % self.website.root script += ' && find "%s" -type f -exec chmod 644 {} ";"' % self.website.root def callback(): self.save() self.activate() if os.path.exists(tmppath): os.unlink(tmppath) self.context.notify('info', _('Download complete')) self.context.launch('terminal', command=script, callback=callback) self.find('download').on('click', download) @on('go-back', 'click') def on_go_back(self): WebsitesPlugin.get().activate() @on('destroy', 'click') def on_destroy(self): for ext in self.website.extensions: try: ext.on_destroy() except Exception, e: logging.error(str(e)) self.manager.config.websites.remove(self.website) self.save() self.on_go_back()
class Firewall(SectionPlugin): platforms = ['centos', 'debian', 'arch', 'mageia'] manager_class = FirewallManager def init(self): self.title = _('Firewall') self.icon = 'fire' self.category = _('System') self.append(self.ui.inflate('iptables:main')) self.fw_mgr = self.manager_class.get() self.config = IPTablesConfig(path=self.fw_mgr.config_path_ajenti) self.binder = Binder(None, self.find('config')) self.find('tables').new_item = lambda c: TableData() self.find('chains').new_item = lambda c: ChainData() self.find('rules').new_item = lambda c: RuleData() self.find('options').new_item = lambda c: OptionData() self.find('options').binding = OptionsBinding self.find('options').filter = lambda i: not i.name in ['j', 'jump'] def post_rule_bind(o, c, i, u): u.find('add-option').on('change', self.on_add_option, c, i, u) action = '' j_option = i.get_option('j', 'jump') if j_option: action = j_option.arguments[0].value u.find('action').text = action u.find('action').style = 'iptables-action iptables-%s' % action u.find('action-select').value = action u.find('title').text = i.comment if i.comment else i.summary def post_rule_update(o, c, i, u): action = u.find('action-select').value j_option = i.get_option('j', 'jump') if j_option: j_option.arguments[0].value = action else: if action: o = OptionData.create_destination() o.arguments[0].value = action i.options.append(o) self.find('rules').post_item_bind = post_rule_bind self.find('rules').post_item_update = post_rule_update self.find('add-option').values = self.find('add-option').labels = [ _('Add option') ] + sorted(OptionData.templates.keys()) def on_page_load(self): if not os.path.exists(self.fw_mgr.config_path_ajenti): if not os.path.exists(self.fw_mgr.config_path): open(self.fw_mgr.config_path, 'w').write(""" *mangle :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] COMMIT *nat :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] COMMIT *filter :INPUT DROP [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] -A INPUT -i lo -j ACCEPT -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT -A INPUT -p tcp -m tcp --dport 8000 -j ACCEPT COMMIT """) open(self.fw_mgr.config_path_ajenti, 'w').write(open(self.fw_mgr.config_path).read()) self.config.load() self.refresh() @on('load-current', 'click') def on_load_current(self): subprocess.call( '%s > %s' % (self.fw_mgr.iptables_save_binary, self.fw_mgr.config_path_ajenti), shell=True) self.config.load() self.refresh() def refresh(self): self.find('autostart').text = (_('Disable') if self.fw_mgr.get_autostart_state() else _('Enable')) + _(' autostart') actions = ['ACCEPT', 'DROP', 'REJECT', 'LOG', 'MASQUERADE', 'DNAT', 'SNAT'] + \ list(set(itertools.chain.from_iterable([[c.name for c in t.chains] for t in self.config.tree.tables]))) self.find('action-select').labels = actions self.find('action-select').values = actions self.find('chain-action-select').labels = actions self.find('chain-action-select').values = actions self.binder.setup(self.config.tree).populate() @on('autostart', 'click') def on_autostart_change(self): self.fw_mgr.set_autostart_state(not self.fw_mgr.get_autostart_state()) self.refresh() def on_add_option(self, options, rule, ui): self.binder.update() o = OptionData.create(ui.find('add-option').value) ui.find('add-option').value = '' rule.options.append(o) self.binder.populate() @on('save', 'click') def save(self): self.binder.update() for t in self.config.tree.tables: for c in t.chains: for r in c.rules: r.verify() self.config.save() open(self.fw_mgr.config_path, 'w').write(''.join( l.split('#')[0] + '\n' for l in open(self.fw_mgr.config_path_ajenti).read().splitlines())) self.refresh() self.context.notify('info', _('Saved')) @on('edit', 'click') def raw_edit(self): self.context.launch('notepad', path=self.fw_mgr.config_path_ajenti) @on('apply', 'click') def apply(self): self.save() cmd = 'cat %s | %s' % (self.fw_mgr.config_path, self.fw_mgr.iptables_restore_binary) if subprocess.call(cmd, shell=True) != 0: self.context.launch('terminal', command=cmd) else: self.context.notify('info', _('Applied successfully'))
class FileManager(SectionPlugin): default_classconfig = {'root': '/'} classconfig_editor = FileManagerConfigEditor classconfig_root = True def init(self): self.title = _('File Manager') self.category = _('Tools') self.icon = 'folder-open' self.backend = FMBackend().get() self.append(self.ui.inflate('fm:main')) self.controller = Controller() def post_item_bind(object, collection, item, ui): ui.find('name').on('click', self.on_item_click, object, item) ui.find('edit').on('click', self.edit, item.fullpath) self.find('items').post_item_bind = post_item_bind def post_bc_bind(object, collection, item, ui): ui.find('name').on('click', self.on_bc_click, object, item) self.find('breadcrumbs').post_item_bind = post_bc_bind self.clipboard = [] self.tabs = self.find('tabs') self.find('dialog').buttons = [ { 'id': 'save', 'text': _('Save') }, { 'id': 'cancel', 'text': _('Cancel') }, ] def on_first_page_load(self): self.controller.new_tab(self.classconfig['root']) self.binder = Binder( self.controller, self.find('filemanager')).autodiscover().populate() self.binder_c = Binder( self, self.find('bind-clipboard')).autodiscover().populate() def refresh_clipboard(self): self.binder_c.reset().autodiscover().populate() @on('tabs', 'switch') def on_tab_switch(self): if self.tabs.active == (len(self.controller.tabs) - 1): self.controller.new_tab(self.classconfig['root']) self.refresh() @on('close', 'click') def on_tab_close(self): if len(self.controller.tabs) > 2: self.controller.tabs.pop(self.tabs.active) self.tabs.active = 0 self.refresh() @on('new-file', 'click') def on_new_file(self): open( os.path.join(self.controller.tabs[self.tabs.active].path, 'new file'), 'w').close() self.refresh() def upload(self, name, file): open(os.path.join(self.controller.tabs[self.tabs.active].path, name), 'w').write(file.read()) self.refresh() @on('new-dir', 'click') def on_new_directory(self): os.mkdir( os.path.join(self.controller.tabs[self.tabs.active].path, 'new directory')) self.refresh() @on('mass-cut', 'click') def on_cut(self): l = self._get_checked() for i in l: i.action = 'cut' self.clipboard += l self.refresh_clipboard() @on('mass-copy', 'click') def on_copy(self): l = self._get_checked() for i in l: i.action = 'copy' self.clipboard += l self.refresh_clipboard() @on('mass-delete', 'click') def on_delete(self): self.backend.remove(self._get_checked()) self.refresh() @on('paste', 'click') def on_paste(self): tab = self.controller.tabs[self.tabs.active] for_move = [] for_copy = [] for i in self.clipboard: if i.action == 'cut': for_move.append(i) else: for_copy.append(i) if for_move: self.backend.move(for_move, tab.path) if for_copy: self.backend.copy(for_copy, tab.path) self.clipboard = [] self.refresh_clipboard() self.refresh() def _get_checked(self): self.binder.update() tab = self.controller.tabs[self.tabs.active] r = [] for item in tab.items: if item.checked: r.append(item) item.checked = False self.refresh() return r @on('clear-clipboard', 'click') def on_clear_clipboard(self): self.clipboard = [] self.refresh_clipboard() def on_item_click(self, tab, item): path = os.path.join(tab.path, item.name) if not os.path.isdir(path): self.edit(path) if not path.startswith(self.classconfig['root']): return tab.navigate(path) self.refresh() def edit(self, path): self.find('dialog').visible = True self.item = Item(path) self.item.read() self.binder_d = Binder(self.item, self.find('dialog')).autodiscover().populate() @on('dialog', 'button') def on_close_dialog(self, button): self.find('dialog').visible = False if button == 'save': self.binder_d.update() self.item.write() self.refresh() def on_bc_click(self, tab, item): if not item.path.startswith(self.classconfig['root']): return tab.navigate(item.path) self.refresh() def refresh(self): for tab in self.controller.tabs: tab.refresh() self.binder.populate()
class MailPlugin(SectionPlugin): def init(self): self.title = _('Mail') self.icon = 'envelope' self.category = 'Web' self.manager = MailManager.get() if not self.manager.is_configured: self.append(self.ui.inflate('vh-mail:not-configured')) else: self.post_init() @on('initial-enable', 'click') def on_initial_enable(self): self.post_init() self.manager.save() self.refresh() def post_init(self): self.empty() self.append(self.ui.inflate('vh-mail:main')) self.binder = Binder(None, self) def post_mb_bind(object, collection, item, ui): ui.find('size').text = str_fsize(self.manager.get_usage(item)) def post_mb_update(object, collection, item, ui): if ui.find('password').value: item.password = ui.find('password').value self.find('mailboxes').post_item_bind = post_mb_bind self.find('mailboxes').post_item_update = post_mb_update self.binder.setup(self.manager.config) @on('new-mailbox', 'click') def on_new_mailbox(self): self.binder.update() mb = Mailbox.create() mb.local = self.find('new-mailbox-local').value mb.domain = self.find('new-mailbox-domain').value or self.find( 'new-mailbox-domain-custom').value mb.password = '' self.find('new-mailbox-local').value = '' if not mb.local: self.context.error(_('Invalid mailbox name')) if not mb.domain: self.context.error(_('Invalid mailbox domain')) self.manager.config.mailboxes.append(mb) self.manager.save() self.binder.populate() def on_page_load(self): self.refresh() def refresh(self): domains = [] for ws in VHManager.get().config.websites: domains += [d.domain for d in ws.domains] domains = sorted(list(set(domains))) self.find('new-mailbox-domain').labels = domains + [_('Custom domain')] self.find('new-mailbox-domain').values = domains + [None] if self.manager.is_configured: self.binder.unpopulate().populate() @on('save', 'click') def save(self): self.binder.update() self.manager.save() self.refresh() self.context.notify('info', _('Saved'))
class Configurator(SectionPlugin): def init(self): self.title = 'Configure' self.icon = 'wrench' self.category = '' self.order = 50 self.append(self.ui.inflate('configurator:main')) self.binder = Binder(ajenti.config.tree, self.find('ajenti-config')) self.ccmgr = ClassConfigManager.get() self.classconfig_binding = Binder(self.ccmgr, self.find('classconfigs')) self.classconfig_rows = {} def post_classconfig_bind(object, collection, item, ui): self.classconfig_rows[item] = ui editor = item.classconfig_editor.new(self.ui) ui.find('container').append(editor) binder = DictAutoBinding(item, 'classconfig', editor.find('bind')) binder.populate() def save(): binder.update() item.save_classconfig() self.context.notify('info', 'Saved') ui.find('save').on('click', save) self.find('classconfigs').find( 'classes').post_item_bind = post_classconfig_bind self.find('users').new_item = lambda c: UserData() def post_user_bind(object, collection, item, ui): box = ui.find('permissions') box.empty() for prov in PermissionProvider.get_all(): line = self.ui.create('tab', title=prov.get_name()) box.append(line) for perm in prov.get_permissions(): line.append(self.ui.create('checkbox', id=perm[0], text=perm[1], \ value=(perm[0] in item.permissions))) self.find('users').post_item_bind = post_user_bind def post_user_update(object, collection, item, ui): box = ui.find('permissions') for prov in PermissionProvider.get_all(): for perm in prov.get_permissions(): has = box.find(perm[0]).value if has and not perm[0] in item.permissions: item.permissions.append(perm[0]) if not has and perm[0] in item.permissions: item.permissions.remove(perm[0]) if ui.find('password').value: item.password = ui.find('password').value self.find('users').post_item_update = post_user_update self.refresh() def on_page_load(self): self.refresh() def refresh(self): self.binder.reset().autodiscover().populate() self.ccmgr.reload() self.classconfig_binding.reset().autodiscover().populate() @on('save-button', 'click') @restrict('configurator:configure') def save(self): self.binder.update() for user in ajenti.config.tree.users.values(): if not '|' in user.password: user.password = UserManager.get().hash_password(user.password) self.binder.populate() ajenti.config.save() self.context.notify( 'info', 'Saved. Please restart Ajenti for changes to take effect.') @on('fake-ssl', 'click') def on_gen_ssl(self): host = self.find('fake-ssl-host').value if host == '': self.context.notify('error', 'Please supply hostname') else: self.gen_ssl(host) @on('restart-button', 'click') def on_restart(self): ajenti.restart() @intent('configure-plugin') def configure_plugin(self, plugin=None): self.find('tabs').active = 1 self.refresh() #if plugin in self.classconfig_rows: # self.classconfig_rows[plugin].children[0].expanded = True # print self.classconfig_rows[plugin].children[0] if plugin: self.context.notify( 'info', 'Please configure %s plugin!' % plugin.classconfig_editor.title) self.activate() @intent('setup-fake-ssl') def gen_ssl(self, host): self.save() subprocess.call(['ajenti-ssl-gen', host, '-f']) ajenti.config.load() self.refresh()
class DBPlugin(SectionPlugin): service_name = '' service_buttons = [] has_users = True def init(self): self.append(self.ui.inflate('db_common:main')) self.binder = Binder(None, self) self.find_type('servicebar').buttons = self.service_buttons def delete_db(db, c): self.query_drop(db) self.refresh() self.find('databases').delete_item = delete_db def delete_user(user, c): self.query_drop_user(user) self.refresh() self.find('users').delete_item = delete_user def on_page_load(self): self.refresh() @on('sql-run', 'click') def on_sql_run(self): try: result = self.query_sql( self.find('sql-db').value, self.find('sql-input').value) self.context.notify('info', _('Query finished')) except Exception as e: self.context.notify('error', str(e)) return tbl = self.find('sql-output') tbl.empty() if len(result) > 200: self.context.notify( 'info', _('Output cut from %i rows to 200') % len(result)) result = result[:200] for row in result: erow = self.ui.create('dtr') tbl.append(erow) for cell in row: ecell = self.ui.create('dtd') ecell.append(self.ui.create('label', text=str(cell))) erow.append(ecell) @on('add-db', 'click') def on_add_db(self): self.find('db-name-dialog').value = '' self.find('db-name-dialog').visible = True @on('add-user', 'click') def on_add_user(self): self.find('add-user-dialog').visible = True def refresh(self): self.binder.setup(self).populate() self.databases = [] self.users = [] try: self.databases = self.query_databases() if self.has_users: self.users = self.query_users() except Exception as e: import traceback traceback.print_exc() self.context.notify('error', str(e)) if hasattr(self, 'config_class'): self.context.launch('configure-plugin', plugin=self.config_class.get()) return self.binder.unpopulate() self.find('sql-db').labels = self.find('sql-db').values = [ x.name for x in self.databases ] self.binder.populate() self.find_type('servicebar').reload() @on('db-name-dialog', 'submit') def on_db_name_dialog_submit(self, value=None): try: self.query_create(value) except Exception as e: self.context.notify('error', str(e)) return self.refresh() @on('add-user-dialog', 'button') def on_add_user_dialog(self, button=None): d = self.find('add-user-dialog') d.visible = False if button == 'ok': u = User() u.name = d.find('name').value u.host = d.find('host').value u.password = d.find('password').value try: self.query_create_user(u) except Exception as e: self.context.notify('error', str(e)) return self.refresh() def query_sql(self, db, sql): raise NotImplementedError() def query_databases(self): raise NotImplementedError() def query_drop(self, db): raise NotImplementedError() def query_create(self, name): raise NotImplementedError() def query_users(self): raise NotImplementedError() def query_create_user(self, user): raise NotImplementedError() def query_drop_user(self, user): raise NotImplementedError()
class Configurator (SectionPlugin): def init(self): self.title = _('Configure') self.icon = 'wrench' self.category = '' self.order = 50 self.append(self.ui.inflate('configurator:main')) self.binder = Binder(ajenti.config.tree, self.find('ajenti-config')) self.ccmgr = ClassConfigManager.get() self.classconfig_binding = Binder(self.ccmgr, self.find('classconfigs')) self.classconfig_rows = {} def post_classconfig_bind(object, collection, item, ui): self.classconfig_rows[item] = ui editor = item.classconfig_editor.new(self.ui) ui.find('container').append(editor) binder = DictAutoBinding(item, 'classconfig', editor.find('bind')) binder.populate() def save(): binder.update() item.save_classconfig() self.context.notify('info', _('Saved')) ui.find('save').on('click', save) self.find('classconfigs').find('classes').post_item_bind = post_classconfig_bind self.find('users').new_item = lambda c: UserData() def post_user_bind(object, collection, item, ui): box = ui.find('permissions') box.empty() ui.find('name-edit').visible = item.name != 'root' ui.find('name-label').visible = item.name == 'root' for prov in PermissionProvider.get_all(): line = self.ui.create('tab', title=prov.get_name()) box.append(line) for perm in prov.get_permissions(): line.append( self.ui.create('checkbox', id=perm[0], text=perm[1], value=(perm[0] in item.permissions)) ) self.find('users').post_item_bind = post_user_bind def post_user_update(object, collection, item, ui): box = ui.find('permissions') for prov in PermissionProvider.get_all(): for perm in prov.get_permissions(): has = box.find(perm[0]).value if has and not perm[0] in item.permissions: item.permissions.append(perm[0]) if not has and perm[0] in item.permissions: item.permissions.remove(perm[0]) if ui.find('password').value: item.password = ui.find('password').value self.find('users').post_item_update = post_user_update def on_page_load(self): self.refresh() def refresh(self): self.binder.reset().autodiscover().populate() self.ccmgr.reload() self.classconfig_binding.reset().autodiscover().populate() @on('save-button', 'click') @restrict('configurator:configure') def save(self): self.binder.update() for user in ajenti.config.tree.users.values(): if not '|' in user.password: user.password = UserManager.get().hash_password(user.password) self.binder.populate() ajenti.config.save() self.context.notify('info', _('Saved. Please restart Ajenti for changes to take effect.')) @on('fake-ssl', 'click') def on_gen_ssl(self): host = self.find('fake-ssl-host').value if host == '': self.context.notify('error', _('Please supply hostname')) else: self.gen_ssl(host) @on('restart-button', 'click') def on_restart(self): ajenti.restart() @intent('configure-plugin') def configure_plugin(self, plugin=None): self.find('tabs').active = 1 self.refresh() #if plugin in self.classconfig_rows: # self.classconfig_rows[plugin].children[0].expanded = True # print self.classconfig_rows[plugin].children[0] if plugin: self.context.notify('info', _('Please configure %s plugin!') % plugin.classconfig_editor.title) self.activate() @intent('setup-fake-ssl') def gen_ssl(self, host): self.save() subprocess.call(['ajenti-ssl-gen', host, '-f']) ajenti.config.load() self.refresh()
class SanickioskScreensaverPrefs(SectionPlugin): default_classconfig = { 'enable_browser': False, 'home_url': 'http://*****:*****@on('save', 'click') def save(self): self.binder.update() self.save_classconfig() self.context.notify( 'info', _('Настройките са запаметени. Моля, рестартирайте киоска.')) self.binder.populate() #all_vars = '\n'.join([k + '="' + str(v) + '"' for k,v in self.classconfig.iteritems()]) for k, v in self.classconfig.iteritems(): if k == 'enable_browser': enable_browser = "X-GNOME-Autostart-enabled=%s" % str( v).lower() if k == 'home_url': home_url = v.lower() if enable_browser == "X-GNOME-Autostart-enabled=true": ajenti_config = "/etc/ajenti/config.json" #Disable Video Mode subprocess.call([ 'sed', '-i', r's/\\"enable_videos\\": true/\\"enable_videos\\": false/g', ajenti_config ]) subprocess.call([ 'sed', '-i', r's/X-GNOME-Autostart-enabled=true/X-GNOME-Autostart-enabled=false/g', "/home/kiosk/.config/autostart/2-videos.desktop" ]) #Disable Photo Mode subprocess.call([ 'sed', '-i', r's/\\"photos_enable\\": true/\\"photos_enable\\": false/g', ajenti_config ]) subprocess.call([ 'sed', '-i', r's/X-GNOME-Autostart-enabled=true/X-GNOME-Autostart-enabled=false/g', "/home/kiosk/.config/autostart/2-photos.desktop" ]) cfg = ( "[Desktop Entry]\n" "Type=Application\n" "Exec=chromium-browser --kiosk --no-first-run --disable-infobars --disable-session-crashed-bubble %s\n" "Hidden=false\n" "NoDisplay=false\n" "%s\n" "Name[en_US]=2-browser\n" "Name=2-browser\n" "Comment[en_US]=\n" "Comment=") % (home_url, enable_browser) open('/home/kiosk/.config/autostart/2-browser.desktop', 'w').write(cfg) #save
class Firewall (SectionPlugin): def init(self): self.title = 'Firewall' self.icon = 'fire' self.category = 'System' self.append(self.ui.inflate('iptables:main')) self.fw_mgr = FirewallManager.get() self.config = IPTablesConfig(path=self.fw_mgr.config_path) self.binder = Binder(None, self.find('config')) self.find('tables').new_item = lambda c: TableData() self.find('chains').new_item = lambda c: ChainData() self.find('rules').new_item = lambda c: RuleData() self.find('options').new_item = lambda c: OptionData() self.find('options').binding = OptionsBinding self.find('options').filter = lambda i: not i.name in ['j', 'jump'] def post_rule_bind(o, c, i, u): u.find('add-option').on('change', self.on_add_option, c, i, u) actions = ['ACCEPT', 'DROP', 'REJECT', 'LOG', 'MASQUERADE', 'DNAT', 'SNAT'] + \ list(set(itertools.chain.from_iterable([[c.name for c in t.chains] for t in self.config.tree.tables]))) u.find('action-select').labels = actions u.find('action-select').values = actions action = '' j_option = i.get_option('j', 'jump') if j_option: action = j_option.arguments[0].value u.find('action').text = action u.find('action').style = 'iptables-action iptables-%s' % action u.find('action-select').value = action def post_rule_update(o, c, i, u): action = u.find('action-select').value j_option = i.get_option('j', 'jump') if j_option: j_option.arguments[0].value = action else: o = OptionData.create_destination() o.arguments[0].value = action i.options.append(o) self.find('rules').post_item_bind = post_rule_bind self.find('rules').post_item_update = post_rule_update self.find('add-option').values = self.find('add-option').labels = ['Add option'] + sorted(OptionData.templates.keys()) def on_page_load(self): if not os.path.exists(self.fw_mgr.config_path): subprocess.call('iptables-save > %s' % self.fw_mgr.config_path, shell=True) self.config.load() self.refresh() def refresh(self): self.binder.reset(self.config.tree).autodiscover().populate() self.find('autostart').text = ('Disable' if self.fw_mgr.get_autostart_state() else 'Enable') + ' autostart' @on('autostart', 'click') def on_autostart_change(self): self.fw_mgr.set_autostart_state(not self.fw_mgr.get_autostart_state()) self.refresh() def on_add_option(self, options, rule, ui): o = OptionData.create(ui.find('add-option').value) ui.find('add-option').value = '' rule.options.append(o) self.binder.populate() @on('save', 'click') def save(self): self.binder.update() self.config.save() self.refresh() @on('edit', 'click') def raw_edit(self): self.context.launch('notepad', path='/etc/iptables.up.rules') @on('apply', 'click') def apply(self): self.save() cmd = 'cat /etc/iptables.up.rules | iptables-restore' if subprocess.call(cmd, shell=True) != 0: self.context.launch('terminal', command=cmd) else: self.context.notify('info', 'Saved')
class WebsitesPlugin(SectionPlugin): def init(self): self.title = _('Websites') self.icon = 'globe' self.category = 'Web' self.manager = VHManager.get() if not self.manager.is_configured: from ajenti.plugins.vh import destroyed_configs self.append(self.ui.inflate('vh:not-configured')) self.find('destroyed-configs').text = ', '.join(destroyed_configs) else: self.post_init() @on('initial-enable', 'click') def on_initial_enable(self): self.post_init() self.manager.save() self.refresh() def post_init(self): self.empty() self.append(self.ui.inflate('vh:main')) self.binder = Binder(None, self) def post_ws_bind(object, collection, item, ui): def manage(): self.context.launch('v:manage-website', website=item) ui.find('manage').on('click', manage) self.find('websites').post_item_bind = post_ws_bind self.binder.setup(self.manager.config) @on('new-website', 'click') def on_new_website(self): self.binder.update() name = self.find('new-website-name').value self.find('new-website-name').value = '' if not name: name = '_' slug = slugify(name) slugs = [x.slug for x in self.manager.config.websites] while slug in slugs: slug += '_' w = Website.create(name) w.slug = slug self.manager.config.websites.append(w) self.manager.save() self.binder.populate() def on_page_load(self): self.refresh() def refresh(self): if self.manager.is_configured: self.binder.unpopulate().populate() @on('save', 'click') def save(self): self.binder.update() self.context.endpoint.send_progress(_('Saving changes')) self.manager.save() self.context.endpoint.send_progress(_('Applying changes')) self.manager.update_configuration() self.refresh() self.context.notify('info', _('Saved'))
class Users (SectionPlugin): def init(self): self.title = _('Users') self.icon = 'group' self.category = _('System') self.append(self.ui.inflate('users:main')) def _filterOnlyUsers(x): u = int(x.uid) return u >= 500 def _filterOnlySystemUsers(x): u = int(x.uid) return u < 500 def _sorter(x): g = int(x.gid) if g >= 500: return g - 10000 return g self.find('users').filter = _filterOnlyUsers self.find('system-users').filter = _filterOnlySystemUsers self.find('groups').sorting = _sorter self.config = PasswdConfig(path='/etc/passwd') self.config_g = GroupConfig(path='/etc/group') self.binder = Binder(None, self.find('passwd-config')) self.binder_system = Binder(None, self.find('passwd-config-system')) self.binder_g = Binder(None, self.find('group-config')) self.mgr = UsersBackend.get() def post_item_bind(object, collection, item, ui): ui.find('change-password').on('click', self.change_password, item, ui) ui.find('remove-password').on('click', self.remove_password, item) if not os.path.exists(item.home): ui.find('create-home-dir').on('click', self.create_home_dir, item, ui) ui.find('create-home-dir').visible = True self.find('users').post_item_bind = post_item_bind self.find('system-users').post_item_bind = post_item_bind def on_page_load(self): self.refresh() def refresh(self): self.config.load() self.config_g.load() self.binder.setup(self.config.tree).populate() self.binder_system.setup(self.config.tree).populate() self.binder_g.setup(self.config_g.tree) self.find('group-members').labels = self.find('group-members').values = [x.name for x in self.config.tree.users] self.binder_g.populate() @on('add-user', 'click') def on_add_user(self): self.find('input-username').visible = True @on('input-username', 'submit') def on_add_user_done(self, value): self.mgr.add_user(value) self.refresh() @on('add-group', 'click') def on_add_group(self): self.find('input-groupname').visible = True @on('input-groupname', 'submit') def on_add_group_done(self, value): self.mgr.add_group(value) self.refresh() @on('save-users', 'click') def save_users(self): self.binder.update() self.config.save() @on('save-system-users', 'click') def save_system_users(self): self.binder_system.update() self.config.save() @on('save-groups', 'click') def save_groups(self): self.binder_g.update() self.config_g.save() def create_home_dir(self, user, ui): self.mgr.make_home_dir(user) self.context.notify('info', _('Home dir for %s was created') % user.name) ui.find('create-home-dir').visible = False def change_password(self, user, ui): new_password = ui.find('new-password').value if new_password: try: self.mgr.change_password(user, new_password) self.context.notify('info', _('Password for %s was changed') % user.name) ui.find('new-password').value = '' except Exception as e: self.context.notify('error', _('Error: "%s"') % e.message) else: self.context.notify('error', _('Password shouldn\'t be empty')) def remove_password(self, user): self.mgr.remove_password(user) self.context.notify('info', _('Password for %s was removed') % user.name)
class LetsEncryptPlugin (SectionPlugin): pwd = os.path.join(os.path.dirname(os.path.realpath(__file__)), '') has_domains = False def init(self): self.title = 'LetsEncrypt' # those are not class attributes and can be only set in or after init() self.icon = 'lock' self.category = 'Security' self.append(self.ui.inflate('letsencrypt:main')) self.settings = Settings() self.binder = Binder(self.settings, self) def delete_domain(domain, c): logging.debug('removed domain %s' % domain.name) c.remove(domain) logging.debug("domain info: %s %s" % (domain.subdomains, domain.name)) self.save() def delete_cert(cert, c): logging.debug('removed cert %s' % cert.name) c.remove(cert) logging.debug("cert info: %s %s" % (cert.dir, cert.name)) shutil.rmtree(cert.dir) def on_domain_bind(o, c, domain, u): domain.__old_name = domain.name def on_cert_bind(o, c, cert, u): cert.__old_name = cert.name def new_domain(c): name = 'domain.com' subdomains = 'www.domain.com\nblog.domain.com' return DomainsInfo(self, name, subdomains) self.find('certs').delete_item = delete_cert self.find('certs').post_item_bind = on_cert_bind self.find('domains').new_item = new_domain self.find('domains').delete_item = delete_domain self.find('domains').post_item_bind = on_domain_bind def fix_dependencies(self): if self.settings.dependencies_met is True: return changes = False # Install dehyrated (this is how we communicate with letsencrpyt) if not os.path.isfile(self.settings.basedir + self.settings.scriptname): self.context.notify('info', 'Fixing dehydrated dependency') changes = True if self.git_clone('https://github.com/lukas2511/dehydrated.git', self.settings.basedir): logging.debug("cloning of dehydrated successful") changes = False # Install nginxparser (this is how we edit nginx config files) installed_mods = sorted(["%s==%s" % (i.key, i.version) for i in pip.get_installed_distributions()]) if "nginxparser" not in str(installed_mods): self.context.notify('info', 'Fixing nginxparser dependency') changes = True if self.git_clone('https://github.com/fatiherikli/nginxparser', '~/'): logging.debug("cloning of nginxparser successful.. installing..") command = ['python', 'setup.py', 'install']; p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd='/root/nginxparser/') out, err = p.communicate() installed_mods = sorted(["%s==%s" % (i.key, i.version) for i in pip.get_installed_distributions()]) if "nginxparser" not in str(installed_mods): logging.debug("Something went wrong with installing nginxparser..") else: logging.debug("Installed nginxparser correctly.. importing modules..") from nginxparser import load from nginxparser import dumps changes = False if changes is False: self.settings.dependencies_met = True logging.debug("SUCCESSFULLY MET ALL DEPENDENCIES") def start_thread(self, target, *my_args, **my_keyword_args): thr = threading.Thread(target=target, args=my_args, kwargs=my_keyword_args) thr.daemon = True thr.start() return thr def on_page_load(self): self.refresh() def refresh(self): self.fix_dependencies() filepath = self.settings.basedir + self.settings.domainfile domains_file_read = '' if os.path.isfile(filepath): with open(filepath) as f: domains_file_read = f.readlines() domains_file_read = [x.strip() for x in domains_file_read] domains_file_read = "\n".join(domains_file_read) else: domains_file_read = "" cron = self.check_cron() mysplit = domains_file_read.split("\n") tmp = [] for lines in mysplit: mysecondsplit = lines.split(" ") main_domain = mysecondsplit.pop(0) sub_domains = "" if len(mysecondsplit) >= 1: sub_domains = mysecondsplit tmp.append(DomainsInfo(self, main_domain, "\n".join(sub_domains))) self.settings.domains = tmp self.find('cronjob').value = cron available_domains = self.list_available_certs() available_files = self.list_enabled_nginx_confs() temp_obj = [] if available_domains: for x in available_domains: #expiration date my_date = self.settings.basedir + '/certs/' + x + '/expiration.date' if os.path.isfile(my_date): with open(my_date, 'r') as myfile: my_date=myfile.read().replace('\n', '') else: my_date = "NONE" #cert chain my_ssl_chain = self.settings.basedir + '/certs/' + x + '/chain.pem' if os.path.isfile(my_ssl_chain): with open(my_ssl_chain, 'r') as myfile: my_ssl_chain=myfile.read().replace('\n', '') else: my_ssl_chain = "MISSING" #public key my_ssl_cert = self.settings.basedir + '/certs/' + x + '/cert.pem' if os.path.isfile(my_ssl_cert): with open(my_ssl_cert, 'r') as myfile: my_ssl_cert=myfile.read().replace('\n', '') else: my_ssl_cert = "MISSING" #public key and cert chain my_ssl_fullcert = self.settings.basedir + '/certs/' + x + '/fullchain.pem' if os.path.isfile(my_ssl_fullcert): with open(my_ssl_fullcert, 'r') as myfile: my_ssl_fullcert=myfile.read().replace('\n', '') else: my_ssl_fullcert = "MISSING" #private key my_ssl_key = self.settings.basedir + '/certs/' + x + '/privkey.pem' if os.path.isfile(my_ssl_key): with open(my_ssl_key, 'r') as myfile: my_ssl_key=myfile.read().replace('\n', '') else: my_ssl_key = "MISSING" temp_obj.append(CertificateInfo(self, self.settings.basedir + 'certs/' + x + '/', x, my_date, my_ssl_chain, my_ssl_cert, my_ssl_fullcert, my_ssl_key)) self.settings.certs = temp_obj else: logging.debug("NO DOMAINS AVAILABLE") self.binder.setup(self.settings).populate() def list_enabled_nginx_confs(self): if not os.path.isdir(self.settings.nginx_sites_enabled): return False return [x for x in sorted(os.listdir(self.settings.nginx_sites_enabled)) if os.path.isfile(os.path.join(self.settings.nginx_sites_enabled, x))] def list_available_certs(self): if not os.path.isdir(self.settings.basedir + "certs"): return False return [x for x in sorted(os.listdir(self.settings.basedir + "certs")) if os.path.isdir(os.path.join(self.settings.basedir + "certs", x))] def write_domain_file(self): filepath = self.settings.basedir + self.settings.domainfile if len(self.settings.domains) < 1: self.context.notify('info', 'No domains specified') self.has_domains = False return final_str = '' for domains in self.settings.domains: final_str = final_str + domains.name + " " if domains.subdomains: tmp = domains.subdomains.split("\n") tmp = " ".join(tmp) final_str = final_str + tmp final_str = final_str + "\n" logging.debug("final string %s " % final_str) file = open(filepath, 'w') if file.write(final_str) is None: self.has_domains = True else: self.context.notify('error', 'Domain file write error') file.close() def read_domain_file(self): filepath = self.settings.basedir + self.settings.domainfile if not open(filepath): self.context.notify('error', 'Domain file could not be read') file = open(filepath) with file as f: lines = f.readlines() return lines def create_folders(self): uid = pwd.getpwnam("www-data").pw_uid gid = grp.getgrnam("www-data").gr_gid if not os.path.exists(self.settings.basedir): os.makedirs(self.settings.basedir) os.chown(self.settings.basedir, uid, gid) if not os.path.exists(self.settings.wellknown): os.makedirs(self.settings.wellknown) os.chown(self.settings.wellknown, uid, gid) def create_dehydrated_config_file(self): template = """ BASEDIR=$basedir WELLKNOWN=$wellknown """ dict = { 'basedir': self.settings.basedir, 'wellknown': self.settings.wellknown } filepath = self.settings.basedir + self.settings.configname file = open(filepath, 'w') src = Template( template ) if file.write(src.safe_substitute(dict)) is not None: self.context.notify('info', 'Letsencrypt error') file.close() def cleanup_oldfiles(self): filepath1 = self.settings.nginx_sites_enabled + self.settings.nginx_config filepath2 = self.settings.nginx_sites_available + self.settings.nginx_config filepath3 = self.settings.basedir + self.settings.configname filepath4 = self.settings.basedir + self.settings.domainfile if os.path.isfile(filepath1): os.remove(filepath1) if os.path.isfile(filepath2): os.remove(filepath2) if os.path.isfile(filepath3): os.remove(filepath3) if os.path.isfile(filepath4): os.remove(filepath4) self.context.notify('info', 'removed all local files') def create_nginx_wellknown_config(self): if not self.check_nginx_dir_exists(): self.context.notify('info', 'nginx_custom_dir() is not valid: %s') return False template = """ server { server_name $domains; listen *:80; location $location { alias $alias; } } """ dict = { 'location': '/.well-known/acme-challenge', 'alias': self.settings.wellknown, 'domains': " ".join(self.read_domain_file()) } filepath = self.settings.nginx_sites_available + self.settings.nginx_config filepath2 = self.settings.nginx_sites_enabled + self.settings.nginx_config file = open(filepath, 'w') src = Template( template ) if file.write(src.safe_substitute(dict)) is not None: self.context.notify('info', 'WELLKNOWN config write error') file.close() if os.path.isfile(filepath2): return True os.symlink(filepath, filepath2) def create_cron(self): file = open(self.settings.crontab_dir + self.settings.cronfile, 'w') template = "0 0 1 * * " + self.pwd + 'libs/letsencrypt.sh/letsencrypt.sh -c' if not file.write(template): self.context.notify('info', 'Cron job error') file.close() def remove_cron(self): if os.path.isfile(self.settings.crontab_dir + self.settings.cronfile): if os.remove(self.settings.crontab_dir + self.settings.cronfile): return True else: self.context.notify('info', 'Cron remove error') return False def check_cron(self): if os.path.isfile(self.settings.crontab_dir + self.settings.cronfile): return True return False # Update Nginx config files to use SSL def update_nginx_confs(self): available_domains = self.list_available_certs() filepath = self.settings.basedir + self.settings.domainfile domains = '' if os.path.isfile(filepath): with open(filepath) as f: domains = f.readlines() domains = [x.strip() for x in domains] domains = "\n".join(domains) mysplit = domains.split("\n") # Gets all enabled sites available_nginx_files = self.list_enabled_nginx_confs() for curr_domain in available_domains: for curr_nginx_file in available_nginx_files: logging.debug("## Use Certs: Checking config file - %s ##" % curr_nginx_file) if self.settings.nginx_config in curr_nginx_file: logging.debug("Skipping file: %s" % curr_nginx_file) continue # Path is to the sites_available directory filepath = self.settings.nginx_sites_available + curr_nginx_file if not os.path.isfile(filepath): logging.debug("File not in sites_available directory.. continuing") continue loaded_conf = load(open(filepath)) for servers in loaded_conf: server_conf = servers[1] ssl_true = False listen_position = None server_name_position = None ssl_cert_position = None ssl_cert_key_position = None curr_pos = 0 for lines in server_conf: if isinstance(lines[0], str): if 'server_name' in lines[0] and len(lines) > 1: if server_name_position is not None: logging.debug("There must be more than one server_name_position.. skipping this file because we don't know how to handle it.") print("CONF %s" % loaded_conf) break server_name_position = curr_pos if 'listen' in lines[0] and len(lines) > 1: if listen_position is not None: listen_position = [listen_position] listen_position.append(curr_pos) print("There are multiple listen positions: %s" % listen_position) else: listen_position = curr_pos if 'ssl_certificate' in lines[0] and 'ssl_certificate_key' not in lines[0] and len(lines) > 1: if ssl_cert_position is not None: logging.debug("There must be more than one ssl_certificate.. skipping this file because we don't know how to handle it.") print("CONF %s" % lines[0]) break ssl_cert_position = curr_pos if 'ssl_certificate_key' in lines[0] and len(lines) > 1: if ssl_cert_key_position is not None: logging.debug("There must be more than one ssl_certificate_key.. skipping this file because we don't know how to handle it.") print("CONF %s" % lines[0]) break ssl_cert_key_position = curr_pos curr_pos += 1 if listen_position is not None and server_name_position is not None: logging.debug("We have both listen and server name positions: listen position: %s | server_name_position: %s" % (listen_position, server_name_position)) found_it = False for domain_list_line in mysplit: if found_it is True: break secondsplit = domain_list_line.split(" ") for domains in secondsplit: logging.debug("CHECKING DOMAIN %s with: %s" % (server_conf[server_name_position][1], domains)) if server_conf[server_name_position][1] in domains: logging.debug("found our domain: %s in the list of domains" % server_conf[server_name_position][1]) found_it = True break if found_it is False: logging.debug("Server Config does not have any of our domains with SSL certs in the 'server_name' position.. skipping this config.") continue # Check if SSL is already setup if ssl_cert_key_position is not None and ssl_cert_position is not None: # Check if ports are setup too if isinstance(listen_position, list): already_setup = False for listens in listen_position: if "443" in server_conf[listens][1]: already_setup = True break if already_setup is True: logging.debug("This server is already setup for SSL") continue elif isinstance(listen_position, str): if "443" in server_conf[listens][1]: continue # Lets setup SSL now... logging.debug("Attempting to setup SSL for domain: %s" % curr_domain) # Multiple listen calls.. if isinstance(listen_position, list): already_setup = False for listens in listen_position: if "443" in server_conf[listens][1]: already_setup = True break # Check if ssl port is already set if already_setup is True: logging.debug("Server port already setup for SSL") # 443 not set... set one of the listen calls to 443 else: server_conf[listen_position[0]][1] # ssl port set already if "443 ssl" in server_conf[listen_position[0]][1]: logging.debug("Server port already setup for SSL") # ssl port not set yet.. else: if ":" in server_conf[listen_position[0]][1]: tmp = server_conf[listen_position[0]][1].split(":") if tmp[1]: logging.debug("old port value %s" % server_conf[listen_position[0]][1]) r = re.compile(r"\d{2,5}") tmp[1] = r.sub("443 ssl", tmp[1]) server_conf[listen_position[0]][1] = ':'.join(tmp) logging.debug("new port value: %s" % server_conf[listen_position[0]][1]) else: logging.debug("old port value: %s" % server_conf[listen_position[0]][1]) r = re.compile(r"\d{2,5}") tmp = r.sub("443 ssl", server_conf[listen_position[0]][1]) server_conf[listen_position[0]][1] = tmp logging.debug("new port value: %s" % server_conf[listen_position[0]][1]) # Single listen call.. else: # ssl port set already if "443 ssl" in server_conf[listen_position][1]: logging.debug("Server port already setup for SSL") # ssl port not set yet.. else: if ":" in server_conf[listen_position][1]: tmp = server_conf[listen_position][1].split(":") if tmp[1]: logging.debug("old port value %s" % server_conf[listen_position][1]) r = re.compile(r"\d{2,5}") tmp[1] = r.sub("443 ssl", tmp[1]) server_conf[listen_position][1] = ':'.join(tmp) logging.debug("new port value: %s" % server_conf[listen_position][1]) else: logging.debug("old port value: %s" % server_conf[listen_position][1]) r = re.compile(r"\d{2,5}") tmp = r.sub("443 ssl", server_conf[listen_position][1]) server_conf[listen_position][1] = tmp logging.debug("new port value: %s" % server_conf[listen_position][1]) cert_path = self.settings.basedir + '/certs/' + curr_domain + '/fullchain.pem' cert_key_path = self.settings.basedir + '/certs/' + curr_domain + '/privkey.pem' if ssl_cert_position is None: server_conf.insert(0, ["ssl_certificate", cert_path]) else: server_conf[ssl_cert_position][1] = cert_path if ssl_cert_key_position is None: server_conf.insert(1, ["ssl_certificate_key", cert_key_path]) else: server_conf[ssl_cert_key_position][1] = cert_key_path file = open(filepath,"w") file.write(dumps(loaded_conf)) file.close() logging.debug("## FINISHED WITH SETTING UP NGINX WITH CERTS ##") self.context.notify('info', 'Nginx is now using your valid certs.') def check_nginx_dir_exists(self): if not os.path.exists(self.settings.nginx_sites_available): os.makedirs(self.settings.nginx_sites_available) if not os.path.exists(self.settings.nginx_sites_enabled): os.makedirs(self.settings.nginx_sites_enabled) if os.path.exists(self.settings.nginx_sites_available) and os.path.exists(self.settings.nginx_sites_enabled): return True self.context.notify('error', 'One or more nginx directories is incorrect') def request_certs_helper(self): self.save() time.sleep(5) if not os.path.exists(self.settings.basedir + "accounts"): self.context.notify('error', 'No accounts directory found, Registering before requesting certs..') self.request_certificates(True) if os.path.exists(self.settings.basedir + "accounts"): self.request_certificates(False) def request_certificates(self, register): filepath = self.settings.basedir + self.settings.scriptname params = [filepath, '-c'] out = "" if self.find('renewal').value: params.append('--force') if register: params = [filepath, '--register', '--accept-terms'] self.settings.output = "Disabling currently active SYMLINKS\n" self.binder.setup(self.settings).populate() available_files = self.list_enabled_nginx_confs() for curr_nginx_file in available_files: if self.settings.nginx_config in curr_nginx_file: logging.debug("Skipping file: %s" % curr_nginx_file) continue logging.debug("Disabling SYMLINK: %s" % curr_nginx_file) filepath = self.settings.nginx_sites_enabled + curr_nginx_file os.unlink(filepath) p = subprocess.Popen(params, stdout=subprocess.PIPE, stderr=subprocess.PIPE) success = False while p.poll() is None: output = p.stdout.readline() logging.debug("%s" % output) self.settings.output = output + self.settings.output self.binder.setup(self.settings).populate() if "Creating fullchain.pem" in output or "Skipping renew!" in output: success = True self.settings.output = "Re-enabling SYMLINKS...\n" + self.settings.output self.binder.setup(self.settings).populate() for curr_nginx_file in available_files: if not os.path.isfile(self.settings.nginx_sites_enabled + curr_nginx_file): filepath1 = self.settings.nginx_sites_available + curr_nginx_file filepath2 = self.settings.nginx_sites_enabled + curr_nginx_file os.symlink(filepath1, filepath2) self.settings.output = "Restarting NGINX once more...\n" + self.settings.output self.binder.setup(self.settings).populate() self.restart_nginx() if success is True: self.settings.output = "Success creating certificates! Congratulations!\n" + self.settings.output self.binder.setup(self.settings).populate() else: self.settings.output = "Something went wrong. Read logs below.\n" + self.settings.output self.binder.setup(self.settings).populate() def restart_nginx(self): self.context.notify('info', 'Restarting Nginx') command = ['/usr/sbin/service', 'nginx', 'restart']; p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() def git_clone(self, repo, dir): logging.debug("Cloning REPO: %s into directory: %s" % (repo, dir)) # GIT automatically makes directories command = ['/usr/bin/git', 'clone', repo, dir]; p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() command = ['/usr/bin/git', 'remote', '-v']; p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=dir) out, err = p.communicate() if repo in out: return True else: return False def helper(self): installed_mods = sorted(["%s==%s" % (i.key, i.version) for i in pip.get_installed_distributions()]) if "nginxparser" not in str(installed_mods): if self.git_clone('https://github.com/fatiherikli/nginxparser', '~/'): logging.debug("cloning of nginxparser successful.. installing..") command = ['python', 'setup.py', 'install']; p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd='/root/nginxparser/') out, err = p.communicate() installed_mods = sorted(["%s==%s" % (i.key, i.version) for i in pip.get_installed_distributions()]) if "nginxparser" not in str(installed_mods): logging.debug("Something went wrong with installing nginxparser..") def save(self): self.binder.update() self.binder.populate() self.create_folders() self.write_domain_file() if not self.has_domains: self.cleanup_oldfiles() self.restart_nginx() return self.create_dehydrated_config_file() self.create_nginx_wellknown_config() if self.settings.cronjob: self.create_cron() else: self.remove_cron() self.restart_nginx() @on('done', 'click') def on_output_done(self): self.find('hidden_output').visible = False @on('tabs', 'switch') def tab_switched(self): self.refresh() @on('save', 'click') def save_button(self): self.save() @on('updatenginx', 'click') def updatenginx_button(self): self.update_nginx_confs() self.restart_nginx() @on('helperbutton', 'click') def helper_button(self): self.helper() @on('request', 'click') def request_button(self): self.settings.output = "Saving configuration... Waiting 5 seconds..." self.binder.setup(self.settings).populate() self.find('hidden_output').visible = True self.start_thread(self.request_certs_helper)
class CSFSection (SectionPlugin): def init(self): self.title = _('CSF Firewall') self.icon = 'fire' self.category = _('System') self.backend = CSFBackend.get() self.append(self.ui.inflate('csf:main')) self.config = CSFConfig(path='/etc/csf/csf.conf') self.list_allow = [] self.list_deny = [] self.list_tempallow = [] self.list_tempban = [] def delete_rule(csf_option, i): self.save() subprocess.call(['csf', csf_option, i.value.split('#')[0]]) self.refresh() self.find('list_allow').on_delete = lambda i, c: delete('-ar', i) self.find('list_deny').on_delete = lambda i, c: delete('-dr', i) self.find('list_tempallow').on_delete = lambda i, c: delete('-tr', i) self.find('list_tempban').on_delete = lambda i, c: delete('-tr', i) def add_rule(csf_option, address): self.save() p = subprocess.Popen(['csf', csf_option, address], stdout=subprocess.PIPE) o, e = p.communicate() self.context.notify('info', o) self.refresh() self.find('list_allow-add').on('click', lambda: add_rule('-a', self.find('permanent-lists-add-address').value)) self.find('list_deny-add').on('click', lambda: add_rule('-d', self.find('permanent-lists-add-address').value)) self.find('list_tempallow-add').on('click', lambda: add_rule('-ta', self.find('temporary-lists-add-address').value)) self.find('list_tempban-add').on('click', lambda: add_rule('-td', self.find('temporary-lists-add-address').value)) self.binder = Binder(None, self) self.binder_lists = Binder(self, self.find('lists')) def on_page_load(self): self.refresh() def refresh(self): self.config.load() self.list_allow = self.backend.read_list('allow') self.list_deny = self.backend.read_list('deny') self.list_tempallow = self.backend.read_list('tempallow') self.list_tempban = self.backend.read_list('tempban') self.binder.setup(self.config.tree).populate() self.binder_lists.populate() @on('apply', 'click') def on_apply(self): self.backend.apply() self.context.notify('info', _('Applied')) @on('save', 'click') def save(self): self.binder.update() self.config.save() self.backend.write_list('allow', self.list_allow) self.backend.write_list('deny', self.list_deny) self.backend.write_list('tempallow', self.list_tempallow) self.backend.write_list('tempban', self.list_tempban) self.binder.setup(self.config.tree).populate() self.context.notify('info', _('Saved')) try: self.backend.test_config() self.context.notify('info', _('Self-test OK')) except Exception as e: self.context.notify('error', str(e))
class MailPlugin (SectionPlugin): def init(self): self.title = _('Mail') self.icon = 'envelope' self.category = 'Web' self.manager = MailManager.get() if not self.manager.is_configured: self.append(self.ui.inflate('vh-mail:not-configured')) else: self.post_init() @on('initial-enable', 'click') def on_initial_enable(self): self.post_init() self.manager.save() self.refresh() def post_init(self): self.empty() self.append(self.ui.inflate('vh-mail:main')) self.binder = Binder(None, self) def post_mb_bind(object, collection, item, ui): ui.find('size').text = str_fsize(self.manager.get_usage(item)) def post_mb_update(object, collection, item, ui): if ui.find('password').value: item.password = ui.find('password').value self.find('mailboxes').post_item_bind = post_mb_bind self.find('mailboxes').post_item_update = post_mb_update self.find('mailboxes').filter = lambda mb: self.context.session.identity in ['root', mb.owner] self.binder.setup(self.manager.config) @on('new-mailbox', 'click') def on_new_mailbox(self): self.binder.update() mb = Mailbox.create() mb.local = self.find('new-mailbox-local').value mb.domain = self.find('new-mailbox-domain').value or self.find('new-mailbox-domain-custom').value mb.owner = self.context.session.identity mb.password = '' if not mb.local: self.context.notify('error', _('Invalid mailbox name')) return if not mb.domain: self.context.notify('error', _('Invalid mailbox domain')) return for existing in self.manager.config.mailboxes: if existing.name == mb.name: self.context.notify('error', _('This address is already taken')) return self.find('new-mailbox-local').value = '' self.manager.config.mailboxes.append(mb) self.manager.save() self.binder.populate() def on_page_load(self): self.refresh() def refresh(self): domains = [] for ws in VHManager.get().config.websites: if self.context.session.identity in ['root', ws.owner]: domains += [d.domain for d in ws.domains] domains = sorted(list(set(domains))) if self.find('new-mailbox-domain'): self.find('new-mailbox-domain').labels = domains + [_('Custom domain')] self.find('new-mailbox-domain').values = domains + [None] if self.manager.is_configured: self.binder.unpopulate().populate() @on('save', 'click') def save(self): self.binder.update() self.manager.save() self.refresh() self.context.notify('info', _('Saved'))