Hello community, here is the log from the commit of package ansible for openSUSE:Factory checked in at 2014-07-21 21:39:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ansible (Old) and /work/SRC/openSUSE:Factory/.ansible.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ansible" Changes: -------- --- /work/SRC/openSUSE:Factory/ansible/ansible.changes 2014-06-30 21:50:28.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.ansible.new/ansible.changes 2014-07-21 21:39:51.000000000 +0200 @@ -1,0 +2,11 @@ +Thu Jul 10 12:53:16 UTC 2014 - [email protected] + +- update to 1.6.6: + * Security updates to further protect against the incorrect + execution of untrusted data + * Additional tweaks to prevent the incorrect execution of + untrusted data + * Security update to prevent local operations from executing as + the result of specifically crafted untrusted data + +------------------------------------------------------------------- Old: ---- ansible-1.6.3.tar.bz2 New: ---- ansible-1.6.6.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ansible.spec ++++++ --- /var/tmp/diff_new_pack.fe4DEL/_old 2014-07-21 21:39:52.000000000 +0200 +++ /var/tmp/diff_new_pack.fe4DEL/_new 2014-07-21 21:39:52.000000000 +0200 @@ -18,7 +18,7 @@ Name: ansible -Version: 1.6.3 +Version: 1.6.6 Release: 0 Summary: SSH-based configuration management, deployment, and orchestration engine License: GPL-3.0+ ++++++ CHANGELOG.md ++++++ --- /var/tmp/diff_new_pack.fe4DEL/_old 2014-07-21 21:39:52.000000000 +0200 +++ /var/tmp/diff_new_pack.fe4DEL/_new 2014-07-21 21:39:52.000000000 +0200 @@ -1,6 +1,18 @@ Ansible Changes By Release ========================== +## 1.6.6 "And the Cradle Will Rock" - Jul 01, 2014 + +- Security updates to further protect against the incorrect execution of untrusted data + +## 1.6.5 "And the Cradle Will Rock" - Jun 25, 2014 + +- Additional tweaks to prevent the incorrect execution of untrusted data + +## 1.6.4 "And the Cradle Will Rock" - Jun 25, 2014 + +- Security update to prevent local operations from executing as the result of specifically crafted untrusted data + ## 1.6.3 "And the Cradle Will Rock" - Jun 09, 2014 - Corrects a regression where handlers were run across all hosts, not just those that triggered the handler. ++++++ ansible-1.6.3.tar.bz2 -> ansible-1.6.6.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-1.6.3/Makefile new/ansible-1.6.6/Makefile --- old/ansible-1.6.3/Makefile 2014-06-09 23:23:31.000000000 +0200 +++ new/ansible-1.6.6/Makefile 2014-07-01 21:56:05.000000000 +0200 @@ -5,7 +5,9 @@ # # useful targets: # make sdist ---------------- produce a tarball +# make srpm ----------------- produce a SRPM # make rpm ----------------- produce RPMs +# make deb-src -------------- produce a DEB source # make deb ------------------ produce a DEB # make docs ----------------- rebuild the manpages (results are checked in) # make tests ---------------- run the tests @@ -14,7 +16,7 @@ ######################################################## # variable section -NAME = "ansible" +NAME = ansible OS = $(shell uname -s) # Manpages are currently built with asciidoc -- would like to move to markdown @@ -45,6 +47,29 @@ DATE := $(shell date --utc --date="$(GIT_DATE)" +%Y%m%d%H%M) endif +# DEB build parameters +DEBUILD_BIN ?= debuild +DEBUILD_OPTS = --source-option="-I" +DPUT_BIN ?= dput +DPUT_OPTS ?= +ifeq ($(OFFICIAL),yes) + DEB_RELEASE = 1ppa + # Sign OFFICIAL builds using 'DEBSIGN_KEYID' + # DEBSIGN_KEYID is required when signing + ifneq ($(DEBSIGN_KEYID),) + DEBUILD_OPTS += -k$(DEBSIGN_KEYID) + endif +else + DEB_RELEASE = 0.git$(DATE) + # Do not sign unofficial builds + DEBUILD_OPTS += -uc -us + DPUT_OPTS += -u +endif +DEBUILD = $(DEBUILD_BIN) $(DEBUILD_OPTS) +DEB_PPA ?= ppa +# Choose the desired Ubuntu release: lucid precise saucy trusty +DEB_DIST ?= unstable + # RPM build parameters RPMSPECDIR= packaging/rpm RPMSPEC = $(RPMSPECDIR)/ansible.spec @@ -55,6 +80,10 @@ endif RPMNVR = "$(NAME)-$(VERSION)-$(RPMRELEASE)$(RPMDIST)" +# MOCK build parameters +MOCK_BIN ?= mock +MOCK_CFG ?= + NOSETESTS ?= nosetests ######################################################## @@ -129,6 +158,20 @@ @cp dist/*.gz rpm-build/ @sed -e 's#^Version:.*#Version: $(VERSION)#' -e 's#^Release:.*#Release: $(RPMRELEASE)%{?dist}#' $(RPMSPEC) >rpm-build/$(NAME).spec +mock-srpm: /etc/mock/$(MOCK_CFG).cfg rpmcommon + $(MOCK_BIN) -r $(MOCK_CFG) --resultdir rpm-build/ --buildsrpm --spec rpm-build/$(NAME).spec --sources rpm-build/ + @echo "#############################################" + @echo "Ansible SRPM is built:" + @echo rpm-build/*.src.rpm + @echo "#############################################" + +mock-rpm: /etc/mock/$(MOCK_CFG).cfg mock-srpm + $(MOCK_BIN) -r $(MOCK_CFG) --resultdir rpm-build/ --rebuild rpm-build/$(NAME)-*.src.rpm + @echo "#############################################" + @echo "Ansible RPM is built:" + @echo rpm-build/*.noarch.rpm + @echo "#############################################" + srpm: rpmcommon @rpmbuild --define "_topdir %(pwd)/rpm-build" \ --define "_builddir %{_topdir}" \ @@ -160,12 +203,44 @@ @echo "#############################################" debian: sdist + @for DIST in $(DEB_DIST) ; do \ + mkdir -p deb-build/$${DIST} ; \ + tar -C deb-build/$${DIST} -xvf dist/$(NAME)-$(VERSION).tar.gz ; \ + cp -a packaging/debian deb-build/$${DIST}/$(NAME)-$(VERSION)/ ; \ + sed -ie "s#^$(NAME) (\([^)]*\)) \([^;]*\);#ansible (\1-$(DEB_RELEASE)~$${DIST}) $${DIST};#" deb-build/$${DIST}/$(NAME)-$(VERSION)/debian/changelog ; \ + done + deb: debian - cp -r packaging/debian ./ - chmod 755 debian/rules - fakeroot debian/rules clean - fakeroot dh_install - fakeroot debian/rules binary + @for DIST in $(DEB_DIST) ; do \ + (cd deb-build/$${DIST}/$(NAME)-$(VERSION)/ && $(DEBUILD) -b) ; \ + done + @echo "#############################################" + @echo "Ansible DEB artifacts:" + @for DIST in $(DEB_DIST) ; do \ + echo deb-build/$${DIST}/$(NAME)_$(VERSION)-$(DEB_RELEASE)~$${DIST}_amd64.changes ; \ + done + @echo "#############################################" + +deb-src: debian + @for DIST in $(DEB_DIST) ; do \ + (cd deb-build/$${DIST}/$(NAME)-$(VERSION)/ && $(DEBUILD) -S) ; \ + done + @echo "#############################################" + @echo "Ansible DEB artifacts:" + @for DIST in $(DEB_DIST) ; do \ + echo deb-build/$${DIST}/$(NAME)_$(VERSION)-$(DEB_RELEASE)~$${DIST}_source.changes ; \ + done + @echo "#############################################" + +deb-upload: deb + @for DIST in $(DEB_DIST) ; do \ + $(DPUT_BIN) $(DPUT_OPTS) $(DEB_PPA) deb-build/$${DIST}/$(NAME)_$(VERSION)-$(DEB_RELEASE)~$${DIST}_amd64.changes ; \ + done + +deb-src-upload: deb-src + @for DIST in $(DEB_DIST) ; do \ + $(DPUT_BIN) $(DPUT_OPTS) $(DEB_PPA) deb-build/$${DIST}/$(NAME)_$(VERSION)-$(DEB_RELEASE)~$${DIST}_source.changes ; \ + done # for arch or gentoo, read instructions in the appropriate 'packaging' subdirectory directory diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-1.6.3/PKG-INFO new/ansible-1.6.6/PKG-INFO --- old/ansible-1.6.3/PKG-INFO 2014-06-09 23:23:32.000000000 +0200 +++ new/ansible-1.6.6/PKG-INFO 2014-07-01 21:56:06.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: ansible -Version: 1.6.3 +Version: 1.6.6 Summary: Radically simple IT automation Home-page: http://ansible.com/ Author: Michael DeHaan diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-1.6.3/ansible.egg-info/PKG-INFO new/ansible-1.6.6/ansible.egg-info/PKG-INFO --- old/ansible-1.6.3/ansible.egg-info/PKG-INFO 2014-06-09 23:23:31.000000000 +0200 +++ new/ansible-1.6.6/ansible.egg-info/PKG-INFO 2014-07-01 21:56:06.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: ansible -Version: 1.6.3 +Version: 1.6.6 Summary: Radically simple IT automation Home-page: http://ansible.com/ Author: Michael DeHaan diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-1.6.3/lib/ansible/__init__.py new/ansible-1.6.6/lib/ansible/__init__.py --- old/ansible-1.6.3/lib/ansible/__init__.py 2014-06-09 23:23:31.000000000 +0200 +++ new/ansible-1.6.6/lib/ansible/__init__.py 2014-07-01 21:56:05.000000000 +0200 @@ -14,5 +14,5 @@ # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see <http://www.gnu.org/licenses/>. -__version__ = '1.6.3' +__version__ = '1.6.6' __author__ = 'Michael DeHaan' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-1.6.3/lib/ansible/constants.py new/ansible-1.6.6/lib/ansible/constants.py --- old/ansible-1.6.3/lib/ansible/constants.py 2014-06-09 23:23:31.000000000 +0200 +++ new/ansible-1.6.6/lib/ansible/constants.py 2014-07-01 21:56:05.000000000 +0200 @@ -31,7 +31,7 @@ else: return False -def get_config(p, section, key, env_var, default, boolean=False, integer=False, floating=False): +def get_config(p, section, key, env_var, default, boolean=False, integer=False, floating=False, islist=False): ''' return a configuration variable with casting ''' value = _get_config(p, section, key, env_var, default) if boolean: @@ -40,6 +40,8 @@ return int(value) if value and floating: return float(value) + if value and islist: + return [x.strip() for x in value.split(',')] return value def _get_config(p, section, key, env_var, default): @@ -129,12 +131,12 @@ DEFAULT_HASH_BEHAVIOUR = get_config(p, DEFAULTS, 'hash_behaviour', 'ANSIBLE_HASH_BEHAVIOUR', 'replace') DEFAULT_JINJA2_EXTENSIONS = get_config(p, DEFAULTS, 'jinja2_extensions', 'ANSIBLE_JINJA2_EXTENSIONS', None) DEFAULT_EXECUTABLE = get_config(p, DEFAULTS, 'executable', 'ANSIBLE_EXECUTABLE', '/bin/sh') -DEFAULT_SU_EXE = get_config(p, DEFAULTS, 'su_exe', 'ANSIBLE_SU_EXE', 'su') -DEFAULT_SU = get_config(p, DEFAULTS, 'su', 'ANSIBLE_SU', False, boolean=True) -DEFAULT_SU_FLAGS = get_config(p, DEFAULTS, 'su_flags', 'ANSIBLE_SU_FLAGS', '') -DEFAULT_SU_USER = get_config(p, DEFAULTS, 'su_user', 'ANSIBLE_SU_USER', 'root') -DEFAULT_ASK_SU_PASS = get_config(p, DEFAULTS, 'ask_su_pass', 'ANSIBLE_ASK_SU_PASS', False, boolean=True) -DEFAULT_GATHERING = get_config(p, DEFAULTS, 'gathering', 'ANSIBLE_GATHERING', 'implicit').lower() +DEFAULT_SU_EXE = get_config(p, DEFAULTS, 'su_exe', 'ANSIBLE_SU_EXE', 'su') +DEFAULT_SU = get_config(p, DEFAULTS, 'su', 'ANSIBLE_SU', False, boolean=True) +DEFAULT_SU_FLAGS = get_config(p, DEFAULTS, 'su_flags', 'ANSIBLE_SU_FLAGS', '') +DEFAULT_SU_USER = get_config(p, DEFAULTS, 'su_user', 'ANSIBLE_SU_USER', 'root') +DEFAULT_ASK_SU_PASS = get_config(p, DEFAULTS, 'ask_su_pass', 'ANSIBLE_ASK_SU_PASS', False, boolean=True) +DEFAULT_GATHERING = get_config(p, DEFAULTS, 'gathering', 'ANSIBLE_GATHERING', 'implicit').lower() DEFAULT_ACTION_PLUGIN_PATH = get_config(p, DEFAULTS, 'action_plugins', 'ANSIBLE_ACTION_PLUGINS', '/usr/share/ansible_plugins/action_plugins') DEFAULT_CALLBACK_PLUGIN_PATH = get_config(p, DEFAULTS, 'callback_plugins', 'ANSIBLE_CALLBACK_PLUGINS', '/usr/share/ansible_plugins/callback_plugins') @@ -152,6 +154,7 @@ HOST_KEY_CHECKING = get_config(p, DEFAULTS, 'host_key_checking', 'ANSIBLE_HOST_KEY_CHECKING', True, boolean=True) SYSTEM_WARNINGS = get_config(p, DEFAULTS, 'system_warnings', 'ANSIBLE_SYSTEM_WARNINGS', True, boolean=True) DEPRECATION_WARNINGS = get_config(p, DEFAULTS, 'deprecation_warnings', 'ANSIBLE_DEPRECATION_WARNINGS', True, boolean=True) +DEFAULT_CALLABLE_WHITELIST = get_config(p, DEFAULTS, 'callable_whitelist', 'ANSIBLE_CALLABLE_WHITELIST', [], islist=True) # CONNECTION RELATED ANSIBLE_SSH_ARGS = get_config(p, 'ssh_connection', 'ssh_args', 'ANSIBLE_SSH_ARGS', None) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-1.6.3/lib/ansible/inventory/script.py new/ansible-1.6.6/lib/ansible/inventory/script.py --- old/ansible-1.6.3/lib/ansible/inventory/script.py 2014-06-09 23:23:31.000000000 +0200 +++ new/ansible-1.6.6/lib/ansible/inventory/script.py 2014-07-01 21:56:05.000000000 +0200 @@ -49,7 +49,7 @@ def _parse(self, err): all_hosts = {} - self.raw = utils.parse_json(self.data) + self.raw = utils.parse_json(self.data, from_remote=True) all = Group('all') groups = dict(all=all) group = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-1.6.3/lib/ansible/runner/__init__.py new/ansible-1.6.6/lib/ansible/runner/__init__.py --- old/ansible-1.6.3/lib/ansible/runner/__init__.py 2014-06-09 23:23:31.000000000 +0200 +++ new/ansible-1.6.6/lib/ansible/runner/__init__.py 2014-07-01 21:56:05.000000000 +0200 @@ -507,7 +507,7 @@ cmd2 = "rm -rf %s >/dev/null 2>&1" % tmp self._low_level_exec_command(conn, cmd2, tmp, sudoable=False) - data = utils.parse_json(res['stdout']) + data = utils.parse_json(res['stdout'], from_remote=True) if 'parsed' in data and data['parsed'] == False: data['msg'] += res['stderr'] return ReturnData(conn=conn, result=data) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-1.6.3/lib/ansible/runner/return_data.py new/ansible-1.6.6/lib/ansible/runner/return_data.py --- old/ansible-1.6.3/lib/ansible/runner/return_data.py 2014-06-09 23:23:31.000000000 +0200 +++ new/ansible-1.6.6/lib/ansible/runner/return_data.py 2014-07-01 21:56:05.000000000 +0200 @@ -43,8 +43,7 @@ self.diff = diff if type(self.result) in [ str, unicode ]: - self.result = utils.parse_json(self.result) - + self.result = utils.parse_json(self.result, from_remote=True) if self.host is None: raise Exception("host not set") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-1.6.3/lib/ansible/utils/__init__.py new/ansible-1.6.6/lib/ansible/utils/__init__.py --- old/ansible-1.6.3/lib/ansible/utils/__init__.py 2014-06-09 23:23:31.000000000 +0200 +++ new/ansible-1.6.6/lib/ansible/utils/__init__.py 2014-07-01 21:56:05.000000000 +0200 @@ -45,7 +45,6 @@ import sys import json -#import vault from vault import VaultLib VERBOSITY=0 @@ -69,6 +68,11 @@ except: pass +try: + import builtin +except ImportError: + import __builtin__ as builtin + KEYCZAR_AVAILABLE=False try: try: @@ -309,7 +313,38 @@ return json.loads(data) -def parse_json(raw_data): +def _clean_data(orig_data): + ''' remove template tags from a string ''' + data = orig_data + if isinstance(orig_data, basestring): + for pattern,replacement in (('{{','{#'), ('}}','#}'), ('{%','{#'), ('%}','#}')): + data = data.replace(pattern, replacement) + return data + +def _clean_data_struct(orig_data): + ''' + walk a complex data structure, and use _clean_data() to + remove any template tags that may exist + ''' + if isinstance(orig_data, dict): + data = orig_data.copy() + for key in data: + new_key = _clean_data_struct(key) + new_val = _clean_data_struct(data[key]) + if key != new_key: + del data[key] + data[new_key] = new_val + elif isinstance(orig_data, list): + data = orig_data[:] + for i in range(0, len(data)): + data[i] = _clean_data_struct(data[i]) + elif isinstance(orig_data, basestring): + data = _clean_data(orig_data) + else: + data = orig_data + return data + +def parse_json(raw_data, from_remote=False): ''' this version for module return data only ''' orig_data = raw_data @@ -318,7 +353,7 @@ data = filter_leading_non_json_lines(raw_data) try: - return json.loads(data) + results = json.loads(data) except: # not JSON, but try "Baby JSON" which allows many of our modules to not # require JSON and makes writing modules in bash much simpler @@ -328,7 +363,6 @@ except: print "failed to parse json: "+ data raise - for t in tokens: if "=" not in t: raise errors.AnsibleError("failed to parse: %s" % orig_data) @@ -343,7 +377,11 @@ results[key] = value if len(results.keys()) == 0: return { "failed" : True, "parsed" : False, "msg" : orig_data } - return results + + if from_remote: + results = _clean_data_struct(results) + + return results def smush_braces(data): ''' smush Jinaj2 braces so unresolved templates like {{ foo }} don't get parsed weird by key=value code ''' @@ -1040,22 +1078,22 @@ # visitor class defined below. SAFE_NODES = set( ( - ast.Expression, + ast.Add, + ast.BinOp, + ast.Call, ast.Compare, - ast.Str, - ast.List, - ast.Tuple, ast.Dict, - ast.Call, + ast.Div, + ast.Expression, + ast.List, ast.Load, - ast.BinOp, - ast.UnaryOp, + ast.Mult, ast.Num, ast.Name, - ast.Add, + ast.Str, ast.Sub, - ast.Mult, - ast.Div, + ast.Tuple, + ast.UnaryOp, ) ) @@ -1067,22 +1105,24 @@ ) ) - # builtin functions that are not safe to call - INVALID_CALLS = ( - 'classmethod', 'compile', 'delattr', 'eval', 'execfile', 'file', - 'filter', 'help', 'input', 'object', 'open', 'raw_input', 'reduce', - 'reload', 'repr', 'setattr', 'staticmethod', 'super', 'type', - ) + filter_list = [] + for filter in filter_loader.all(): + filter_list.extend(filter.filters().keys()) + + CALL_WHITELIST = C.DEFAULT_CALLABLE_WHITELIST + filter_list class CleansingNodeVisitor(ast.NodeVisitor): - def generic_visit(self, node): + def generic_visit(self, node, inside_call=False): if type(node) not in SAFE_NODES: - #raise Exception("invalid expression (%s) type=%s" % (expr, type(node))) raise Exception("invalid expression (%s)" % expr) - super(CleansingNodeVisitor, self).generic_visit(node) - def visit_Call(self, call): - if call.func.id in INVALID_CALLS: - raise Exception("invalid function: %s" % call.func.id) + elif isinstance(node, ast.Call): + inside_call = True + elif isinstance(node, ast.Name) and inside_call: + if hasattr(builtin, node.id) and node.id not in CALL_WHITELIST: + raise Exception("invalid function: %s" % node.id) + # iterate over all child nodes + for child_node in ast.iter_child_nodes(node): + self.generic_visit(child_node, inside_call) if not isinstance(expr, basestring): # already templated to a datastructure, perhaps? @@ -1090,9 +1130,9 @@ return (expr, None) return expr + cnv = CleansingNodeVisitor() try: parsed_tree = ast.parse(expr, mode='eval') - cnv = CleansingNodeVisitor() cnv.visit(parsed_tree) compiled = compile(parsed_tree, expr, 'eval') result = eval(compiled, {}, locals) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-1.6.3/lib/ansible/utils/template.py new/ansible-1.6.6/lib/ansible/utils/template.py --- old/ansible-1.6.3/lib/ansible/utils/template.py 2014-06-09 23:23:31.000000000 +0200 +++ new/ansible-1.6.6/lib/ansible/utils/template.py 2014-07-01 21:56:05.000000000 +0200 @@ -80,7 +80,6 @@ FILTER_PLUGINS = None _LISTRE = re.compile(r"(\w+)\[(\d+)\]") -JINJA2_OVERRIDE='#jinja2:' def lookup(name, *args, **kwargs): from ansible import utils @@ -228,16 +227,6 @@ except: raise errors.AnsibleError("unable to read %s" % realpath) - - # Get jinja env overrides from template - if data.startswith(JINJA2_OVERRIDE): - eol = data.find('\n') - line = data[len(JINJA2_OVERRIDE):eol] - data = data[eol+1:] - for pair in line.split(','): - (key,val) = pair.split(':') - setattr(environment,key.strip(),ast.literal_eval(val.strip())) - environment.template_class = J2Template try: t = environment.from_string(data) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible-1.6.3/packaging/rpm/ansible.spec new/ansible-1.6.6/packaging/rpm/ansible.spec --- old/ansible-1.6.3/packaging/rpm/ansible.spec 2014-06-09 23:23:31.000000000 +0200 +++ new/ansible-1.6.6/packaging/rpm/ansible.spec 2014-07-01 21:56:05.000000000 +0200 @@ -115,6 +115,15 @@ %changelog +* Tue Jul 01 2014 Michael DeHaan <[email protected]> - 1.6.6 +- Release 1.6.6 + +* Wed Jun 25 2014 Michael DeHaan <[email protected]> - 1.6.5 +- Release 1.6.5 + +* Wed Jun 25 2014 Michael DeHaan <[email protected]> - 1.6.4 +- Release 1.6.4 + * Mon Jun 09 2014 Michael DeHaan <[email protected]> - 1.6.3 - Release 1.6.3 -- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
