def LoadSysDefaults(self): "Load system defaults and trim domain name (if present) off of user name." # Get system defaults self.PVars = {} self.ThemeVars = ConfigParser.ConfigParser() Filename = os.path.join(os.getcwd(), "defaults.ini") self.ThemeVars.read(Filename) if len(self.ThemeVars.sections()) < 2: CgiUtil.TermError( "Missing defaults.ini", "Cannot load defaults.ini", "import defaults", "%s<br>%s" % (CgiUtil.FileDetails("Theme settings", Filename), CgiUtil.FileDetails("Defaults settings", Default)), "Download a new copy of tmda-cgi.") # Extract out section "NoOverride" self.NoOverride = {} for Option in self.ThemeVars.options("NoOverride"): self.NoOverride[Option] = self.ThemeVars.get( "NoOverride", Option, 1) # Trim out domain name from user name os.environ["LOGIN"] = self.Vars["User"] Match = re.search(self[("NoOverride", "UserSplit")], self.Vars["User"]) if Match: os.environ["USER"] = Match.group(1) os.environ["LOGNAME"] = Match.group(1) else: os.environ["USER"] = self.Vars["User"] os.environ["LOGNAME"] = self.Vars["User"] # Clean up self.CleanUp()
def CreateTgz(Archive, Filelist): """Create a .tgz of the listed files, but take care with ../ files. Return 0 on success, error code on error.""" if not Util.CanWrite(os.environ["HOME"], os.geteuid(), os.getegid(), 0): CgiUtil.TermError("Can't write.", "No write permissions.", "create backup", CgiUtil.FileDetails("home directory", os.environ["HOME"]), "Check file permissions in home directory.") Files = [] Parents = [] for Filename in Filelist: SrcFn = os.path.join(os.environ["HOME"], Filename) if os.path.isfile(SrcFn): if (Filename[:len(Dict["Parent"])] == Dict["Parent"]) and \ (Filename[:len(Dict["Home"])] != Dict["Home"]): try: os.mkdir(os.path.join(os.environ["HOME"], "%(Parent)s")) except OSError: pass NewFilename = "%(Parent)s" + Filename[len(Dict["Parent"]):] DstFn = os.path.join(os.environ["HOME"], NewFilename) Parents.append((SrcFn, DstFn)) os.rename(SrcFn, DstFn) Files.append(NewFilename) else: Files.append(Filename) TarCmd = [PVars[("NoOverride", "WhichTar")], "-C", os.environ["HOME"], "-czf", Archive] + Files try: Util.RunTask(TarCmd) except OSError, ( eno, estr ): CgiUtil.TermError("CreateTgz failed.", "Error: %s (%d)" % (estr, eno), "create backup", " ".join(TarCmd), "Check file permissions in home directory.")
def IgnoreFiles(Anomalies): "Generate a list of files we should ignore during file installation." # Peek at the MAIL_TRANSFER_AGENT to help in generating the file list. Mode = os.environ["TMDA_CGI_MODE"] os.environ["TMDA_CGI_MODE"] = "no-su" Mail = ReimportDefaults([], "")["MAIL_TRANSFER_AGENT"] os.environ["TMDA_CGI_MODE"] = Mode RetVal = [] # To generate the ignore list, add everything and then remove the ones we do # want to install. # Add everything if type(Anomalies["VIRTUAL_ONLY"]) == DictType: for i in Anomalies["VIRTUAL_ONLY"].keys(): RetVal += Anomalies["VIRTUAL_ONLY"][i] else: RetVal += Anomalies["VIRTUAL_ONLY"] if type(Anomalies["REAL_ONLY"]) == DictType: for i in Anomalies["REAL_ONLY"].keys(): RetVal += Anomalies["REAL_ONLY"][i] else: RetVal += Anomalies["REAL_ONLY"] # Remove correct files if re.search(Anomalies["VIRTUAL_TEST"], PVars["HOME"]): Dict["VirtUser"] = 1 if type(Anomalies["VIRTUAL_ONLY"]) == DictType: if Anomalies["VIRTUAL_ONLY"].has_key(Mail): ListDiff(RetVal, Anomalies["VIRTUAL_ONLY"][Mail]) else: CgiUtil.TermError("Unknown mailtype", "VIRTUAL_ONLY dictionary has no key: %s" % Mail, "locate files", "VIRTUAL_ONLY = %s" % repr(Anomalies["VIRTUAL_ONLY"]), "Contact system administrator.") else: ListDiff(RetVal, Anomalies["VIRTUAL_ONLY"]) else: Dict["VirtUser"] = 0 if type(Anomalies["REAL_ONLY"]) == DictType: if Anomalies["REAL_ONLY"].has_key(Mail): ListDiff(RetVal, Anomalies["REAL_ONLY"][Mail]) else: CgiUtil.TermError("Unknown mailtype", "REAL_ONLY dictionary has no key: %s" % Mail, "locate files", "REAL_ONLY = %s" % repr(Anomalies["REAL_ONLY"]), "Contact system administrator.") else: ListDiff(RetVal, Anomalies["REAL_ONLY"]) return RetVal
def Show(Msg="", Debug=0): "Show a login form in HTML." try: T = Template.Template("login.html") except IOError: CgiUtil.TermError( "Cannot access templates.", "Have Python library files " "been moved from where they were unzipped?", "reading templates", CgiUtil.FileDetails("default theme directory", Template.Template.Dict["ThemeDir"]), "Reinstall tmda-cgi.") T["LoginErr"] = Msg T["Debug"] = Debug print T
def GetAnomalies(Dir): "Find any anomaly instructions." global Dict Filename = os.path.join("skel", Dir, "anomalies") RetVal = \ { "PERMISSIONS": {}, "VIRTUAL_TEST": "", "REAL_ONLY": [], "VIRTUAL_ONLY": [] } RetVal.update(Dict) try: execfile(Filename, RetVal) except SyntaxError, ErrStr: CgiUtil.TermError("SyntaxError", ErrStr, "read anomalies", CgiUtil.FileDetails("anomalies", Filename), "Contact system administrator.")
def BecomeUser(self): "Set up everything to *BE* the user." Match = re.search("(.+)/$", self.Vars["HOME"]) if Match: self.Vars["HOME"] = Match.group(1) os.environ["HOME"] = self.Vars["HOME"] self.__suid__("user") self.Valid = 1 # Now that we know who we are, get our defaults from TMDA import Errors try: from TMDA import Defaults except Errors.ConfigError, (ErrStr): if self[("NoOverride", "MayInstall")][0].lower() == "y": if (os.environ["TMDA_CGI_MODE"] == "no-su"): CgiUtil.TermError( "Install failed", "Install not allowed in no-su mode", "install", "", "Either recompile in another mode or install TMDA manually." ) raise CgiUtil.NotInstalled, (ErrStr, self) T = Template.Template("no-install.html") T["ErrMsg"] = ErrStr print T sys.exit()
def ReleaseAndDelete(Threshold): "Release all messages younger than given amount. Delete rest." from TMDA import Defaults from TMDA import Pending # Release Pending.Queue(dispose = "release", threshold = Threshold, younger = 1, verbose = 0).initQueue().mainLoop() # Wait for messages to release... while 1: time.sleep(1) Queue = Pending.Queue(threshold = Threshold, younger = 1) Queue.initQueue() Msgs = 0 for MsgID in Queue.listPendingIds(): Msgs += Queue.checkTreshold(MsgID) if not Msgs: break # Delete Pending.Queue(dispose = "delete", verbose = 0).initQueue().mainLoop() # Tidy-up Dir = CgiUtil.ExpandUser(Defaults.RESPONSE_DIR) for Filename in glob.glob(os.path.join(Dir, "*.*.*")): try: os.unlink(Filename) except OSError: pass try: os.rmdir(Dir) except OSError: pass Filename = CgiUtil.ExpandUser(Defaults.PENDING_CACHE) try: os.unlink(Filename) except OSError: pass Dir = os.path.join(Defaults.DATADIR, "pending") try: os.rmdir(Dir) except OSError: pass
def Save(self): """Save all session variables to disk. Global RealUser determines whether we save Vars or PVars.""" CWD = os.getcwd() if self.RealUser: # Not sure why I have to refer to Defaults via globals(), but it works if globals().has_key("Defaults") and \ (type(globals()["Defaults"]) == DictType): os.chdir(os.path.split(globals()["Defaults"]["TMDARC"])[0]) Filename = globals()["Defaults"]["CGI_SETTINGS"] else: from TMDA import Defaults os.chdir(os.path.split(Defaults.TMDARC)[0]) Filename = Defaults.CGI_SETTINGS Data = self.PVars else: self.__suid__("web") Filename = os.environ["TMDA_SESSION_PREFIX"] + self.SID Data = self.Vars UMask = os.umask(0177) # Save data try: F = open(Filename, "w") pickle.dump(Data, F) F.close() except IOError: CgiUtil.TermError( "Unable to save session data.", "Insufficient privileges.", "write session file", "%s<br>%s" % (CgiUtil.FileDetails("Session file", Filename), CgiUtil.FileDetails("CWD", os.getcwd())), """Either grant the session user sufficient privileges to write the session file,<br>or recompile the CGI and specify a CGI_USER with more rights.""") if not self.RealUser: os.umask(UMask) os.chdir(CWD)
def Revert \ ( Files, Backup, ErrStr, Recommend = "Check file permissions in home directory." ): "Revert back to system before install." for File in Files: os.unlink(File) if Backup: ExtractTgz(Backup) try: os.unlink(Backup) except OSError: pass CgiUtil.TermError("Install aborted.", ErrStr, "install TMDA", "", Recommend)
def Show(): "Edit filter." # Load the display template if Form["cmd"].value == "incoming": T = Template.Template("incoming.html") T["FilePath"] = Filename = CgiUtil.ExpandUser(Defaults.FILTER_INCOMING) else: T = Template.Template("outgoing.html") T["FilePath"] = Filename = CgiUtil.ExpandUser(Defaults.FILTER_OUTGOING) # Get file if CgiUtil.TestTextFilePath(Filename): try: F = open(Filename) T["FileContents"] = F.read() F.close() except IOError: T["FileContents"] = "" else: T["FileContents"] = "(File is not accessible.)" # Are we allowed to save? if PVars[("NoOverride", "MayEditFilters")][0].lower() == "n": T["SaveButton"] else: # Did they try to save? if Form.has_key("subcmd"): # Make sure the list is properly formatted if Form.has_key("filter"): Contents = Form["filter"].value else: Contents = "" Contents = re.sub("\r\n", "\n", Contents) Contents = re.sub("\n*$", "", Contents) Contents += "\n" if CgiUtil.TestTextFilePath(Filename): try: F = open(Filename, "w") F.write(Contents) F.close() T["FileContents"] = Contents except IOError, ErrStr: CgiUtil.TermError( "Unable to save filter.", "Insufficient privileges", "save filter", "%s<br>%s" % (ErrStr, CgiUtil.FileDetails("Filter", Filename)), "Change file permissions on <tt>%s</tt>" % Filename) else: FileContents = "(File is not accessible.)"
def AddIcon(Part): "Add an appropriate attachment." Filename = Part.get_filename("") Icon = "exe" if ImageType1.search(Filename): Icon = "image" elif MovieType1.search(Filename): Icon = "movie" elif PythonType1.search(Filename): Icon = "python" elif SoundType1.search(Filename): Icon = "sound" elif TextType1.search(Filename): Icon = "text" elif ZipType1.search(Filename): Icon = "zip" elif ImageType2.search(Part.get_type("text/plain")): Icon = "image" elif MovieType2.search(Part.get_type("text/plain")): Icon = "movie" elif SoundType2.search(Part.get_type("text/plain")): Icon = "sound" elif ZipType2.search(Part.get_type("text/plain")): Icon = "zip" Attachment["Icon"] = Icon Attachment["Filename"] = Filename Attachment["Size"] = CgiUtil.Size(MsgSize=len(Part.as_string())) Attachment.Add()
self.Vars["HOME"], self.Vars["UID"], self.Vars["GID"] = \ Util.getuserparams(self.Vars["User"]) self.Vars["NAME"] = pwd.getpwuid(self.Vars["UID"])[4] except KeyError, str: Template.Template.Dict["ErrMsg"] = \ "Username %s not found in system.\nstr=%s" % (self.Vars["User"], str) return # When getuserparams returns a UID of 0 or 1, assume it is a virtual user if int(self.Vars["UID"]) < 2: PasswordRecord = pwd.getpwnam(os.environ["TMDA_VUSER"]) self.Vars["UID"] = PasswordRecord[2] self.Vars["GID"] = PasswordRecord[3] if not int(self.Vars["UID"]): CgiUtil.TermError("TMDA_VUSER is UID 0.", "It is not safe to run " "tmda-cgi as root.", "set euid", "TMDA_VUSER = %s" % os.environ["TMDA_VUSER"], "Recompile CGI.") # Is there a TMDARC variable? if os.environ.has_key("TMDARC"): # Yes, replace it os.environ["TMDARC"] = os.environ["TMDARC"].replace( "/~/", "/%s/" % self.Vars["User"]) # Initialize the auth mechanism import Authenticate try: if os.environ.has_key("TMDA_AUTH_TYPE"): if os.environ["TMDA_AUTH_TYPE"] == "program": Authenticate.InitProgramAuth(os.environ["TMDA_AUTH_ARG"])
def Show(): "Show all pending e-mail in an HTML form." ReadList = [] if Form.has_key("subcmd"): # Batch operation if Form["subcmd"].value == "batch": ReleaseList = [] WhiteList = [] BlackList = [] DeleteList = [] OtherList = [] OtherAction = "Pass" for Count in range(int(PVars[("PendingList", "PagerSize")])): # Check for radioboxes (a0 through a%(PagerSize)d) if Form.has_key("a%d" % Count): # Check to make sure they're not trying to access anything other than # email if not GoodFN.search(Form["m%d" % Count].value): CgiUtil.TermError( "<tt>%s</tt> is not a valid message ID." % Form["m%d" % Count].value, "Program error / corrupted link.", "process pending e-mail", "", "Recheck link or contact TMDA programmers.") if Form["a%d" % Count].value == "pass": continue try: MsgObj = Pending.Message(Form["m%d" % Count].value) if Form["a%d" % Count].value == "release": ReleaseList.append(MsgObj) elif Form["a%d" % Count].value == "delete": DeleteList.append(MsgObj) elif Form["a%d" % Count].value == "whitelist": WhiteList.append(MsgObj) ReleaseList.append(MsgObj) elif Form["a%d" % Count].value == "blacklist": BlackList.append(MsgObj) DeleteList.append(MsgObj) elif Form["a%d" % Count].value == "report": OtherAction = "Report" OtherList.append(MsgObj) elif Form["a%d" % Count].value == "other": if Form["Action"].value == "Release": ReleaseList.append(MsgObj) elif Form["Action"].value == "Delete": DeleteList.append(MsgObj) elif Form["Action"].value == "Whitelist": WhiteList.append(MsgObj) ReleaseList.append(MsgObj) elif Form["Action"].value == "Blacklist": BlackList.append(MsgObj) DeleteList.append(MsgObj) elif Form["Action"].value == "Read": ReadList.append(MsgObj) else: OtherList.append(MsgObj) OtherAction = Form["Action"].value except (IOError, Errors.MessageError): pass # Check for checkboxes (c0 through c%(PagerSize)d) elif Form.has_key("c%d" % Count): # Check to make sure they're not trying to access anything other than # email if not GoodFN.search(Form["m%d" % Count].value): CgiUtil.TermError( "<tt>%s</tt> is not a valid message ID." % Form["m%d" % Count].value, "Program error / corrupted link.", "process pending e-mail", "", "Recheck link or contact TMDA programmers.") try: MsgObj = Pending.Message(Form["m%d" % Count].value) if Form.has_key("ReleaseButton"): ReleaseList.append(MsgObj) elif Form.has_key("DeleteButton"): DeleteList.append(MsgObj) elif Form.has_key("BlacklistButton"): BlackList.append(MsgObj) DeleteList.append(MsgObj) elif Form.has_key("WhitelistButton"): WhiteList.append(MsgObj) ReleaseList.append(MsgObj) elif Form.has_key("ReportButton"): OtherAction = "Report" OtherList.append(MsgObj) elif Form.has_key("ExecuteButton"): OtherAction = Form["Action"].value if OtherAction == "Release": ReleaseList.append(MsgObj) elif OtherAction == "Delete": DeleteList.append(MsgObj) elif OtherAction == "Whitelist": WhiteList.append(MsgObj) ReleaseList.append(MsgObj) elif OtherAction == "Blacklist": BlackList.append(MsgObj) DeleteList.append(MsgObj) elif OtherAction == "Read": ReadList.append(MsgObj) else: OtherList.append(MsgObj) except IOError: pass # Process the messages found: # Apply "other" action... May be Report or a custom filter for MsgObj in OtherList: if OtherAction == "Report": CgiUtil.ReportToSpamCop(MsgObj) DeleteList.append(MsgObj) # TODO: Check if OtherAction is a custom filter # If so, run it on the message and check the return value # and add the MsgObj to the appropriate action list based on the # filter output. for MsgObj in WhiteList: # Whitelist (and release) each message MsgObj.whitelist() for MsgObj in ReleaseList: # Release each message PVars["InProcess"][MsgObj.msgid] = 1 MsgObj.release() for MsgObj in BlackList: # Blacklist (and delete) each message MsgObj.blacklist() for MsgObj in DeleteList: # Delete each message MsgObj.delete() # Locate messages in pending dir Queue = Pending.Queue(descending=1, cache=1) try: Queue.initQueue() Queue._loadCache() Msgs = Queue.listPendingIds() except (Errors.QueueError, TypeError): Msgs = [] # Search the Pending List: # # If the from has the keys searchPattern and search # we can search. # # - searchPattern: a basic RE pattern which contains exactly one "%s" # where the user's search string goes. # - search: The user's search string for incorporation into the searchPattern # # For an example, check out the source for the Pending List in the theme # 'Blue' # Searching = 0 if Form.has_key("searchPattern") and Form.has_key("search"): Searching = 1 # By default, set no flags. flags = 0 # Check what sort of search - a full body search or just a header search if (re.search('%s', Form['searchPattern'].value)): searchScope = 'fullMessage' expression = Form['searchPattern'].value % Form['search'].value # Do a multiline search through the entire message, matching a newline # with '.' flags = re.MULTILINE | re.DOTALL elif (re.match('^_header:', Form['searchPattern'].value)): (searchScope, headerList) = Form['searchPattern'].value.split(':') headerList = headerList.split(',') expression = Form['search'].value elif( Form['searchPattern'].value == "_header" and \ Form.has_key("searchHeaderList" ) ): searchScope = Form['searchPattern'].value headerList = Form['searchHeaderList'].value.split(',') expression = Form['search'].value # Assume case-insensitive unless the form has 'searchCaseSensitive' if not Form.has_key("searchCaseSensitive"): flags = flags | re.I exp = re.compile(expression, flags) matchingMsgs = [] for Msg in Msgs: try: MsgObj = Pending.Message(Msg) except (IOError, Errors.MessageError, TypeError), ErrStr: continue # Slow search - Search the fulltext of the message if searchScope == 'fullMessage' and \ exp.search( MsgObj.show() ) != None: matchingMsgs = matchingMsgs + [Msg] # Faster search - just matches a header elif searchScope == '_header': for header in headerList: if MsgObj.msgobj.has_key( header ) and \ exp.search( MsgObj.msgobj[ header ] ): matchingMsgs = matchingMsgs + [Msg] break Msgs = matchingMsgs
def Release(QueryString): """Release the message represented in the QueryString. QueryString is in one of two formats, real users MAY confirm with: <UID>.<confirm_cookie> Virtual users MUST confirm with: <UID>&<recipient_address>&<confirm_cookie> Where <UID> is the UID of the TMDA account, <recipient_address> is the untagged address of the original message recipient, and <confirm_cookie> is used to find and validate the pending email in question.""" # Prepare the traceback in case of uncaught exception MyCgiTb.ErrTemplate = "prog_err2.html" CgiUtil.ErrTemplate = "error2.html" try: UID, Recipient, Cookie = QueryString.split("&") UID = int(UID) OldStyle = 0 # Get base address from Recipient RecipUser, RecipDomain = Recipient.split("@") User = RecipUser.split('-')[0] + "@" + RecipDomain except (ValueError, KeyError): try: # Check for old-style format UID, Cookie = QueryString.split(".", 1) UID = int(UID) User = pwd.getpwuid(UID)[0] OldStyle = 1 except (ValueError, KeyError): CgiUtil.TermError( "Unable to parse query string.", "Program error / corrupted link.", "locate pending e-mail", "", """Please check the link you followed and make sure that it is typed in exactly as it was sent to you.""") try: # Get real user from UID Timestamp, PID, HMAC = Cookie.split(".") except ValueError: CgiUtil.TermError( "Unable to parse query string.", "Program error / corrupted link.", "locate pending e-mail", "", """Please check the link you followed and make sure that it is typed in exactly as it was sent to you.""") MsgID = "%s.%s.msg" % (Timestamp, PID) # Check to make sure they're not trying to access anything other than email if not re.compile("^\d+\.\d+\.msg$").search(MsgID): CgiUtil.TermError("<tt>%s.%s.%s</tt> is not a valid message ID." % \ (Timestamp, PID, HMAC), "Program error / corrupted link.", "retrieve pending e-mail", "", """Please check the link you followed and make sure that it is typed in exactly as it was sent to you.""") # Set up the user's home directory. try: os.seteuid(0) os.setegid(0) os.setuid(0) except OSError: pass try: if os.environ.has_key("TMDA_VLOOKUP") and not OldStyle: VLookup = \ CgiUtil.ParseString(os.environ["TMDA_VLOOKUP"], User ) List = Util.RunTask(VLookup[1:]) Sandbox = {"User": User} Filename = os.path.join("stubs", "%s.py" % VLookup[0]) try: execfile(Filename, Sandbox) except IOError: CgiUtil.TermError( "Can't load virtual user stub.", "Cannot execute %s" % Filename, "execute stub", "TMDA_VLOOKUP = %s" % os.environ["TMDA_VLOOKUP"], """Contact this message's sender by an alternate means and inform them of this error, or try confirming your message using an alternate method.""") Home, UID, GID = Sandbox["getuserparams"](List)[0:3] else: Home, UID, GID = Util.getuserparams(pwd.getpwuid(UID)[0]) except KeyError: CgiUtil.TermError( "No such user", "User %s not found" % User, "find user %s" % User, "", """Contact this message's sender by an alternate means and inform them of this error, or try confirming your message using an alternate method.""") if UID < 2: PasswordRecord = pwd.getpwnam(os.environ["TMDA_VUSER"]) UID = PasswordRecord[2] GID = PasswordRecord[3] if not int(UID): CgiUtil.TermError( "TMDA_VUSER is UID 0.", "It is not safe to run " "tmda-cgi as root.", "set euid", "TMDA_VUSER = %s" % os.environ["TMDA_VUSER"], """Contact this message's sender by an alternate means and inform them of this error, or try confirming your message using an alternate method.""") # We now have the home directory and the User. Set this in the environment. os.environ["USER"] = User os.environ["LOGNAME"] = User os.environ["HOME"] = Home # Is there a TMDARC variable? if os.environ.has_key("TMDARC"): # Yes, replace it os.environ["TMDARC"] = os.environ["TMDARC"].replace( "/~/", "/%s/" % User) # Try to change users try: os.seteuid(0) os.setegid(0) os.setgid(int(GID)) os.setuid(int(UID)) except OSError: pass # Now that we know who we are, get our defaults try: from TMDA import Defaults except Errors.ConfigError: CgiUtil.TermError( "Confirm Failed", "Old-style URL is not compatible with virtual users", "use incompatible URL", "", """Contact this message's sender by an alternate means and inform them of this error, or try confirming your message using an alternate method.""") from TMDA import Pending from TMDA import Cookie try: Defaults.CRYPT_KEY except AttributeError: CgiUtil.TermError( "Could not read CRYPT_KEY.", "CRYPT_KEY can not be read by group %d." % os.getegid(), "read CRYPT_KEY", "ALLOW_MODE_640 = %d<br>%s" % (Defaults.ALLOW_MODE_640, CgiUtil.FileDetails("Cryptography key", Defaults.CRYPT_KEY_FILE)), """Any of the following solutions:<br> 1. Place <tt>%s</tt> in any of the groups that user %d belongs to.<br> 2. Do all three of the following:<br> • Place <tt>%s</tt> in group %d.<br> • Assign permissions 640 to <tt>%s</tt>.<br> • Set ALLOW_MODE_640 = 1 in your configuration file.<br> 3. Disable URL confirmation in your confirmation template.""" % (Defaults.CRYPT_KEY_FILE, os.geteuid(), Defaults.CRYPT_KEY_FILE, os.getegid(), Defaults.CRYPT_KEY_FILE)) # Validate the HMAC if Cookie.confirmationmac(Timestamp, PID, "accept") != HMAC: CgiUtil.TermError("<tt>%s.%s.%s</tt> is not a valid message ID." % \ (Timestamp, PID, HMAC), "Program error / corrupted link.", "retrieve pending e-mail", "", "Recheck link or contact TMDA programmers.") # Read in e-mail try: MsgObj = Pending.Message(MsgID) except Errors.MessageError: CgiUtil.TermError("Message could not be fetched.", "Message has already been released or deleted.", "retrieve pending e-mail", "", "Inquire with recipient about e-mail.") T = Template.Template("released.html") # Fetch row Row = T["Row"] # Generate header rows for Header in Defaults.SUMMARY_HEADERS: T["Name"] = Header.capitalize() T["Value"] = CgiUtil.Escape(MsgObj.msgobj[Header]) Row.Add() # Can we add this address to a do-not-confirm-again list? if Defaults.CONFIRM_APPEND: ConfirmAddr = Util.confirm_append_address \ ( parseaddr(MsgObj.msgobj["x-primary-address"])[1], parseaddr(MsgObj.msgobj["return-path"])[1] ) if ConfirmAddr: Util.append_to_file(ConfirmAddr, Defaults.CONFIRM_APPEND) T["Address"] = ConfirmAddr else: T["Future"] else: T["Future"] print T # Make sure release does not write to PENDING_RELEASE_APPEND Defaults.PENDING_RELEASE_APPEND = None MsgObj.release()
def Show(): "Show an e-mail in HTML." global Allow, Remove, Attachment, Divider, PartTemplate, T # Deal with a particular message? if Form.has_key("msgid"): PVars["MsgID"] = Form["msgid"].value PVars.Save() # Check to make sure they're not trying to access anything other than email if not re.compile("^\d+\.\d+\.msg$").search(PVars["MsgID"]): CgiUtil.TermError("<tt>%s</tt> is not a valid message ID." % PVars["MsgID"], "Program error / corrupted link.", "retrieve pending e-mail", "", "Recheck link or contact TMDA programmers.") # Fetch the queue Queue = Pending.Queue(descending = 1, cache = 1) Queue.initQueue() Queue._loadCache() # Get e-mail template T = Template.Template("view.html") T["EmailClass"] = PVars[("ViewPending", "EmailClass")] # Locate messages in pending dir Msgs = Queue.listPendingIds() try: MsgIdx = Msgs.index(PVars["MsgID"]) except ValueError: # Oops. Perhaps they released the message? Get the list! raise Errors.MessageError # Any subcommands? if Form.has_key("subcmd"): # first/prev/next/last subcommands if Form["subcmd"].value == "first": MsgIdx = 0 PVars["Pager"] = 0 elif Form["subcmd"].value == "prev": if MsgIdx > 0: MsgIdx -= 1 PVars["Pager"] -= 1 elif Form["subcmd"].value == "next": if MsgIdx < (len(Msgs) - 1): MsgIdx += 1 PVars["Pager"] += 1 elif Form["subcmd"].value == "last": MsgIdx = len(Msgs) - 1 PVars["Pager"] = len(Msgs) # Toggle headers? elif Form["subcmd"].value == "headers": if PVars[("ViewPending", "Headers")] == "short": PVars[("ViewPending", "Headers")] = "all" else: PVars[("ViewPending", "Headers")] = "short" else: # Read in e-mail try: MsgObj = Pending.Message(PVars["MsgID"]) if Form["subcmd"].value == "pass": pass if Form["subcmd"].value == "delete": MsgObj.delete() elif Form["subcmd"].value == "release": MsgObj.release() PVars["InProcess"][PVars["MsgID"]] = 1 elif Form["subcmd"].value == "white": MsgObj.whitelist() MsgObj.release() PVars["InProcess"][PVars["MsgID"]] = 1 elif Form["subcmd"].value == "black": MsgObj.blacklist() MsgObj.delete() elif Form["subcmd"].value == "spamcop": CgiUtil.ReportToSpamCop(MsgObj) MsgObj.delete() # TODO: Check if subcmd is a custom filter and process accordingly del Msgs[MsgIdx] except IOError: pass # So which message are we on now? if len(Msgs) == 0: # Oops! None left! PVars.Save() raise Errors.MessageError if MsgIdx >= len(Msgs): MsgIdx = len(Msgs) - 1 PVars["MsgID"] = Msgs[MsgIdx] # Save session PVars.Save() # Get message ID T["MsgID"] = Msgs[MsgIdx] PVars["MsgID"] = Msgs[MsgIdx] # Grey out the first & prev buttons? if MsgIdx == 0: T["FirstButton1Active"] T["PrevButton1Active"] T["FirstButton2Active"] T["PrevButton2Active"] else: T["FirstButton1Inactive"] T["PrevButton1Inactive"] T["FirstButton2Inactive"] T["PrevButton2Inactive"] # Grey out the next & last buttons? if MsgIdx == (len(Msgs) - 1): T["NextButton1Active"] T["LastButton1Active"] T["NextButton2Active"] T["LastButton2Active"] else: T["NextButton1Inactive"] T["LastButton1Inactive"] T["NextButton2Inactive"] T["LastButton2Inactive"] # Use Javascript confirmation? if PVars[("General", "UseJSConfirm")] == "Yes": T["OnSubmit"] = "onSubmit=\"return TestConfirm()\"" T["DeleteURL"] = "javascript:ConfirmDelete()" T["BlacklistURL"] = "javascript:ConfirmBlacklist()" T["SpamCopURL"] = "javascript:ConfirmSpamCop()" else: T["OnSubmit"] = "" T["DeleteURL"] = "%s?cmd=view&subcmd=delete&SID=%s" % \ (os.environ["SCRIPT_NAME"], PVars.SID) T["BlacklistURL"] = "%s?cmd=view&subcmd=black&SID=%s" % \ (os.environ["SCRIPT_NAME"], PVars.SID) T["SpamCopURL"] = "%s?cmd=view&subcmd=spamcop&SID=%s" % \ (os.environ["SCRIPT_NAME"], PVars.SID) T["DispRange"] = "%d of %d" % (MsgIdx + 1, len(Msgs)) # Read in e-mail MsgObj = Pending.Message(PVars["MsgID"]) Queue._addCache(PVars["MsgID"]) Queue._saveCache() # Extract header row HeaderRow = T["HeaderRow"] if PVars[("ViewPending", "Headers")] == "all": # Remove header table T["ShortHeaders"] # Generate all headers Headers = "" for Line in CgiUtil.Escape(MsgObj.show()).split("\n"): if Line == "": break # Decode internationalized headers for decoded in email.Header.decode_header( Line ): Headers += decoded[0] + " " if decoded[1]: cset = email.Charset.Charset(decoded[1]).input_charset.split() T["CharSet"] = cset[0] Headers += "\n" T["Headers"] = '<pre class="Headers">%s</pre>' % Headers else: # Remove all header block T["AllHeaders"] # Generate short headers for Header in Defaults.SUMMARY_HEADERS: T["Name"] = Header.capitalize() value = "" # Decode internationalazed headers for decoded in email.Header.decode_header( MsgObj.msgobj[Header] ): value += decoded[0] + " " if decoded[1]: cset = email.Charset.Charset(decoded[1]).input_charset.split() T["CharSet"] = cset[0] T["Value"] = CgiUtil.Escape(value) HeaderRow.Add() # Go through each part and generate HTML Allow = re.split("[,\s]+", PVars[("ViewPending", "AllowTags")]) Remove = re.split("[,\s]+", PVars[("ViewPending", "BlockRemove")]) Attachment = T["Attachment"] Divider = T["Divider"] PartTemplate = T["Part"] ShowPart(MsgObj.msgobj) # Remove unneeded bits? NumCols = int(T["NumCols"]) # TODO: Programatically check a setting to see which are allowed, # and which should be shown. # For now, allow and show everything RlAllowed = 1 DlAllowed = 1 WhAllowed = 1 and Defaults.PENDING_WHITELIST_APPEND BlAllowed = 1 and Defaults.PENDING_BLACKLIST_APPEND ScAllowed = 1 and PVars[("General", "SpamCopAddr")] FltAllowed = 1 RlShow = RlAllowed and 1 DlShow = DlAllowed and 1 WhShow = WhAllowed and 1 BlShow = BlAllowed and 1 ScShow = ScAllowed and 1 if not RlAllowed: T["RlAction"] if not RlShow: NumCols -= 1 T["RlIcon1"] T["RlIcon2"] if not DlAllowed: T["DlAction"] if not DlShow: NumCols -= 1 T["DlIcon1"] T["DlIcon2"] if not BlAllowed: T["BlAction"] if not BlShow: NumCols -= 1 T["BlIcon1"] T["BlIcon2"] if not WhAllowed: T["WhAction"] if not WhShow: NumCols -= 1 T["WhIcon1"] T["WhIcon2"] if not ScAllowed: T["ScAction"] if not ScShow: NumCols -= 1 T["SCIcon1"] T["SCIcon2"] if FltAllowed: T["FilterOptions"] = CgiUtil.getFilterOptions() else: T["FilterOptions"] = "" T["NumCols"] = NumCols if len(Attachment.HTML) == 0: T["NoAttachments"] # Display HTML page with email included. print T
def Show(): "Generate dynamic addresses." # Load the display template T = Template.Template("gen_addr.html") # Not sure yet which form we'll use, so put something in each field. T["Keyword"] = PVars[("GenAddr", "Keyword")] T["KeywordAddr"] = "" T["ExpAmt"] = int(PVars[("GenAddr", "ExpireNum")]) T["mSel"] = "" T["hSel"] = "" T["dSel"] = "" T["wSel"] = "" T["MSel"] = "" T["YSel"] = "" T["DatedAddr"] = "" T["Sender"] = PVars[("GenAddr", "Sender")] T["SenderAddr"] = "" # # What shall we create? # T["defaultAddress"] = Address.Factory().create(None).address # Default is "dated" Tag = Defaults.TAGS_DATED[0].lower() if Form.has_key("dated_addr"): Base = Form["dated_addr"].value else: Base = None DestField = "DatedAddr" if Form.has_key("subcmd"): if Form["subcmd"].value == "sender": # Make a "sender" address Tag = Defaults.TAGS_SENDER[0].lower() if Form.has_key("sender_addr"): Base = Form["sender_addr"].value else: Base = None DestField = "SenderAddr" if Form.has_key("sender"): Option = T["Sender"] = PVars[("GenAddr", "Sender")] = \ PVars[("TestAddr", "From")] = Form["sender"].value else: if PVars.has_key(("GenAddr", "Sender")): del PVars[("GenAddr", "Sender")] if PVars.has_key(("TestAddr", "From")): del PVars[("TestAddr", "From")] T["Sender"] = "" DestField = "" PVars.Save() elif Form["subcmd"].value == "keyword": # Make a "keyword" address Tag = Defaults.TAGS_KEYWORD[0].lower() if Form.has_key("keyword_addr"): Base = Form["keyword_addr"].value else: Base = None DestField = "KeywordAddr" if Form.has_key("keyword"): Option = T["Keyword"] = PVars[("GenAddr", "Keyword")] = \ Form["keyword"].value else: if PVars.has_key(("GenAddr", "Keyword")): del PVars[("GenAddr", "Keyword")] T["Keyword"] = "" if PVars.has_key(("TestAddr", "From")): del PVars[("TestAddr", "From")] PVars.Save() else: # Make a "dated" address PVars[("GenAddr", "ExpireUnit")] = Form["exp_units"].value if Form.has_key("exp_amt"): T["ExpAmt"] = PVars[("GenAddr", "ExpireNum")] = \ int(Form["exp_amt"].value) Option = "%d%s" % (int(PVars[("GenAddr", "ExpireNum")]), PVars[("GenAddr", "ExpireUnit")]) else: if PVars.has_key(("GenAddr", "ExpireNum")): del PVars[("GenAddr", "ExpireNum")] if PVars.has_key(("TestAddr", "From")): del PVars[("TestAddr", "From")] PVars.Save() # Default to dated else: if PVars.has_key(("TestAddr", "From")): del PVars[("TestAddr", "From")] Option = "%d%s" % (int(PVars[("GenAddr", "ExpireNum")]), PVars[("GenAddr", "ExpireUnit")]) # Show correct units T["%sSel" % PVars[("GenAddr", "ExpireUnit")]] = " selected" # Strip the Base if it is already a tagged address. if Base is not None: try: at = Base.rindex('@') local = Base[:at].lower() domain = Base[at+1:].lower() except ValueError: CgiUtil.TermError("Value Error", "'%s' is not a valid email address" % Base, "generate address", "", "Try again with a valid email address in the <i>Use Address</i> blank." ) addr = Address.Factory(Base) addrTag = addr.tag() if addrTag != "": tagindex = local.rindex( addrTag ) Base = local[:tagindex - 1] + "@" + domain # Create the address try: T[DestField] = PVars[("TestAddr", "To")] = \ Address.Factory(tag = Tag).create(Base, Option).address PVars.Save() except (TypeError, UnboundLocalError): pass # Display template print T
def __init__(self, Form): "Reload an existing SID or create a new one." global Defaults # Existing, valid looking session? if Form.has_key("SID") and \ re.compile("^[a-zA-Z0-9]{8}$").search(Form["SID"].value): # Provide SID to templates Template.Template.Dict["SID"] = self.SID = Form["SID"].value # Resurrect session try: self.__suid__("web") Filename = os.environ["TMDA_SESSION_PREFIX"] + self.SID if os.stat(Filename)[4] != os.geteuid(): CgiUtil.TermError( "CGI_USER does not own session file.", "Something suspicious is going on here. This should not happen.", "open file", CgiUtil.FileDetails("Session data", Filename), "No recommendation.") try: F = open(Filename) self.Vars = pickle.load(F) F.close() except (IOError, EOFError): self.Vars = {} # Make sure the session has not been hijacked if os.environ["REMOTE_ADDR"] != self.Vars["IP"]: CgiUtil.TermError( "User's IP address has changed.", "Your IP address has changed. This is not allowed.", "read session data", "%s->%s" % (self.Vars["IP"], os.environ["REMOTE_ADDR"]), '<a href="%s">Log back in</a>.' % os.environ["SCRIPT_NAME"]) # Are they logging out? if Form.has_key("cmd") and (Form["cmd"].value == "logout"): os.unlink(Filename) return # Touch session file to keep it from getting cleaned too soon os.utime(Filename, None) # Is there a TMDARC variable? if os.environ.has_key("TMDARC"): # Yes, replace it os.environ["TMDARC"] = os.environ["TMDARC"].replace( "/~/", "/%s/" % self.Vars["User"]) # Load system defaults self.LoadSysDefaults() # Become the user self.BecomeUser() # Done! return # Failed to resurrect session, fall through to make new SID except (IOError, OSError): pass # New session SessionChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" \ "0123456789" self.SID = "" for i in range(8): self.SID += SessionChars[self.Rands.randrange(len(SessionChars))] Template.Template.Dict["SID"] = self.SID self.Vars = {} # Are they logging in? if not Form.has_key("user"): Template.Template.Dict["ErrMsg"] = "No user name supplied." return if not Form.has_key("password"): Template.Template.Dict["ErrMsg"] = "No password supplied." return # Get IP, User, UID, & Home directory self.Vars["IP"] = os.environ["REMOTE_ADDR"] self.Vars["User"] = Form["user"].value.lower() self.__suid__("root") try: if os.environ.has_key("TMDA_VLOOKUP"): VLookup = \ CgiUtil.ParseString(os.environ["TMDA_VLOOKUP"], self.Vars["User"]) List = Util.RunTask(VLookup[1:]) Sandbox = {"User": self.Vars["User"]} Filename = os.path.join("stubs", "%s.py" % VLookup[0]) try: execfile(Filename, Sandbox) except IOError: CgiUtil.TermError( "Can't load virtual user stub.", "Cannot execute %s" % Filename, "execute stub", "TMDA_VLOOKUP = %s" % os.environ["TMDA_VLOOKUP"], "Recompile CGI.") Params = Sandbox["getuserparams"](List) self.Vars["HOME"], self.Vars["UID"], self.Vars["GID"] = Params[ 0:3] if len(Params) > 3: self.Vars["NAME"] = Params[3] else: self.Vars["NAME"] = None else: self.Vars["HOME"], self.Vars["UID"], self.Vars["GID"] = \ Util.getuserparams(self.Vars["User"]) self.Vars["NAME"] = pwd.getpwuid(self.Vars["UID"])[4] except KeyError, str: Template.Template.Dict["ErrMsg"] = \ "Username %s not found in system.\nstr=%s" % (self.Vars["User"], str) return
def Show(): "Edit templates." global AssignVar, Assignments, Config, LineNum, LastLine # Load the display template T = Template.Template("templates.html") Row = T["Row"] # Hide save button? if PVars[("NoOverride", "MayEditTemplates")][0].lower() == "n": T["SaveButton"] else: T["NoSave"] if Defaults.TEMPLATE_DIR: baseDir = Defaults.TEMPLATE_DIR else: baseDir = os.environ["TMDA_BASE_DIR"] for BaseName in os.listdir( baseDir ): if BaseName.endswith( ".txt" ): Filename = os.path.join( baseDir, BaseName ) T["Var"] = BaseName # Get file T["FilePath"] = Filename if CgiUtil.TestTextFilePath(Filename): try: F = open(Filename) FileContents = F.read() F.close() except IOError: FileContents = "" else: FileContents = "(File is not accessible.)" # Saving? if Form.has_key("subcmd") and \ (PVars[("NoOverride", "MayEditTemplates")][0].lower() == "y"): try: # Make sure the list is properly formatted try: FileContents = re.sub("\r\n", "\n", Form[BaseName].value) except KeyError: FileContents = "" FileContents = re.sub("\n*$", "", FileContents) FileContents += "\n" if CgiUtil.TestTextFilePath(Filename): F = open(Filename, "w") F.write(FileContents) F.close() except IOError, ErrStr: CgiUtil.TermError("Unable to save template file.", "Insufficient privileges", "save template", "%s<br>%s" % (ErrStr, CgiUtil.FileDetails("Template", Filename)), "Change file permissions on <tt>%s</tt>" % Filename) T["FileContents"] = FileContents Row.Add()
NumBlankCols -= 1 if not BlAllowed: T["BlAction"] if not BlShow: T["BlIcon"] NumCols -= 1 NumBlankCols -= 1 if not ScAllowed: T["ScAction"] if not ScShow: T["SCIcon"] NumCols -= 1 NumBlankCols -= 1 if FltAllowed: T["FilterOptions"] = CgiUtil.getFilterOptions() else: T["FilterOptions"] = "" T["NumCols"] = NumCols T["NumBlankCols"] = NumBlankCols # Javascript confirmation? if PVars[("General", "UseJSConfirm")] == "Yes": T["OnSubmit"] = 'onSubmit="return TestConfirm()"' else: T["OnSubmit"] = "" T["PagerSize"] = PVars[("PendingList", "PagerSize")] ReadArray = []
def ShowPart(Part): "Analyze message part and display it as best possible." # Each part is one of five things and must be handled accordingly # multipart/alternative - pick one and display it # message or multipart - recurse through each # text/plain - escape & display # text/html - sterilize & display # other - show as an attachment # Display this part if Part.is_multipart(): if Part.get_type("multipart/mixed") == "multipart/alternative": # Pick preferred alternative PrefPart = None PrefRating = -1 for SubPart in Part.get_payload(): Type = SubPart.get_type("text/plain") Rating = PVars[("ViewPending", "AltPref")].find(Type) # Is this part preferred? if (not PrefPart) or ((PrefRating == -1) and (Rating >= 0)) \ or ((Rating >= 0) and (Rating < PrefRating)): PrefPart = SubPart PrefRating = Rating if PrefPart: ShowPart(PrefPart) else: # Recurse through all subparts for SubPart in Part.get_payload(): ShowPart(SubPart) else: Type = Part.get_type("text/plain") # Display the easily display-able parts if Type == "text/plain": # Check if there's a character set for this part. if Part.get_content_charset(): cset = email.Charset.Charset( Part.get_content_charset()).input_charset.split() T["CharSet"] = cset[0] # Escape & display try: Str = Part.get_payload(decode=1).strip() T["Content"] = CgiUtil.Escape(Str).replace("\n", " <br>") if len(PartTemplate.HTML) == 1: Divider.Add() PartTemplate.Add() except AttributeError: pass elif Type == "text/html": # Sterilize & display # Check if there's a character set for this part. if Part.get_content_charset(): cset = email.Charset.Charset( Part.get_content_charset()).input_charset.split() T["CharSet"] = cset[0] try: T["Content"] = \ CgiUtil.Sterilize(Part.get_payload(decode=1), Allow, Remove) if len(PartTemplate.HTML) == 1: Divider.Add() PartTemplate.Add() except AttributeError: pass else: # Display as an attachment AddIcon(Part)
# Clear out blank TMDARC if os.environ["TMDARC"] == "None": del os.environ["TMDARC"] # Make some global stuff available to all Template.Template.Dict["Script"] = os.environ["SCRIPT_NAME"] Template.Template.Dict["SID"] = "" Template.Template.Dict["DispDir"] = os.environ["TMDA_CGI_DISP_DIR"] Template.Template.Dict["ErrMsg"] = "No error message returned. Sorry!" # Check version information try: import CgiVersion CgiVersion.Test() except ImportError, ErrStr: CgiUtil.TermError("Failed to import TMDA module.", ErrStr, "import TMDA", "", "Upgrade to the most recent release of TMDA.") # Process any CGI fields Form = cgi.FieldStorage() # Get any persistent variables try: try: PVars = Session.Session(Form) CgiUtil.ErrTemplate = "error.html" except CgiUtil.NotInstalled, (ErrStr, PVars): Template.Template.Dict["ErrMsg"] = ErrStr # Can log in but TMDA is not installed correctly if ErrStr[0].find("must be chmod 400 or 600") < 0: # Serious error. Suggest an install import Install
def Show(): "Show an e-mail in HTML." global Allow, Remove, Attachment, Divider, PartTemplate, T # Deal with a particular message? if Form.has_key("msgid"): PVars["MsgID"] = Form["msgid"].value PVars.Save() # Check to make sure they're not trying to access anything other than email if not re.compile("^\d+\.\d+\.msg$").search(PVars["MsgID"]): CgiUtil.TermError( "<tt>%s</tt> is not a valid message ID." % PVars["MsgID"], "Program error / corrupted link.", "retrieve pending e-mail", "", "Recheck link or contact TMDA programmers.") # Fetch the queue Queue = Pending.Queue(descending=1, cache=1) Queue.initQueue() Queue._loadCache() # Get e-mail template T = Template.Template("view.html") T["EmailClass"] = PVars[("ViewPending", "EmailClass")] # Locate messages in pending dir Msgs = Queue.listPendingIds() try: MsgIdx = Msgs.index(PVars["MsgID"]) except ValueError: # Oops. Perhaps they released the message? Get the list! raise Errors.MessageError # Any subcommands? if Form.has_key("subcmd"): # first/prev/next/last subcommands if Form["subcmd"].value == "first": MsgIdx = 0 PVars["Pager"] = 0 elif Form["subcmd"].value == "prev": if MsgIdx > 0: MsgIdx -= 1 PVars["Pager"] -= 1 elif Form["subcmd"].value == "next": if MsgIdx < (len(Msgs) - 1): MsgIdx += 1 PVars["Pager"] += 1 elif Form["subcmd"].value == "last": MsgIdx = len(Msgs) - 1 PVars["Pager"] = len(Msgs) # Toggle headers? elif Form["subcmd"].value == "headers": if PVars[("ViewPending", "Headers")] == "short": PVars[("ViewPending", "Headers")] = "all" else: PVars[("ViewPending", "Headers")] = "short" else: # Read in e-mail try: MsgObj = Pending.Message(PVars["MsgID"]) if Form["subcmd"].value == "pass": pass if Form["subcmd"].value == "delete": MsgObj.delete() elif Form["subcmd"].value == "release": MsgObj.release() PVars["InProcess"][PVars["MsgID"]] = 1 elif Form["subcmd"].value == "white": MsgObj.whitelist() MsgObj.release() PVars["InProcess"][PVars["MsgID"]] = 1 elif Form["subcmd"].value == "black": MsgObj.blacklist() MsgObj.delete() elif Form["subcmd"].value == "spamcop": CgiUtil.ReportToSpamCop(MsgObj) MsgObj.delete() # TODO: Check if subcmd is a custom filter and process accordingly del Msgs[MsgIdx] except IOError: pass # So which message are we on now? if len(Msgs) == 0: # Oops! None left! PVars.Save() raise Errors.MessageError if MsgIdx >= len(Msgs): MsgIdx = len(Msgs) - 1 PVars["MsgID"] = Msgs[MsgIdx] # Save session PVars.Save() # Get message ID T["MsgID"] = Msgs[MsgIdx] PVars["MsgID"] = Msgs[MsgIdx] # Grey out the first & prev buttons? if MsgIdx == 0: T["FirstButton1Active"] T["PrevButton1Active"] T["FirstButton2Active"] T["PrevButton2Active"] else: T["FirstButton1Inactive"] T["PrevButton1Inactive"] T["FirstButton2Inactive"] T["PrevButton2Inactive"] # Grey out the next & last buttons? if MsgIdx == (len(Msgs) - 1): T["NextButton1Active"] T["LastButton1Active"] T["NextButton2Active"] T["LastButton2Active"] else: T["NextButton1Inactive"] T["LastButton1Inactive"] T["NextButton2Inactive"] T["LastButton2Inactive"] # Use Javascript confirmation? if PVars[("General", "UseJSConfirm")] == "Yes": T["OnSubmit"] = "onSubmit=\"return TestConfirm()\"" T["DeleteURL"] = "javascript:ConfirmDelete()" T["BlacklistURL"] = "javascript:ConfirmBlacklist()" T["SpamCopURL"] = "javascript:ConfirmSpamCop()" else: T["OnSubmit"] = "" T["DeleteURL"] = "%s?cmd=view&subcmd=delete&SID=%s" % \ (os.environ["SCRIPT_NAME"], PVars.SID) T["BlacklistURL"] = "%s?cmd=view&subcmd=black&SID=%s" % \ (os.environ["SCRIPT_NAME"], PVars.SID) T["SpamCopURL"] = "%s?cmd=view&subcmd=spamcop&SID=%s" % \ (os.environ["SCRIPT_NAME"], PVars.SID) T["DispRange"] = "%d of %d" % (MsgIdx + 1, len(Msgs)) # Read in e-mail MsgObj = Pending.Message(PVars["MsgID"]) Queue._addCache(PVars["MsgID"]) Queue._saveCache() # Extract header row HeaderRow = T["HeaderRow"] if PVars[("ViewPending", "Headers")] == "all": # Remove header table T["ShortHeaders"] # Generate all headers Headers = "" for Line in CgiUtil.Escape(MsgObj.show()).split("\n"): if Line == "": break # Decode internationalized headers for decoded in email.Header.decode_header(Line): Headers += decoded[0] + " " if decoded[1]: cset = email.Charset.Charset( decoded[1]).input_charset.split() T["CharSet"] = cset[0] Headers += "\n" T["Headers"] = '<pre class="Headers">%s</pre>' % Headers else: # Remove all header block T["AllHeaders"] # Generate short headers for Header in Defaults.SUMMARY_HEADERS: T["Name"] = Header.capitalize() value = "" # Decode internationalazed headers for decoded in email.Header.decode_header(MsgObj.msgobj[Header]): value += decoded[0] + " " if decoded[1]: cset = email.Charset.Charset( decoded[1]).input_charset.split() T["CharSet"] = cset[0] T["Value"] = CgiUtil.Escape(value) HeaderRow.Add() # Go through each part and generate HTML Allow = re.split("[,\s]+", PVars[("ViewPending", "AllowTags")]) Remove = re.split("[,\s]+", PVars[("ViewPending", "BlockRemove")]) Attachment = T["Attachment"] Divider = T["Divider"] PartTemplate = T["Part"] ShowPart(MsgObj.msgobj) # Remove unneeded bits? NumCols = int(T["NumCols"]) # TODO: Programatically check a setting to see which are allowed, # and which should be shown. # For now, allow and show everything RlAllowed = 1 DlAllowed = 1 WhAllowed = 1 and Defaults.PENDING_WHITELIST_APPEND BlAllowed = 1 and Defaults.PENDING_BLACKLIST_APPEND ScAllowed = 1 and PVars[("General", "SpamCopAddr")] FltAllowed = 1 RlShow = RlAllowed and 1 DlShow = DlAllowed and 1 WhShow = WhAllowed and 1 BlShow = BlAllowed and 1 ScShow = ScAllowed and 1 if not RlAllowed: T["RlAction"] if not RlShow: NumCols -= 1 T["RlIcon1"] T["RlIcon2"] if not DlAllowed: T["DlAction"] if not DlShow: NumCols -= 1 T["DlIcon1"] T["DlIcon2"] if not BlAllowed: T["BlAction"] if not BlShow: NumCols -= 1 T["BlIcon1"] T["BlIcon2"] if not WhAllowed: T["WhAction"] if not WhShow: NumCols -= 1 T["WhIcon1"] T["WhIcon2"] if not ScAllowed: T["ScAction"] if not ScShow: NumCols -= 1 T["SCIcon1"] T["SCIcon2"] if FltAllowed: T["FilterOptions"] = CgiUtil.getFilterOptions() else: T["FilterOptions"] = "" T["NumCols"] = NumCols if len(Attachment.HTML) == 0: T["NoAttachments"] # Display HTML page with email included. print T
def Show(): "Handle installation." from TMDA import Util # Make a substitution dictionary if os.environ.has_key( "USER" ): user = os.environ["USER"] elif os.environ.has_key( "LOGNAME" ): user = os.environ["LOGNAME"] global Dict Dict = \ { "Base": os.path.abspath(os.environ["TMDA_BASE_DIR"]), "CryptKey": KeyGen(), "Domain": Util.gethostname(), "Home": os.environ["HOME"], "Name": repr(PVars["NAME"]), "Parent": "..", "RealHome": pwd.getpwuid(os.geteuid())[5], "UrlDomain": os.environ["SERVER_NAME"], "User": user, "VPop": PVars[("NoOverride", "VPop")], "VPopBin": PVars[("NoOverride", "VPopBin")] } Dict["ShortUrlDom"] = re.sub("^www\.", "", Dict["UrlDomain"], re.I) Dict["qUser"] = re.sub("\.", ":", Dict["User"]) Match = re.search(".*/([^\./]+\.[^/]+)/[^/]+/?$", Dict["Home"]) if Match: Dict["Domain"] = Match.group(1) # Load the display template if Form["cmd"].value == "conf-example": TemplateFN = "conf-example.html" elif Form["cmd"].value == "faq": TemplateFN = "faq.html" elif Form["cmd"].value == "install": if not Util.CanWrite(os.environ["HOME"]): CgiUtil.TermError("Can't write to home dir.", "No write permissions.", "installing", CgiUtil.FileDetails("home directory", os.environ["HOME"]), "Check file permissions in home directory.") Install() # Does not return. elif Form["cmd"].value == "restore": Restore() # Does not return. elif Form["cmd"].value == "uninstall": if PVars[("NoOverride", "MayInstall")][0].lower() == "n": CgiUtil.TermError("No permission.", "Uninstallation disabled by sysadmin.", "uninstall", "", "Contact system administrator.") if Form.has_key("subcmd"): try: ReleaseAndDelete(Form["release"].value) except Errors.QueueError: pass Uninstall() # Does not return. else: TemplateFN = "uninstall.html" elif Form["cmd"].value == "welcome": TemplateFN = "welcome.html" else: # Have they installed before? if Util.CanRead(os.path.join(os.environ["HOME"], PVars[("NoOverride", "UninstallBackupTGZ")]), os.geteuid(), os.getegid()): TemplateFN = "re-enroll.html" else: TemplateFN = "welcome.html" # Load template T = Template.Template(TemplateFN) # Javascript confirmation? if PVars.has_key(["General", "UseJSConfirm"]) and \ PVars[("General", "UseJSConfirm")] == "Yes": T["OnSubmit"] = 'onSubmit="return JSConfirm()"' else: T["OnSubmit"] = "" # Display template print T
def __suid__(self, User): """Try to change to a new user. User can be "root", "web", or "user". "root" attempts to setuid to root. "web" attempts to seteuid to the base RUID. "user" attempt to setuid to Vars["UID"]. An exception will be thrown if __suid_ is called after __suid__("user"). __suid__ reports an error if we can't change IDs, but should be able to.""" # Don't allow "user" to be called twice if User == "user": if self.RealUser: raise OSError, "Cannot setuid twice." self.RealUser = 1 if os.environ["TMDA_CGI_MODE"] == "system-wide": try: # First, strip off other users os.seteuid(0) os.setegid(0) # If they wanted "root", we're done if User == "root": os.setuid(0) # If they want "web", go find out who that is elif User == "web": PasswordRecord = pwd.getpwuid(WebUID) UID = PasswordRecord[2] GID = PasswordRecord[3] if not UID: CgiUtil.TermError( "CGI_USER is UID 0.", "It is not safe to allow " "root to process session files.", "set euid", "", "Do not run your webserver as root.") os.setegid(GID) os.seteuid(UID) # If they want "user", go do it elif User == "user": os.setgid(int(self.Vars["GID"])) os.setuid(int(self.Vars["UID"])) except OSError: CgiUtil.TermError( "Cannot SUID.", "File permissions on the CGI have " "been changed or the CGI is located in a nosuid partition.", "set euid", "", """Recheck the CGI's permissions and owner. The file permissions should be 6711 (-rws--s--x) and the owner should be root.<br>Also check in which partition you placed the CGI. You cannot run the CGI in system-wide mode if its partition is marked "nosuid" in /etc/fstab.""") else: if not os.geteuid(): if os.environ["TMDA_CGI_MODE"] == "single-user": Detail = """The file permissions should be 6711 (-rws--s--x) and the owner should <b><i>not</i></b> be root.""" else: Detail = "The file permissions should be 711 (-rwx--x--x)." CgiUtil.TermError( "Running as root.", "CGI should not be running as " "root. This is unsafe.", "set euid", "", "Recheck the CGI's permissions and owner. %s" % Detail)
# Check code for syntax errors BEFORE saving try: try: exec(Config) except (ImportError, NameError): pass T["ErrRow"] F = open(Defaults.TMDARC, "w") F.write(Config) F.close() except SyntaxError, (ErrStr, Details): T["ErrStr"] = "SyntaxError: line %d, char %d<br>(%s)" % Details[ 1:4] T["FileContents"] = Config except IOError, ErrStr: CgiUtil.TermError( "Unable to save config file.", "Insufficient privileges", "save config", "%s<br>%s" % (ErrStr, CgiUtil.FileDetails("Local config", Defaults.TMDARC)), "Change file permissions on <tt>%s</tt>" % Defaults.TMDARC) # Did we find any errors? if ErrStr: T["PathRow"] # Hide path else: T["ErrRow"] # Hide error # Display template print T
def Show(): "Edit any plaintext list file." # Load the display template T = Template.Template("editlist.html") NoneList = T["NoneList"] List = "" # Find pre-calculated buttons Filename = os.path.join \ ( PVars.ThemesDir, PVars[("General", "Theme")], "subtopics.p" ) F = open(Filename) SysButtons = pickle.load(F) F.close() # Load filters, find files used Buttons = {} for Filters in [Defaults.FILTER_INCOMING, Defaults.FILTER_OUTGOING]: Parser = FilterParser.FilterParser() Parser.read(CgiUtil.ExpandUser(Filters)) for Test in Parser.filterlist: if Test[0] in ["from-file", "to-file", "body-file", "headers-file"]: if CgiUtil.TestTextFilePath(CgiUtil.ExpandUser(Test[2])): Filename = os.path.split(Test[2])[1].lower() if SysButtons.has_key(Filename): Buttons[CgiUtil.ExpandUser(Test[2])] = \ (Filename, SysButtons[Filename]) else: Buttons[CgiUtil.ExpandUser(Test[2])] = \ (Filename, SysButtons["other"]) Files = Buttons.keys() Files.sort() if len(Files): # Which filter are we editing? if Form["cmd"].value == "editlist": EditFile = Files[0] else: EditFile = Files[int(Form["cmd"].value[8:])] # Get file T["FilePath"] = EditFile if CgiUtil.TestTextFilePath(EditFile): try: F = open(EditFile) List = F.read() F.close() except IOError: pass else: List = "(File is not accessible.)" else: # The user has no text-based lists defined in their filters. T["NoLists"] NoneList.Add() # Generate dynamic list of Lists to edit in the sidebar HTML = "" CurrentListEntry = T['CurrentListEntry'] ListEntry = T['ListEntry'] for FileNum in range(len(Files)): File = Files[FileNum] listDict = {} listDict['Theme'] = Template.Template.Dict["ThemeDir"] if File == EditFile: listDict["listGraphicFilename"] = Buttons[File][1]['hfn'] listDict["listGraphicHeight"] = Buttons[File][1]['height'] listDict["listGraphicWidth"] = Buttons[File][1]['width'] listDict["listGraphicAlt"] = Buttons[File][0] listDict["listName"] = os.path.basename(File) HTML += CurrentListEntry % listDict T["ListNum"] = FileNum else: listDict["listLink"] = "%s?cmd=editlist%d&SID=%s" % (os.environ["SCRIPT_NAME"], FileNum, PVars.SID ) listDict["listGraphicFilename"] = Buttons[File][1]['fn'] listDict["listGraphicHeight"] = Buttons[File][1]['height'] listDict["listGraphicWidth"] = Buttons[File][1]['width'] listDict["listGraphicAlt"] = Buttons[File][0] listDict["listName"] = os.path.basename(File) HTML += ListEntry % listDict T["Lists"] = HTML # Any subcommand? if Form.has_key("subcmd"): if Form["subcmd"].value == "sort": List = List.split("\n") List.sort(lambda a, b: cmp(a.upper(), b.upper())) List = "\n".join(List) elif Form["subcmd"].value == "domsort": List = List.split("\n") List.sort(DomainSort) List = "\n".join(List) else: if Form.has_key("list"): List = Form["list"].value elif Form["subcmd"].value == "save": List = "" # Make sure the list is properly formatted List = re.sub("\r\n", "\n", List) List = re.sub("\n*$", "", List) List = re.sub("^\n*", "", List) List += "\n" if CgiUtil.TestTextFilePath(EditFile): try: F = open(EditFile, "w") F.write(List) F.close() except IOError, ErrStr: CgiUtil.TermError("Unable to save filter list.", "Insufficient privileges", "save list", "%s<br>%s" % (ErrStr, CgiUtil.FileDetails("Filter list", EditFile)), "Change file permissions on <tt>%s</tt>" % EditFile)