diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,14 @@
+Copyright (c) 2008 Marco Túlio Gontijo e Silva <marcot@riseup.net>
+
+Permission is hereby granted, free of charge, to any person obtaining this work
+(the "Work"), to deal in the Work without restriction, including without
+limitation the rights to use, copy, modify, merge, publish, distribute,
+sublicense, and/or sell copies of the Work, and to permit persons to whom the
+Work is furnished to do so.
+
+THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS IN THE WORK.
diff --git a/Main.hs b/Main.hs
new file mode 100644
--- /dev/null
+++ b/Main.hs
@@ -0,0 +1,69 @@
+-------------------------------------------------------------------------------
+-- |
+-- Module: Main
+-- Copyright: (c) 2008 Marco Túlio Gontijo e Silva <marcot@riseup.net>
+-- License: Simple permissive license (see LICENSE)
+--
+-- Maintainer: Marco Túlio Gontijo e Silva <marcot@riseup.net>
+-- Stability: unstable
+-- Portability: unportable
+--
+-- Utility to call iwconfig with open networks.
+-------------------------------------------------------------------------------
+
+-- base
+import Control.Applicative
+import Data.List
+import Data.Maybe
+import Data.IORef
+import GHC.Conc
+import System.Environment
+import System.Exit
+
+-- mtl
+import Control.Monad.Reader
+
+-- process
+import System.Cmd
+
+-- HSH
+import HSH.Command
+
+-- n-m
+import Network.Cell
+
+dhclient :: String -> IORef (Maybe ThreadId) -> IO ()
+dhclient interface waitThread
+  = do
+    exitCode <- system $ "dhclient " ++ interface
+    when (exitCode == ExitSuccess)
+      $ readIORef waitThread >>= killThread . fromJust
+
+main :: IO ()
+main
+  = do
+    interface <- fromMaybe "wlan0" <$> listToMaybe <$> getArgs
+    run ("iwlist " ++ interface ++ " scan")
+      >>= iwconfig interface
+      . getOpenEssids
+      . getCells
+
+iwconfig :: String -> [String] -> IO ()
+iwconfig _ [] = return ()
+iwconfig interface (essid : rest)
+  = do
+    iWaitThread <- newIORef Nothing
+    dhclientThread
+      <- liftIO
+      $ system ("iwconfig " ++ interface ++ " essid " ++ essid)
+      >> forkIO (dhclient interface iWaitThread)
+    waitThread <- forkIO $ wait interface dhclientThread rest
+    writeIORef iWaitThread $ Just waitThread
+
+wait :: String -> ThreadId -> [String] -> IO ()
+wait interface dhclientThread rest
+  = do
+    char <- liftIO getChar
+    if char == 'n'
+      then liftIO (killThread dhclientThread) >> iwconfig interface rest
+      else wait interface dhclientThread rest
diff --git a/Network/Cell.hs b/Network/Cell.hs
new file mode 100644
--- /dev/null
+++ b/Network/Cell.hs
@@ -0,0 +1,83 @@
+-------------------------------------------------------------------------------
+-- |
+-- Module: Network.Cell
+-- Copyright: (c) 2008 Marco Túlio Gontijo e Silva <marcot@riseup.net>
+-- License: Simple permissive license (see LICENSE)
+--
+-- Maintainer: Marco Túlio Gontijo e Silva <marcot@riseup.net>
+-- Stability: unstable
+-- Portability: unportable
+--
+-- This module provides functions to parse the output of @iwlist scan@.
+-------------------------------------------------------------------------------
+
+module
+  Network.Cell
+  (
+    -- * Data type
+    Cell (..)
+  , getCells
+    -- * Queries
+  , getQuality
+  , isOpen
+    -- ** General
+  , getValue
+    -- ** Utilities
+  , getOpenEssids)
+  where
+
+-- base
+import Data.List
+
+-- | Each available network.
+newtype Cell = Cell [String] deriving Eq
+
+instance Ord Cell where
+  compare c d = compare (getQuality c) (getQuality d)
+
+-- | Get the list of available networks, given the output of @iwlist scan@.
+getCells
+  :: [String] -- ^ Output of @iwlist scan@
+  -> [Cell]
+getCells [] = []
+getCells iwlist
+  = case cell of
+  (_ : _) -> Cell cell : rest
+  _ -> rest
+  where
+    isPrefix = isPrefixOf prefix
+    cell = map (drop (length prefix)) $ takeWhile isPrefix usable
+    rest = getCells (dropWhile isPrefix usable)
+    usable = dropWhile (not . isPrefix) iwlist
+
+-- | Gets the field @Quality@.
+getQuality :: Cell -> Int
+getQuality = read . takeWhile (/= '/') . getValue "Quality:"
+
+-- | Checks whether the network is open.
+isOpen :: Cell -> Bool
+isOpen cell = getValue "Encryption key:" cell == "off"
+
+-- | Gets the field with @name@.
+getValue
+  :: String -- ^ @name@
+  -> Cell
+  -> String
+getValue name (Cell cell)
+  = case mValue of
+  (value : _) -> value
+  _ -> error $ show cell
+  where
+    mValue = map (drop (length name)) $ filter (isPrefixOf name) cell
+
+-- | Gets the @essid@s of networks that don't use encryption.
+getOpenEssids :: [Cell] -> [String]
+getOpenEssids
+  = nub
+  . map (getValue "ESSID:")
+  . reverse
+  . sort
+  . filter isOpen
+
+prefix :: String
+prefix = replicate 20 ' '
diff --git a/README b/README
new file mode 100644
--- /dev/null
+++ b/README
@@ -0,0 +1,9 @@
+n-m
+===
+
+Usage: n-m [INTERFACE]
+
+If the interface is not specified, wlan0 is used.
+
+It will try to connect via DHCP to the best open network available.  If you
+want to pass to the next one, just press n.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/n-m.cabal b/n-m.cabal
new file mode 100644
--- /dev/null
+++ b/n-m.cabal
@@ -0,0 +1,24 @@
+name: n-m
+version: 0.0.1
+cabal-version: >= 1.2.3.0
+build-type: Simple
+license: OtherLicense
+license-file: LICENSE
+copyright: (c) 2008 Marco Túlio Gontijo e Silva <marcot@riseup.net>
+author: Marco Túlio Gontijo e Silva
+maintainer: Marco Túlio Gontijo e Silva <marcot@riseup.net>
+synopsis: Utility to call iwconfig.
+description:
+  This program choses between the available open wireless networks and tries to
+  connect to them using DHCP.
+category: Network
+tested-with: GHC == 6.8.2
+extra-source-files: README
+Library
+  build-depends: base
+  exposed-modules: Network.Cell
+  ghc-options: -Wall
+Executable n-m
+  main-is: Main.hs
+  build-depends: base, mtl, process, HSH
+  ghc-options: -Wall
