[X2Go-Commits] python-paramiko.git - master (branch) updated: d77a4d6421a726584da228c30587bfa265fee8ea

X2Go dev team git-admin at x2go.org
Wed May 8 13:40:30 CEST 2013


The branch, master has been updated
       via  d77a4d6421a726584da228c30587bfa265fee8ea (commit)
       via  3c2f01c91f798bc2ecefbd7c3864dc5e8c5af5c8 (commit)
       via  777d1576ca825f43d00f699865ead9bc0c37c637 (commit)
       via  d0c7a5d8847903dd30cc6deeb5446e071d9f2bec (commit)
       via  b33d9c7bca23704fe473f36a19bcfb74b2056dd8 (commit)
       via  b2b8d5d0a6e808b385e6f3ec41f3cfef7ee1a6ec (commit)
       via  0b9393b063d038f2d4ea985ff4763cccead31b02 (commit)
       via  f00f87c9bed2149c35b410ca02aa17e229c3de95 (commit)
       via  8c7f120c2cfb119ece27b4eea375de10ef5df554 (commit)
       via  675d79d743be0b9753ef36ca25b3af5e38de0ae2 (commit)
       via  aee2355d24277405867a8cb4ce8ce9616fee9d5b (commit)
       via  a1fa1ba9cc2cf6cc65ffebb629da30333de84058 (commit)
       via  3966ac103c988218be02371803817b66b4deab17 (commit)
       via  e6c23f23f4e6b8e6eb577d9b486dc21114b5a398 (commit)
       via  f861c2ff48f420b3be16f15bbf36c6ee3f779c38 (commit)
       via  6747d9944af483796890809b37a9cb260e08ee6d (commit)
       via  1b928df15ec4b95b0f96f26dc3740f1a0a91ca4a (commit)
       via  81f87f1d5eb4fa89472114f45faa3a994fc7db7d (commit)
       via  080bece2586d348b9791892cd6e5670a7afdb1a9 (commit)
       via  4f481a57a291316e75b0ce0b5f159e66b49ffe3a (commit)
       via  b96e7e41322e0c71d379cc04a524287a1c5fae4d (commit)
       via  b329512636d5f7610c02cc452943a65e2c39470a (commit)
       via  02d071be07fef362764b13a99dd9335a668c9117 (commit)
       via  1d494eb0db49316365d9328059a8bf869c7e95c0 (commit)
       via  2e2a915807f647528f971743e489b0052f46d288 (commit)
       via  73a0d03bdc86a89ca1b452210887f9e86439a8cc (commit)
       via  17ba0d5b61fe0c87b6b7b657d1b74176931f890f (commit)
       via  2a774d1e8a3a607bc88eaf712224d3cdd3352f4f (commit)
       via  9695747875ab26d4acb7feea27e1ece689ad2572 (commit)
       via  5c9aa3dcdce10167a76b4abfff73de6097cd56a5 (commit)
       via  2e069824ed8a53c47dde442805487051b6fc3ef3 (commit)
       via  0392e3df8f5efab2551ca8f5f758f3acdb5d5ecc (commit)
       via  a7ee2509e48d7e2bb533ce5e50796b9a887c7a8a (commit)
       via  d5db60329701df2423909773af9cc9aa5df4d4f6 (commit)
       via  0cc6bb970f54467841084f5a6199985d0ec045b1 (commit)
       via  c3056914926e0940ed5716fcdaf2f4a0a8b71fb7 (commit)
       via  9858ccf207f4d2df5b006fcf114d3ee303789afb (commit)
       via  abe009b14980d8547b111f07ca9ca782c0bb47a6 (commit)
       via  3cd7f585d01afe7eb97374e26a9f72197fc77a42 (commit)
       via  a3fe422198b1faf75afaf8e2ccfc752dcba64c82 (commit)
       via  3a9119d78a8745e4e45bea0881bfae3deb2adab3 (commit)
       via  721f74d8c240071e444ed1cdfff432c75339e8b2 (commit)
       via  bd1a97a0453afb07466ff3cacc2df9b3ee065224 (commit)
       via  dd7edd8ec85e339b2ad6dd7d64abf25c8396b0ea (commit)
       via  edc9eaf4f2993858b10594dbda3d321c45444417 (commit)
       via  277526315e18c7190a92471eab61d3610331c7df (commit)
       via  1903ee1432676f1d3a51daba7ae2edffdd4a28cd (commit)
       via  732417bf988228811943873bc7cab29e425847bc (commit)
       via  06f970482060c707d210561159c3d9e5ba5a5f1f (commit)
       via  93dce43e8643c9dc79fadd546426d9434ae6e534 (commit)
       via  38767982cd1a5181536a5f24f830e71933f7fb10 (commit)
       via  f41fc8fd28c9fd7a41fd6c291d507a7a1b3cde56 (commit)
       via  109d2b200a1e419f139166380fd0d136f4d57321 (commit)
       via  ea3c3f53b662eafa5dfa8e25d9543a568b81b353 (commit)
       via  c79e6a3f92938af07915c23ea1d7cbb75dac6d89 (commit)
       via  32424ba109f2c9a3843b69c6768e04616fceb5fd (commit)
       via  ac1310c4a171587ae8fd15268a1981eaf139be57 (commit)
       via  b3d5156019566c972a3dbbea851cffed3cf08514 (commit)
       via  42d77483e846db5e43296d7055d059e43168dab7 (commit)
       via  85551dffd6d6008cf0409ee776d6792a8e51468f (commit)
       via  57d776b31851bf6f50e46b3c41d064fff90d4385 (commit)
       via  21689d964798fbbd037e7b995088cee883796225 (commit)
       via  98ae4e975dd8fd25af4b615bbbd034cc0831e1fa (commit)
       via  3563fca9942a752697cb17af71b179c4f6cc4361 (commit)
       via  b9242c654abd3ec5137f482436f57a306b151573 (commit)
       via  9d2fb82284a42de81bcb35d2536e682687d1bd81 (commit)
       via  bf4b535920eee426ceae1ade30a62e7afd698619 (commit)
       via  2f1daad1b9dfb2a8ff38795d5b3a1d95765a0ea4 (commit)
       via  e034a24f87ca24171fceb9f9069149f3fc76d51b (commit)
       via  7e5911a1ff67c1d7be58e3cb8faa456156a455ed (commit)
       via  adad068b132d0f0c64174b75fb72ee238c289992 (commit)
       via  8e697988af494f4cd499911c9a15ee25d6e2a492 (commit)
       via  a69abd46067743c965c4eb687fc2b4a52f9b57b5 (commit)
       via  f493a00c1173fd7b15018639fe77fcfdd7787200 (commit)
       via  ac9370d3e0e4a645f893028c9dee0ece20c4f0d3 (commit)
       via  37d0247301cafe1cf683cc7b3edb95dc6b30caac (commit)
       via  e761502e8ec5958c27228006314e423057ff8c9d (commit)
       via  6b5d7483583627ae5cceee2a59c9c23b78ed0a0b (commit)
       via  0d38f3f1f266092a2a5e91cae9527acd8bbb72e7 (commit)
       via  0c56e2a40bae88163c9422b1bca5fa6fc4c97640 (commit)
       via  6284666cfda3174aac619221a518b57a2bcd5ba0 (commit)
       via  bf87cd124d1e973e1ba80de6cba0585d217801d8 (commit)
       via  235050a67ccf0500b91de10e2f208c0456cbe43c (commit)
       via  08109136b4217b3fc620436819f4c92434189955 (commit)
       via  0b6aebb8a95d44102968a3e0caf94cd234369e5d (commit)
       via  602250fdf9515a8127cd567d79afa8134d4cf923 (commit)
       via  21cb9a2d86b2c0419444ea7c2e5e1c35a62b6373 (commit)
       via  876c9bdbdaf111793c06ab154c808910766ea8d9 (commit)
       via  d5edad63a37c36aae83be0d5ddd61a2ec816f1b1 (commit)
       via  5f5137414c91c931f04522b1bb8c2800294d6a90 (commit)
       via  6c4c00a3f3941a37e3bc25e9ed59e889b93cd06d (commit)
       via  ce86a53a37c1573a5633699cf7f795768907f9a2 (commit)
       via  7bde7840dd4ffeae3c794eca216244975c856f94 (commit)
       via  9f21d360409b1ef45cab70a3b5cb79674be34fa2 (commit)
       via  3bbcf808d8da43a379cee5ce3d004d3c6eb6e1b7 (commit)
       via  cd51bfc031eb9163fd6c85bb3d4ec23476bb2090 (commit)
       via  0ae0e9800c7bfb3f8ea40fa0d33ebf6dff49f759 (commit)
       via  2cbe38308097f920cec0aa87fed85f2cfe6d9612 (commit)
       via  9c0d467667568fa67d45ef288cc1f6ee8a7ebfe6 (commit)
       via  70fce374b403788d665de896ecffc65f7b2ad875 (commit)
       via  5073b7236da0f8052049701136cd2f94acad07d2 (commit)
       via  7255dcf042d3099d8b8f7d618da0fbc19e401008 (commit)
       via  531606b0d6ebb46f8f36de554a571d96790ce3f8 (commit)
       via  2223aa10ccc473c440de7233e9daa69019a8bb01 (commit)
       via  2ae06c70afb11c5229188d5c8cbbef4c5d4d1b0f (commit)
       via  287f9c3423eaa5a48357bcccd5892437f764706e (commit)
       via  bda161330ffe6556db63afd3742a1727b969acb6 (commit)
       via  03c350903e9ece9d9729ec2af9f9107188c3d8a7 (commit)
       via  682a3eff8447ef83c10b0917c06f595803db97bb (commit)
       via  7a4d3c4e424e6669d091be495eb8a000b4815ebe (commit)
       via  203c7379ac1211cc393d127ab83be774faa0f913 (commit)
       via  8496eff0b7afc07675e1f42815f83633d87097ee (commit)
       via  537f95dbb36c578e65cfd9cbc1a1eabc03f38428 (commit)
       via  bc3674d0f0c61b5c7af9cfbc9248cf574d0998b0 (commit)
       via  962d4a3cece3ebd2e941dccf1cd555f33f3bbd83 (commit)
       via  f6ed6a8bbff9aabc58176b04e61c84b0509ecb7b (commit)
       via  b9c39fc1d2d634967b125f776347b422e7a96ab4 (commit)
       via  2575b3efc48cb9f42c3105015284b9afd9c67625 (commit)
       via  d47e6b9e7f3df5497d0ba664656e1a8b396ddd20 (commit)
       via  5ed0e11a7f6a85c8190f3167e64f279741dd8359 (commit)
       via  2832f3c60fdf078066a266cd32027a0b8ed65ce9 (commit)
       via  2403504b44de773c3f566e7d647bc0e8661af918 (commit)
       via  10c51e27263afb62c395ca2905195482d7c3b630 (commit)
       via  c4d4818cddf851665c8e9777355209d1aab8d4bb (commit)
       via  13892788c315bc2a3aeb7f2c22a5307ed7a3f47e (commit)
       via  c0ef3fd4936984be100f9a4738c89b02344ff141 (commit)
       via  0698254b188736e47dc804e16595e96e8e37bd21 (commit)
       via  64d6734086fbf5db898495c69959921715617617 (commit)
       via  a07a339006259eb7ad60993415493d2d25a760d3 (commit)
       via  5670e111c95612fc1c96135ca0e942ed89ec5420 (commit)
       via  78654e82ecfceba30c61974b18fd4809b1ea9899 (commit)
       via  71f8c5c9f53e3eda550ddb68ba573eed289d74ec (commit)
       via  d7aa342c20b67f1846c7458b09fdd3b0f308dd71 (commit)
       via  b42c73356c2c5afbfbd4bf0b1278368d5910ea5d (commit)
       via  79dffacf4e4ec63a4db8869c03aae33bbbbfa0b5 (commit)
       via  45aa88b530cff0ad09f5da83efd4697ba7986563 (commit)
       via  e0d71b5efb47434ad5957632e8fba0e6fec6389f (commit)
       via  668870aa83e095c81371ffce8d69404ac29e536f (commit)
       via  23f3099b6f7808934400b96b707e122ed2dd302c (commit)
       via  c78a5856e88a67344cb47e170ec8e237df1fc2c6 (commit)
       via  221131fa21e624a30d0d7cd5ed29e2749ddfd246 (commit)
       via  04cc4d55102c111d3e054ef8d83a0a62c34de561 (commit)
       via  7ce9875ed7d3e3738ed775ffda4ded1ce12d35f2 (commit)
       via  d66d75f277cfd49b8adc84498b0b09d71092bd0b (commit)
       via  b22c11ab1b889032345a80b89af6ebee50ef7904 (commit)
       via  2dd74f953d0a789a9c65662492f8340d66246e5b (commit)
       via  ad587fa0ef32eeeb633245c7ae68759765c0e439 (commit)
       via  f33481cc44fb57a788b5726b529383f300d06b36 (commit)
       via  3174b6c894b125426a7a4bae7f934a0dbe64d5b1 (commit)
      from  06d987c362e75a5d86c411c20932a3818fb4fa40 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit d77a4d6421a726584da228c30587bfa265fee8ea
Merge: 777d157 3c2f01c
Author: Jeff Forcier <jeff at bitprophet.org>
Date:   Sun May 5 13:59:41 2013 -0700

    Merge branch '1.10'

commit 3c2f01c91f798bc2ecefbd7c3864dc5e8c5af5c8
Author: Jeff Forcier <jeff at bitprophet.org>
Date:   Sun May 5 13:59:34 2013 -0700

    Flip bad known_hosts line to INFO from WARN re #153

-----------------------------------------------------------------------

Summary of changes:
 .gitignore              |    2 +
 .travis.yml             |    4 +-
 Makefile                |    2 +-
 NEWS                    |   76 ++++++++++++-
 README                  |   27 +----
 demos/demo.py           |    1 -
 paramiko/__init__.py    |    8 +-
 paramiko/_winapi.py     |  269 +++++++++++++++++++++++++++++++++++++++++++++++
 paramiko/agent.py       |   17 ++-
 paramiko/channel.py     |   24 +++--
 paramiko/client.py      |   26 +++--
 paramiko/config.py      |  180 +++++++++++++++++++++----------
 paramiko/file.py        |    4 +
 paramiko/hostkeys.py    |   18 +++-
 paramiko/message.py     |    3 +-
 paramiko/packet.py      |   12 ++-
 paramiko/sftp_client.py |  130 ++++++++++++++++-------
 paramiko/sftp_file.py   |   25 +++--
 paramiko/transport.py   |    5 +-
 paramiko/win_pageant.py |   81 +++++---------
 requirements.txt        |    2 +
 setup.py                |    2 +-
 tests/test_sftp.py      |   45 +++++---
 tests/test_util.py      |  117 ++++++++++++++++++---
 tox.ini                 |    6 ++
 25 files changed, 838 insertions(+), 248 deletions(-)
 create mode 100644 paramiko/_winapi.py
 create mode 100644 requirements.txt
 create mode 100644 tox.ini

The diff of changes is:
diff --git a/.gitignore b/.gitignore
index 3283ff3..4b57895 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
 *.pyc
 build/
 dist/
+.tox/
 paramiko.egg-info/
 test.log
+docs/
diff --git a/.travis.yml b/.travis.yml
index 90dbb80..6896b89 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,9 +8,7 @@ install:
   - pip install -e .
 script: python test.py
 notifications:
-  email:
-    on_failure: change
   irc:
-    channels: "irc.freenode.org#fabric"
+    channels: "irc.freenode.org#paramiko"
     on_success: change
     on_failure: change
diff --git a/Makefile b/Makefile
index 3c12990..572f867 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 release: docs
 	python setup.py sdist register upload
 
-docs:
+docs: paramiko/*
 	epydoc --no-private -o docs/ paramiko
 
 clean:
diff --git a/NEWS b/NEWS
index 5542046..ef85eff 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,80 @@ Issues noted as "Fabric #NN" can be found at https://github.com/fabric/fabric/.
 Releases
 ========
 
+v1.11.0 (DD MM YYYY)
+--------------------
+
+* #98: On Windows, when interacting with the PuTTY PAgeant, Paramiko now
+  creates the shared memory map with explicit Security Attributes of the user,
+  which is the same technique employed by the canonical PuTTY library to avoid
+  permissions issues when Paramiko is running under a different UAC context
+  than the PuTTY Ageant process. Thanks to Jason R. Coombs for the patch.
+* #100: Remove use of PyWin32 in `win_pageant` module. Module was already
+  dependent on ctypes for constructing appropriate structures and had ctypes
+  implementations of all functionality. Thanks to Jason R. Coombs for the
+  patch.
+* #87: Ensure updates to `known_hosts` files account for any updates to said
+  files after Paramiko initially read them. (Includes related fix to guard
+  against duplicate entries during subsequent `known_hosts` loads.) Thanks to
+  `@sunweaver` for the contribution.
+
+v1.10.2 (DD MM 2013)
+--------------------
+
+* #153, #67: Warn on parse failure when reading known_hosts
+  file. Thanks to `@glasserc` for patch.
+* #146: Indentation fixes for readability. Thanks to Abhinav Upadhyay for catch
+  & patch.
+
+v1.10.1 (5th Apr 2013)
+----------------------
+
+* #142: (Fabric #811) SFTP put of empty file will still return the attributes
+  of the put file. Thanks to Jason R. Coombs for the patch.
+* #154: (Fabric #876) Forwarded SSH agent connections left stale local pipes
+  lying around, which could cause local (and sometimes remote or network)
+  resource starvation when running many agent-using remote commands. Thanks to
+  Kevin Tegtmeier for catch & patch.
+
+v1.10.0 (1st Mar 2013)
+--------------------
+
+* #66: Batch SFTP writes to help speed up file transfers. Thanks to Olle
+  Lundberg for the patch.
+* #133: Fix handling of window-change events to be on-spec and not
+  attempt to wait for a response from the remote sshd; this fixes problems with
+  less common targets such as some Cisco devices. Thanks to Phillip Heller for
+  catch & patch.
+* #93: Overhaul SSH config parsing to be in line with `man ssh_config` (& the
+  behavior of `ssh` itself), including addition of parameter expansion within
+  config values. Thanks to Olle Lundberg for the patch.
+* #110: Honor SSH config `AddressFamily` setting when looking up local
+  host's FQDN. Thanks to John Hensley for the patch.
+* #128: Defer FQDN resolution until needed, when parsing SSH config files.
+  Thanks to Parantapa Bhattacharya for catch & patch.
+* #102: Forego random padding for packets when running under `*-ctr` ciphers.
+  This corrects some slowdowns on platforms where random byte generation is
+  inefficient (e.g. Windows). Thanks to  `@warthog618` for catch & patch, and
+  Michael van der Kolff for code/technique review.
+* #127: Turn `SFTPFile` into a context manager. Thanks to Michael Williamson
+  for the patch.
+* #116: Limit `Message.get_bytes` to an upper bound of 1MB to protect against
+  potential DoS vectors. Thanks to `@mvschaik` for catch & patch.
+* #115: Add convenience `get_pty` kwarg to `Client.exec_command` so users not
+  manually controlling a channel object can still toggle PTY creation. Thanks
+  to Michael van der Kolff for the patch.
+* #71: Add `SFTPClient.putfo` and `.getfo` methods to allow direct
+  uploading/downloading of file-like objects. Thanks to Eric Buehl for the
+  patch.
+* #113: Add `timeout` parameter to `SSHClient.exec_command` for easier setting
+  of the command's internal channel object's timeout. Thanks to Cernov Vladimir
+  for the patch.
+* #94: Remove duplication of SSH port constant. Thanks to Olle Lundberg for the
+  catch.
+* #80: Expose the internal "is closed" property of the file transfer class
+  `BufferedFile` as `.closed`, better conforming to Python's file interface.
+  Thanks to `@smunaut` and James Hiscock for catch & patch.
+
 v1.9.0 (6th Nov 2012)
 ---------------------
 
@@ -286,7 +360,7 @@ v1.5 (paras) 02oct05
     separation
   * demo scripts fixed to have a better chance of loading the host keys
     correctly on windows/cygwin
-  
+
 v1.4 (oddish) 17jul05
 ---------------------
   * added SSH-agent support (for posix) from john rochester
diff --git a/README b/README
index 9e9cc3a..1899819 100644
--- a/README
+++ b/README
@@ -5,22 +5,17 @@ paramiko
 
 :Paramiko: Python SSH module
 :Copyright: Copyright (c) 2003-2009  Robey Pointer <robeypointer at gmail.com>
-:Copyright: Copyright (c) 2012  Jeff Forcier <jeff at bitprophet.org>
+:Copyright: Copyright (c) 2013  Jeff Forcier <jeff at bitprophet.org>
 :License: LGPL
 :Homepage: https://github.com/paramiko/paramiko/
-
-
-paramiko 1.8.0
-==============
-
-Release of MM.YY.DD
+:API docs: http://docs.paramiko.org
 
 
 What
 ----
 
 "paramiko" is a combination of the esperanto words for "paranoid" and
-"friend".  it's a module for python 2.2+ that implements the SSH2 protocol
+"friend".  it's a module for python 2.5+ that implements the SSH2 protocol
 for secure (encrypted and authenticated) connections to remote machines.
 unlike SSL (aka TLS), SSH2 protocol does not require hierarchical
 certificates signed by a powerful central authority. you may know SSH2 as
@@ -39,8 +34,7 @@ that should have come with this archive.
 Requirements
 ------------
 
-  - python 2.3 or better <http://www.python.org/>
-    (python 2.2 is also supported, but not recommended)
+  - python 2.5 or better <http://www.python.org/>
   - pycrypto 2.1 or better <https://www.dlitz.net/software/pycrypto/>
 
 If you have setuptools, you can build and install paramiko and all its
@@ -58,19 +52,6 @@ should also work on Windows, though i don't test it as frequently there.
 if you run into Windows problems, send me a patch: portability is important
 to me.
 
-python 2.2 may work, thanks to some patches from Roger Binns. things to
-watch out for:
-
-    * sockets in 2.2 don't support timeouts, so the 'select' module is
-      imported to do polling.  
-    * logging is mostly stubbed out. it works just enough to let paramiko
-      create log files for debugging, if you want them. to get real logging,
-      you can backport python 2.3's logging package. Roger has done that
-      already:
-      http://sourceforge.net/project/showfiles.php?group_id=75211&package_id=113804
-
-you really should upgrade to python 2.3. laziness is no excuse! :)
-
 some python distributions don't include the utf-8 string encodings, for
 reasons of space (misdirected as that is). if your distribution is
 missing encodings, you'll see an error like this::
diff --git a/demos/demo.py b/demos/demo.py
index 05524d3..c21a926 100755
--- a/demos/demo.py
+++ b/demos/demo.py
@@ -26,7 +26,6 @@ import os
 import select
 import socket
 import sys
-import threading
 import time
 import traceback
 
diff --git a/paramiko/__init__.py b/paramiko/__init__.py
index 29e470a..099314e 100644
--- a/paramiko/__init__.py
+++ b/paramiko/__init__.py
@@ -18,7 +18,7 @@
 
 """
 I{Paramiko} (a combination of the esperanto words for "paranoid" and "friend")
-is a module for python 2.3 or greater that implements the SSH2 protocol for
+is a module for python 2.5 or greater that implements the SSH2 protocol for
 secure (encrypted and authenticated) connections to remote machines.  Unlike
 SSL (aka TLS), the SSH2 protocol does not require hierarchical certificates
 signed by a powerful central authority.  You may know SSH2 as the protocol that
@@ -50,12 +50,12 @@ Website: U{https://github.com/paramiko/paramiko/}
 
 import sys
 
-if sys.version_info < (2, 2):
-    raise RuntimeError('You need python 2.2 for this module.')
+if sys.version_info < (2, 5):
+    raise RuntimeError('You need python 2.5+ for this module.')
 
 
 __author__ = "Jeff Forcier <jeff at bitprophet.org>"
-__version__ = "1.9.0"
+__version__ = "1.10.1"
 __license__ = "GNU Lesser General Public License (LGPL)"
 
 
diff --git a/paramiko/_winapi.py b/paramiko/_winapi.py
new file mode 100644
index 0000000..f141b00
--- /dev/null
+++ b/paramiko/_winapi.py
@@ -0,0 +1,269 @@
+"""
+Windows API functions implemented as ctypes functions and classes as found
+in jaraco.windows (2.10).
+
+If you encounter issues with this module, please consider reporting the issues
+in jaraco.windows and asking the author to port the fixes back here.
+"""
+
+import ctypes
+import ctypes.wintypes
+import __builtin__
+
+######################
+# jaraco.windows.error
+
+def format_system_message(errno):
+	"""
+	Call FormatMessage with a system error number to retrieve
+	the descriptive error message.
+	"""
+	# first some flags used by FormatMessageW
+	ALLOCATE_BUFFER = 0x100
+	ARGUMENT_ARRAY = 0x2000
+	FROM_HMODULE = 0x800
+	FROM_STRING = 0x400
+	FROM_SYSTEM = 0x1000
+	IGNORE_INSERTS = 0x200
+
+	# Let FormatMessageW allocate the buffer (we'll free it below)
+	# Also, let it know we want a system error message.
+	flags = ALLOCATE_BUFFER | FROM_SYSTEM
+	source = None
+	message_id = errno
+	language_id = 0
+	result_buffer = ctypes.wintypes.LPWSTR()
+	buffer_size = 0
+	arguments = None
+	bytes = ctypes.windll.kernel32.FormatMessageW(
+		flags,
+		source,
+		message_id,
+		language_id,
+		ctypes.byref(result_buffer),
+		buffer_size,
+		arguments,
+		)
+	# note the following will cause an infinite loop if GetLastError
+	#  repeatedly returns an error that cannot be formatted, although
+	#  this should not happen.
+	handle_nonzero_success(bytes)
+	message = result_buffer.value
+	ctypes.windll.kernel32.LocalFree(result_buffer)
+	return message
+
+
+class WindowsError(__builtin__.WindowsError):
+	"more info about errors at http://msdn.microsoft.com/en-us/library/ms681381(VS.85).aspx"
+
+	def __init__(self, value=None):
+		if value is None:
+			value = ctypes.windll.kernel32.GetLastError()
+		strerror = format_system_message(value)
+		super(WindowsError, self).__init__(value, strerror)
+
+	@property
+	def message(self):
+		return self.strerror
+
+	@property
+	def code(self):
+		return self.winerror
+
+	def __str__(self):
+		return self.message
+
+	def __repr__(self):
+		return '{self.__class__.__name__}({self.winerror})'.format(**vars())
+
+def handle_nonzero_success(result):
+	if result == 0:
+		raise WindowsError()
+
+
+#####################
+# jaraco.windows.mmap
+
+CreateFileMapping = ctypes.windll.kernel32.CreateFileMappingW
+CreateFileMapping.argtypes = [
+	ctypes.wintypes.HANDLE,
+	ctypes.c_void_p,
+	ctypes.wintypes.DWORD,
+	ctypes.wintypes.DWORD,
+	ctypes.wintypes.DWORD,
+	ctypes.wintypes.LPWSTR,
+]
+CreateFileMapping.restype = ctypes.wintypes.HANDLE
+
+MapViewOfFile = ctypes.windll.kernel32.MapViewOfFile
+MapViewOfFile.restype = ctypes.wintypes.HANDLE
+
+class MemoryMap(object):
+	"""
+	A memory map object which can have security attributes overrideden.
+	"""
+	def __init__(self, name, length, security_attributes=None):
+		self.name = name
+		self.length = length
+		self.security_attributes = security_attributes
+		self.pos = 0
+
+	def __enter__(self):
+		p_SA = (
+			ctypes.byref(self.security_attributes)
+			if self.security_attributes else None
+		)
+		INVALID_HANDLE_VALUE = -1
+		PAGE_READWRITE = 0x4
+		FILE_MAP_WRITE = 0x2
+		filemap = ctypes.windll.kernel32.CreateFileMappingW(
+			INVALID_HANDLE_VALUE, p_SA, PAGE_READWRITE, 0, self.length,
+			unicode(self.name))
+		handle_nonzero_success(filemap)
+		if filemap == INVALID_HANDLE_VALUE:
+			raise Exception("Failed to create file mapping")
+		self.filemap = filemap
+		self.view = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0)
+		return self
+
+	def seek(self, pos):
+		self.pos = pos
+
+	def write(self, msg):
+		ctypes.windll.msvcrt.memcpy(self.view + self.pos, msg, len(msg))
+		self.pos += len(msg)
+
+	def read(self, n):
+		"""
+		Read n bytes from mapped view.
+		"""
+		out = ctypes.create_string_buffer(n)
+		ctypes.windll.msvcrt.memcpy(out, self.view + self.pos, n)
+		self.pos += n
+		return out.raw
+
+	def __exit__(self, exc_type, exc_val, tb):
+		ctypes.windll.kernel32.UnmapViewOfFile(self.view)
+		ctypes.windll.kernel32.CloseHandle(self.filemap)
+
+#########################
+# jaraco.windows.security
+
+class TokenInformationClass:
+	TokenUser = 1
+
+class TOKEN_USER(ctypes.Structure):
+	num = 1
+	_fields_ = [
+		('SID', ctypes.c_void_p),
+		('ATTRIBUTES', ctypes.wintypes.DWORD),
+	]
+
+
+class SECURITY_DESCRIPTOR(ctypes.Structure):
+	"""
+	typedef struct _SECURITY_DESCRIPTOR
+		{
+		UCHAR Revision;
+		UCHAR Sbz1;
+		SECURITY_DESCRIPTOR_CONTROL Control;
+		PSID Owner;
+		PSID Group;
+		PACL Sacl;
+		PACL Dacl;
+		}   SECURITY_DESCRIPTOR;
+	"""
+	SECURITY_DESCRIPTOR_CONTROL = ctypes.wintypes.USHORT
+	REVISION = 1
+
+	_fields_ = [
+		('Revision', ctypes.c_ubyte),
+		('Sbz1', ctypes.c_ubyte),
+		('Control', SECURITY_DESCRIPTOR_CONTROL),
+		('Owner', ctypes.c_void_p),
+		('Group', ctypes.c_void_p),
+		('Sacl', ctypes.c_void_p),
+		('Dacl', ctypes.c_void_p),
+	]
+
+class SECURITY_ATTRIBUTES(ctypes.Structure):
+	"""
+	typedef struct _SECURITY_ATTRIBUTES {
+		DWORD  nLength;
+		LPVOID lpSecurityDescriptor;
+		BOOL   bInheritHandle;
+	} SECURITY_ATTRIBUTES;
+	"""
+	_fields_ = [
+		('nLength', ctypes.wintypes.DWORD),
+		('lpSecurityDescriptor', ctypes.c_void_p),
+		('bInheritHandle', ctypes.wintypes.BOOL),
+	]
+
+	def __init__(self, *args, **kwargs):
+		super(SECURITY_ATTRIBUTES, self).__init__(*args, **kwargs)
+		self.nLength = ctypes.sizeof(SECURITY_ATTRIBUTES)
+
+	def _get_descriptor(self):
+		return self._descriptor
+	def _set_descriptor(self, descriptor):
+		self._descriptor = descriptor
+		self.lpSecurityDescriptor = ctypes.addressof(descriptor)
+	descriptor = property(_get_descriptor, _set_descriptor)
+
+def GetTokenInformation(token, information_class):
+	"""
+	Given a token, get the token information for it.
+	"""
+	data_size = ctypes.wintypes.DWORD()
+	ctypes.windll.advapi32.GetTokenInformation(token, information_class.num,
+		0, 0, ctypes.byref(data_size))
+	data = ctypes.create_string_buffer(data_size.value)
+	handle_nonzero_success(ctypes.windll.advapi32.GetTokenInformation(token,
+		information_class.num,
+		ctypes.byref(data), ctypes.sizeof(data),
+		ctypes.byref(data_size)))
+	return ctypes.cast(data, ctypes.POINTER(TOKEN_USER)).contents
+
+class TokenAccess:
+	TOKEN_QUERY = 0x8
+
+def OpenProcessToken(proc_handle, access):
+	result = ctypes.wintypes.HANDLE()
+	proc_handle = ctypes.wintypes.HANDLE(proc_handle)
+	handle_nonzero_success(ctypes.windll.advapi32.OpenProcessToken(
+		proc_handle, access, ctypes.byref(result)))
+	return result
+
+def get_current_user():
+	"""
+	Return a TOKEN_USER for the owner of this process.
+	"""
+	process = OpenProcessToken(
+		ctypes.windll.kernel32.GetCurrentProcess(),
+		TokenAccess.TOKEN_QUERY,
+	)
+	return GetTokenInformation(process, TOKEN_USER)
+
+def get_security_attributes_for_user(user=None):
+	"""
+	Return a SECURITY_ATTRIBUTES structure with the SID set to the
+	specified user (uses current user if none is specified).
+	"""
+	if user is None:
+		user = get_current_user()
+
+	assert isinstance(user, TOKEN_USER), "user must be TOKEN_USER instance"
+
+	SD = SECURITY_DESCRIPTOR()
+	SA = SECURITY_ATTRIBUTES()
+	# by attaching the actual security descriptor, it will be garbage-
+	# collected with the security attributes
+	SA.descriptor = SD
+	SA.bInheritHandle = 1
+
+	ctypes.windll.advapi32.InitializeSecurityDescriptor(ctypes.byref(SD),
+		SECURITY_DESCRIPTOR.REVISION)
+	ctypes.windll.advapi32.SetSecurityDescriptorOwner(ctypes.byref(SD),
+		user.SID, 0)
+	return SA
diff --git a/paramiko/agent.py b/paramiko/agent.py
index 5d04dce..d4ff703 100644
--- a/paramiko/agent.py
+++ b/paramiko/agent.py
@@ -130,15 +130,22 @@ class AgentProxyThread(threading.Thread):
                     if len(data) != 0:
                         self.__inr.send(data)
                     else:
+                        self._close()
                         break
                 elif self.__inr == fd:
                     data = self.__inr.recv(512)
                     if len(data) != 0:
                         self._agent._conn.send(data)
                     else:
+                        self._close()
                         break
             time.sleep(io_sleep)
 
+    def _close(self):
+        self._exit = True
+        self.__inr.close()
+        self._agent._conn.close()
+
 class AgentLocalProxy(AgentProxyThread):
     """
     Class to be used when wanting to ask a local SSH Agent being
@@ -248,11 +255,11 @@ class AgentServerProxy(AgentSSH):
         self.close()
 
     def connect(self):
-         conn_sock = self.__t.open_forward_agent_channel()
-         if conn_sock is None:
-             raise SSHException('lost ssh-agent')
-         conn_sock.set_name('auth-agent')
-         self._connect(conn_sock)
+        conn_sock = self.__t.open_forward_agent_channel()
+        if conn_sock is None:
+            raise SSHException('lost ssh-agent')
+        conn_sock.set_name('auth-agent')
+        self._connect(conn_sock)
 
     def close(self):
         """
diff --git a/paramiko/channel.py b/paramiko/channel.py
index 534f8d7..0c603c6 100644
--- a/paramiko/channel.py
+++ b/paramiko/channel.py
@@ -122,7 +122,8 @@ class Channel (object):
         out += '>'
         return out
 
-    def get_pty(self, term='vt100', width=80, height=24):
+    def get_pty(self, term='vt100', width=80, height=24, width_pixels=0,
+                height_pixels=0):
         """
         Request a pseudo-terminal from the server.  This is usually used right
         after creating a client channel, to ask the server to provide some
@@ -136,6 +137,10 @@ class Channel (object):
         @type width: int
         @param height: height (in characters) of the terminal screen
         @type height: int
+        @param width_pixels: width (in pixels) of the terminal screen
+        @type width_pixels: int
+        @param height_pixels: height (in pixels) of the terminal screen
+        @type height_pixels: int
         
         @raise SSHException: if the request was rejected or the channel was
             closed
@@ -150,8 +155,8 @@ class Channel (object):
         m.add_string(term)
         m.add_int(width)
         m.add_int(height)
-        # pixel height, width (usually useless)
-        m.add_int(0).add_int(0)
+        m.add_int(width_pixels)
+        m.add_int(height_pixels)
         m.add_string('')
         self._event_pending()
         self.transport._send_user_message(m)
@@ -239,7 +244,7 @@ class Channel (object):
         self.transport._send_user_message(m)
         self._wait_for_event()
 
-    def resize_pty(self, width=80, height=24):
+    def resize_pty(self, width=80, height=24, width_pixels=0, height_pixels=0):
         """
         Resize the pseudo-terminal.  This can be used to change the width and
         height of the terminal emulation created in a previous L{get_pty} call.
@@ -248,6 +253,10 @@ class Channel (object):
         @type width: int
         @param height: new height (in characters) of the terminal screen
         @type height: int
+        @param width_pixels: new width (in pixels) of the terminal screen
+        @type width_pixels: int
+        @param height_pixels: new height (in pixels) of the terminal screen
+        @type height_pixels: int
 
         @raise SSHException: if the request was rejected or the channel was
             closed
@@ -258,13 +267,12 @@ class Channel (object):
         m.add_byte(chr(MSG_CHANNEL_REQUEST))
         m.add_int(self.remote_chanid)
         m.add_string('window-change')
-        m.add_boolean(True)
+        m.add_boolean(False)
         m.add_int(width)
         m.add_int(height)
-        m.add_int(0).add_int(0)
-        self._event_pending()
+        m.add_int(width_pixels)
+        m.add_int(height_pixels)
         self.transport._send_user_message(m)
-        self._wait_for_event()
 
     def exit_status_ready(self):
         """
diff --git a/paramiko/client.py b/paramiko/client.py
index 07560a3..493d548 100644
--- a/paramiko/client.py
+++ b/paramiko/client.py
@@ -28,6 +28,7 @@ import warnings
 
 from paramiko.agent import Agent
 from paramiko.common import *
+from paramiko.config import SSH_PORT
 from paramiko.dsskey import DSSKey
 from paramiko.hostkeys import HostKeys
 from paramiko.resource import ResourceManager
@@ -37,8 +38,6 @@ from paramiko.transport import Transport
 from paramiko.util import retry_on_signal
 
 
-SSH_PORT = 22
-
 class MissingHostKeyPolicy (object):
     """
     Interface for defining the policy that L{SSHClient} should use when the
@@ -187,8 +186,13 @@ class SSHClient (object):
 
         @raise IOError: if the file could not be written
         """
+
+        # update local host keys from file (in case other SSH clients
+        # have written to the known_hosts file meanwhile.
+        if self.known_hosts is not None:
+            self.load_host_keys(self.known_hosts)
+
         f = open(filename, 'w')
-        f.write('# SSH host keys collected by paramiko\n')
         for hostname, keys in self._host_keys.iteritems():
             for keytype, key in keys.iteritems():
                 f.write('%s %s %s\n' % (hostname, keytype, key.get_base64()))
@@ -350,7 +354,7 @@ class SSHClient (object):
             self._agent.close()
             self._agent = None
 
-    def exec_command(self, command, bufsize=-1):
+    def exec_command(self, command, bufsize=-1, timeout=None, get_pty=False):
         """
         Execute a command on the SSH server.  A new L{Channel} is opened and
         the requested command is executed.  The command's input and output
@@ -361,19 +365,25 @@ class SSHClient (object):
         @type command: str
         @param bufsize: interpreted the same way as by the built-in C{file()} function in python
         @type bufsize: int
+        @param timeout: set command's channel timeout. See L{Channel.settimeout}.settimeout
+        @type timeout: int
         @return: the stdin, stdout, and stderr of the executing command
         @rtype: tuple(L{ChannelFile}, L{ChannelFile}, L{ChannelFile})
 
         @raise SSHException: if the server fails to execute the command
         """
         chan = self._transport.open_session()
+        if(get_pty):
+            chan.get_pty()
+        chan.settimeout(timeout)
         chan.exec_command(command)
         stdin = chan.makefile('wb', bufsize)
         stdout = chan.makefile('rb', bufsize)
         stderr = chan.makefile_stderr('rb', bufsize)
         return stdin, stdout, stderr
 
-    def invoke_shell(self, term='vt100', width=80, height=24):
+    def invoke_shell(self, term='vt100', width=80, height=24, width_pixels=0,
+                height_pixels=0):
         """
         Start an interactive shell session on the SSH server.  A new L{Channel}
         is opened and connected to a pseudo-terminal using the requested
@@ -385,13 +395,17 @@ class SSHClient (object):
         @type width: int
         @param height: the height (in characters) of the terminal window
         @type height: int
+        @param width_pixels: the width (in pixels) of the terminal window
+        @type width_pixels: int
+        @param height_pixels: the height (in pixels) of the terminal window
+        @type height_pixels: int
         @return: a new channel connected to the remote shell
         @rtype: L{Channel}
 
         @raise SSHException: if the server fails to invoke a shell
         """
         chan = self._transport.open_session()
-        chan.get_pty(term, width, height)
+        chan.get_pty(term, width, height, width_pixels, height_pixels)
         chan.invoke_shell()
         return chan
 
diff --git a/paramiko/config.py b/paramiko/config.py
index 2828d90..31caf29 100644
--- a/paramiko/config.py
+++ b/paramiko/config.py
@@ -1,4 +1,5 @@
 # Copyright (C) 2006-2007  Robey Pointer <robeypointer at gmail.com>
+# Copyright (C) 2012  Olle Lundberg <geek at nerd.sh>
 #
 # This file is part of paramiko.
 #
@@ -25,10 +26,55 @@ import os
 import re
 import socket
 
-SSH_PORT=22
+SSH_PORT = 22
 proxy_re = re.compile(r"^(proxycommand)\s*=*\s*(.*)", re.I)
 
 
+class LazyFqdn(object):
+    """
+    Returns the host's fqdn on request as string.
+    """
+
+    def __init__(self, config):
+        self.fqdn = None
+        self.config = config
+
+    def __str__(self):
+        if self.fqdn is None:
+            #
+            # If the SSH config contains AddressFamily, use that when
+            # determining  the local host's FQDN. Using socket.getfqdn() from
+            # the standard library is the most general solution, but can
+            # result in noticeable delays on some platforms when IPv6 is
+            # misconfigured or not available, as it calls getaddrinfo with no
+            # address family specified, so both IPv4 and IPv6 are checked.
+            #
+
+            # Handle specific option
+            fqdn = None
+            address_family = self.config.get('addressfamily', 'any').lower()
+            if address_family != 'any':
+                family = socket.AF_INET if address_family == 'inet' \
+                    else socket.AF_INET6
+                results = socket.getaddrinfo(host,
+                                             None,
+                                             family,
+                                             socket.SOCK_DGRAM,
+                                             socket.IPPROTO_IP,
+                                             socket.AI_CANONNAME)
+                for res in results:
+                    af, socktype, proto, canonname, sa = res
+                    if canonname and '.' in canonname:
+                        fqdn = canonname
+                        break
+            # Handle 'any' / unspecified
+            if fqdn is None:
+                fqdn = socket.getfqdn()
+            # Cache
+            self.fqdn = fqdn
+        return self.fqdn
+
+
 class SSHConfig (object):
     """
     Representation of config information as stored in the format used by
@@ -44,7 +90,7 @@ class SSHConfig (object):
         """
         Create a new OpenSSH config object.
         """
-        self._config = [ { 'host': '*' } ]
+        self._config = []
 
     def parse(self, file_obj):
         """
@@ -53,7 +99,7 @@ class SSHConfig (object):
         @param file_obj: a file-like object to read the config file from
         @type file_obj: file
         """
-        configs = [self._config[0]]
+        host = {"host": ['*'], "config": {}}
         for line in file_obj:
             line = line.rstrip('\n').lstrip()
             if (line == '') or (line[0] == '#'):
@@ -77,20 +123,20 @@ class SSHConfig (object):
                 value = line[i:].lstrip()
 
             if key == 'host':
-                del configs[:]
-                # the value may be multiple hosts, space-delimited
-                for host in value.split():
-                    # do we have a pre-existing host config to append to?
-                    matches = [c for c in self._config if c['host'] == host]
-                    if len(matches) > 0:
-                        configs.append(matches[0])
-                    else:
-                        config = { 'host': host }
-                        self._config.append(config)
-                        configs.append(config)
-            else:
-                for config in configs:
-                    config[key] = value
+                self._config.append(host)
+                value = value.split()
+                host = {key: value, 'config': {}}
+            #identityfile is a special case, since it is allowed to be
+            # specified multiple times and they should be tried in order
+            # of specification.
+            elif key == 'identityfile':
+                if key in host['config']:
+                    host['config']['identityfile'].append(value)
+                else:
+                    host['config']['identityfile'] = [value]
+            elif key not in host['config']:
+                host['config'].update({key: value})
+        self._config.append(host)
 
     def lookup(self, hostname):
         """
@@ -105,31 +151,45 @@ class SSHConfig (object):
         will win out.
 
         The keys in the returned dict are all normalized to lowercase (look for
-        C{"port"}, not C{"Port"}. No other processing is done to the keys or
-        values.
+        C{"port"}, not C{"Port"}. The values are processed according to the
+        rules for substitution variable expansion in C{ssh_config}.
 
         @param hostname: the hostname to lookup
         @type hostname: str
         """
-        matches = [x for x in self._config if fnmatch.fnmatch(hostname, x['host'])]
-        # Move * to the end
-        _star = matches.pop(0)
-        matches.append(_star)
+
+        matches = [config for config in self._config if
+                   self._allowed(hostname, config['host'])]
+
         ret = {}
-        for m in matches:
-            for k,v in m.iteritems():
-                if not k in ret:
-                    ret[k] = v
+        for match in matches:
+            for key, value in match['config'].iteritems():
+                if key not in ret:
+                    # Create a copy of the original value,
+                    # else it will reference the original list
+                    # in self._config and update that value too
+                    # when the extend() is being called.
+                    ret[key] = value[:]
+                elif key == 'identityfile':
+                    ret[key].extend(value)
         ret = self._expand_variables(ret, hostname)
-        del ret['host']
         return ret
 
-    def _expand_variables(self, config, hostname ):
+    def _allowed(self, hostname, hosts):
+        match = False
+        for host in hosts:
+            if host.startswith('!') and fnmatch.fnmatch(hostname, host[1:]):
+                return False
+            elif fnmatch.fnmatch(hostname, host):
+                match = True
+        return match
+
+    def _expand_variables(self, config, hostname):
         """
         Return a dict of config options with expanded substitutions
         for a given hostname.
 
-        Please refer to man ssh_config(5) for the parameters that
+        Please refer to man C{ssh_config} for the parameters that
         are replaced.
 
         @param config: the config for the hostname
@@ -139,7 +199,7 @@ class SSHConfig (object):
         """
 
         if 'hostname' in config:
-            config['hostname'] = config['hostname'].replace('%h',hostname)
+            config['hostname'] = config['hostname'].replace('%h', hostname)
         else:
             config['hostname'] = hostname
 
@@ -155,34 +215,42 @@ class SSHConfig (object):
             remoteuser = user
 
         host = socket.gethostname().split('.')[0]
-        fqdn = socket.getfqdn()
+        fqdn = LazyFqdn(config)
         homedir = os.path.expanduser('~')
-        replacements = {
-            'controlpath': [
-                ('%h', config['hostname']),
-                ('%l', fqdn),
-                ('%L', host),
-                ('%n', hostname),
-                ('%p', port),
-                ('%r', remoteuser),
-                ('%u', user)
-            ],
-            'identityfile': [
-                ('~', homedir),
-                ('%d', homedir),
-                ('%h', config['hostname']),
-                ('%l', fqdn),
-                ('%u', user),
-                ('%r', remoteuser)
-            ],
-            'proxycommand': [
-                ('%h', config['hostname']),
-                ('%p', port),
-                ('%r', remoteuser),
-            ],
-        }
+        replacements = {'controlpath':
+                        [
+                            ('%h', config['hostname']),
+                            ('%l', fqdn),
+                            ('%L', host),
+                            ('%n', hostname),
+                            ('%p', port),
+                            ('%r', remoteuser),
+                            ('%u', user)
+                        ],
+                        'identityfile':
+                        [
+                            ('~', homedir),
+                            ('%d', homedir),
+                            ('%h', config['hostname']),
+                            ('%l', fqdn),
+                            ('%u', user),
+                            ('%r', remoteuser)
+                        ],
+                        'proxycommand':
+                        [
+                            ('%h', config['hostname']),
+                            ('%p', port),
+                            ('%r', remoteuser)
+                        ]
+                        }
+
         for k in config:
             if k in replacements:
                 for find, replace in replacements[k]:
+                    if isinstance(config[k], list):
+                        for item in range(len(config[k])):
+                            config[k][item] = config[k][item].\
+                                replace(find, str(replace))
+                    else:
                         config[k] = config[k].replace(find, str(replace))
         return config
diff --git a/paramiko/file.py b/paramiko/file.py
index d4aec8e..7e2904e 100644
--- a/paramiko/file.py
+++ b/paramiko/file.py
@@ -354,6 +354,10 @@ class BufferedFile (object):
         """
         return self
 
+    @property
+    def closed(self):
+        return self._closed
+
 
     ###  overrides...
 
diff --git a/paramiko/hostkeys.py b/paramiko/hostkeys.py
index e739312..f64e6e6 100644
--- a/paramiko/hostkeys.py
+++ b/paramiko/hostkeys.py
@@ -28,6 +28,7 @@ import UserDict
 from paramiko.common import *
 from paramiko.dsskey import DSSKey
 from paramiko.rsakey import RSAKey
+from paramiko.util import get_logger
 
 
 class InvalidHostKey(Exception):
@@ -48,7 +49,7 @@ class HostKeyEntry:
         self.hostnames = hostnames
         self.key = key
 
-    def from_line(cls, line):
+    def from_line(cls, line, lineno=None):
         """
         Parses the given line of text to find the names for the host,
         the type of key, and the key data. The line is expected to be in the
@@ -61,9 +62,12 @@ class HostKeyEntry:
         @param line: a line from an OpenSSH known_hosts file
         @type line: str
         """
+        log = get_logger('paramiko.hostkeys')
         fields = line.split(' ')
         if len(fields) < 3:
             # Bad number of fields
+            log.info("Not enough fields found in known_hosts in line %s (%r)" %
+                     (lineno, line))
             return None
         fields = fields[:3]
 
@@ -78,6 +82,7 @@ class HostKeyEntry:
             elif keytype == 'ssh-dss':
                 key = DSSKey(data=base64.decodestring(key))
             else:
+                log.info("Unable to handle key of type %s" % (keytype,))
                 return None
         except binascii.Error, e:
             raise InvalidHostKey(line, e)
@@ -160,13 +165,18 @@ class HostKeys (UserDict.DictMixin):
         @raise IOError: if there was an error reading the file
         """
         f = open(filename, 'r')
-        for line in f:
+        for lineno, line in enumerate(f):
             line = line.strip()
             if (len(line) == 0) or (line[0] == '#'):
                 continue
-            e = HostKeyEntry.from_line(line)
+            e = HostKeyEntry.from_line(line, lineno)
             if e is not None:
-                self._entries.append(e)
+                _hostnames = e.hostnames
+                for h in _hostnames:
+                    if self.check(h, e.key):
+                        e.hostnames.remove(h)
+                if len(e.hostnames):
+                    self._entries.append(e)
         f.close()
 
     def save(self, filename):
diff --git a/paramiko/message.py b/paramiko/message.py
index 366c43c..47acc34 100644
--- a/paramiko/message.py
+++ b/paramiko/message.py
@@ -110,7 +110,8 @@ class Message (object):
         @rtype: string
         """
         b = self.packet.read(n)
-        if len(b) < n:
+        max_pad_size = 1<<20  # Limit padding to 1 MB
+        if len(b) < n and n < max_pad_size:
             return b + '\x00' * (n - len(b))
         return b
 
diff --git a/paramiko/packet.py b/paramiko/packet.py
index 5d918e2..38a6d4b 100644
--- a/paramiko/packet.py
+++ b/paramiko/packet.py
@@ -87,6 +87,7 @@ class Packetizer (object):
         self.__mac_size_in = 0
         self.__block_engine_out = None
         self.__block_engine_in = None
+        self.__sdctr_out = False
         self.__mac_engine_out = None
         self.__mac_engine_in = None
         self.__mac_key_out = ''
@@ -110,11 +111,12 @@ class Packetizer (object):
         """
         self.__logger = log
 
-    def set_outbound_cipher(self, block_engine, block_size, mac_engine, mac_size, mac_key):
+    def set_outbound_cipher(self, block_engine, block_size, mac_engine, mac_size, mac_key, sdctr=False):
         """
         Switch outbound data cipher.
         """
         self.__block_engine_out = block_engine
+        self.__sdctr_out = sdctr
         self.__block_size_out = block_size
         self.__mac_engine_out = mac_engine
         self.__mac_size_out = mac_size
@@ -490,12 +492,12 @@ class Packetizer (object):
         padding = 3 + bsize - ((len(payload) + 8) % bsize)
         packet = struct.pack('>IB', len(payload) + padding + 1, padding)
         packet += payload
-        if self.__block_engine_out is not None:
-            packet += rng.read(padding)
-        else:
-            # cute trick i caught openssh doing: if we're not encrypting,
+        if self.__sdctr_out or self.__block_engine_out is None:
+            # cute trick i caught openssh doing: if we're not encrypting or SDCTR mode (RFC4344),
             # don't waste random bytes for the padding
             packet += (chr(0) * padding)
+        else:
+            packet += rng.read(padding)
         return packet
 
     def _trigger_rekey(self):
diff --git a/paramiko/sftp_client.py b/paramiko/sftp_client.py
index 3eaefc9..17ea493 100644
--- a/paramiko/sftp_client.py
+++ b/paramiko/sftp_client.py
@@ -198,7 +198,7 @@ class SFTPClient (BaseSFTP):
         Open a file on the remote server.  The arguments are the same as for
         python's built-in C{file} (aka C{open}).  A file-like object is
         returned, which closely mimics the behavior of a normal python file
-        object.
+        object, including the ability to be used as a context manager.
 
         The mode indicates how the file is to be opened: C{'r'} for reading,
         C{'w'} for writing (truncating an existing file), C{'a'} for appending,
@@ -533,6 +533,56 @@ class SFTPClient (BaseSFTP):
         """
         return self._cwd
 
+    def putfo(self, fl, remotepath, file_size=0, callback=None, confirm=True):
+        """
+        Copy the contents of an open file object (C{fl}) to the SFTP server as
+        C{remotepath}. Any exception raised by operations will be passed through.
+
+        The SFTP operations use pipelining for speed.
+
+        @param fl: opened file or file-like object to copy
+        @type localpath: object
+        @param remotepath: the destination path on the SFTP server
+        @type remotepath: str
+        @param file_size: optional size parameter passed to callback. If none is
+            specified, size defaults to 0
+        @type file_size: int
+        @param callback: optional callback function that accepts the bytes
+            transferred so far and the total bytes to be transferred
+            (since 1.7.4)
+        @type callback: function(int, int)
+        @param confirm: whether to do a stat() on the file afterwards to
+            confirm the file size (since 1.7.7)
+        @type confirm: bool
+
+        @return: an object containing attributes about the given file
+            (since 1.7.4)
+        @rtype: SFTPAttributes
+
+        @since: 1.4
+        """
+        fr = self.file(remotepath, 'wb')
+        fr.set_pipelined(True)
+        size = 0
+        try:
+            while True:
+                data = fl.read(32768)
+                fr.write(data)
+                size += len(data)
+                if callback is not None:
+                    callback(size, file_size)
+                if len(data) == 0:
+                    break
+        finally:
+            fr.close()
+        if confirm:
+            s = self.stat(remotepath)
+            if s.st_size != size:
+                raise IOError('size mismatch in put!  %d != %d' % (s.st_size, size))
+        else:
+            s = SFTPAttributes()
+        return s
+
     def put(self, localpath, remotepath, callback=None, confirm=True):
         """
         Copy a local file (C{localpath}) to the SFTP server as C{remotepath}.
@@ -562,29 +612,46 @@ class SFTPClient (BaseSFTP):
         file_size = os.stat(localpath).st_size
         fl = file(localpath, 'rb')
         try:
-            fr = self.file(remotepath, 'wb')
-            fr.set_pipelined(True)
-            size = 0
-            try:
-                while True:
-                    data = fl.read(32768)
-                    if len(data) == 0:
-                        break
-                    fr.write(data)
-                    size += len(data)
-                    if callback is not None:
-                        callback(size, file_size)
-            finally:
-                fr.close()
+            return self.putfo(fl, remotepath, os.stat(localpath).st_size, callback, confirm)
         finally:
             fl.close()
-        if confirm:
-            s = self.stat(remotepath)
-            if s.st_size != size:
-                raise IOError('size mismatch in put!  %d != %d' % (s.st_size, size))
-        else:
-            s = SFTPAttributes()
-        return s
+
+    def getfo(self, remotepath, fl, callback=None):
+        """
+        Copy a remote file (C{remotepath}) from the SFTP server and write to
+        an open file or file-like object, C{fl}.  Any exception raised by
+        operations will be passed through.  This method is primarily provided
+        as a convenience.
+
+        @param remotepath: opened file or file-like object to copy to
+        @type remotepath: object
+        @param fl: the destination path on the local host or open file
+            object
+        @type localpath: str
+        @param callback: optional callback function that accepts the bytes
+            transferred so far and the total bytes to be transferred
+            (since 1.7.4)
+        @type callback: function(int, int)
+        @return: the number of bytes written to the opened file object
+
+        @since: 1.4
+        """
+        fr = self.file(remotepath, 'rb')
+        file_size = self.stat(remotepath).st_size
+        fr.prefetch()
+        try:
+            size = 0
+            while True:
+                data = fr.read(32768)
+                fl.write(data)
+                size += len(data)
+                if callback is not None:
+                    callback(size, file_size)
+                if len(data) == 0:
+                    break
+        finally:
+            fr.close()
+        return size
 
     def get(self, remotepath, localpath, callback=None):
         """
@@ -603,25 +670,12 @@ class SFTPClient (BaseSFTP):
 
         @since: 1.4
         """
-        fr = self.file(remotepath, 'rb')
         file_size = self.stat(remotepath).st_size
-        fr.prefetch()
+        fl = file(localpath, 'wb')
         try:
-            fl = file(localpath, 'wb')
-            try:
-                size = 0
-                while True:
-                    data = fr.read(32768)
-                    fl.write(data)
-                    size += len(data)
-                    if callback is not None:
-                        callback(size, file_size)
-                    if len(data) == 0:
-                        break
-            finally:
-                fl.close()
+            size = self.getfo(remotepath, fl, callback)
         finally:
-            fr.close()
+            fl.close()
         s = os.stat(localpath)
         if s.st_size != size:
             raise IOError('size mismatch in get!  %d != %d' % (s.st_size, size))
diff --git a/paramiko/sftp_file.py b/paramiko/sftp_file.py
index 8c5c7ac..e056d70 100644
--- a/paramiko/sftp_file.py
+++ b/paramiko/sftp_file.py
@@ -21,6 +21,7 @@ L{SFTPFile}
 """
 
 from binascii import hexlify
+from collections import deque
 import socket
 import threading
 import time
@@ -34,6 +35,9 @@ from paramiko.sftp_attr import SFTPAttributes
 class SFTPFile (BufferedFile):
     """
     Proxy object for a file on the remote server, in client mode SFTP.
+
+    Instances of this class may be used as context managers in the same way
+    that built-in Python file objects are.
     """
 
     # Some sftp servers will choke if you send read/write requests larger than
@@ -51,6 +55,7 @@ class SFTPFile (BufferedFile):
         self._prefetch_data = {}
         self._prefetch_reads = []
         self._saved_exception = None
+        self._reqs = deque()
 
     def __del__(self):
         self._close(async=True)
@@ -160,12 +165,14 @@ class SFTPFile (BufferedFile):
     def _write(self, data):
         # may write less than requested if it would exceed max packet size
         chunk = min(len(data), self.MAX_REQUEST_SIZE)
-        req = self.sftp._async_request(type(None), CMD_WRITE, self.handle, long(self._realpos), str(data[:chunk]))
-        if not self.pipelined or self.sftp.sock.recv_ready():
-            t, msg = self.sftp._read_response(req)
-            if t != CMD_STATUS:
-                raise SFTPError('Expected status')
-            # convert_status already called
+        self._reqs.append(self.sftp._async_request(type(None), CMD_WRITE, self.handle, long(self._realpos), str(data[:chunk])))
+        if not self.pipelined or (len(self._reqs) > 100 and self.sftp.sock.recv_ready()):
+            while len(self._reqs):
+                req = self._reqs.popleft()
+                t, msg = self.sftp._read_response(req)
+                if t != CMD_STATUS:
+                    raise SFTPError('Expected status')
+                # convert_status already called
         return chunk
 
     def settimeout(self, timeout):
@@ -474,3 +481,9 @@ class SFTPFile (BufferedFile):
             x = self._saved_exception
             self._saved_exception = None
             raise x
+            
+    def __enter__(self):
+        return self
+
+    def __exit__(self, type, value, traceback):
+        self.close()
diff --git a/paramiko/transport.py b/paramiko/transport.py
index c801031..201a253 100644
--- a/paramiko/transport.py
+++ b/paramiko/transport.py
@@ -1439,7 +1439,7 @@ class Transport (threading.Thread):
                 break
             self.clear_to_send_lock.release()
             if time.time() > start + self.clear_to_send_timeout:
-              raise SSHException('Key-exchange timed out waiting for key negotiation')
+                raise SSHException('Key-exchange timed out waiting for key negotiation')
         try:
             self._send_message(data)
         finally:
@@ -1885,7 +1885,8 @@ class Transport (threading.Thread):
             mac_key = self._compute_key('F', mac_engine.digest_size)
         else:
             mac_key = self._compute_key('E', mac_engine.digest_size)
-        self.packetizer.set_outbound_cipher(engine, block_size, mac_engine, mac_size, mac_key)
+        sdctr = self.local_cipher.endswith('-ctr')
+        self.packetizer.set_outbound_cipher(engine, block_size, mac_engine, mac_size, mac_key, sdctr)
         compress_out = self._compression_info[self.local_compression][0]
         if (compress_out is not None) and ((self.local_compression != 'zlib at openssh.com') or self.authenticated):
             self._log(DEBUG, 'Switching on outbound compression ...')
diff --git a/paramiko/win_pageant.py b/paramiko/win_pageant.py
index d77d58f..b96a740 100644
--- a/paramiko/win_pageant.py
+++ b/paramiko/win_pageant.py
@@ -21,28 +21,15 @@
 Functions for communicating with Pageant, the basic windows ssh agent program.
 """
 
-import os
+from __future__ import with_statement
+
 import struct
-import tempfile
-import mmap
+import threading
 import array
 import platform
 import ctypes.wintypes
 
-# if you're on windows, you should have one of these, i guess?
-# ctypes is part of standard library since Python 2.5
-_has_win32all = False
-_has_ctypes = False
-try:
-    # win32gui is preferred over win32ui to avoid MFC dependencies
-    import win32gui
-    _has_win32all = True
-except ImportError:
-    try:
-        import ctypes
-        _has_ctypes = True
-    except ImportError:
-        pass
+from . import _winapi
 
 _AGENT_COPYDATA_ID = 0x804e50ba
 _AGENT_MAX_MSGLEN = 8192
@@ -52,16 +39,7 @@ win32con_WM_COPYDATA = 74
 
 
 def _get_pageant_window_object():
-    if _has_win32all:
-        try:
-            hwnd = win32gui.FindWindow('Pageant', 'Pageant')
-            return hwnd
-        except win32gui.error:
-            pass
-    elif _has_ctypes:
-        # Return 0 if there is no Pageant window.
-        return ctypes.windll.user32.FindWindowA('Pageant', 'Pageant')
-    return None
+    return ctypes.windll.user32.FindWindowA('Pageant', 'Pageant')
 
 
 def can_talk_to_agent():
@@ -71,9 +49,7 @@ def can_talk_to_agent():
     This checks both if we have the required libraries (win32all or ctypes)
     and if there is a Pageant currently running.
     """
-    if (_has_win32all or _has_ctypes) and _get_pageant_window_object():
-        return True
-    return False
+    return bool(_get_pageant_window_object())
 
 ULONG_PTR = ctypes.c_uint64 if platform.architecture()[0] == '64bit' else ctypes.c_uint32
 class COPYDATASTRUCT(ctypes.Structure):
@@ -88,48 +64,39 @@ class COPYDATASTRUCT(ctypes.Structure):
         ]
 
 def _query_pageant(msg):
+    """
+    Communication with the Pageant process is done through a shared
+    memory-mapped file.
+    """
     hwnd = _get_pageant_window_object()
     if not hwnd:
         # Raise a failure to connect exception, pageant isn't running anymore!
         return None
 
-    # Write our pageant request string into the file (pageant will read this to determine what to do)
-    filename = tempfile.mktemp('.pag')
-    map_filename = os.path.basename(filename)
-
-    f = open(filename, 'w+b')
-    f.write(msg )
-    # Ensure the rest of the file is empty, otherwise pageant will read this
-    f.write('\0' * (_AGENT_MAX_MSGLEN - len(msg)))
-    # Create the shared file map that pageant will use to read from
-    pymap = mmap.mmap(f.fileno(), _AGENT_MAX_MSGLEN, tagname=map_filename, access=mmap.ACCESS_WRITE)
-    try:
+    # create a name for the mmap
+    map_name = 'PageantRequest%08x' % threading.current_thread().ident
+
+    pymap = _winapi.MemoryMap(map_name, _AGENT_MAX_MSGLEN,
+        _winapi.get_security_attributes_for_user(),
+        )
+    with pymap:
+        pymap.write(msg)
         # Create an array buffer containing the mapped filename
-        char_buffer = array.array("c", map_filename + '\0')
+        char_buffer = array.array("c", map_name + '\0')
         char_buffer_address, char_buffer_size = char_buffer.buffer_info()
         # Create a string to use for the SendMessage function call
-        cds = COPYDATASTRUCT(_AGENT_COPYDATA_ID, char_buffer_size, char_buffer_address)
+        cds = COPYDATASTRUCT(_AGENT_COPYDATA_ID, char_buffer_size,
+            char_buffer_address)
 
-        if _has_win32all:
-            # win32gui.SendMessage should also allow the same pattern as
-            # ctypes, but let's keep it like this for now...
-            response = win32gui.SendMessage(hwnd, win32con_WM_COPYDATA, ctypes.sizeof(cds), ctypes.addressof(cds))
-        elif _has_ctypes:
-            response = ctypes.windll.user32.SendMessageA(hwnd, win32con_WM_COPYDATA, ctypes.sizeof(cds), ctypes.byref(cds))
-        else:
-            response = 0
+        response = ctypes.windll.user32.SendMessageA(hwnd,
+            win32con_WM_COPYDATA, ctypes.sizeof(cds), ctypes.byref(cds))
 
         if response > 0:
+            pymap.seek(0)
             datalen = pymap.read(4)
             retlen = struct.unpack('>I', datalen)[0]
             return datalen + pymap.read(retlen)
         return None
-    finally:
-        pymap.close()
-        f.close()
-        # Remove the file, it was temporary only
-        os.unlink(filename)
-
 
 class PageantConnection (object):
     """
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..75112a2
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,2 @@
+pycrypto
+tox
diff --git a/setup.py b/setup.py
index 1bb1a71..02a71e5 100644
--- a/setup.py
+++ b/setup.py
@@ -52,7 +52,7 @@ if sys.platform == 'darwin':
 
 
 setup(name = "paramiko",
-      version = "1.9.0",
+      version = "1.11.0",
       description = "SSH2 protocol library",
       author = "Jeff Forcier",
       author_email = "jeff at bitprophet.org",
diff --git a/tests/test_sftp.py b/tests/test_sftp.py
index 2eadabc..b1697ea 100755
--- a/tests/test_sftp.py
+++ b/tests/test_sftp.py
@@ -23,15 +23,15 @@ a real actual sftp server is contacted, and a new folder is created there to
 do test file operations in (so no existing files will be harmed).
 """
 
+from __future__ import with_statement
+
 from binascii import hexlify
-import logging
 import os
-import random
-import struct
+import warnings
 import sys
 import threading
-import time
 import unittest
+import StringIO
 
 import paramiko
 from stub_sftp import StubServer, StubSFTPServer
@@ -188,6 +188,17 @@ class SFTPTest (unittest.TestCase):
         finally:
             sftp.remove(FOLDER + '/duck.txt')
 
+    def test_3_sftp_file_can_be_used_as_context_manager(self):
+        """
+        verify that an opened file can be used as a context manager
+        """
+        try:
+            with sftp.open(FOLDER + '/duck.txt', 'w') as f:
+                f.write(ARTICLE)
+            self.assertEqual(sftp.stat(FOLDER + '/duck.txt').st_size, 1483)
+        finally:
+            sftp.remove(FOLDER + '/duck.txt')
+
     def test_4_append(self):
         """
         verify that a file can be opened for append, and tell() still works.
@@ -214,7 +225,7 @@ class SFTPTest (unittest.TestCase):
         """
         f = sftp.open(FOLDER + '/first.txt', 'w')
         try:
-            f.write('content!\n');
+            f.write('content!\n')
             f.close()
             sftp.rename(FOLDER + '/first.txt', FOLDER + '/second.txt')
             try:
@@ -425,7 +436,7 @@ class SFTPTest (unittest.TestCase):
             self.assertEqual(sftp.readlink(FOLDER + '/link.txt'), 'original.txt')
 
             f = sftp.open(FOLDER + '/link.txt', 'r')
-            self.assertEqual(f.readlines(), [ 'original\n' ])
+            self.assertEqual(f.readlines(), ['original\n'])
             f.close()
 
             cwd = sftp.normalize('.')
@@ -553,7 +564,6 @@ class SFTPTest (unittest.TestCase):
         """
         verify that get/put work.
         """
-        import os, warnings
         warnings.filterwarnings('ignore', 'tempnam.*')
 
         localname = os.tempnam()
@@ -618,7 +628,7 @@ class SFTPTest (unittest.TestCase):
             try:
                 f = sftp.open(FOLDER + '/unusual.txt', 'wx')
                 self.fail('expected exception')
-            except IOError, x:
+            except IOError:
                 pass
         finally:
             sftp.unlink(FOLDER + '/unusual.txt')
@@ -658,12 +668,12 @@ class SFTPTest (unittest.TestCase):
         f.close()
         try:
             f = sftp.open(FOLDER + '/zero', 'r')
-            data = f.readv([(0, 12)])
+            f.readv([(0, 12)])
             f.close()
 
             f = sftp.open(FOLDER + '/zero', 'r')
             f.prefetch()
-            data = f.read(100)
+            f.read(100)
             f.close()
         finally:
             sftp.unlink(FOLDER + '/zero')
@@ -672,7 +682,6 @@ class SFTPTest (unittest.TestCase):
         """
         verify that get/put work without confirmation.
         """
-        import os, warnings
         warnings.filterwarnings('ignore', 'tempnam.*')
 
         localname = os.tempnam()
@@ -684,7 +693,7 @@ class SFTPTest (unittest.TestCase):
         def progress_callback(x, y):
             saved_progress.append((x, y))
         res = sftp.put(localname, FOLDER + '/bunny.txt', progress_callback, False)
-        
+
         self.assertEquals(SFTPAttributes().attr, res.attr)
 
         f = sftp.open(FOLDER + '/bunny.txt', 'r')
@@ -717,3 +726,15 @@ class SFTPTest (unittest.TestCase):
         finally:
             sftp.remove(FOLDER + '/append.txt')
 
+    def test_putfo_empty_file(self):
+        """
+        Send an empty file and confirm it is sent.
+        """
+        target = FOLDER + '/empty file.txt'
+        stream = StringIO.StringIO()
+        try:
+            attrs = sftp.putfo(stream, target)
+            # the returned attributes should not be null
+            self.assertNotEqual(attrs, None)
+        finally:
+            sftp.remove(target)
diff --git a/tests/test_util.py b/tests/test_util.py
index 093a215..efda9b2 100644
--- a/tests/test_util.py
+++ b/tests/test_util.py
@@ -104,23 +104,32 @@ class UtilTest(ParamikoTest):
         f = cStringIO.StringIO(test_config_file)
         config = paramiko.util.parse_ssh_config(f)
         self.assertEquals(config._config,
-                          [ {'identityfile': '~/.ssh/id_rsa', 'host': '*', 'user': 'robey',
-                             'crazy': 'something dumb  '},
-                            {'host': '*.example.com', 'user': 'bjork', 'port': '3333'},
-                            {'host': 'spoo.example.com', 'crazy': 'something else'}])
+            [{'host': ['*'], 'config': {}}, {'host': ['*'], 'config': {'identityfile': ['~/.ssh/id_rsa'], 'user': 'robey'}},
+            {'host': ['*.example.com'], 'config': {'user': 'bjork', 'port': '3333'}},
+            {'host': ['*'], 'config': {'crazy': 'something dumb  '}},
+            {'host': ['spoo.example.com'], 'config': {'crazy': 'something else'}}])
 
     def test_3_host_config(self):
         global test_config_file
         f = cStringIO.StringIO(test_config_file)
         config = paramiko.util.parse_ssh_config(f)
+
         for host, values in {
-            'irc.danger.com': {'user': 'robey', 'crazy': 'something dumb  '},
-            'irc.example.com': {'user': 'bjork', 'crazy': 'something dumb  ', 'port': '3333'},
-            'spoo.example.com': {'user': 'bjork', 'crazy': 'something else', 'port': '3333'}
+            'irc.danger.com':   {'crazy': 'something dumb  ',
+                                'hostname': 'irc.danger.com',
+                                'user': 'robey'},
+            'irc.example.com':  {'crazy': 'something dumb  ',
+                                'hostname': 'irc.example.com',
+                                'user': 'robey',
+                                'port': '3333'},
+            'spoo.example.com': {'crazy': 'something dumb  ',
+                                'hostname': 'spoo.example.com',
+                                'user': 'robey',
+                                'port': '3333'}
         }.items():
             values = dict(values,
                 hostname=host,
-                identityfile=os.path.expanduser("~/.ssh/id_rsa")
+                identityfile=[os.path.expanduser("~/.ssh/id_rsa")]
             )
             self.assertEquals(
                 paramiko.util.lookup_ssh_host_config(host, config),
@@ -151,8 +160,8 @@ class UtilTest(ParamikoTest):
         # just verify that we can pull out 32 bytes and not get an exception.
         x = rng.read(32)
         self.assertEquals(len(x), 32)
-        
-    def test_7_host_config_expose_ssh_issue_33(self):
+
+    def test_7_host_config_expose_issue_33(self):
         test_config_file = """
 Host www13.*
     Port 22
@@ -220,16 +229,16 @@ Host equals-delimited
         ProxyCommand should perform interpolation on the value
         """
         config = paramiko.util.parse_ssh_config(cStringIO.StringIO("""
-Host *
-    Port 25
-    ProxyCommand host %h port %p
-
 Host specific
     Port 37
     ProxyCommand host %h port %p lol
 
 Host portonly
     Port 155
+
+Host *
+    Port 25
+    ProxyCommand host %h port %p
 """))
         for host, val in (
             ('foo.com', "host foo.com port 25"),
@@ -240,3 +249,83 @@ Host portonly
                 host_config(host, config)['proxycommand'],
                 val
             )
+
+    def test_11_host_config_test_negation(self):
+        test_config_file = """
+Host www13.* !*.example.com
+    Port 22
+
+Host *.example.com !www13.*
+    Port 2222
+
+Host www13.*
+    Port 8080
+
+Host *
+    Port 3333
+    """
+        f = cStringIO.StringIO(test_config_file)
+        config = paramiko.util.parse_ssh_config(f)
+        host = 'www13.example.com'
+        self.assertEquals(
+            paramiko.util.lookup_ssh_host_config(host, config),
+            {'hostname': host, 'port': '8080'}
+        )
+
+    def test_12_host_config_test_proxycommand(self):
+        test_config_file = """
+Host proxy-with-equal-divisor-and-space
+ProxyCommand = foo=bar
+
+Host proxy-with-equal-divisor-and-no-space
+ProxyCommand=foo=bar
+
+Host proxy-without-equal-divisor
+ProxyCommand foo=bar:%h-%p
+    """
+        for host, values in {
+            'proxy-with-equal-divisor-and-space'   :{'hostname': 'proxy-with-equal-divisor-and-space',
+                                                     'proxycommand': 'foo=bar'},
+            'proxy-with-equal-divisor-and-no-space':{'hostname': 'proxy-with-equal-divisor-and-no-space',
+                                                     'proxycommand': 'foo=bar'},
+            'proxy-without-equal-divisor'          :{'hostname': 'proxy-without-equal-divisor',
+                                                     'proxycommand':
+                                                     'foo=bar:proxy-without-equal-divisor-22'}
+        }.items():
+
+            f = cStringIO.StringIO(test_config_file)
+            config = paramiko.util.parse_ssh_config(f)
+            self.assertEquals(
+                paramiko.util.lookup_ssh_host_config(host, config),
+                values
+            )
+
+    def test_11_host_config_test_identityfile(self):
+        test_config_file = """
+
+IdentityFile id_dsa0
+
+Host *
+IdentityFile id_dsa1
+
+Host dsa2
+IdentityFile id_dsa2
+
+Host dsa2*
+IdentityFile id_dsa22
+    """
+        for host, values in {
+            'foo'   :{'hostname': 'foo',
+                      'identityfile': ['id_dsa0', 'id_dsa1']},
+            'dsa2'  :{'hostname': 'dsa2',
+                      'identityfile': ['id_dsa0', 'id_dsa1', 'id_dsa2', 'id_dsa22']},
+            'dsa22' :{'hostname': 'dsa22',
+                      'identityfile': ['id_dsa0', 'id_dsa1', 'id_dsa22']}
+        }.items():
+
+            f = cStringIO.StringIO(test_config_file)
+            config = paramiko.util.parse_ssh_config(f)
+            self.assertEquals(
+                paramiko.util.lookup_ssh_host_config(host, config),
+                values
+            )
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..6cb8001
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,6 @@
+[tox]
+envlist = py25,py26,py27
+
+[testenv]
+commands = pip install --use-mirrors -q -r requirements.txt
+           python test.py


hooks/post-receive
-- 
python-paramiko.git (Debian package python-paramiko)

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "python-paramiko.git" (Debian package python-paramiko).




More information about the x2go-commits mailing list