1 # Copyright (C) 2006-2009 Novell Inc. All rights reserved.
2 # This program is free software; it may be used, copied, modified
3 # and distributed under the terms of the GNU General Public Licence,
4 # either version 2, or version 3 (at your option).
6 """Read osc configuration and store it in a dictionary
8 This module reads and parses ~/.oscrc. The resulting configuration is stored
9 for later usage in a dictionary named 'config'.
10 The .oscrc is kept mode 0600, so that it is not publically readable.
11 This gives no real security for storing passwords.
12 If in doubt, use your favourite keyring.
13 Password is stored on ~/.oscrc as bz2 compressed and base64 encoded, so that is fairly
14 large and not to be recognized or remembered easily by an occasional spectator.
16 If information is missing, it asks the user questions.
18 After reading the config, urllib2 is initialized.
20 The configuration dictionary could look like this:
22 {'apisrv': 'https://api.opensuse.org/',
24 'api_host_options': {'api.opensuse.org': {'user': 'joe', 'pass': 'secret'},
25 'apitest.opensuse.org': {'user': 'joe', 'pass': 'secret',
26 'http_headers':(('Host','api.suse.de'),
28 'foo.opensuse.org': {'user': 'foo', 'pass': 'foo'}},
29 'build-cmd': '/usr/bin/build',
30 'build-root': '/abuild/oscbuild-%(repo)s-%(arch)s',
31 'packagecachedir': '/var/cache/osbuild',
37 import OscConfigParser
38 from osc import oscerr
39 from oscsslexcp import NoSecureSSLError
42 GENERIC_KEYRING = False
47 GENERIC_KEYRING = True
52 gobject.set_application_name('osc')
54 if os.environ['GNOME_DESKTOP_SESSION_ID']:
55 # otherwise gnome keyring bindings spit out errors, when you have
56 # it installed, but you are not under gnome
57 # (even though hundreds of gnome-keyring daemons got started in parallel)
58 # another option would be to support kwallet here
59 GNOME_KEYRING = gnomekeyring.is_available()
63 DEFAULTS = { 'apiurl': 'https://api.opensuse.org',
64 'user': 'your_username',
65 'pass': 'your_password',
67 'packagecachedir': '/var/tmp/osbuild-packagecache',
68 'su-wrapper': 'su -c',
71 'build-cmd': '/usr/bin/build',
72 'build-type': '', # may be empty for chroot, kvm or xen
73 'build-root': '/var/tmp/build-root',
74 'build-uid': '', # use the default provided by build
75 'build-device': '', # required for VM builds
76 'build-memory': '',# required for VM builds
77 'build-swap': '', # optional for VM builds
78 'build-vmdisk-rootsize': '', # optional for VM builds
79 'build-vmdisk-swapsize': '', # optional for VM builds
81 'build-jobs': os.sysconf('SC_NPROCESSORS_ONLN'), # compile with N jobs
82 'builtin_signature_check': '1', # by default use builtin check for verify pkgs
87 'http_full_debug': '0',
93 'cookiejar': '~/.osc_cookiejar',
94 # fallback for osc build option --no-verify
96 # enable project tracking by default
97 'do_package_tracking': '1',
98 # default for osc build
101 'build_repository': 'openSUSE_Factory',
102 # default project for branch or bco
103 'getpac_default_project': 'openSUSE:Factory',
104 # alternate filesystem layout: have multiple subdirs, where colons were.
105 'checkout_no_colon': '0',
106 # local files to ignore with status, addremove, ....
107 # local files to ignore with status, addremove, ....
108 'exclude_glob': '.osc CVS .svn .* _linkerror *~ #*# *.orig *.bak *.changes.vctmp.*',
109 # keep passwords in plaintext. If you see this comment, your osc
110 # already uses the encrypted password, and only keeps them in plain text
111 # for backwards compatibility. Default will change to 0 in future releases.
112 'plaintext_passwd': '1',
113 # limit the age of requests shown with 'osc req list'.
114 # this is a default only, can be overridden by 'osc req list -D NNN'
115 # Use 0 for unlimted.
116 'request_list_days': 0,
117 # check for unversioned/removed files before commit
118 'check_filelist': '1',
119 # External scripts to validate sources, esp before commit. This is a directory
120 'source_validator_directory': '/usr/lib/osc/source_validators',
121 # check for pending requests after executing an action (e.g. checkout, update, commit)
122 'check_for_request_on_action': '0',
123 # what to do with the source package if the submitrequest has been accepted
124 'submitrequest_on_accept_action': '',
125 'request_show_interactive': '0',
126 'submitrequest_accepted_template': '',
127 'submitrequest_declined_template': '',
130 # Maintenance defaults to OBS instance defaults
131 'maintained_attribute': 'OBS:Maintained',
132 'maintained_update_project_attribute': 'OBS:UpdateProject',
133 'show_download_progress': '0',
136 # being global to this module, this dict can be accessed from outside
137 # it will hold the parsed configuration
138 config = DEFAULTS.copy()
140 boolean_opts = ['debug', 'do_package_tracking', 'http_debug', 'post_mortem', 'traceback', 'check_filelist', 'plaintext_passwd',
141 'checkout_no_colon', 'check_for_request_on_action', 'linkcontrol', 'show_download_progress', 'request_show_interactive',
142 'use_keyring', 'gnome_keyring', 'no_verify', 'builtin_signature_check', 'http_full_debug']
144 api_host_options = ['user', 'pass', 'passx', 'aliases', 'http_headers', 'email', 'sslcertck', 'cafile', 'capath', 'trusted_prj']
146 new_conf_template = """
149 # URL to access API server, e.g. %(apiurl)s
150 # you also need a section [%(apiurl)s] with the credentials
153 # Downloaded packages are cached here. Must be writable by you.
154 #packagecachedir = %(packagecachedir)s
156 # Wrapper to call build as root (sudo, su -, ...)
157 #su-wrapper = %(su-wrapper)s
159 # rootdir to setup the chroot environment
160 # can contain %%(repo)s, %%(arch)s, %%(project)s and %%(package)s for replacement, e.g.
161 # /srv/oscbuild/%%(repo)s-%%(arch)s or
162 # /srv/oscbuild/%%(repo)s-%%(arch)s-%%(project)s-%%(package)s
163 #build-root = %(build-root)s
165 # compile with N jobs (default: "getconf _NPROCESSORS_ONLN")
168 # build-type to use - values can be (depending on the capabilities of the 'build' script)
169 # empty - chroot build
170 # kvm - kvm VM build (needs build-device, build-swap, build-memory)
171 # xen - xen VM build (needs build-device, build-swap, build-memory)
173 # qemu - qemu VM build
177 # build-device is the disk-image file to use as root for VM builds
178 # e.g. /var/tmp/FILE.root
179 #build-device = /var/tmp/FILE.root
181 # build-swap is the disk-image to use as swap for VM builds
182 # e.g. /var/tmp/FILE.swap
183 #build-swap = /var/tmp/FILE.swap
185 # build-memory is the amount of memory used in the VM
186 # value in MB - e.g. 512
189 # build-vmdisk-rootsize is the size of the disk-image used as root in a VM build
190 # values in MB - e.g. 4096
191 #build-vmdisk-rootsize = 4096
193 # build-vmdisk-swapsize is the size of the disk-image used as swap in a VM build
194 # values in MB - e.g. 1024
195 #build-vmdisk-swapsize = 1024
197 # Numeric uid:gid to assign to the "abuild" user in the build-root
198 # or "caller" to use the current users uid:gid
199 # This is convenient when sharing the buildroot with ordinary userids
201 # This should not be 0
204 # extra packages to install when building packages locally (osc build)
205 # this corresponds to osc build's -x option and can be overridden with that
206 # -x '' can also be given on the command line to override this setting, or
207 # you can have an empty setting here.
208 #extra-pkgs = vim gdb strace
210 # build platform is used if the platform argument is omitted to osc build
211 #build_repository = %(build_repository)s
213 # default project for getpac or bco
214 #getpac_default_project = %(getpac_default_project)s
216 # alternate filesystem layout: have multiple subdirs, where colons were.
217 #checkout_no_colon = %(checkout_no_colon)s
219 # local files to ignore with status, addremove, ....
220 #exclude_glob = %(exclude_glob)s
222 # keep passwords in plaintext. If you see this comment, your osc
223 # already uses the encrypted password, and only keeps them in plain text
224 # for backwards compatibility. Default will change to 0 in future releases.
225 # You can remove the plaintext password without harm, if you do not need
226 # backwards compatibility.
227 #plaintext_passwd = %(plaintext_passwd)s
229 # limit the age of requests shown with 'osc req list'.
230 # this is a default only, can be overridden by 'osc req list -D NNN'
231 # Use 0 for unlimted.
232 #request_list_days = %(request_list_days)s
234 # show info useful for debugging
237 # show HTTP traffic useful for debugging
240 # Skip signature verification of packages used for build.
243 # jump into the debugger in case of errors
246 # print call traces in case of errors
249 # use KDE/Gnome/MacOS/Windows keyring for credentials if available
252 # check for unversioned/removed files before commit
255 # check for pending requests after executing an action (e.g. checkout, update, commit)
256 #check_for_request_on_action = 0
258 # what to do with the source package if the submitrequest has been accepted. If
259 # nothing is specified the API default is used
260 #submitrequest_on_accept_action = cleanup|update|noupdate
262 # template for an accepted submitrequest
263 #submitrequest_accepted_template = Hi %%(who)s,\\n
264 # thanks for working on:\\t%%(dst_project)s/%%(dst_package)s.
265 # SR %%(reqid)s has been accepted.\\n\\nYour maintainers
267 # template for a declined submitrequest
268 #submitrequest_declined_template = Hi %%(who)s,\\n
269 # sorry your SR %%(reqid)s (request type: %%(type)s) for
270 # %%(dst_project)s/%%(dst_package)s has been declined because...
272 #review requests interactively (default: off)
273 #request_show_review = 1
275 # Directory with executables to validate sources, esp before committing
276 #source_validator_directory = /usr/lib/osc/source_validators
282 # set aliases for this apiurl
284 # email used in .changes, unless the one from osc meta prj <user> will be used
286 # additional headers to pass to a request, e.g. for special authentication
287 #http_headers = Host: foofoobar,
289 # Force using of keyring for this API
294 account_not_configured_text ="""
295 Your user account / password are not configured yet.
296 You will be asked for them below, and they will be stored in
300 config_incomplete_text = """
302 Your configuration file %s is not complete.
303 Make sure that it has a [general] section.
304 (You can copy&paste the below. Some commented defaults are shown.)
308 config_missing_apiurl_text = """
309 the apiurl \'%s\' does not exist in the config file. Please enter
310 your credentials for this apiurl.
315 def parse_apisrv_url(scheme, apisrv):
317 if apisrv.startswith('http://') or apisrv.startswith('https://'):
318 return urlparse.urlsplit(apisrv)[0:2]
320 # the split/join is needed to get a proper url (e.g. without a trailing slash)
321 return urlparse.urlsplit(urljoin(scheme, apisrv))[0:2]
323 from urllib2 import URLError
324 msg = 'invalid apiurl \'%s\' (specify the protocol (http:// or https://))' % apisrv
327 def urljoin(scheme, apisrv):
328 return '://'.join([scheme, apisrv])
330 def is_known_apiurl(url):
331 """returns true if url is a known apiurl"""
332 apiurl = urljoin(*parse_apisrv_url(None, url))
333 return config['api_host_options'].has_key(apiurl)
335 def get_apiurl_api_host_options(apiurl):
337 Returns all apihost specific options for the given apiurl, None if
338 no such specific optiosn exist.
340 # FIXME: in A Better World (tm) there was a config object which
341 # knows this instead of having to extract it from a url where it
342 # had been mingled into before. But this works fine for now.
344 apiurl = urljoin(*parse_apisrv_url(None, apiurl))
345 if is_known_apiurl(apiurl):
346 return config['api_host_options'][apiurl]
347 raise oscerr.ConfigMissingApiurl('missing credentials for apiurl: \'%s\'' % apiurl,
350 def get_apiurl_usr(apiurl):
352 returns the user for this host - if this host does not exist in the
353 internal api_host_options the default user is returned.
355 # FIXME: maybe there should be defaults not just for the user but
356 # for all apihost specific options. The ConfigParser class
357 # actually even does this but for some reason we don't use it
362 return get_apiurl_api_host_options(apiurl)['user']
364 print >>sys.stderr, 'no specific section found in config file for host of [\'%s\'] - using default user: \'%s\'' \
365 % (apiurl, config['user'])
366 return config['user']
368 # workaround m2crypto issue:
369 # if multiple SSL.Context objects are created
370 # m2crypto only uses the last object which was created.
371 # So we need to build a new opener everytime we switch the
372 # apiurl (because different apiurls may have different
373 # cafile/capath locations)
374 def _build_opener(url):
375 from osc.core import __version__
379 apiurl = urljoin(*parse_apisrv_url(None, url))
380 if not _build_opener.__dict__.has_key('last_opener'):
381 _build_opener.last_opener = (None, None)
382 if apiurl == _build_opener.last_opener[0]:
383 return _build_opener.last_opener[1]
385 # workaround for http://bugs.python.org/issue9639
386 authhandler_class = urllib2.HTTPBasicAuthHandler
387 if sys.version_info >= (2, 6, 6) and sys.version_info < (2, 7, 1) \
388 and not 'reset_retry_count' in dir(urllib2.HTTPBasicAuthHandler):
389 print >>sys.stderr, 'warning: your urllib2 version seems to be broken. ' \
390 'Using a workaround for http://bugs.python.org/issue9639'
391 class OscHTTPBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
392 def http_error_401(self, *args):
393 response = urllib2.HTTPBasicAuthHandler.http_error_401(self, *args)
397 authhandler_class = OscHTTPBasicAuthHandler
398 elif sys.version_info >= (2, 6, 5) and sys.version_info < (2, 6, 6):
399 # workaround for broken urllib2 in python 2.6.5: wrong credentials
400 # lead to an infinite recursion
401 class OscHTTPBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
402 def retry_http_basic_auth(self, host, req, realm):
403 # don't retry if auth failed
404 if req.get_header(self.auth_header, None) is not None:
406 return urllib2.HTTPBasicAuthHandler.retry_http_basic_auth(self, host, req, realm)
408 authhandler_class = OscHTTPBasicAuthHandler
410 options = config['api_host_options'][apiurl]
411 # with None as first argument, it will always use this username/password
412 # combination for urls for which arg2 (apisrv) is a super-url
413 authhandler = authhandler_class( \
414 urllib2.HTTPPasswordMgrWithDefaultRealm())
415 authhandler.add_password(None, apiurl, options['user'], options['pass'])
417 if options['sslcertck']:
420 from M2Crypto import m2urllib2
421 except ImportError, e:
423 raise NoSecureSSLError('M2Crypto is needed to access %s in a secure way.\nPlease install python-m2crypto.' % apiurl)
425 cafile = options.get('cafile', None)
426 capath = options.get('capath', None)
427 if not cafile and not capath:
428 for i in ['/etc/pki/tls/cert.pem', '/etc/ssl/certs' ]:
429 if os.path.isfile(i):
432 elif os.path.isdir(i):
435 ctx = oscssl.mySSLContext()
436 if ctx.load_verify_locations(capath=capath, cafile=cafile) != 1: raise Exception('No CA certificates found')
437 opener = m2urllib2.build_opener(ctx, oscssl.myHTTPSHandler(ssl_context = ctx, appname = 'osc'), urllib2.HTTPCookieProcessor(cookiejar), authhandler)
440 print >>sys.stderr, "WARNING: SSL certificate checks disabled. Connection is insecure!\n"
441 opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar), authhandler)
442 opener.addheaders = [('User-agent', 'osc/%s' % __version__)]
443 _build_opener.last_opener = (apiurl, opener)
446 def init_basicauth(config):
447 """initialize urllib2 with the credentials for Basic Authentication"""
453 def filterhdrs(meth, ishdr, *hdrs):
457 # this is so ugly but httplib doesn't use
458 # a logger object or such
459 def new_method(*args, **kwargs):
461 sys.stdout = StringIO.StringIO()
462 meth(*args, **kwargs)
463 hdr = sys.stdout.getvalue()
467 hdr = re.sub(r'%s:[^\\r]*\\r\\n' % i, '', hdr)
469 hdr = re.sub(i, '', hdr)
470 sys.stdout.write(hdr)
471 new_method.__name__ = meth.__name__
474 if config['http_debug'] and not config['http_full_debug']:
475 httplib.HTTPConnection.send = filterhdrs(httplib.HTTPConnection.send, True, 'Cookie', 'Authorization')
476 httplib.HTTPResponse.begin = filterhdrs(httplib.HTTPResponse.begin, False, 'header: Set-Cookie.*\n')
478 if sys.version_info < (2, 6):
479 # HTTPS proxy is not supported in old urllib2. It only leads to an error
480 # or, at best, a warning.
481 if 'https_proxy' in os.environ:
482 del os.environ['https_proxy']
483 if 'HTTPS_PROXY' in os.environ:
484 del os.environ['HTTPS_PROXY']
486 if config['http_debug']:
488 def urllib2_debug_init(self, debuglevel=0):
490 urllib2.AbstractHTTPHandler.__init__ = urllib2_debug_init
492 cookie_file = os.path.expanduser(config['cookiejar'])
494 cookiejar = cookielib.LWPCookieJar(cookie_file)
496 cookiejar.load(ignore_discard=True)
499 open(cookie_file, 'w').close()
500 os.chmod(cookie_file, 0600)
502 #print 'Unable to create cookiejar file: \'%s\'. Using RAM-based cookies.' % cookie_file
503 cookiejar = cookielib.CookieJar()
506 def get_configParser(conffile=None, force_read=False):
508 Returns an ConfigParser() object. After its first invocation the
509 ConfigParser object is stored in a method attribute and this attribute
510 is returned unless you pass force_read=True.
512 conffile = conffile or os.environ.get('OSC_CONFIG', '~/.oscrc')
513 conffile = os.path.expanduser(conffile)
514 if not get_configParser.__dict__.has_key('conffile'):
515 get_configParser.conffile = conffile
516 if force_read or not get_configParser.__dict__.has_key('cp') or conffile != get_configParser.conffile:
517 get_configParser.cp = OscConfigParser.OscConfigParser(DEFAULTS)
518 get_configParser.cp.read(conffile)
519 get_configParser.conffile = conffile
520 return get_configParser.cp
522 def config_set_option(section, opt, val=None, delete=False, update=True, **kwargs):
524 Sets a config option. If val is not specified the current/default value is
525 returned. If val is specified, opt is set to val and the new value is returned.
526 If an option was modified get_config is called with **kwargs unless update is set
527 to False (override_conffile defaults to config['conffile']).
528 If val is not specified and delete is True then the option is removed from the
529 config/reset to the default value.
531 def write_config(fname, cp):
532 """write new configfile in a safe way"""
534 f = open(fname + '.new', 'w')
535 cp.write(f, comments=True)
537 os.rename(fname + '.new', fname)
539 if os.path.exists(fname + '.new'):
540 os.unlink(fname + '.new')
543 cp = get_configParser(config['conffile'])
544 # don't allow "internal" options
545 general_opts = [i for i in DEFAULTS.keys() if not i in ['user', 'pass', 'passx']]
546 if section != 'general':
547 section = config['apiurl_aliases'].get(section, section)
549 parse_apisrv_url(config.get('scheme', 'https'), section)
550 section = urljoin(scheme, host)
553 for url in cp.sections():
558 parse_apisrv_url(config.get('scheme', 'https'), url)
559 apiurl = urljoin(scheme, host)
560 sections[apiurl] = url
562 section = sections.get(section.rstrip('/'), section)
563 if not section in cp.sections():
564 raise oscerr.ConfigError('unknown section \'%s\'' % section, config['conffile'])
565 if section == 'general' and not opt in general_opts or \
566 section != 'general' and not opt in api_host_options:
567 raise oscerr.ConfigError('unknown config option \'%s\'' % opt, config['conffile'])
570 cp.set(section, opt, val)
571 write_config(config['conffile'], cp)
573 elif delete and cp.has_option(section, opt):
574 cp.remove_option(section, opt)
575 write_config(config['conffile'], cp)
578 kw = {'override_conffile': config['conffile']}
581 if cp.has_option(section, opt):
582 return (opt, cp.get(section, opt, raw=True))
585 def write_initial_config(conffile, entries, custom_template = ''):
587 write osc's intial configuration file. entries is a dict which contains values
588 for the config file (e.g. { 'user' : 'username', 'pass' : 'password' } ).
589 custom_template is an optional configuration template.
591 import StringIO, sys, base64
592 conf_template = custom_template or new_conf_template
593 config = DEFAULTS.copy()
594 config.update(entries)
595 config['passx'] = base64.b64encode(config['pass'].encode('bz2'))
596 # at this point use_keyring and gnome_keyring are str objects
597 if config['use_keyring'] == '1' and GENERIC_KEYRING:
599 parse_apisrv_url(None, config['apiurl'])
600 keyring.set_password(host, config['user'], config['pass'])
603 elif config['gnome_keyring'] == '1' and GNOME_KEYRING:
605 parse_apisrv_url(None, config['apiurl'])
606 gnomekeyring.set_network_password_sync(
607 user = config['user'],
608 password = config['pass'],
614 if not config['plaintext_passwd']:
616 sio = StringIO.StringIO(conf_template.strip() % config)
617 cp = OscConfigParser.OscConfigParser(DEFAULTS)
622 file = open(conffile, 'w')
624 raise oscerr.OscIOError(e, 'cannot open configfile \'%s\'' % conffile)
627 os.chmod(conffile, 0600)
630 raise oscerr.OscIOError(e, 'cannot write configfile \'s\'' % conffile)
632 if file: file.close()
634 def add_section(filename, url, user, passwd):
636 Add a section to config file for new api url.
640 cp = get_configParser(filename)
643 except OscConfigParser.ConfigParser.DuplicateSectionError:
644 # Section might have existed, but was empty
646 if config['use_keyring'] and GENERIC_KEYRING:
648 parse_apisrv_url(None, url)
649 keyring.set_password(host, user, passwd)
650 cp.set(url, 'keyring', '1')
651 cp.set(url, 'user', user)
652 cp.remove_option(url, 'pass')
653 cp.remove_option(url, 'passx')
654 elif config['gnome_keyring'] and GNOME_KEYRING:
656 parse_apisrv_url(None, url)
657 gnomekeyring.set_network_password_sync(
662 cp.set(url, 'keyring', '1')
663 cp.remove_option(url, 'pass')
664 cp.remove_option(url, 'passx')
666 cp.set(url, 'user', user)
667 if not config['plaintext_passwd']:
668 cp.remove_option(url, 'pass')
669 cp.set(url, 'passx', base64.b64encode(passwd.encode('bz2')))
670 file = open(filename, 'w')
672 if file: file.close()
675 def get_config(override_conffile = None,
676 override_apiurl = None,
677 override_debug = None,
678 override_http_debug = None,
679 override_http_full_debug = None,
680 override_traceback = None,
681 override_post_mortem = None,
682 override_no_keyring = None,
683 override_no_gnome_keyring = None,
684 override_verbose = None):
685 """do the actual work (see module documentation)"""
690 conffile = override_conffile or os.environ.get('OSC_CONFIG', '~/.oscrc')
691 conffile = os.path.expanduser(conffile)
693 if not os.path.exists(conffile):
694 raise oscerr.NoConfigfile(conffile, \
695 account_not_configured_text % conffile)
697 # okay, we made sure that .oscrc exists
699 # make sure it is not world readable, it may contain a password.
700 os.chmod(conffile, 0600)
702 cp = get_configParser(conffile)
704 if not cp.has_section('general'):
705 # FIXME: it might be sufficient to just assume defaults?
706 msg = config_incomplete_text % conffile
707 msg += new_conf_template % DEFAULTS
708 raise oscerr.ConfigError(msg, conffile)
710 config = dict(cp.items('general', raw=1))
711 config['conffile'] = conffile
713 for i in boolean_opts:
715 config[i] = cp.getboolean('general', i)
716 except ValueError, e:
717 raise oscerr.ConfigError('cannot parse \'%s\' setting: ' % i + str(e), conffile)
719 config['packagecachedir'] = os.path.expanduser(config['packagecachedir'])
720 config['exclude_glob'] = config['exclude_glob'].split()
722 re_clist = re.compile('[, ]+')
723 config['extra-pkgs'] = [ i.strip() for i in re_clist.split(config['extra-pkgs'].strip()) if i ]
725 # collect the usernames, passwords and additional options for each api host
726 api_host_options = {}
728 # Regexp to split extra http headers into a dictionary
729 # the text to be matched looks essentially looks this:
730 # "Attribute1: value1, Attribute2: value2, ..."
731 # there may be arbitray leading and intermitting whitespace.
732 # the following regexp does _not_ support quoted commas within the value.
733 http_header_regexp = re.compile(r"\s*(.*?)\s*:\s*(.*?)\s*(?:,\s*|\Z)")
735 # override values which we were called with
736 # This needs to be done before processing API sections as it might be already used there
737 if override_no_keyring:
738 config['use_keyring'] = False
739 if override_no_gnome_keyring:
740 config['gnome_keyring'] = False
743 for url in [ x for x in cp.sections() if x != 'general' ]:
744 # backward compatiblity
746 parse_apisrv_url(config.get('scheme', 'https'), url)
747 apiurl = urljoin(scheme, host)
749 if config['use_keyring'] and GENERIC_KEYRING:
751 # Read from keyring lib if available
752 user = cp.get(url, 'user', raw=True)
753 password = keyring.get_password(host, user)
755 # Fallback to file based auth.
757 elif config['gnome_keyring'] and GNOME_KEYRING:
758 # Read from gnome keyring if available
760 gk_data = gnomekeyring.find_network_password_sync(
763 password = gk_data[0]['password']
764 user = gk_data[0]['user']
765 except gnomekeyring.NoMatchError:
766 # Fallback to file based auth.
768 # Read credentials from config
770 #FIXME: this could actually be the ideal spot to take defaults
771 #from the general section.
772 user = cp.get(url, 'user', raw=True) # need to set raw to prevent '%' expansion
773 password = cp.get(url, 'pass', raw=True) # especially on password!
774 passwordx = cp.get(url, 'passx', raw=True) # especially on password!
775 if password is None or password == 'your_password':
777 password = passwordx.decode('base64').decode('bz2')
779 print "%s: no credentials known" % url
780 password = 'your_password'
781 elif password != passwordx.decode('base64').decode('bz2'):
784 print '%s: rewriting from plain pass to encoded pass\n' % url
786 print '%s: pass and passx mismatch (rewriting from plain pass to encoded pass)\n' % url
787 add_section(conffile, url, user, password)
789 if cp.has_option(url, 'http_headers'):
790 http_headers = cp.get(url, 'http_headers')
791 http_headers = http_header_regexp.findall(http_headers)
794 if cp.has_option(url, 'aliases'):
795 for i in cp.get(url, 'aliases').split(','):
799 if aliases.has_key(key):
800 msg = 'duplicate alias entry: \'%s\' is already used for another apiurl' % key
801 raise oscerr.ConfigError(msg, conffile)
804 api_host_options[apiurl] = { 'user': user,
806 'http_headers': http_headers}
808 optional = ('email', 'sslcertck', 'cafile', 'capath')
810 if cp.has_option(url, key):
811 if key == 'sslcertck':
812 api_host_options[apiurl][key] = cp.getboolean(url, key)
814 api_host_options[apiurl][key] = cp.get(url, key)
816 if not 'sslcertck' in api_host_options[apiurl]:
817 api_host_options[apiurl]['sslcertck'] = True
820 api_host_options[apiurl]['sslcertck'] = False
822 if cp.has_option(url, 'trusted_prj'):
823 api_host_options[apiurl]['trusted_prj'] = cp.get(url, 'trusted_prj').split(' ')
825 api_host_options[apiurl]['trusted_prj'] = []
827 # add the auth data we collected to the config dict
828 config['api_host_options'] = api_host_options
829 config['apiurl_aliases'] = aliases
831 apiurl = aliases.get(config['apiurl'], config['apiurl'])
832 config['apiurl'] = urljoin(*parse_apisrv_url(None, apiurl))
833 # backward compatibility
834 if config.has_key('apisrv'):
835 apisrv = config['apisrv'].lstrip('http://')
836 apisrv = apisrv.lstrip('https://')
837 scheme = config.get('scheme', 'https')
838 config['apiurl'] = urljoin(scheme, apisrv)
839 if config.has_key('apisrv') or config.has_key('scheme'):
840 print >>sys.stderr, 'Warning: Use of the \'scheme\' or \'apisrv\' in ~/.oscrc is deprecated!\n' \
841 'Warning: See README for migration details.'
842 if config.has_key('build_platform'):
843 print >>sys.stderr, 'Warning: Use of \'build_platform\' config option is deprecated! (use \'build_repository\' instead)'
844 config['build_repository'] = config['build_platform']
846 config['verbose'] = int(config['verbose'])
847 # override values which we were called with
849 config['verbose'] = override_verbose + 1
852 config['debug'] = override_debug
853 if override_http_debug:
854 config['http_debug'] = override_http_debug
855 if override_http_full_debug:
856 config['http_debug'] = override_http_full_debug or config['http_debug']
857 config['http_full_debug'] = override_http_full_debug
858 if override_traceback:
859 config['traceback'] = override_traceback
860 if override_post_mortem:
861 config['post_mortem'] = override_post_mortem
863 apiurl = aliases.get(override_apiurl, override_apiurl)
864 # check if apiurl is a valid url
865 config['apiurl'] = urljoin(*parse_apisrv_url(None, apiurl))
867 # XXX unless config['user'] goes away (and is replaced with a handy function, or
868 # config becomes an object, even better), set the global 'user' here as well,
869 # provided that there _are_ credentials for the chosen apiurl:
871 config['user'] = get_apiurl_usr(config['apiurl'])
872 except oscerr.ConfigMissingApiurl, e:
873 e.msg = config_missing_apiurl_text % config['apiurl']
877 # finally, initialize urllib2 for to use the credentials for Basic Authentication
878 init_basicauth(config)