def setgid(group): if group is None: return if not hasattr(os, 'setgid'): return # if root, setgid to the running user if os.getuid(): print(_('WARNING: ignoring "-g" argument, not root')) return try: import grp except ImportError: raise ValueError(_("Can't change groups - no grp module")) try: try: gid = int(group) except ValueError: gid = grp.getgrnam(group)[2] else: grp.getgrgid(gid) except KeyError: raise ValueError(_("Group %(group)s doesn't exist") % locals()) os.setgid(gid)
def loadTemplateInfo(dir): ''' Attempt to load a Roundup template from the indicated directory. Return None if there's no template, otherwise a template info dictionary. ''' ti = os.path.join(dir, 'TEMPLATE-INFO.txt') if not os.path.exists(ti): return None if os.path.exists(os.path.join(dir, 'config.py')): print _("WARNING: directory '%s'\n" "\tcontains old-style template - ignored" ) % os.path.abspath(dir) return None # load up the template's information f = open(ti) try: m = rfc822.Message(open(ti)) ti = {} ti['name'] = m['name'] ti['description'] = m['description'] ti['intended-for'] = m['intended-for'] ti['path'] = dir finally: f.close() return ti
def loadTemplateInfo(path): ''' Attempt to load a Roundup template from the indicated directory. Return None if there's no template, otherwise a template info dictionary. ''' tif = os.path.join(path, 'TEMPLATE-INFO.txt') if not os.path.exists(tif): return None if os.path.exists(os.path.join(path, 'config.py')): print _( "WARNING: directory '%s'\n" "\tcontains old-style template - ignored") % os.path.abspath(path) return None # load up the template's information try: f = open(tif) m = email.parser.Parser().parse(f, True) ti = {} ti['name'] = m['name'] ti['description'] = m['description'] ti['intended-for'] = m['intended-for'] ti['path'] = path finally: f.close() return ti
def rawToHyperdb(db, klass, itemid, propname, value, **kw): ''' Convert the raw (user-input) value to a hyperdb-storable value. The value is for the "propname" property on itemid (may be None for a new item) of "klass" in "db". The value is usually a string, but in the case of multilink inputs it may be either a list of strings or a string with comma-separated values. ''' properties = klass.getprops() # ensure it's a valid property name propname = propname.strip() try: proptype = properties[propname] except KeyError: raise HyperdbValueError, _('%r is not a property of %s') % ( propname, klass.classname) # if we got a string, strip it now if isinstance(value, type('')): value = value.strip() # convert the input value to a real property value value = proptype.from_raw(value, db=db, klass=klass, propname=propname, itemid=itemid, **kw) return value
def run(): home = DEFAULT_HOME template = DEFAULT_TEMPLATE nuke = sys.argv[-1] == 'nuke' # if there is no tracker in home, force nuke try: instance.open(home) except configuration.NoConfigError: nuke = 1 # if we are to create the tracker, prompt for home if nuke: if len(sys.argv) > 2: backend = sys.argv[-2] else: backend = 'anydbm' # FIXME: i'd like to have an option to abort the tracker creation # say, by entering a single dot. but i cannot think of # appropriate prompt for that. home = raw_input( _('Enter directory path to create demo tracker [%s]: ') % home) if not home: home = DEFAULT_HOME templates = admin.AdminTool().listTemplates().keys() template = raw_input( _('Enter tracker template to use (one of (%s)) [%s]: ') % (','.join(templates),template)) if not template: template = DEFAULT_TEMPLATE # install demo.install_demo(home, backend, admin.AdminTool().listTemplates()[template]['path']) # run demo.run_demo(home)
def setorderprop(self, orderprop): """Set the order property. Used for override of orderprop resolution order """ if orderprop not in self.getprops(): raise ValueError, _("Not a property name: %s") % orderprop self._orderprop = orderprop
def run(): home = DEFAULT_HOME template = DEFAULT_TEMPLATE nuke = sys.argv[-1] == 'nuke' # if there is no tracker in home, force nuke try: instance.open(home) except configuration.NoConfigError: nuke = 1 # if we are to create the tracker, prompt for home if nuke: if len(sys.argv) > 2: backend = sys.argv[-2] else: backend = 'anydbm' # FIXME: i'd like to have an option to abort the tracker creation # say, by entering a single dot. but i cannot think of # appropriate prompt for that. home = my_input( _('Enter directory path to create demo tracker [%s]: ') % home) if not home: home = DEFAULT_HOME templates = admin.AdminTool().listTemplates().keys() template = my_input( _('Enter tracker template to use (one of (%s)) [%s]: ') % (','.join(templates),template)) if not template: template = DEFAULT_TEMPLATE # install demo.install_demo(home, backend, admin.AdminTool().listTemplates()[template]['path']) # run demo.run_demo(home)
def setuid(user): if not hasattr(os, 'getuid'): return # People can remove this check if they're really determined if user is None: if os.getuid(): return raise ValueError(_("Can't run as root!")) if os.getuid(): print(_('WARNING: ignoring "-u" argument, not root')) return try: import pwd except ImportError: raise ValueError(_("Can't change users - no pwd module")) try: try: uid = int(user) except ValueError: uid = pwd.getpwnam(user)[2] else: pwd.getpwuid(uid) except KeyError: raise ValueError(_("User %(user)s doesn't exist") % locals()) os.setuid(uid)
def run(): # return unless command line arguments contain single directory path if (len(sys.argv) != 2) or (sys.argv[1] in ("-h", "--help")): print _("Usage: %(program)s <tracker home>") % {"program": sys.argv[0]} return # collect file paths of html templates home = os.path.abspath(sys.argv[1]) htmldir = os.path.join(home, "html") if os.path.isdir(htmldir): # glob is not used because i want to match file names # without case sensitivity, and that is easier done this way. htmlfiles = [filename for filename in os.listdir(htmldir) if os.path.isfile(os.path.join(htmldir, filename)) and filename.lower().endswith(".html")] else: htmlfiles = [] # return if no html files found if not htmlfiles: print _("No tracker templates found in directory %s") % home return # change to locale dir to have relative source references locale = os.path.join(home, "locale") if not os.path.isdir(locale): os.mkdir(locale) os.chdir(locale) # tweak sys.argv as this is the only way to tell talgettext what to do # Note: unix-style paths used instead of os.path.join deliberately sys.argv[1:] = ["-o", TEMPLATE_FILE] \ + ["../html/" + filename for filename in htmlfiles] # run talgettext.main()
def rawToHyperdb(db, klass, itemid, propname, value, **kw): ''' Convert the raw (user-input) value to a hyperdb-storable value. The value is for the "propname" property on itemid (may be None for a new item) of "klass" in "db". The value is usually a string, but in the case of multilink inputs it may be either a list of strings or a string with comma-separated values. ''' properties = klass.getprops() # ensure it's a valid property name propname = propname.strip() try: proptype = properties[propname] except KeyError: raise HyperdbValueError, _('%r is not a property of %s')%(propname, klass.classname) # if we got a string, strip it now if isinstance(value, type('')): value = value.strip() # convert the input value to a real property value value = proptype.from_raw(value, db=db, klass=klass, propname=propname, itemid=itemid, **kw) return value
def run(): # return unless command line arguments contain single directory path if (len(sys.argv) != 2) or (sys.argv[1] in ("-h", "--help")): print _("Usage: %(program)s <tracker home>") % {"program": sys.argv[0]} return # collect file paths of html templates home = os.path.abspath(sys.argv[1]) htmldir = os.path.join(home, "html") if os.path.isdir(htmldir): # glob is not used because i want to match file names # without case sensitivity, and that is easier done this way. htmlfiles = [ filename for filename in os.listdir(htmldir) if os.path.isfile(os.path.join(htmldir, filename)) and filename.lower().endswith(".html") ] else: htmlfiles = [] # return if no html files found if not htmlfiles: print _("No tracker templates found in directory %s") % home return # change to locale dir to have relative source references locale = os.path.join(home, "locale") if not os.path.isdir(locale): os.mkdir(locale) os.chdir(locale) # tweak sys.argv as this is the only way to tell talgettext what to do # Note: unix-style paths used instead of os.path.join deliberately sys.argv[1:] = ["-o", TEMPLATE_FILE] \ + ["../html/" + filename for filename in htmlfiles] # run talgettext.main()
def setlabelprop(self, labelprop): """Set the label property. Used for override of labelprop resolution order. """ if labelprop not in self.getprops(): raise ValueError, _("Not a property name: %s") % labelprop self._labelprop = labelprop
def loadTemplateInfo(path): ''' Attempt to load a Roundup template from the indicated directory. Return None if there's no template, otherwise a template info dictionary. ''' tif = os.path.join(path, 'TEMPLATE-INFO.txt') if not os.path.exists(tif): return None if os.path.exists(os.path.join(path, 'config.py')): print _("WARNING: directory '%s'\n" "\tcontains old-style template - ignored" ) % os.path.abspath(path) return None # load up the template's information try: f = open(tif) m = email.parser.Parser().parse(f, True) ti = {} ti['name'] = m['name'] ti['description'] = m['description'] ti['intended-for'] = m['intended-for'] ti['path'] = path finally: f.close() return ti
def splitDesignator(designator, dre=re.compile(r'([^\d]+)(\d+)')): ''' Take a foo123 and return ('foo', 123) ''' m = dre.match(designator) if m is None: raise DesignatorError, _('"%s" not a node designator')%designator return m.group(1), m.group(2)
def splitDesignator(designator, dre=re.compile(r'([^\d]+)(\d+)')): ''' Take a foo123 and return ('foo', 123) ''' m = dre.match(designator) if m is None: raise DesignatorError, _('"%s" not a node designator') % designator return m.group(1), m.group(2)
def get_server(self): """Return HTTP server object to run""" # we don't want the cgi module interpreting the command-line args ;) sys.argv = sys.argv[:1] # preload all trackers unless we are in "debug" mode tracker_homes = self.trackers() if self["MULTIPROCESS"] == "debug": trackers = None else: trackers = dict([(name, roundup.instance.open(home, optimize=1)) for (name, home) in tracker_homes]) # build customized request handler class class RequestHandler(RoundupRequestHandler): LOG_IPADDRESS = not self["LOG_HOSTNAMES"] TRACKER_HOMES = dict(tracker_homes) TRACKERS = trackers DEBUG_MODE = self["MULTIPROCESS"] == "debug" CONFIG = self if self["SSL"]: base_server = SecureHTTPServer else: base_server = BaseHTTPServer.HTTPServer # obtain request server class if self["MULTIPROCESS"] not in MULTIPROCESS_TYPES: print _("Multiprocess mode \"%s\" is not available, " "switching to single-process") % self["MULTIPROCESS"] self["MULTIPROCESS"] = "none" server_class = base_server elif self["MULTIPROCESS"] == "fork": class ForkingServer(SocketServer.ForkingMixIn, base_server): pass server_class = ForkingServer elif self["MULTIPROCESS"] == "thread": class ThreadingServer(SocketServer.ThreadingMixIn, base_server): pass server_class = ThreadingServer else: server_class = base_server # obtain server before changing user id - allows to # use port < 1024 if started as root try: args = ((self["HOST"], self["PORT"]), RequestHandler) kwargs = {} if self["SSL"]: kwargs['ssl_pem'] = self["PEM"] httpd = server_class(*args, **kwargs) except socket.error, e: if e[0] == errno.EADDRINUSE: raise socket.error, \ _("Unable to bind to port %s, port already in use.") \ % self["PORT"] raise
def from_raw(self, value, **kw): value = value.strip() try: value = float(value) except ValueError: raise HyperdbValueError, _('property %s: %r is not a number')%( kw['propname'], value) return value
def timecheck(self, field, delay): try: created = unpack_timestamp(self.form[field].value) except KeyError: raise FormError(_("Form is corrupted, missing: %s." % field)) if time.time() - created < delay: raise FormError(_("Responding to form too quickly.")) return True
def from_raw(self, value, **kw): value = value.strip() try: value = float(value) except ValueError: raise HyperdbValueError, _('property %s: %r is not a number') % ( kw['propname'], value) return value
def create_client( ): tracker = valid_dir(DBPATH ) if not tracker: print _("There is no correct roundup data directory!") client = None else: try: client = ajaxClient.Client(instance=tracker) except: client = None return client
def convertLinkValue(db, propname, prop, value, idre=re.compile('^\d+$')): ''' Convert the link value (may be id or key value) to an id value. ''' linkcl = db.classes[prop.classname] if not idre.match(value): if linkcl.getkey(): try: value = linkcl.lookup(value) except KeyError, message: raise HyperdbValueError, _('property %s: %r is not a %s.') % ( propname, value, prop.classname) else: raise HyperdbValueError, _('you may only enter ID values '\ 'for property %s')%propname
def local_user_exists(self): """Verify if the given user exists. As a side effect set the 'client.userid'.""" # make sure the user exists try: self.client.userid = self.db.user.lookup(self.client.user) except KeyError: msg = _("Unknown user '%s'") % self.client.user self.LOG.debug("__['%s'", msg) self.client.error_message.append( _("Unknown user '%s'") % self.client.user) return False return True
def convertLinkValue(db, propname, prop, value, idre=re.compile('^\d+$')): ''' Convert the link value (may be id or key value) to an id value. ''' linkcl = db.classes[prop.classname] if not idre.match(value): if linkcl.getkey(): try: value = linkcl.lookup(value) except KeyError, message: raise HyperdbValueError, _('property %s: %r is not a %s.')%( propname, value, prop.classname) else: raise HyperdbValueError, _('you may only enter ID values '\ 'for property %s')%propname
def setuid(user): if not hasattr(os, 'getuid'): return # People can remove this check if they're really determined if user is None: if os.getuid(): return raise ValueError, _("Can't run as root!") if os.getuid(): print _('WARNING: ignoring "-u" argument, not root') return try: import pwd except ImportError: raise ValueError, _("Can't change users - no pwd module") try: try: uid = int(user) except ValueError: uid = pwd.getpwnam(user)[2] else: pwd.getpwuid(uid) except KeyError: raise ValueError, _("User %(user)s doesn't exist")%locals() os.setuid(uid)
def index(self): ''' Print up an index of the available trackers ''' keys = list(self.TRACKER_HOMES.keys()) if len(keys) == 1: self.send_response(302) self.send_header('Location', urllib_.quote(keys[0]) + '/index') self.end_headers() else: self.send_response(200) self.send_header('Content-Type', 'text/html') self.end_headers() w = self.wfile.write if self.CONFIG and self.CONFIG['TEMPLATE']: template = open(self.CONFIG['TEMPLATE']).read() pt = PageTemplate() pt.write(template) extra = { 'trackers': self.TRACKERS, 'nothing' : None, 'true' : 1, 'false' : 0, } w(s2b(pt.pt_render(extra_context=extra))) else: w(s2b(_('<html><head><title>Roundup trackers index</title></head>\n' '<body><h1>Roundup trackers index</h1><ol>\n'))) keys.sort() for tracker in keys: w(s2b('<li><a href="%(tracker_url)s/index">%(tracker_name)s</a>\n'%{ 'tracker_url': urllib_.quote(tracker), 'tracker_name': html_escape(tracker)})) w(b'</ol></body></html>')
def cache_db_type(self, path): ''' determine which DB wrote the class file, and cache it as an attribute of __class__ (to allow for subclassed DBs to be different sorts) ''' db_type = '' if os.path.exists(path): db_type = whichdb.whichdb(path) if not db_type: raise hyperdb.DatabaseError, \ _("Couldn't identify database type") elif os.path.exists(path+'.db'): # if the path ends in '.db', it's a dbm database, whether # anydbm says it's dbhash or not! db_type = 'dbm' self.__class__._db_type = db_type
def index(self): ''' Print up an index of the available trackers ''' keys = self.TRACKER_HOMES.keys() if len(keys) == 1: self.send_response(302) self.send_header('Location', urllib.quote(keys[0]) + '/index') self.end_headers() else: self.send_response(200) self.send_header('Content-Type', 'text/html') self.end_headers() w = self.wfile.write if self.CONFIG and self.CONFIG['TEMPLATE']: template = open(self.CONFIG['TEMPLATE']).read() pt = PageTemplate() pt.write(template) extra = { 'trackers': self.TRACKERS, 'nothing' : None, 'true' : 1, 'false' : 0, } w(pt.pt_render(extra_context=extra)) else: w(_('<html><head><title>Roundup trackers index</title></head>\n' '<body><h1>Roundup trackers index</h1><ol>\n')) keys.sort() for tracker in keys: w('<li><a href="%(tracker_url)s/index">%(tracker_name)s</a>\n'%{ 'tracker_url': urllib.quote(tracker), 'tracker_name': cgi.escape(tracker)}) w('</ol></body></html>')
class NewItemAction(EditCommon): def handle(self): ''' Add a new item to the database. This follows the same form as the EditItemAction, with the same special form values. ''' # ensure modification comes via POST if self.client.env['REQUEST_METHOD'] != 'POST': raise roundup.exceptions.Reject(self._('Invalid request')) # parse the props from the form try: props, links = self.client.parsePropsFromForm(create=1) except (ValueError, KeyError), message: self.client.add_error_message(self._('Error: %s') % str(message)) return # handle the props - edit or create try: # when it hits the None element, it'll set self.nodeid messages = self._editnodes(props, links) except (ValueError, KeyError, IndexError, roundup.exceptions.Reject), message: # these errors might just be indicative of user dumbness self.client.add_error_message(_('Error: %s') % str(message)) return
def encrypt_to(self, message, sendto): """ Encrypt given message to sendto receivers. Returns a new RFC 3156 conforming message. """ plain = gpg.core.Data(message.as_string()) cipher = gpg.core.Data() ctx = gpg.core.Context() ctx.set_armor(1) keys = [] for adr in sendto: ctx.op_keylist_start(adr, 0) # only first key per email k = ctx.op_keylist_next() if k is not None: keys.append(k) else: msg = _('No key for "%(adr)s" in keyring') % locals() raise MessageSendError(msg) ctx.op_keylist_end() ctx.op_encrypt(keys, 1, plain, cipher) cipher.seek(0, 0) msg = MIMEMultipart('encrypted', boundary=None, _subparts=None, protocol="application/pgp-encrypted") part = MIMEBase('application', 'pgp-encrypted') part.set_payload("Version: 1\r\n") msg.attach(part) part = MIMEBase('application', 'octet-stream') part.set_payload(cipher.read()) msg.attach(part) return msg
def verifyLogin(self, username, password): """Verify the login of `username` with `password`. Try first LDAP if this is specified as authentication source, and then login against local database.""" self.LOG.debug("username=%s password=%s", username, '*' * len(password)) authenticated = False if not self.use_local_auth: self.LOG.debug("LDAP authentication") authenticated = self.ldap_login(username, password) if authenticated: self.LOG.debug("User '%s' authenticated against LDAP.", username) if not authenticated: self.LOG.debug("Local database authentication") authenticated = self.local_login(password) if authenticated: self.LOG.debug( "User '%s' authenticated against local database.", username) if not authenticated: msg = _("Could not authenticate user '%s'" % username) self.LOG.debug(msg) raise exceptions.LoginError, msg return authenticated
def cache_db_type(self, path): ''' determine which DB wrote the class file, and cache it as an attribute of __class__ (to allow for subclassed DBs to be different sorts) ''' db_type = '' if os.path.exists(path): db_type = whichdb.whichdb(path) if not db_type: raise hyperdb.DatabaseError, \ _("Couldn't identify database type") elif os.path.exists(path + '.db'): # if the path ends in '.db', it's a dbm database, whether # anydbm says it's dbhash or not! db_type = 'dbm' self.__class__._db_type = db_type
def encrypt_to(self, message, sendto): """ Encrypt given message to sendto receivers. Returns a new RFC 3156 conforming message. """ plain = pyme.core.Data(message.as_string()) cipher = pyme.core.Data() ctx = pyme.core.Context() ctx.set_armor(1) keys = [] for adr in sendto: ctx.op_keylist_start(adr, 0) # only first key per email k = ctx.op_keylist_next() if k is not None: keys.append(k) else: msg = _('No key for "%(adr)s" in keyring')%locals() raise MessageSendError, msg ctx.op_keylist_end() ctx.op_encrypt(keys, 1, plain, cipher) cipher.seek(0,0) msg = MIMEMultipart('encrypted', boundary=None, _subparts=None, protocol="application/pgp-encrypted") part = MIMEBase('application', 'pgp-encrypted') part.set_payload("Version: 1\r\n") msg.attach(part) part = MIMEBase('application', 'octet-stream') part.set_payload(cipher.read()) msg.attach(part) return msg
def find(self, wordlist): """look up all the words in the wordlist. For testing wordlist is actually a list. In production, wordlist is a list of a single string that is a sqlite MATCH query. https://www.sqlite.org/fts5.html#full_text_query_syntax """ if not wordlist: return [] a = self.db.arg # arg is the token for positional parameters # removed filtering of word in wordlist to include only # words with: self.minlength <= len(word) <= self.maxlength sql = 'select _class, _itemid, _prop from __fts '\ 'where _textblob MATCH %s' % a try: # tests supply a multi element word list. Join them. self.db.cursor.execute(sql, (" ".join(wordlist),)) except sqlite.OperationalError as e: if 'no such column' in e.args[0]: raise IndexerQueryError( _("Search failed. Try quoting any terms that " "include a '-' and retry the search.")) else: raise IndexerQueryError(e.args[0].replace("fts5:", "Query error:")) return self.db.cursor.fetchall()
def index(self): """ Print up an index of the available trackers """ keys = self.TRACKER_HOMES.keys() if len(keys) == 1: self.send_response(302) self.send_header("Location", urllib.quote(keys[0]) + "/index") self.end_headers() else: self.send_response(200) self.send_header("Content-Type", "text/html") self.end_headers() w = self.wfile.write if self.CONFIG and self.CONFIG["TEMPLATE"]: template = open(self.CONFIG["TEMPLATE"]).read() pt = PageTemplate() pt.write(template) extra = {"trackers": self.TRACKERS, "nothing": None, "true": 1, "false": 0} w(pt.pt_render(extra_context=extra)) else: w( _( "<html><head><title>Roundup trackers index</title></head>\n" "<body><h1>Roundup trackers index</h1><ol>\n" ) ) keys.sort() for tracker in keys: w( '<li><a href="%(tracker_url)s/index">%(tracker_name)s</a>\n' % {"tracker_url": urllib.quote(tracker), "tracker_name": cgi.escape(tracker)} ) w("</ol></body></html>")
def local_login(self, password): """Try local authentication.""" self.auth_method = 'localdb' if not self.local_user_exists(): return LOGIN_FAILED if not self.verifyPassword(self.client.userid, password): msg = _('Invalid password') self.LOG.warning("%s for userid=%s", msg, self.client.userid) self.client.error_message.append(msg) return LOGIN_FAILED # Determine whether the user has permission to log in. Base behaviour # is to check the user has "Web Access". rights = "Web Access" if not self.hasPermission(rights): msg = _("You do not have permission to login") self.LOG.debug("%s, %s, %s", msg, self.client.user, rights) raise exceptions.LoginError, msg return LOGIN_SUCCEDED
def auto_ssl(): print _('WARNING: generating temporary SSL certificate') import OpenSSL, random pkey = OpenSSL.crypto.PKey() pkey.generate_key(OpenSSL.crypto.TYPE_RSA, 768) cert = OpenSSL.crypto.X509() cert.set_serial_number(random.randint(0, sys.maxint)) cert.gmtime_adj_notBefore(0) cert.gmtime_adj_notAfter(60 * 60 * 24 * 365) # one year cert.get_subject().CN = '*' cert.get_subject().O = 'Roundup Dummy Certificate' cert.get_issuer().CN = 'Roundup Dummy Certificate Authority' cert.get_issuer().O = 'Self-Signed' cert.set_pubkey(pkey) cert.sign(pkey, 'md5') ctx = SSL.Context(SSL.SSLv23_METHOD) ctx.use_privatekey(pkey) ctx.use_certificate(cert) return ctx
def auto_ssl(): print _('WARNING: generating temporary SSL certificate') import OpenSSL, time, random, sys pkey = OpenSSL.crypto.PKey() pkey.generate_key(OpenSSL.crypto.TYPE_RSA, 768) cert = OpenSSL.crypto.X509() cert.set_serial_number(random.randint(0, sys.maxint)) cert.gmtime_adj_notBefore(0) cert.gmtime_adj_notAfter(60 * 60 * 24 * 365) # one year cert.get_subject().CN = '*' cert.get_subject().O = 'Roundup Dummy Certificate' cert.get_issuer().CN = 'Roundup Dummy Certificate Authority' cert.get_issuer().O = 'Self-Signed' cert.set_pubkey(pkey) cert.sign(pkey, 'md5') ctx = SSL.Context(SSL.SSLv23_METHOD) ctx.use_privatekey(pkey) ctx.use_certificate(cert) return ctx
def from_raw(self, value, **kw): if not value: return None m = password.Password.pwre.match(value) if m: # password is being given to us encrypted p = password.Password() p.scheme = m.group(1) if p.scheme not in 'SHA crypt plaintext'.split(): raise HyperdbValueError, \ ('property %s: unknown encryption scheme %r') %\ (kw['propname'], p.scheme) p.password = m.group(2) value = p else: try: value = password.Password(value) except password.PasswordValueError, message: raise HyperdbValueError, \ _('property %s: %s')%(kw['propname'], message)
def create_client( ): tracker = valid_dir(DBPATH ) if not tracker: PRINT( _("There is no correct roundup data directory!")) client = (CONFIG.data_dir, DBPATH) else: try: client = ajaxClient.Client(instance=tracker) except: client = None return client
def setgid(group): if group is None: return if not hasattr(os, 'setgid'): return # if root, setgid to the running user if os.getuid(): print _('WARNING: ignoring "-g" argument, not root') return try: import grp except ImportError: raise ValueError, _("Can't change groups - no grp module") try: try: gid = int(group) except ValueError: gid = grp.getgrnam(group)[2] else: grp.getgrgid(gid) except KeyError: raise ValueError,_("Group %(group)s doesn't exist")%locals() os.setgid(gid)
class RegisterAction(RegoCommon, EditCommon): name = 'register' permissionType = 'Create' def handle(self): """Attempt to create a new user based on the contents of the form and then set the cookie. Return 1 on successful login. """ # parse the props from the form try: props, links = self.client.parsePropsFromForm(create=1) except (ValueError, KeyError), message: self.client.error_message.append( self._('Error: %s') % str(message)) return # registration isn't allowed to supply roles user_props = props[('user', None)] if user_props.has_key('roles'): raise exceptions.Unauthorised, self._( "It is not permitted to supply roles at registration.") # skip the confirmation step? if self.db.config['INSTANT_REGISTRATION']: # handle the create now try: # when it hits the None element, it'll set self.nodeid messages = self._editnodes(props, links) except (ValueError, KeyError, IndexError, roundup.exceptions.Reject), message: # these errors might just be indicative of user dumbness self.client.error_message.append(_('Error: %s') % str(message)) return # fix up the initial roles self.db.user.set(self.nodeid, roles=self.db.config['NEW_WEB_USER_ROLES']) # commit now that all the tricky stuff is done self.db.commit() # finish off by logging the user in self.userid = self.nodeid return self.finishRego()
class RegisterAction(RegoCommon, EditCommon): name = 'register' permissionType = 'Register' def handle(self): """Attempt to create a new user based on the contents of the form and then remember it in session. Return 1 on successful login. """ # ensure modification comes via POST if self.client.env['REQUEST_METHOD'] != 'POST': raise roundup.exceptions.Reject(self._('Invalid request')) # parse the props from the form try: props, links = self.client.parsePropsFromForm(create=1) except (ValueError, KeyError), message: self.client.add_error_message(self._('Error: %s') % str(message)) return # skip the confirmation step? if self.db.config['INSTANT_REGISTRATION']: # handle the create now try: # when it hits the None element, it'll set self.nodeid messages = self._editnodes(props, links) except (ValueError, KeyError, IndexError, roundup.exceptions.Reject), message: # these errors might just be indicative of user dumbness self.client.add_error_message(_('Error: %s') % str(message)) return # fix up the initial roles self.db.user.set(self.nodeid, roles=self.db.config['NEW_WEB_USER_ROLES']) # commit now that all the tricky stuff is done self.db.commit() # finish off by logging the user in self.userid = self.nodeid return self.finishRego()
def confirm_registration(self, otk): props = self.getOTKManager().getall(otk) for propname, proptype in self.user.getprops().items(): value = props.get(propname, None) if value is None: pass elif isinstance(proptype, hyperdb.Date): props[propname] = date.Date(value) elif isinstance(proptype, hyperdb.Interval): props[propname] = date.Interval(value) elif isinstance(proptype, hyperdb.Password): props[propname] = password.Password(encrypted=value) # tag new user creation with 'admin' self.journaltag = 'admin' # create the new user cl = self.user props['roles'] = self.config.NEW_WEB_USER_ROLES try: # ASSUME:: ValueError raised during create due to key value # conflict. I an use message in exception to determine # when I should intercept the exception with a more # friendly error message. If i18n is used to translate # original exception message this will fail and translated # text (probably unfriendly) will be used. userid = cl.create(**props) except ValueError as e: username = props['username'] # Try to make error message less cryptic to the user. if str(e) == 'node with key "%s" exists' % username: raise ValueError(_("Username '%s' already exists." % username)) else: raise # clear the props from the otk database self.getOTKManager().destroy(otk) # commit cl.create (and otk changes) self.commit() return userid
class NewItemAction(EditCommon): def handle(self): ''' Add a new item to the database. This follows the same form as the EditItemAction, with the same special form values. ''' # parse the props from the form try: props, links = self.client.parsePropsFromForm(create=1) except (ValueError, KeyError), message: self.client.error_message.append( self._('Error: %s') % str(message)) return # handle the props - edit or create try: # when it hits the None element, it'll set self.nodeid messages = self._editnodes(props, links) except (ValueError, KeyError, IndexError, roundup.exceptions.Reject), message: # these errors might just be indicative of user dumbness self.client.error_message.append(_('Error: %s') % str(message)) return
def from_raw(self, value, db, **kw): try: value = date.Date(value, self.offset(db)) except ValueError, message: raise HyperdbValueError, _('property %s: %r is an invalid '\ 'date (%s)')%(kw['propname'], value, message)
def usage(args, message=None): if message is not None: print(message) print( _("""Usage: %(program)s [-v] [-c class] [[-C class] -S field=value]* [instance home] [mail source [specification]] Options: -v: print version and exit -c: default class of item to create (else the tracker's MAIL_DEFAULT_CLASS) -C / -S: see below The roundup mail gateway may be called in one of the following ways: . without arguments. Then the env var ROUNDUP_INSTANCE will be tried. . with an instance home as the only argument, . with both an instance home and a mail spool file, . with an instance home, a mail source type and its specification. It also supports optional -C and -S arguments that allows you to set a fields for a class created by the roundup-mailgw. The default class if not specified is msg, but the other classes: issue, file, user can also be used. The -S or --set options uses the same property=value[;property=value] notation accepted by the command line roundup command or the commands that can be given on the Subject line of an email message. It can let you set the type of the message on a per email address basis. PIPE: If there is no mail source specified, the mail gateway reads a single message from the standard input and submits the message to the roundup.mailgw module. Mail source "mailbox": In this case, the gateway reads all messages from the UNIX mail spool file and submits each in turn to the roundup.mailgw module. The file is emptied once all messages have been successfully handled. The file is specified as: mailbox /path/to/mailbox In all of the following mail source type the username and password can be stored in a ~/.netrc file. If done so case only the server name need to be specified on the command-line. The username and/or password will be prompted for if not supplied on the command-line or in ~/.netrc. POP: For the mail source "pop", the gateway reads all messages from the POP server specified and submits each in turn to the roundup.mailgw module. The server is specified as: pop username:password@server Alternatively, one can omit one or both of username and password: pop username@server pop server are both valid. POPS: Connect to a POP server over ssl. This requires python 2.4 or later. This supports the same notation as POP. APOP: Same as POP, but using Authenticated POP: apop username:password@server IMAP: Connect to an IMAP server. This supports the same notation as that of POP mail. imap username:password@server It also allows you to specify a specific mailbox other than INBOX using this format: imap username:password@server mailbox IMAPS: Connect to an IMAP server over ssl. This supports the same notation as IMAP. imaps username:password@server [mailbox] IMAPS_CRAM: Connect to an IMAP server over ssl using CRAM-MD5 authentication. This supports the same notation as IMAP. imaps_cram username:password@server [mailbox] """) % {'program': args[0]}) return 1
def main(argv): '''Handle the arguments to the program and initialise environment. ''' # take the argv array and parse it leaving the non-option # arguments in the args array. try: optionsList, args = getopt.getopt(argv[1:], 'vc:C:S:', ['set=', 'class=']) except getopt.GetoptError: # print help information and exit: usage(argv) sys.exit(2) for (opt, _arg) in optionsList: if opt == '-v': print('%s (python %s)' % (roundup_version, sys.version.split()[0])) return # figure the instance home if len(args) > 0: instance_home = args[0] else: instance_home = os.environ.get('ROUNDUP_INSTANCE', '') if not (instance_home and os.path.isdir(instance_home)): return usage(argv) # get the instance import roundup.instance instance = roundup.instance.open(instance_home) if hasattr(instance, 'MailGW'): handler = instance.MailGW(instance, optionsList) else: handler = mailgw.MailGW(instance, optionsList) # if there's no more arguments, read a single message from stdin if len(args) == 1: return handler.do_pipe() # otherwise, figure what sort of mail source to handle if len(args) < 3: return usage(argv, _('Error: not enough source specification information')) source, specification = args[1:3] # time out net connections after a minute if we can if source not in ('mailbox', 'imaps', 'imaps_cram'): if hasattr(socket, 'setdefaulttimeout'): socket.setdefaulttimeout(60) if source == 'mailbox': return handler.do_mailbox(specification) # the source will be a network server, so obtain the credentials to # use in connecting to the server try: # attempt to obtain credentials from a ~/.netrc file authenticator = netrc.netrc().authenticators(specification) username = authenticator[0] password = authenticator[2] server = specification # IOError if no ~/.netrc file, TypeError if the hostname # not found in the ~/.netrc file: except (IOError, TypeError): match = re.match(r'((?P<user>[^:]+)(:(?P<pass>.+))?@)?(?P<server>.+)', specification) if match: username = match.group('user') password = match.group('pass') server = match.group('server') else: return usage(argv, _('Error: %s specification not valid') % source) # now invoke the mailgw handler depending on the server handler requested if source.startswith('pop'): ssl = source.endswith('s') return handler.do_pop(server, username, password, ssl) elif source == 'apop': return handler.do_apop(server, username, password) elif source.startswith('imap'): ssl = cram = 0 if source.endswith('s'): ssl = 1 elif source.endswith('s_cram'): ssl = cram = 1 mailbox = '' if len(args) > 3: mailbox = args[3] return handler.do_imap(server, username, password, mailbox, ssl, cram) return usage( argv, _('Error: The source must be either "mailbox",' ' "pop", "pops", "apop", "imap", "imaps" or' ' "imaps_cram'))
def usage(message=''): if RoundupService: os_part = \ ""''' -c <Command> Windows Service options. If you want to run the server as a Windows Service, you must use configuration file to specify tracker homes. Logfile option is required to run Roundup Tracker service. Typing "roundup-server -c help" shows Windows Services specifics.''' else: os_part = ""''' -u <UID> runs the Roundup web server as this UID -g <GID> runs the Roundup web server as this GID -d <PIDfile> run the server in the background and write the server's PID to the file indicated by PIDfile. The -l option *must* be specified if -d is used.''' if message: message += '\n' print _('''%(message)sUsage: roundup-server [options] [name=tracker home]* Options: -v print the Roundup version number and exit -h print this text and exit -S create or update configuration file and exit -C <fname> use configuration file <fname> -n <name> set the host name of the Roundup web server instance, specifies on which network interfaces to listen for connections, defaults to localhost, use 0.0.0.0 to bind to all network interfaces -p <port> set the port to listen on (default: %(port)s) -l <fname> log to the file indicated by fname instead of stderr/stdout -N log client machine names instead of IP addresses (much slower) -i <fname> set tracker index template -s enable SSL -e <fname> PEM file containing SSL key and certificate -t <mode> multiprocess mode (default: %(mp_def)s). Allowed values: %(mp_types)s. %(os_part)s Long options: --version print the Roundup version number and exit --help print this text and exit --save-config create or update configuration file and exit --config <fname> use configuration file <fname> All settings of the [main] section of the configuration file also may be specified in form --<name>=<value> Examples: roundup-server -S -C /opt/roundup/etc/roundup-server.ini \\ -n localhost -p 8917 -l /var/log/roundup.log \\ support=/var/spool/roundup-trackers/support roundup-server -C /opt/roundup/etc/roundup-server.ini roundup-server support=/var/spool/roundup-trackers/support roundup-server -d /var/run/roundup.pid -l /var/log/roundup.log \\ support=/var/spool/roundup-trackers/support Configuration file format: Roundup Server configuration file has common .ini file format. Configuration file created with 'roundup-server -S' contains detailed explanations for each option. Please see that file for option descriptions. How to use "name=tracker home": These arguments set the tracker home(s) to use. The name is how the tracker is identified in the URL (it's the first part of the URL path). The tracker home is the directory that was identified when you did "roundup-admin init". You may specify any number of these name=home pairs on the command-line. Make sure the name part doesn't include any url-unsafe characters like spaces, as these confuse IE. ''') % { "message": message, "os_part": os_part, "port": DEFAULT_PORT, "mp_def": DEFAULT_MULTIPROCESS, "mp_types": ", ".join(MULTIPROCESS_TYPES), }
def get_server(self): """Return HTTP server object to run""" # we don't want the cgi module interpreting the command-line args ;) sys.argv = sys.argv[:1] # preload all trackers unless we are in "debug" mode tracker_homes = self.trackers() if self["MULTIPROCESS"] == "debug": trackers = None else: trackers = dict([(name, roundup.instance.open(home, optimize=1)) for (name, home) in tracker_homes]) # build customized request handler class class RequestHandler(RoundupRequestHandler): LOG_IPADDRESS = not self["LOG_HOSTNAMES"] TRACKER_HOMES = dict(tracker_homes) TRACKERS = trackers DEBUG_MODE = self["MULTIPROCESS"] == "debug" CONFIG = self def setup(self): if self.CONFIG["SSL"]: # perform initial ssl handshake. This will set # internal state correctly so that later closing SSL # socket works (with SSL end-handshake started) self.request.do_handshake() RoundupRequestHandler.setup(self) def finish(self): RoundupRequestHandler.finish(self) if self.CONFIG["SSL"]: self.request.shutdown() self.request.close() if self["SSL"]: base_server = SecureHTTPServer else: # time out after a minute if we can # This sets the socket to non-blocking. SSL needs a blocking # socket, so we do this only for non-SSL connections. if hasattr(socket, 'setdefaulttimeout'): socket.setdefaulttimeout(60) base_server = BaseHTTPServer.HTTPServer # obtain request server class if self["MULTIPROCESS"] not in MULTIPROCESS_TYPES: print _("Multiprocess mode \"%s\" is not available, " "switching to single-process") % self["MULTIPROCESS"] self["MULTIPROCESS"] = "none" server_class = base_server elif self["MULTIPROCESS"] == "fork": class ForkingServer(SocketServer.ForkingMixIn, base_server): pass server_class = ForkingServer elif self["MULTIPROCESS"] == "thread": class ThreadingServer(SocketServer.ThreadingMixIn, base_server): pass server_class = ThreadingServer else: server_class = base_server # obtain server before changing user id - allows to # use port < 1024 if started as root try: args = ((self["HOST"], self["PORT"]), RequestHandler) kwargs = {} if self["SSL"]: kwargs['ssl_pem'] = self["PEM"] httpd = server_class(*args, **kwargs) except socket.error, e: if e[0] == errno.EADDRINUSE: raise socket.error, \ _("Unable to bind to port %s, port already in use.") \ % self["PORT"] raise
def main(argv): '''Handle the arguments to the program and initialise environment. ''' # take the argv array and parse it leaving the non-option # arguments in the args array. try: optionsList, args = getopt.getopt(argv[1:], 'vc:C:S:', ['set=', 'class=']) except getopt.GetoptError: # print help information and exit: usage(argv) sys.exit(2) for (opt, arg) in optionsList: if opt == '-v': print '%s (python %s)'%(roundup_version, sys.version.split()[0]) return # figure the instance home if len(args) > 0: instance_home = args[0] else: instance_home = os.environ.get('ROUNDUP_INSTANCE', '') if not (instance_home and os.path.isdir(instance_home)): return usage(argv) # get the instance import roundup.instance instance = roundup.instance.open(instance_home) if hasattr(instance, 'MailGW'): handler = instance.MailGW(instance, optionsList) else: handler = mailgw.MailGW(instance, optionsList) # if there's no more arguments, read a single message from stdin if len(args) == 1: return handler.do_pipe() # otherwise, figure what sort of mail source to handle if len(args) < 3: return usage(argv, _('Error: not enough source specification information')) source, specification = args[1:3] # time out net connections after a minute if we can if source not in ('mailbox', 'imaps', 'imaps_cram'): if hasattr(socket, 'setdefaulttimeout'): socket.setdefaulttimeout(60) if source == 'mailbox': return handler.do_mailbox(specification) # the source will be a network server, so obtain the credentials to # use in connecting to the server try: # attempt to obtain credentials from a ~/.netrc file authenticator = netrc.netrc().authenticators(specification) username = authenticator[0] password = authenticator[2] server = specification # IOError if no ~/.netrc file, TypeError if the hostname # not found in the ~/.netrc file: except (IOError, TypeError): match = re.match(r'((?P<user>[^:]+)(:(?P<pass>.+))?@)?(?P<server>.+)', specification) if match: username = match.group('user') password = match.group('pass') server = match.group('server') else: return usage(argv, _('Error: %s specification not valid') % source) # now invoke the mailgw handler depending on the server handler requested if source.startswith('pop'): ssl = source.endswith('s') if ssl and sys.version_info<(2,4): return usage(argv, _('Error: a later version of python is required')) return handler.do_pop(server, username, password, ssl) elif source == 'apop': return handler.do_apop(server, username, password) elif source.startswith('imap'): ssl = cram = 0 if source.endswith('s'): ssl = 1 elif source.endswith('s_cram'): ssl = cram = 1 mailbox = '' if len(args) > 3: mailbox = args[3] return handler.do_imap(server, username, password, mailbox, ssl, cram) return usage(argv, _('Error: The source must be either "mailbox",' ' "pop", "pops", "apop", "imap", "imaps" or "imaps_cram'))
def usage(args, message=None): if message is not None: print message print _( """Usage: %(program)s [-v] [-c class] [[-C class] -S field=value]* [instance home] [mail source [specification]] Options: -v: print version and exit -c: default class of item to create (else the tracker's MAIL_DEFAULT_CLASS) -C / -S: see below The roundup mail gateway may be called in one of the following ways: . without arguments. Then the env var ROUNDUP_INSTANCE will be tried. . with an instance home as the only argument, . with both an instance home and a mail spool file, . with an instance home, a mail source type and its specification. It also supports optional -C and -S arguments that allows you to set a fields for a class created by the roundup-mailgw. The default class if not specified is msg, but the other classes: issue, file, user can also be used. The -S or --set options uses the same property=value[;property=value] notation accepted by the command line roundup command or the commands that can be given on the Subject line of an email message. It can let you set the type of the message on a per email address basis. PIPE: If there is no mail source specified, the mail gateway reads a single message from the standard input and submits the message to the roundup.mailgw module. Mail source "mailbox": In this case, the gateway reads all messages from the UNIX mail spool file and submits each in turn to the roundup.mailgw module. The file is emptied once all messages have been successfully handled. The file is specified as: mailbox /path/to/mailbox In all of the following mail source type the username and password can be stored in a ~/.netrc file. If done so case only the server name need to be specified on the command-line. The username and/or password will be prompted for if not supplied on the command-line or in ~/.netrc. POP: For the mail source "pop", the gateway reads all messages from the POP server specified and submits each in turn to the roundup.mailgw module. The server is specified as: pop username:password@server Alternatively, one can omit one or both of username and password: pop username@server pop server are both valid. POPS: Connect to a POP server over ssl. This requires python 2.4 or later. This supports the same notation as POP. APOP: Same as POP, but using Authenticated POP: apop username:password@server IMAP: Connect to an IMAP server. This supports the same notation as that of POP mail. imap username:password@server It also allows you to specify a specific mailbox other than INBOX using this format: imap username:password@server mailbox IMAPS: Connect to an IMAP server over ssl. This supports the same notation as IMAP. imaps username:password@server [mailbox] IMAPS_CRAM: Connect to an IMAP server over ssl using CRAM-MD5 authentication. This supports the same notation as IMAP. imaps_cram username:password@server [mailbox] """)%{'program': args[0]} return 1
def error(): exc_type, exc_value = sys.exc_info()[:2] return _('Error: %s: %s' % (exc_type, exc_value))
def from_raw(self, value, **kw): try: value = date.Interval(value) except ValueError, message: raise HyperdbValueError, _('property %s: %r is an invalid '\ 'date interval (%s)')%(kw['propname'], value, message)
elif opt != "-c": svc_args.extend(opt) RoundupService._exe_args_ = " ".join(svc_args) # pass the control to serviceutil win32serviceutil.HandleCommandLine(RoundupService, argv=sys.argv[:1] + args) return # add tracker names from command line. # this is done early to let '--save-config' handle the trackers. if args: for arg in args: try: name, home = arg.split('=') except ValueError: raise ValueError, _("Instances must be name=home") config.add_option(TrackerHomeOption(config, "trackers", name)) config["TRACKERS_" + name.upper()] = home # handle remaining options if optlist: for (opt, arg) in optlist: if opt in ("-h", "--help"): usage() elif opt in ("-v", "--version"): print '%s (python %s)' % (roundup_version, sys.version.split()[0]) elif opt in ("-S", "--save-config"): config.save() print _("Configuration saved to %s") % config.filepath # any of the above options prevent server from running
def from_raw(self, value, db, klass, propname, itemid, **kw): if not value: return [] # get the current item value if it's not a new item if itemid and not itemid.startswith('-'): curvalue = klass.get(itemid, propname) else: curvalue = [] # if the value is a comma-separated string then split it now if isinstance(value, type('')): value = value.split(',') # handle each add/remove in turn # keep an extra list for all items that are # definitely in the new list (in case of e.g. # <propname>=A,+B, which should replace the old # list with A,B) set = 1 newvalue = [] for item in value: item = item.strip() # skip blanks if not item: continue # handle +/- remove = 0 if item.startswith('-'): remove = 1 item = item[1:] set = 0 elif item.startswith('+'): item = item[1:] set = 0 # look up the value itemid = convertLinkValue(db, propname, self, item) # perform the add/remove if remove: try: curvalue.remove(itemid) except ValueError: raise HyperdbValueError, _('property %s: %r is not ' \ 'currently an element')%(propname, item) else: newvalue.append(itemid) if itemid not in curvalue: curvalue.append(itemid) # that's it, set the new Multilink property value, # or overwrite it completely if set: value = newvalue else: value = curvalue # TODO: one day, we'll switch to numeric ids and this will be # unnecessary :( value = [int(x) for x in value] value.sort() value = [str(x) for x in value] return value
def unpack_timestamp(s): try: timestamp = struct.unpack("i", base64.b64decode(s2b(s)))[0] except (struct.error, binascii.Error, TypeError): raise FormError(_("Form is corrupted.")) return timestamp