#! /usr/bin/env python #-*- coding: utf-8 -*- ########################################################################## # Mucous - a Python/Curses client for Museek # ########################################################################## # # Majority of code (C) 2005-2006 daelstorm # # Based on Museekchat # Copyright (C) 2003-2004 Hyriand # Config-parsing code modified from Nicotine's config.py # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import sys try: from Crypto.Hash import SHA256 except ImportError: try: import mucipher except: print "WARNING: The Mucipher Module for Python wasn't found and neither was PyCrypto. One of these is necessary to allow Mucous to connect to the Museek Daemon.\nDownload mucipher here: http://thegraveyard.org/files/pymucipher-0.0.1.tar.gz\nExtract the tarball, and as Root or sudo, run:\npython setup.py install\nYou'll need GCC, Python and SWIG.\nOr download PyCrypto from here: http://www.amk.ca/python/code/crypto.html" sys.exit() REQUIRE_PYMUSEEK = '0.3.0' def versionCheck(version): build = 255 def _required(): global REQUIRE_PYMUSEEK r = REQUIRE_PYMUSEEK.split(".") r_major, r_minor, r_micro = [int(i) for i in r[:3]] return (r_major << 24) + (r_minor << 16) + (r_micro << 8) + build s = version.split(".") major, minor, micro = [int(i) for i in s[:3]] if (major << 24) + (minor << 16) + (micro << 8) + build >= _required(): return True return False try: import messages, driver except Exception, error: try: import museek from museek import messages, driver if not hasattr(museek, 'VERSION'): raise Exception, "Cannot use unknown museek python bindings version." elif hasattr(museek, 'VERSION') and not versionCheck(museek.VERSION): raise Exception, "Cannot use museek python bindings version: %s" % (museek.VERSION) except Exception, error: print "WARNING: The Museek Message-Parsing modules, messages.py and/or driver.py were not found, or are an old version. Please install them into your '/usr/lib/python2.X/site-packages/museek' directory, or place them in a 'museek' subdirectory of the directory that contains the mucous python script." print error sys.exit() import signal, time, os, commands, getopt, thread, threading, select, string, re from pymucous import ConfigParser import curses.wrapper, curses.ascii import traceback from time import sleep pcg = 0 try: from pymucous.MucousTransfers import Transfers from pymucous.MucousUserInfo import UserInfo from pymucous.MucousFormat import FormatData, SortedDict from pymucous.MucousMuscan import Muscan from pymucous.MucousPrivateChat import PrivateChat from pymucous.MucousPopup import PopupMenu from pymucous.MucousHelp import Help from pymucous.MucousLists import UsersLists from pymucous.MucousInput import CharacterParse from pymucous.MucousNetworking import Networking from pymucous.MucousSearch import Search from pymucous.MucousSetup import Setup from pymucous.MucousRoomsList import RoomsList from pymucous.MucousRecommendations import Recommendations from pymucous.MucousChatRooms import ChatRooms from pymucous.MucousShares import BrowseShares from pymucous.MucousAlerts import Alerts except ImportError, e: print "Failed loading Mucous modules:", e try: geoip_fail=0 import GeoIP gi = GeoIP.new(GeoIP.GEOIP_MEMORY_CACHE) except ImportError: geoip_fail=1 print "Optional Python Module GeoIP not found, you can safely disregard this message." config_dir = str(os.path.expanduser("~/.mucous/")) log_dir = None #str(os.path.expanduser("~/.mucous/logs/")) config_file = config_dir+"config" Version = "0.9.3" ## Command Options def usage(): print ("""Mucous is a client for Museek, the P2P Soulseek Daemon Author: Daelstorm Credit: Hyriand Version: %s Default options: none -c, --config Use a different config file -l, --log Use a different logging directory -v, --version Display version and quit -d, --debug Debug mode -p, --profile Use the python profiler -g, --pycallgraph Use pycallgraph -h, --help Display this help and exit """ %Version) sys.exit(2) try: opts, args = getopt.getopt(sys.argv[1:], "hc:vdl:pg", ["profile", "pycallgraph", "help", "config=", "version", "log=", "debug"]) except getopt.GetoptError: usage() sys.exit(2) profile = 0 debug=False for opts, args in opts: if opts in ("-p", "--profile"): profile = 1 elif opts in ("-g", "--pycallgraph"): pcg = 1 elif opts in ("-d", "--debug"): debug = True elif opts in ("-h", "--help"): usage() sys.exit() if opts in ("-c", "--config"): config_file=str(os.path.expanduser(args)) if opts in ("-l", "--log"): log_dir=str(os.path.expanduser(args)) if opts in ("-v", "--version"): print "Mucous version: %s" % Version sys.exit(2) ## Modify and read the Mucous Config # class ConfigManager: ## Constructor # @param self ConfigManager # @param mucous Mucous (Class) def __init__(self, mucous): ## @var parser # ConfigParser instance self.parser = ConfigParser.ConfigParser() ## @var mucous # Mucous (class) self.mucous = mucous ## Create config dict from config file # @param self ConfigManager def create_config(self): self.parser.read([config_file]) mucous_config_file = file(config_file, 'w') for i in self.mucous.Config.keys(): if not self.parser.has_section(i): self.parser.add_section(i) for j in self.mucous.Config[i].keys(): if j not in ["nonexisting", "hated", "options"]: self.parser.set(i,j, self.mucous.Config[i][j]) else: self.parser.remove_option(i,j) self.parser.write(mucous_config_file) mucous_config_file.close() ## Create config file and parse options # @param self ConfigManager def read_config(self): self.parser.read([config_file]) for i in self.parser.sections(): for j in self.parser.options(i): val = self.parser.get(i,j, raw = 1) if j in ['login','passw','interface', 'roombox', 'tickers_enabled', "ticker_cycle", "ticker_scroll", "scrolltime", "cycletime", 'default_room', "autobuddy", "now-playing", "log_dir", "aliases" "now-playing-prefix", "browse_display_mode", "url reader", "url custom", "transbox", "autoaway", "rooms_sort", "logging", "beep", "auto-clear", "auto-retry", "extra_requests"] : if val != "None": self.mucous.Config[i][j] = val elif i == 'aliases': if val != "None": self.mucous.Config[i][j] = val else: try: self.mucous.Config[i][j] = eval(val, {}) except: self.mucous.Config[i][j] = None ## Write config file to disk # @param self ConfigManager def update_config(self): mucous_config_file = file(config_file, 'w') for i in self.mucous.Config.keys(): if not self.parser.has_section(i): self.parser.add_section(i) for j in self.mucous.Config[i].keys(): if j not in ["somethingwrong"]: self.parser.set(i,j, self.mucous.Config[i][j]) else: self.parser.remove_option(i,j) self.parser.write(mucous_config_file) mucous_config_file.close() self.mucous.Help.Log("status", "Config Saved") ## Check the filesystem for the existance of the config file # Create it if it doesn't exist # Read it if it does exist # @param self ConfigManager def check_path(self): if os.path.exists(config_dir): if os.path.exists(config_file) and os.stat(config_file)[6] > 0: self.read_config() else: self.create_config() else: os.mkdir(config_dir, 0700) self.create_config() ## Main class class Mucous: ## Constructor ## @author daelstorm ## @brief Subclass driver.Driver, load default variables, create interface, attempt connection to museekd # @param self Mucous (class) def __init__(self): ## @var config # config copied from museekd self.config = {} ## @var usernames # Dict of special usernames self.usernames = {"privileges": None } ## @var username # Your username self.username = None ## @var config_dir # Config Directory -- where to save data self.config_dir = config_dir ## @var geoip_fail # Did GeoIP fail to load? (True/False) self.geoip_fail = geoip_fail ## @var Version # Version of program self.Version = Version ## @var gi # GeoIP Instance (Class) if not self.geoip_fail: self.gi = gi else: self.gi = None self.traceback = traceback ## @var Config # default config self.Config = {"connection":{"interface":'localhost:2240', "passw":None}, \ "mucous":{"autobuddy": "no", "roomlistminsize": 5, "rooms_sort": "size", \ "roombox": "big", "log_dir": "~/.mucous/logs/", "now-playing": "default", \ "now-playing-prefix": None, "browse_display_mode": "filesystem", "browse_width": 25, \ "url reader": "firefox", "url custom": "$", \ "transbox" : "split", "language": "iso-8859-1", "beep": "yes", "autoaway": "yes", \ "auto-retry": "yes", "auto-clear": "no", "logging": "yes", "extra_requests": "Yes"}, \ "tickers":{'tickers_enabled': 'yes', "ticker_cycle": "no", "rooms":{}, "ticker_scroll": "yes", "scrolltime": "0.3", "cycletime": "3.0"}, \ "aliases": {"mucous":"Mucous is a Curses client for the Museek Soulseek Daemon. Website: http://thegraveyard.org/daelstorm/mucous.php", "museek":"Museek+ is a Soulseek Daemon/Client. The website is http://museek-plus.org/"},\ "rooms": {"default_room":None}\ } ## @var config_manager # ConfigManager (Class) self.config_manager = ConfigManager(self) self.config_manager.check_path() # Config corrections if log_dir != None and log_dir != "": self.Config["mucous"]["log_dir"] = str(os.path.expanduser(log_dir)) elif "log_dir" in self.Config["mucous"]: if self.Config["mucous"]["log_dir"] in ("", None, "None"): self.Config["mucous"]["log_dir"] = str(os.path.expanduser("~/.mucous/logs/")) if str(self.Config["mucous"]["logging"]) == "True": self.Config["mucous"]["logging"] = "yes" ## @var Spl # Special Variables self.Spl = {"title": None, "status": None, "connected": 0, \ "history_count": 0, "ticker_room": None, "ticker_num": 0, \ "museekconfigfile": ""} ## @var data # data dict (variable storage) self.data = { "mystats": [], } ## @var logs # store lists of data here self.logs = {"tab_completion": [], "search_count": ["Results: ", 0], "history": [""], "onlinestatus": "Offline", } ## @var activeitems # Tab button positions self.activeitems = {"positions": {}} ## @var requests # dict of users with requested data self.requests = {"ip":[], "statistics": []} ## @var mode # which part of Mucous is displayed self.mode = "chat" ## @var user # Dicts of User status and statistics self.user = { "status": {}, "statistics": {} } ## @var windows # Curses window instances self.windows = {"text": {}, "border": {}, "browse": {}, "tab": {} } ## @var dimensions # Window dimensions / coordinates self.dimensions = {} ## @var SortedDict # SortedDict (Class) self.SortedDict = SortedDict ## @var D # Networking (Class) self.D = Networking(self) ## @var Help # Help (Class) self.Help = Help(self) ## @var FormatData # FormatData (Class) self.FormatData = FormatData(self) ## @var PopupMenu # PopupMenu (Class) self.PopupMenu = PopupMenu(self) ## @var Muscan # Muscan (Class) self.Muscan = Muscan(self) ## @var UserInfo # UserInfo (Class) self.UserInfo = UserInfo(self) ## @var ChatRooms # ChatRooms (Class) self.ChatRooms = ChatRooms(self) ## @var PrivateChat # PrivateChat (Class) self.PrivateChat = PrivateChat(self) ## @var BrowseShares # BrowseShares (Class) self.BrowseShares = BrowseShares(self) ## @var RoomsList # RoomsList (Class) self.RoomsList = RoomsList(self) ## @var Recommendations # Recommendations (Class) self.Recommendations = Recommendations(self) ## @var Setup # Setup (Class) self.Setup = Setup(self) ## @var UsersLists # UsersLists (Class) self.UsersLists = UsersLists(self) ## @var Search # Search (Class) self.Search = Search(self) ## @var Alerts # Alerts (Class) self.Alerts = Alerts(self) ## @var Transfers # Transfers (Class) self.Transfers = Transfers(self) ## @var url # A Url collected from chat logs self.url = None ## @var timedout # Away status timed out after a period of user inactivity self.timedout = False ## @var listline # Tab Completion Line (split into a list) self.listline = [] ## @var line # Input Line self.line = "" ## @var edit # CharacterParse (Class) self.edit = CharacterParse(self) ## @var keepit # Keep autocompletion list while tabbing self.keepit = [] ## @var invalidpass # If True, password to museekd was invalid self.invalidpass = False self.subprocess_fail=0 try: import subprocess self.subprocess = subprocess except ImportError: self.subprocess_fail=1 ## @var encodings # List of encodings, used in encodings popup :: Recommended: ISO-8859-1 # UTF-16 AND ISO-8859-12 crash Mucous # UTF-8 is bad, since it's usually the original encoding being converted from self.encodings = ['iso-8859-1', 'iso-8859-2', 'iso-8859-3', 'iso-8859-4', 'iso-8859-5', 'iso-8859-6', 'iso-8859-7', 'iso-8859-8', 'iso-8859-9', 'iso-8859-10', 'iso-8859-11', 'iso-8859-13', 'iso-8859-14', 'iso-8859-15', 'utf-8', 'utf-7', 'ascii'] if "language" in self.Config["mucous"]: if self.Config["mucous"]["language"] not in self.encodings: self.Config["mucous"]["language"] = "iso-8859-1" else: self.Config["mucous"]["language"] = "iso-8859-1" ## @var timers # Dictionary of timers self.timers = {} #transfers :: Calls: ThreadTransfersRetry self.timers["retry"] = threading.Timer(30.0, self.ThreadTransfersRetry) ## @var timers["clear"] # Clear tramsfers timer :: Calls: Mucous.ThreadTransfersClear self.timers["clear"] = threading.Timer(30.0, self.ThreadTransfersClear) ## @var timers["nick"] # Clear tramsfers timer :: Calls: Mucous.ThreadTransfersClear self.timers["nick"] = threading.Timer(10.0, self.ThreadNickCheck) ## @var timeout_time # How long before away timer starts (900 seconds) self.timeout_time = 900 * 1.0 ## @var timers["timeout"] # AutoAway time instance self.timers["timeout"] = threading.Timer(self.timeout_time, self.AwayTimeout) if "ticker_cycle" in self.Config["tickers"].keys(): pass else: self.Config["tickers"]["ticker_cycle"] = "yes" ## @var commandlist # List of /commands (for Tab-completion) self.commandlist = ["/me", "/j", "/join", "/p", "/part", "/l", "/leave", "/talk", "/say", "/alias", "/list", "/users", \ "/cd", "/get", "/getdir", "/nick", "/privs", "/privileges", "/giveprivs",\ "/help", "/info", "/autojoin", "/roombox", "/autoaway", "/transbox", "/roomlist", "/roomlistrefresh", \ "/inrooms", "/pm", "/msg", "/np", "/npset", "/npcheck", "/browsewidth", \ "/npprefix", "/tickroom", "/tickcycle", "/listtick", "/tickers", "/interface", "/password",\ "/save", "/connect", "/disconnect", "/autobuddy", "/autoclear", "/autoretry", "/privbuddy", "/onlybuddy",\ "/slots","/buddy", "/unbuddy", "/ban", "/banlist", "/beep", "/trust", "/distrust", "/unban", "/nuke", "/unnuke",\ "/ignore", "/unignore", "/unhide", "/userinfo", "/ip", "/stat", "/away", "/abortup", "/percent", \ "/abortdown", "/removeup", "/removedown", "/retry", "/retryall", "/clearup", "/cleardown", "/clearroom", "/clearsearchs", "/url", "/urlreader", "/urlcustom",\ "/search", "/searchfor", "/searchbuddy", "/searchroom", "/download", "/downdir", "/browse",\ "/browseuser", "/browsesearch", "/browsedown", "/downuser",\ "/downpath", "/downpathdir", "/chat", "/ignorelist", "/banlist", "/transfer", "/transfers", "/private",\ "/buddylist", "/setup", "/quit", "/logging", "/logdir", "/reloadshares", "/rescanshares", "/version", "/extra", "/exist", \ "/logout", "/login", "/like", "/donotlike", "/donothate", "/hate", "/similar", "/globalrex", "/recommendations", "/rex", "/itemsimilar", "/itemrex", "/uploadto", "/upload", "/ctcpversion", "/defaulttick", "/settemptick", "/settick "] for alias in self.Config["aliases"].keys(): self.commandlist.append("/"+alias) ## @var stdscr # Curses screen Startup self.stdscr = curses.initscr() curses.def_shell_mode() #curses.flushinp() #curses.setupterm() #self.log["help"].append(str(curses.termattrs() ) ) #self.log["help"].append(str(curses.termname() )) curses.meta(1) h, w = self.stdscr.getmaxyx() #h,w = struct.unpack("HHHH", fcntl.ioctl(sys.stdout.fileno(),termios.TIOCGWINSZ, struct.pack("HHHH", 0, 0, 0, 0)))[:2] if h <=7 or w <=37: self.stdscr.keypad(1) curses.echo() curses.endwin() print "Console kinda small, resize it, please" sys.exit() #--------------- curses.start_color() curses.mousemask(curses.ALL_MOUSE_EVENTS) curses.mouseinterval(110) ## @var colors # Dict of color pairs self.colors = {} if curses.has_colors() == True: try: curses.use_default_colors() curses.init_pair(1, curses.COLOR_RED, -1) curses.init_pair(2, curses.COLOR_YELLOW, -1) curses.init_pair(3, curses.COLOR_CYAN, -1) curses.init_pair(4, curses.COLOR_BLUE, -1) curses.init_pair(5, curses.COLOR_GREEN, -1) curses.init_pair(6, curses.COLOR_BLACK, -1) curses.init_pair(7, curses.COLOR_WHITE, -1) curses.init_pair(8, curses.COLOR_MAGENTA, -1) curses.init_pair(9, curses.COLOR_BLACK, curses.COLOR_CYAN) #if curses.can_change_color(): #curses.init_pair(5, 33, -1) #curses.init_pair(10, curses.COLOR_GREEN, curses.COLOR_BLACK ) #curses.init_pair(11, curses.COLOR_YELLOW, curses.COLOR_BLUE) #curses.init_pair(12, curses.COLOR_BLACK, curses.COLOR_WHITE) except AttributeError: curses.init_pair(1, curses.COLOR_RED, 0) curses.init_pair(2, curses.COLOR_YELLOW, 0) curses.init_pair(3, curses.COLOR_CYAN, 0) curses.init_pair(4, curses.COLOR_BLUE, 0) curses.init_pair(5, curses.COLOR_GREEN, 0) curses.init_pair(6, curses.COLOR_BLACK, curses.COLOR_WHITE) curses.init_pair(7, curses.COLOR_WHITE, 0) curses.init_pair(8, curses.COLOR_MAGENTA, 0) curses.init_pair(9, 0, curses.COLOR_CYAN) curses.init_pair(10, curses.COLOR_GREEN, curses.COLOR_BLACK ) curses.init_pair(11, curses.COLOR_YELLOW, curses.COLOR_BLUE) curses.init_pair(12, curses.COLOR_BLACK, curses.COLOR_WHITE) curses.init_pair(13, curses.COLOR_YELLOW, curses.COLOR_CYAN) curses.init_pair(14, curses.COLOR_BLACK, curses.COLOR_GREEN) curses.init_pair(15, curses.COLOR_BLACK, curses.COLOR_RED) curses.init_pair(16, curses.COLOR_WHITE, curses.COLOR_RED) curses.init_pair(17, curses.COLOR_BLACK, curses.COLOR_YELLOW) curses.init_pair(18, curses.COLOR_BLACK, curses.COLOR_MAGENTA) self.colors["cyanyellow"] = curses.color_pair(13) self.colors["blackgreen"] = curses.color_pair(14) self.colors["blackred"] = curses.color_pair(15) self.colors["whitered"] = curses.color_pair(16) self.colors["blackyellow"] = curses.color_pair(17) self.colors["blackmagenta"] = curses.color_pair(18) self.colors["normal"] = curses.color_pair(0) self.colors["red"] = curses.color_pair(1) self.colors["yellow"] = curses.color_pair(2) self.colors["cyan"] = curses.color_pair(3) self.colors["blue"] = curses.color_pair(4) self.colors["green"] = curses.color_pair(5) #self.colors["green"] = curses.color_pair(27) self.colors["black"] = curses.color_pair(6) self.colors["white"] = curses.color_pair(7) self.colors["magenta"] = curses.color_pair(8) self.colors["cybg"] = curses.color_pair(9) self.colors["greenblack"] = curses.color_pair(10) self.colors["hotkey"] = curses.color_pair(11) self.colors["blackwhite"] = curses.color_pair(12) else: self.colors["cyanyellow"] = self.colors["blackgreen"] = self.colors["blackred"] = self.colors["whitered"] = self.colors["blackmagenta"] = self.colors["blackyellow"] = self.colors["normal"] = self.colors["blackwhite"] = self.colors["hotkey"] = self.colors["greenblack"] = self.colors["cybg"] = self.colors["magenta"] = self.colors["white"] =self.colors["black"] = self.colors["cyan"] = self.colors["yellow"] =self.colors["blue"] =self.colors["green"] = self.colors["red"] = curses.color_pair(0) #Disable cursor (bad idea) curses.curs_set(1) # Enable Function Keys self.stdscr.keypad(1) self.Spl["input_horizontal"] = self.Spl["input_vertical"] = 0 self._run = True thread.start_new_thread(self.InputThread, ()) self.run() def run(self): curses.noecho() curses.cbreak() while self._run: try: self.line = self.Build() except Exception, e: self.Help.Log("debug", str(e) ) sleep(0.1) if self.D.socket is None: try: self.D.connect() except select.error, e: # Terminal resized self.line = self.Build() except KeyboardInterrupt, error: # Ctrl-C self.shutdown(error) try: self.D.processWrap() except select.error, e: # Terminal resized self.line = self.Build() except KeyboardInterrupt, error: # Ctrl-C self.shutdown(error) break curses.nocbreak() curses.echo() curses.endwin() def InputThread(self): keys = [] try: while 1: # Place Cursor c = self.GetKey() if c != None: keys.append(c) while keys: # process each key, one at a time c, keys = keys[0], keys[1:] if self.edit.process(c): self.line = self.edit.line feedback = self.edit.InputCommands(self.line) if feedback == 0: break elif feedback == 2: # Exit self.Help.Mode() self._run = False return else: self.edit.reset() sleep(0.001) except Exception, e: self.Help.Mode() self.Help.Log("debug", "Processing... " + str(e)) ## Find the current Cursor Position and get a keypress # @param self Networking (Driver Class) def GetKey(self): #try: # Find Cursor Position y = self.Spl["input_vertical"] x = self.Spl["input_horizontal"] + self.edit.x if self.edit.wrap: ScrollLine = self.edit.scroll/self.edit.w y = self.Spl["input_vertical"] + ScrollLine ScrollRemainder = self.edit.scroll % self.edit.w x = ScrollRemainder + self.Spl["input_horizontal"] try: c = self.stdscr.getkey( y, x) return c except curses.error, e: pass return None ## Create curses input window def UseAnotherEntryBox(self, window=None, height=None, width=None, top=None, left=None, contents=None, wrap=None): if window: self.Spl["input_vertical"] = top self.Spl["input_horizontal"] = left else: if self.edit.win == self.windows["input"]: return w = self.dimensions["input"] window = self.windows["input"] self.Spl["input_vertical"] = w["top"] self.Spl["input_horizontal"] = w["left"] self.edit.line = "" self.edit.SelectEntryBox(window, contents, wrap) ## Create curses input window # @param self Mucous (class) def CreateEntryBox(self): try: # Clean stale windows if "input" in self.windows: del self.windows["input"] if "inputborder" in self.windows: del self.windows["inputborder"] w = self.dimensions["input"] = {"height":1, "width":self.w-2, "top":self.h-3, "left":1} self.Spl["input_vertical"] = w["top"] self.Spl["input_horizontal"] = w["left"] bi = self.windows["inputborder"] = curses.newwin(w["height"]+2, w["width"]+2, w["top"]-1, w["left"]-1) bi.attron(self.colors["blue"] | curses.A_BOLD) bi.border() bi.noutrefresh() bi.attroff(self.colors["blue"] | curses.A_BOLD) self.windows["input"] = bi.subwin(w["height"], w["width"], w["top"], w["left"]) self.windows["input"].attroff(self.colors["blue"] | curses.A_BOLD) except Exception, e: self.Help.Log("debug", "CreateEntryBox: " + str(e)) ## Create curses parent window, get terminal size, draw windows # @param self Mucous (class) # @return line def Build(self): # h, w = struct.unpack("HHHH", fcntl.ioctl(sys.stdout.fileno(),termios.TIOCGWINSZ, struct.pack("HHHH", 0, 0, 0, 0)))[:2] # os.environ["LINES"] = str(h) # os.environ["COLUMNS"] =str(w) try: self.stdscr = curses.initscr() self.stdscr.erase() self.stdscr.refresh() ## @var h # Height of terminal / curses screen ## @var w # Width of terminal / curses screen self.h, self.w = self.stdscr.getmaxyx() self.CreateEntryBox() self.ModeTopbar() self.PopupMenu.show = False except Exception, e: self.Help.Log("debug", "Build: " + str(e)) try: if self.mode == "chat": self.ChatRooms.Mode() elif self.mode == "private": self.PrivateChat.Mode() elif self.mode == "browse": self.BrowseShares.Mode() elif self.mode == "transfer": self.Transfers.ModeTransfers() elif self.mode == "info": self.UserInfo.Mode() elif self.mode == "search": self.Search.Mode() elif self.mode == "lists": self.UsersLists.ModeLists() elif self.mode == "roomlist": self.RoomsList.Mode() elif self.mode == "setup": self.Setup.Default() elif self.mode in ("debug", "help"): self.Help.Mode() except Exception, e: self.Help.Log("debug", "Build part 2: " + str(e)) curses.doupdate() try: self.stdscr.nodelay(1) self.edit.SelectEntryBox(self.windows["input"]) except Exception, e: self.Help.Log("debug", "Build: " + str(e)) return self.line ## Connect to Museekd if not connected # @param self Mucous (Class) def ManuallyConnect(self): try: if self.Spl["connected"] == 0: self.invalidpass = False self.line = "" self.edit.reset() self.D.connect() return 0 else: self.Help.Log("status", "Already connected... aborting connection attempt.") except Exception, e: self.Help.Log("debug", "ManuallyConnect: " + str(e)) ## Close mucous after disconnecting from museekd # @param self Mucous (Class) # @param error message to be displayed in the help log def shutdown(self, error=None): if error is not None: self.Help.Mode() self.Help.Log("status", "Shutting Down Mucous.. " +str(error) ) sleep(1) try: self.timers["timeout"].cancel() self.timers["nick"].cancel() self.ChatRooms.ticker_timer.cancel() self.Muscan.timer.cancel() self.timers["retry"].cancel() self.timers["clear"].cancel() for timer in self.timers: timer.cancel() except Exception, e: self.Help.Log("debug", "shutdown: " + str(e)) try: self.disconnect() # Quit except Exception, e: self.Help.Log("debug", "shutdown: " + str(e)) self.stdscr.keypad(0) curses.nocbreak() curses.echo() curses.endwin() #os._exit(0) ## Disconnect from museekd # @param self Mucous (class) def disconnect(self): try: if self.Spl["connected"] == 1: #driver.Driver.close(self.D) self.D.close() except Exception,e: self.Help.Log("debug", "disconnect: " + str(e)) ## Toggle the logging of chat messages to disk # @param self Mucous (class) def ToggleLogging(self): try: if "logging" in self.Config["mucous"]: if str(self.Config["mucous"]["logging"]) not in ("yes", "no"): self.Config["mucous"]["logging"] = "yes" else: if str(self.Config["mucous"]["logging"]) == "yes": self.Config["mucous"]["logging"] = "no" else: self.Config["mucous"]["logging"] = "yes" else: if str(self.Config["mucous"]["logging"]) == "yes": self.Config["mucous"]["logging"] = "no" else: self.Config["mucous"]["logging"] = "yes" if str(self.Config["mucous"]["logging"]) == "yes": self.Help.Log("status", "Logging Chat is now Enabled.") else: self.Help.Log("status", "Logging Chat is now Disabled.") except Exception,e: self.Help.Log("debug", "ToggleLogging: " + str(e)) ## Toggle alerts making beeps # @param self Mucous (class) def ToggleBeep(self): try: if str(self.Config["mucous"]["beep"]) == "yes": self.Config["mucous"]["beep"] = "no" else: self.Config["mucous"]["beep"] = "yes" if self.mode=="setup": self.Setup.Mode() except Exception, e: self.Help.Log("debug", "ToggleBeep: "+str(e)) ## Toggle Away Status # @param self Mucous (class) def ToggleAwayStatus(self): try: if self.Spl["status"] == 0: self.D.SetStatus(1) elif self.Spl["status"] == 1: self.D.SetStatus(0) except Exception,e: self.Help.Log("debug", "ToggleAwayStatus: " +str( e) ) ## Display Mucous version and away status in the terminal title, if possible # :: May cause display corruption # @param self Mucous (class) def TerminalTitle(self): # Changes Terminal's Title when Away Status changes try: if os.path.expandvars("$SHELL") in ("/bin/bash", "/bin/sh"): if str(curses.termname() ) != "linux": os.system("echo -ne \"\033]0;Mucous %s: %s\007\" " %(self.Version, self.logs["onlinestatus"] )) except Exception,e: self.Help.Log("debug", "TerminalTitle: " +str( e) ) ## Emit pcspeaker style beep, if enabled # @param self Mucous (class) def Beep(self): try: if str(self.Config["mucous"]["beep"]) == "yes": if os.path.expandvars("$SHELL") in ("/bin/bash", "/bin/sh"): os.system("echo -ne \\\\a " ) except Exception,e: self.Help.Log("debug", "beep: " + str(e)) ## Redraw windows if user is in the current mode :: called on peer status updates # @param self Mucous (class) # @param user Username def ModeReload(self, user): if self.mode == "private": if user in self.PrivateChat.logs.keys(): self.PrivateChat.Mode() elif self.mode == "chat": if user in self.ChatRooms.rooms[self.ChatRooms.current]: self.ChatRooms.Mode() elif self.mode == "info": if user in self.UserInfo.users: self.UserInfo.Mode() elif self.mode == "lists": if self.UsersLists.current in self.config.keys(): if user in self.config[self.UsersLists.current].keys(): self.UsersLists.ModeLists() elif self.mode == "browse": if user in self.BrowseShares.users: self.BrowseShares.Mode() elif self.mode == "search": if user in self.Search.tickets.keys(): self.Search.Mode() elif self.mode == "transfers": self.Transfers.ModeTransfers() elif self.mode == "roomlist": self.RoomsList.Mode() elif self.mode == "setup": self.Setup.Default() elif self.mode in ("help", "debug", "status"): self.Help.Mode() ## Add new/replace old keys in Mucous.config # @param self Mucous (class) # @param changetype type of change (Ex: buddy, unignore) # @param username the username of user being modified # @param value comment def ModifyConfig(self, changetype, username, value): try: username = self.dlang(username) if changetype == "buddy": if not self.config.has_key("buddies") or username not in self.config["buddies"].keys(): self.D.ConfigSet("buddies", username, "buddied by mucous") elif changetype == "unbuddy": if self.config.has_key("buddies") and username in self.config["buddies"].keys(): self.D.ConfigRemove("buddies", username) else: self.Help.Log("status", "User not in buddy list: %s" % username) elif changetype == "ban": if not self.config.has_key("banned") or username not in self.config["banned"].keys(): self.D.ConfigSet("banned", username, "banned by mucous") elif changetype == "trusted": if not self.config.has_key("trusted") or username not in self.config["trusted"].keys(): self.D.ConfigSet("trusted", username, "") elif changetype == "unban": if self.config.has_key("banned") and username in self.config["banned"].keys(): self.D.ConfigRemove("banned", username) else: self.Help.Log("status", "User not in ban list: %s" % username) elif changetype == "ignore": if not self.config.has_key("ignored") or username not in self.config["ignored"].keys(): self.D.ConfigSet("ignored", username, "") self.Help.Log("status", "Ignored: %s" % username) elif changetype == "unignore": if self.config.has_key("ignored") and username in self.config["ignored"].keys(): self.D.ConfigRemove("ignored", username) else: self.Help.Log("status", "User not in ignore list: %s" % username) elif changetype == "autojoin": room = username if not self.config.has_key("autojoin") or room not in self.config["autojoin"].keys(): self.D.ConfigSet("autojoin", room, "") else: self.D.ConfigRemove("autojoin", room) elif changetype == "unautojoin": room = username if room in self.config["autojoin"].keys(): self.D.ConfigRemove("autojoin", room) else: self.D.ConfigSet("autojoin", room, "") elif changetype == "trust": if not self.config.has_key("trusted") or username not in self.config["trusted"].keys(): self.D.ConfigSet("trusted", username, "") elif changetype == "distrust": if self.config.has_key("trusted") and username in self.config["trusted"].keys(): self.D.ConfigRemove("trusted", username) except Exception, e: self.Help.Log("debug", "ModifyConfig: " + str(e)) ## Update windows if a config change effects them # @param self Mucous (class) # @param domain which domain the config change was in def ConfigUpdateDisplay(self, domain): try: if domain in ("buddies", "banned", "trusted", "ignored", "autojoin", "trusted"): if self.mode == "lists": self.UsersLists.ModeLists() elif self.mode == "chat" : self.ChatRooms.DrawBox() if self.PopupMenu.show == True: self.PopupMenu.Draw() elif domain in ("interests.like", "interests.hate"): if self.mode == "lists" and self.UsersLists.current == "interests": self.Recommendations.ModeInterests() elif domain in ("clients", "transfers", "server", "interfaces", "interfaces.bind", "userinfo") and self.mode == "setup": self.Setup.Mode() elif domain in ("encoding.users", "encoding" ) and self.mode == "browse": self.BrowseShares.Mode() elif domain in ("encoding.users" ) and self.mode == "private": self.PrivateChat.Mode() elif domain in ("encoding.rooms" ) and self.mode == "chat": self.ChatRooms.Mode() except Exception, e: self.Help.Log("debug", "ConfigUpdateDisplay: " + str(e)) ## Autobuddy a user you're downloading from # :: Valid if self.Config["mucous"]["autobuddy"] is True # @param self Mucous (Class) # @param user Username def AutobuddyUser(self, user): if user == None: return if self.Config["mucous"]["autobuddy"] != "yes": return if not self.config["buddies"].has_key(user): self.D.ConfigSet("buddies", user, "buddied by mucous") self.Help.Log("status", "Auto-Buddied: %s" % user) ## Append list of users in room to status (help mode) log # @param self Mucous (Class) # @param room Room Name def show_nick_list(self, room): try: ## @var colorednicks # display names in status log self.colorednicks = {} if self.ChatRooms.rooms[room] == None: return self.colorednicks[room] = [] alphanicks=[] alphanicks = self.ChatRooms.rooms[room] alphanicks.sort(key=str.lower) self.Help.Log("status", "Users in " + room) for username in alphanicks: if username == self.username: self.colorednicks[room].append([username, "Me"]) elif username not in self.ChatRooms.rooms[room]: self.colorednicks[room].append([username, "Left"]) elif username in self.config["banned"]: self.colorednicks[room].append([username, "Banned"]) elif username in self.config["buddies"]: self.colorednicks[room].append([username, "Buddies" ]) else: self.colorednicks[room].append([username, "Normal"]) self.colorednicks[room].append([" ["+str(self.user["statistics"][username])+"]", "Normal"]) line = username + (" " * (30 - len(username)) ) + "Files: " + str(self.user["statistics"][username][2]) #line = "%s": [%s] Files" % (username, ) self.Help.Log("status", line) if username is not alphanicks[-1]: self.colorednicks[room].append([", ", "NotLast"]) mtype = "List" user = "!!!!" msg = self.colorednicks[room] #self.ChatRooms.AppendChat("List", room, '!!!!', msg) except Exception, e: self.Help.Log("debug", "show_nick_list: " + str(e)) ## Redraw windows for current mode # @param self is Mucous (Class) def refresh_windows(self): try: if self.mode =="transfer": if self.Config["mucous"]["transbox"]=="split": self.Transfers.windows["border"]["uploads"].redrawwin() self.Transfers.windows["border"]["uploads"].refresh() self.Transfers.windows["border"]["downloads"].redrawwin() self.Transfers.windows["border"]["downloads"].refresh() else: if self.Transfers.current == "downloads": self.Transfers.windows["border"]["downloads"].redrawwin() self.Transfers.windows["border"]["downloads"].refresh() elif self.Transfers.current == "uploads": self.Transfers.windows["border"]["uploads"].redrawwin() self.Transfers.windows["border"]["uploads"].refresh() elif self.mode == "lists": self.UsersLists.windows["border"][self.UsersLists.current].redrawwin() self.UsersLists.windows["border"][self.UsersLists.current].refresh() self.UsersLists.windows["text"][self.UsersLists.current].redrawwin() self.UsersLists.windows["text"][self.UsersLists.current].refresh() elif self.mode == "chat": self.ChatRooms.windows["border"]["chat"].redrawwin() self.ChatRooms.windows["border"]["chat"].refresh() self.ChatRooms.windows["text"]["chat"].redrawwin() self.ChatRooms.windows["text"]["chat"].refresh() if self.ChatRooms.shape not in ("chat-only", "nostatuslog"): if "roomstatus" in self.ChatRooms.windows["border"]: self.ChatRooms.windows["border"]["roomstatus"].redrawwin() self.ChatRooms.windows["border"]["roomstatus"].refresh() if "roomstatus" in self.ChatRooms.windows["text"]: self.ChatRooms.windows["text"]["roomstatus"].redrawwin() self.ChatRooms.windows["text"]["roomstatus"].refresh() if self.ChatRooms.shape not in ( "noroombox", "chat-only"): self.ChatRooms.windows["border"]["roombox"].redrawwin() self.ChatRooms.windows["border"]["roombox"].refresh() elif self.mode == "search": #self.BrowseShares.DrawBrowseWin() self.Search.FormatResults(self.Search.current) curses.doupdate() elif self.mode == "browse": self.BrowseShares.DrawBrowseWin() self.BrowseShares.FormatBrowse() curses.doupdate() elif self.mode == "setup": self.Setup.Mode() elif self.mode == "info": self.UserInfo.Mode() else: pass self.windows["inputborder"].redrawwin() self.windows["inputborder"].refresh() self.ModeTopbar() except Exception,e: self.Help.Log("debug", "Refresh Windows: "+str(e)) ## NewPlaying parser for InfoPipe or custom command # @param self is Mucous (Class) def NowPlaying(self): try: m = self.Config["mucous"] if "now-playing" not in m.keys(): return if m["now-playing"] == "default": p = "/tmp/xmms-info" if os.path.exists(p): fsock = open(p) for i in range(3): s = fsock.readline()[8:-1] for i in range(10): e = fsock.readline()[7:-1] if "now-playing-prefix" in m.keys(): if m["now-playing-prefix"] != 'None' and m["now-playing-prefix"] != None: message = ("%s %s") %(m["now-playing-prefix"], e) else: message ="Now %s: %s " % (s, e) else: message ="Now %s: %s " % (s, e) fsock.close() if self.mode == "chat": self.ChatRooms.SayInChat( self.ChatRooms.current, message) elif self.mode == "private": self.PrivateChat.Send(self.PrivateChat.current, message) else: self.Help.Log("status", "WARNING: XMMS or BMP isn't running or the InfoPipe plugin isn't enabled") else: p = m["now-playing"] nowplaying = commands.getoutput(p).split('\n') nowplaying = nowplaying[0] if m["now-playing-prefix"] != None and m["now-playing-prefix"] != 'None': message = "%s %s" % (m["now-playing-prefix"], nowplaying) if self.mode == "chat" and self.ChatRooms.current != None: self.ChatRooms.SayInChat( self.ChatRooms.current, message) elif self.mode == "private" and self.PrivateChat.current != None: self.PrivateChat.Send(self.PrivateChat.current, message ) else: message = nowplaying if self.mode == "chat" and self.ChatRooms.current != None: self.ChatRooms.SayInChat( self.ChatRooms.current, message) elif self.mode == "private" and self.PrivateChat.current != None: self.PrivateChat.Send(self.PrivateChat.current, message ) except Exception, e: self.Help.Log("debug", "NowPlaying " +str(e)) ## Remove control characters and attempt to encoding/decode string # @param self Mucous (class) # @param string the string # @return string def dlang(self, string): try: string1 = string.decode(self.Config["mucous"]["language"], "replace") string1 = string1.encode(self.Config["mucous"]["language"], "replace") string1 = string1.encode(self.Config["mucous"]["language"], "replace") try: z = "" for s in string1: if s not in ("\n", chr(10)) and curses.ascii.isctrl(s): z += curses.ascii.unctrl(s) else: z += s return z except: return string1 except Exception, e: return string ## One attempt at decoding string # @param self Mucous (class) # @param string a string # @return string def dencode_language(self, string): try: string = string.decode(self.Config["mucous"]["language"]).decode(self.Config["mucous"]["language"]).encode(self.Config["mucous"]["language"]) except: pass return string ## One attempt at encoding string # @param self Mucous (class) # @param string a string # @return string def encode_language(self, string): try: string = string.encode(self.Config["mucous"]["language"]) except: pass return string ## Set Input line's title # @param self Mucous (class) # @param title string def SetEditTitle(self, title, selected=True): try: if title != None: self.Spl["title"]= title else: self.Spl["title"] = "Join a room or something." ibw = self.windows["inputborder"] itw = self.windows["input"] ibw.erase() if selected: attribute = self.colors["blue"] | curses.A_BOLD else: attribute = self.colors["normal"] | curses.A_BOLD ibw.attroff(self.colors["blue"] | curses.A_BOLD) ibw.attron(attribute) ibw.border() #ibw.attroff(self.colors["blue"]) if self.Spl["title"]: current = self.dlang(self.Spl["title"]) if selected: attribute = self.colors["cyan"] | curses.A_BOLD else: attribute = self.colors["normal"] | curses.A_BOLD ibw.addstr(0, 2, "< ") ibw.addstr(0, 4, current[:self.w-8], attribute) ibw.addstr(0, 4+len(current[:self.w-8]), " >") itw.redrawwin() itw.noutrefresh() ibw.noutrefresh() except Exception, e: self.Help.Log("debug", "SetEditTitle: " + str(e)) ## Draw Buttons for switching with the mouse to Instructions view # @param self Mucous (class) def DrawInstructionsButtons(self): try: if self.mode == "browse": gi = "Instructions" w = self.BrowseShares.dimensions["browse"] elif self.mode == "info": gi = "Instructions" w = self.UserInfo.dimensions["info"] pos = w["width"]-3-len(gi) if self.mode != "lists": if self.mode == "browse": mw = self.BrowseShares.windows["border"] elif self.mode == "info": mw = self.UserInfo.windows["border"] else: mw = self.windows["border"][ self.mode ] else: mw = self.windows["border"][ self.UsersLists.current ] mw.addstr(0,pos, "< ") mw.addstr(0,pos+2, gi, self.colors["cyan"] | curses.A_BOLD) mw.addstr(0,pos+2+len(gi), " >") vertex = w["height"]+1 if self.mode == "browse": blah = None if "encoding.users" in self.config: if self.BrowseShares.current in self.config["encoding.users"]: blah = self.config["encoding.users"][self.BrowseShares.current] else: blah = self.config["encoding"]["filesystem"] if blah != None: mw.addstr(vertex,w["width"]-17-len(blah)-4, "<" + (" " *( len(blah) +2) )+ ">") mw.addstr(vertex,w["width"]-17-len(blah)-2, blah, self.colors["cyan"] | curses.A_BOLD) mw.addstr(vertex,w["width"]-11, "< ") mw.addstr(vertex,w["width"]-9, "Close ", self.colors["cyan"] | curses.A_BOLD) mw.addstr(vertex,w["width"]-3, ">") elif self.mode == "info": isw = self.UserInfo.windows["statsborder"] isw.addstr(vertex,3, "< ") isw.addstr(vertex,5, "Close ", self.colors["cyan"] | curses.A_BOLD) isw.addstr(vertex,11, ">") isw.noutrefresh() mw.noutrefresh() except Exception, e: self.Help.Log("debug", "DrawInstructionsButtons: " + str(e)) ## Draw tabs from a list of strings # @param self Mucous (class) # @param tab_box_list list of strings # @param selected_tab current tab def DrawTabs(self, tab_box_list, selected_tab, selected=False): try: if tab_box_list == [None]: return lang = self.Config["mucous"]["language"] if "bar" in self.windows["tab"]: del self.windows["tab"]["bar"] tbar = self.windows["tab"]["bar"] = curses.newwin(3, self.w, 1, 0) if selected: attribute = self.colors["cyan"] else: attribute = self.colors["white"] tbar.hline(1, 1, curses.ACS_HLINE, self.w-2, attribute) self.activeitems["positions"]= {} tbar.addstr(1,0, "<", attribute) tbar.addstr(1,self.w-1, ">", attribute) tbar.noutrefresh() if tab_box_list == []: return pos = 1 current = False if self.mode == "search": alpha_list = self.SortedDict() for keyname, keyvalue in self.Search.tickets.items(): alpha_list[keyname] = keyvalue tab_box_list = alpha_list.keys() for string in tab_box_list: if self.mode == "search": sting = self.Search.tickets[string][:13] if string == self.Search.current: current = True else: current = False else: sting = string[:13] if string == selected_tab: current = True else: current = False move = len(sting)+2 sting = self.dlang(sting) self.activeitems["positions"][string] = pos, move+pos if pos + move >= self.w -2: return tb = curses.newwin(3, len(sting)+2, 1, pos) if current == True: tb.attron(self.colors["green"]) tb.border() tb.noutrefresh() tl = tb.subwin(1,len(sting),2,pos+1) try: if self.mode == "search": if current: tl.addstr(sting, self.colors["green"] | curses.A_BOLD) else: tl.addstr(sting, curses.A_BOLD) continue username = string if current: if string in self.user["status"]: attr = curses.A_BOLD if self.user["status"][username] == 1: # Away attr = self.colors["yellow"] | curses.A_BOLD elif self.user["status"][username] == 2: # Online attr = self.colors["green"] | curses.A_BOLD elif self.user["status"][username] == 0: # Offline attr = self.colors["red"] | curses.A_BOLD tl.addstr(sting, attr) else: tl.addstr(sting, self.colors["red"] | curses.A_BOLD) else: if string in self.user["status"]: attr = curses.A_NORMAL if self.user["status"][username] == 1: attr = self.colors["yellow"] elif self.user["status"][username] == 2: attr = self.colors["green"] elif self.user["status"][username] == 0: attr = self.colors["red"] tl.addstr(sting, attr) else: tl.addstr(sting, self.colors["red"]) except: # Always get errors, because text is the same size as window pass pos += len(sting)+2 tl.noutrefresh() # Cleanup stale windows del tl del tb except Exception, e: self.Help.Log("debug", "DrawTabs: " + str(e)) ## Draw the Online Status in Specific colors # @param self Mucous (class) def DrawOnlineStatus(self): try: osw = self.windows["border"]["onlinestatus"] if self.logs["onlinestatus"] == "Away": color = self.colors["blackyellow"] status = " " + self.logs["onlinestatus"] elif self.logs["onlinestatus"] == "Online": color = self.colors["blackgreen"] #color = self.colors["cyanyellow"] status = " " + self.logs["onlinestatus"] elif self.logs["onlinestatus"] == "Offline": color = self.colors["blackred"] status = self.logs["onlinestatus"] elif self.logs["onlinestatus"] == "Closed": color = self.colors["whitered"] status = " " + self.logs["onlinestatus"] else: color = self.colors["blackwhite"] status = self.logs["onlinestatus"] osw.bkgdset(" ", color) osw.erase() osw.addstr(status, color ) osw.refresh() except Exception,e : self.Help.Log("debug", "DrawOnlineStatus: " + str(e)) ## Create and Draw the Topbar # @param self Mucous (class) def ModeTopbar(self): try: # Clean stale windows if "top" in self.windows["border"]: del self.windows["border"]["top"] if "onlinestatus" in self.windows["border"]: del self.windows["border"]["onlinestatus"] if "username" in self.windows["border"]: del self.windows["border"]["username"] tb = self.windows["border"]["top"] = curses.newwin(1, self.w, 0, 0) tb.bkgdset(" ", self.colors["blackwhite"] | curses.A_REVERSE | curses.A_BOLD) tb.idlok(1) tb.erase() tb.noutrefresh() osw = self.windows["border"]["onlinestatus"] = curses.newwin(1, 8, 0, 0) #osw.bkgdset(" ", self.colors["blackwhite"] |curses.A_REVERSE | curses.A_BOLD) osw.idlok(1) self.DrawOnlineStatus() self.Search.Count(None) un = self.windows["border"]["username"] = curses.newwin(1, 16, 0, 9) un.idlok(1) un.bkgdset(" ", self.colors["blackwhite"] | curses.A_REVERSE | curses.A_BOLD) un.erase() if self.username != None: try: un.addstr(self.dlang(self.username[:15]), self.colors["blackwhite"] ) except: pass un.noutrefresh() self.Alerts.Mode() self.Transfers.Status() except Exception,e : self.Help.Log("debug", "topbar mode" + str(e)) ## Create and draw the HotKeyBar (bottom of UI) # @param self Mucous (class) def HotKeyBar(self): try: # Clean stale windows if "bottom" in self.windows["border"]: del self.windows["border"]["bottom"] bb = self.windows["border"]["bottom"] = curses.newwin(1, self.w-1, self.h-1, 0) bb.addstr(" 1", curses.A_BOLD) if self.mode == "chat": if self.Alerts.alert["CHAT"] != {}: nick = 0 for room, status in self.Alerts.alert["CHAT"].items(): if status == "nick": nick = 1 if nick == 1: bb.addstr("Chat", self.colors["red"] |curses.A_BOLD |curses.A_REVERSE ) else: bb.addstr("Chat", self.colors["yellow"] |curses.A_BOLD |curses.A_REVERSE ) else: bb.addstr("Chat", curses.A_REVERSE | self.colors["greenblack"]) else: if self.Alerts.alert["CHAT"] != {}: nick = 0 for room, status in self.Alerts.alert["CHAT"].items(): if status == "nick": nick = 1 if nick == 1: bb.addstr("Chat", self.colors["red"] | curses.A_REVERSE ) else: bb.addstr("Chat", self.colors["yellow"] | curses.A_REVERSE ) else: bb.addstr("Chat", self.colors["cybg"]) bb.addstr(" 2", curses.A_BOLD) if self.mode == "private": if self.Alerts.alert["PRIVATE"] != []: bb.addstr("Private", self.colors["yellow"] |curses.A_BOLD |curses.A_REVERSE ) else: bb.addstr("Private",curses.A_REVERSE | self.colors["greenblack"]) else: if self.Alerts.alert["PRIVATE"] != []: bb.addstr("Private", self.colors["yellow"] |curses.A_REVERSE ) else: bb.addstr("Private", self.colors["cybg"]) bb.addstr(" 3", curses.A_BOLD) if self.mode == "transfer": bb.addstr("Transfers",curses.A_REVERSE | self.colors["greenblack"]) else: bb.addstr("Transfers", self.colors["cybg"]) bb.addstr(" 4", curses.A_BOLD) if self.mode == "search": if self.Alerts.alert["SEARCH"] != []: bb.addstr("Search", self.colors["yellow"] |curses.A_REVERSE |curses.A_BOLD) else: bb.addstr("Search",curses.A_REVERSE | self.colors["greenblack"]) else: if self.Alerts.alert["SEARCH"] != []: bb.addstr("Search", self.colors["yellow"] |curses.A_REVERSE ) else: bb.addstr("Search", self.colors["cybg"]) bb.addstr(" 5", curses.A_BOLD) if self.mode == "info": if self.Alerts.alert["INFO"] != []: bb.addstr("Info", self.colors["yellow"] |curses.A_REVERSE |curses.A_BOLD) else: bb.addstr("Info",curses.A_REVERSE | self.colors["greenblack"]) else: if self.Alerts.alert["INFO"] != []: bb.addstr("Info", self.colors["yellow"] |curses.A_REVERSE ) else: bb.addstr("Info", self.colors["cybg"]) bb.addstr(" 6", curses.A_BOLD) if self.mode == "browse": if self.Alerts.alert["BROWSE"] != []: bb.addstr("Browse", self.colors["yellow"] |curses.A_REVERSE |curses.A_BOLD) else: bb.addstr("Browse",curses.A_REVERSE | self.colors["greenblack"]) else: if self.Alerts.alert["BROWSE"] != []: bb.addstr("Browse", self.colors["yellow"] |curses.A_REVERSE) else: bb.addstr("Browse", self.colors["cybg"]) bb.addstr(" 7", curses.A_BOLD) if self.mode == "lists": bb.addstr("Users",curses.A_REVERSE | self.colors["greenblack"]) else: bb.addstr("Users", self.colors["cybg"]) bb.addstr(" 8", curses.A_BOLD) if self.mode == "roomlist": bb.addstr("Rooms",curses.A_REVERSE | self.colors["greenblack"]) else: bb.addstr("Rooms", self.colors["cybg"]) bb.addstr(" 9", curses.A_BOLD) if self.mode == "setup": bb.addstr("Setup",curses.A_REVERSE | self.colors["greenblack"]) else: bb.addstr("Setup", self.colors["cybg"]) bb.addstr(" 10", curses.A_BOLD) if self.mode in ("debug", "help", "status"): if self.Alerts.alert["HELP"] != []: bb.addstr("Help", self.colors["yellow"] |curses.A_BOLD |curses.A_REVERSE ) else: bb.addstr("Help",curses.A_REVERSE | self.colors["greenblack"]) else: if self.Alerts.alert["HELP"] != []: bb.addstr("Help", self.colors["yellow"] | curses.A_REVERSE ) else: bb.addstr("Help", self.colors["cybg"]) except: pass bb.noutrefresh() ## Save to log # @param self Mucous (class) # @param messagetype Type of message (Ex: private, room) # @param timestamp Timestamp of when message was recieved # @param place User's name for private chat; room name for chat rooms # @param message the message to be logged def FileLog(self, messagetype, timestamp, place, message): try: if '/' in place: place = place.replace("/", "\\") path = os.path.join(os.path.expanduser(self.Config["mucous"]["log_dir"]), messagetype, place) dir = os.path.split(path)[0] try: if not os.path.isdir(dir): os.makedirs(dir) f = open(path, "a") ## replace inline newlines to preserve formatting message.replace("\n","\\n") f.write("%s %s\n" % (timestamp, message)) f.close() except: self.Help.Log("status", "Cannot write to file %s, check permissions" % path) except Exception, e: self.Help.Log("debug", "FileLog: " + str(e)) ## Check if we've recieved a username # @param self Mucous (class) def ThreadNickCheck(self): try: if self.username != None: return self.mode = "status" self.Help.Mode() self.Help.Log("status", "Connection is taking a while to start, maybe you are trying to connect to a FTP daemon?") self.Help.Log("status", "Killing connection..") self.Help.Log("status", "Try using /interface to connect to a different port.") for line in self.Help.log["connect"]: self.Help.Log("status", line) if self.D.socket is not None: self.D.close() except Exception,e: self.Help.Log("debug", "ThreadNickCheck: " + str(e)) ## Automatically Retry failed downloads every 30 seconds # @param self Mucous (class) def ThreadTransfersRetry(self): try: if self.Config["mucous"]["auto-retry"] != "yes": self.timers["retry"].cancel() else: for user_path, transfer in self.Transfers.transfers["downloads"].items(): if int(transfer[3]) in (11, 12, 13, 14): self.D.DownloadFile(transfer[1], transfer[2]) self.timers["retry"].cancel() self.timers["retry"] = threading.Timer(30.0, self.ThreadTransfersRetry) self.timers["retry"].start() except Exception,e: self.Help.Log("debug", "ThreadTransfersRetry: " + str(e)) ## Automatically Retry failed downloads every 30 seconds # @param self Mucous (class) def AwayTimeout(self): try: self.timers["timeout"].cancel() if self.Spl["status"] == 0: self.timedout = True self.ToggleAwayStatus() except Exception,e: self.Help.Log("debug", "AwayTimeout: " + str(e)) ## Automatically Clear failed uploads and finished downloads every 30 seconds # @param self Mucous (class) def ThreadTransfersClear(self): try: if self.Config["mucous"]["auto-clear"] != "yes": self.timers["clear"].cancel() else: for userpath, values in self.Transfers.transfers["uploads"].items(): if values[3] in (0, 10, 11, 12, 13, 14): self.D.TransferRemove(1, values[1], values[2]) for userpath, values in self.Transfers.transfers["downloads"].items(): if values[3] == 0: self.D.TransferRemove(0, values[1], values[2]) self.timers["clear"].cancel() self.timers["clear"] = threading.Timer(30.0, self.ThreadTransfersClear) self.timers["clear"].start() except Exception,e: self.Help.Log("debug", "ThreadTransfersClear: " + str(e)) try: import ctypes # Linux style libc = ctypes.CDLL('libc.so.6') libc.prctl(15, 'mucous', 0, 0, 0) except: pass try: if pcg: import pycallgraph pycallgraph.start_trace() if profile: import hotshot ## @var log # Profiler's log file (Ex: config.profile) log = os.path.expanduser(config_file) + ".profile" ## @var profiler # Hotshot profiler profiler = hotshot.Profile(log) print ("Starting using the profiler (saving log to %s)") % log sleep(1) profiler.runcall(Mucous) else: Mucous() except Exception, e: curses.nocbreak() curses.noecho() curses.endwin() sys.stdout.flush() print e else: e = "" if pcg: pycallgraph.make_dot_graph('calltrace.png') if debug: debug_log = open("mucous_debug.log", 'a') log = [e] tbe = sys.exc_info() ex = "BUG " for line in tbe: #if line is tbe[0]: log.append("TB: %s" % (line) ) tb= traceback.extract_tb(sys.exc_info()[2]) for line in tb: if type(line) is tuple: xline = "" for item in line: xline += str(item) + " " line = xline newline = "" for character in line: if curses.ascii.isctrl(character): character = curses.ascii.unctrl(character) newline += character if line is tb[0]: log.append( "%s%s" % (ex,newline)) else: log.append( "%s%s" % (ex,newline)) for line in log: debug_log.write(str(line)+"\n") debug_log.close() sys.exit()