Exemple #1
0
    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.")
Exemple #6
0
    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
Exemple #8
0
    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)
Exemple #10
0
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.)"
Exemple #11
0
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()
Exemple #12
0
                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
Exemple #14
0
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>
&nbsp;&nbsp;&nbsp;&#8226; Place <tt>%s</tt> in group %d.<br>
&nbsp;&nbsp;&nbsp;&#8226; Assign permissions 640 to <tt>%s</tt>.<br>
&nbsp;&nbsp;&nbsp;&#8226; 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()
Exemple #15
0
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
Exemple #16
0
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
Exemple #17
0
    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
Exemple #18
0
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 = []
        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 = []
Exemple #21
0
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", "&nbsp;<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)
Exemple #22
0
# 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
Exemple #23
0
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
Exemple #24
0
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
Exemple #25
0
    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)