diff --git a/ConfsolveArgs.hs b/ConfsolveArgs.hs
new file mode 100644
--- /dev/null
+++ b/ConfsolveArgs.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+
+module ConfsolveArgs where
+import System.Console.CmdArgs
+
+data Confsolve = Confsolve {
+   dropbox   :: Bool,
+   wuala     :: Bool,
+   directory :: FilePath
+   } deriving (Data, Typeable, Show, Eq)
+
+confsolve = Confsolve {
+   dropbox   = def &= help "Resolve Dropbox file conflicts",
+   wuala     = def &= help "Resolve Wuala file conflicts",
+   directory = def &= args &= typ "DIRECTORY"
+   }
+   &= summary summaryInfo
+   &= help "A command line tool for resolving conflicts of file synchronizers."
+   &= helpArg [explicit, name "help", name "h"]
+   &= versionArg [explicit, name "version", name "v", summary versionInfo ]
+
+versionInfo = "confsolve version 0.3.7"
+summaryInfo = ""
+
+confsolveArgs :: IO Confsolve
+confsolveArgs = cmdArgs confsolve
diff --git a/Dropbox/Conflict.hs b/Dropbox/Conflict.hs
new file mode 100644
--- /dev/null
+++ b/Dropbox/Conflict.hs
@@ -0,0 +1,18 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Dropbox.Conflict where
+import FileConflict
+import qualified Dropbox.FileNameParser as P
+import qualified Data.Text as T
+
+data Parser = Parser
+
+(<++>) = T.append
+
+instance ConflictParser Parser where
+   parseConflict _ baseName =
+      case P.parse baseName of
+           Just (P.FileInfo realBaseName host date) ->
+              Just (realBaseName, "Version " <++> date <++> " from " <++> host)
+
+           Nothing -> Nothing
diff --git a/Dropbox/FileNameParser.hs b/Dropbox/FileNameParser.hs
new file mode 100644
--- /dev/null
+++ b/Dropbox/FileNameParser.hs
@@ -0,0 +1,29 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Dropbox.FileNameParser where
+
+import Control.Applicative ((<*>), (<$>), (*>), (<*), (<|>))
+import qualified Data.Attoparsec.Text as P
+import qualified Data.Text as T
+
+parse :: T.Text -> Maybe FileInfo
+parse fileName =
+   case P.feed (P.parse fileInfo fileName) T.empty of
+        P.Fail _ _ _ -> Nothing
+        P.Partial _  -> Nothing
+        P.Done _ r   -> Just r
+
+fileInfo :: P.Parser FileInfo
+fileInfo = FileInfo <$> (T.strip <$> tillLeftPar)
+                    <*> (P.char '(' *> till')
+                    <*> (P.string "'s conflicted copy " *> tillRightPar <* P.char ')')
+   where
+      tillLeftPar  = P.takeWhile1 (/= '(')
+      till'        = P.takeWhile1 (/= '\'')
+      tillRightPar = P.takeWhile1 (/= ')')
+
+data FileInfo = FileInfo {
+   fileName :: T.Text,
+   host     :: T.Text,
+   date     :: T.Text
+   } deriving (Show)
diff --git a/FileConflict.hs b/FileConflict.hs
new file mode 100644
--- /dev/null
+++ b/FileConflict.hs
@@ -0,0 +1,29 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module FileConflict where
+import qualified Data.Text as T
+import qualified Filesystem.Path.CurrentOS as FP
+import Filesystem.Path.CurrentOS ((</>), (<.>))
+
+-- base name possibly containing conflict info
+type BaseName     = T.Text
+
+-- base name without conflict info
+type RealBaseName = T.Text
+
+-- conflicting file details displayed to the user
+type Details      = T.Text
+
+class ConflictParser a where
+   parseConflict :: a -> BaseName -> Maybe (RealBaseName, Details)
+
+-- the details and filePath of a conflicting file
+data ConflictingFile = ConflictingFile {
+   details  :: Details,
+   filePath :: FP.FilePath } deriving (Show)
+
+-- file path having a conflict
+data Conflict = Conflict {
+   origFilePath     :: FP.FilePath,
+   conflictingFiles :: [ConflictingFile]
+   } deriving (Show)
diff --git a/ParseInput.hs b/ParseInput.hs
new file mode 100644
--- /dev/null
+++ b/ParseInput.hs
@@ -0,0 +1,54 @@
+
+module ParseInput where
+import Data.Char (toLower)
+
+type FileNum = Int
+data UserReply = TakeFile FileNum
+               | MoveToTrash
+               | ShowDiff
+               | ShowDiffWith FileNum
+               | ShowDiffBetween FileNum FileNum
+               | Skip
+               | Quit
+               | Help
+               deriving Show
+
+
+type NumFiles = Int
+parseInput :: String -> NumFiles -> Maybe UserReply
+parseInput str numFiles =
+   case map toLower str of
+        ('t':rest) | [num] <- parseInts rest -> if validFileNum num 
+                                                   then Just $ TakeFile num
+                                                   else Nothing
+                   | otherwise               -> Nothing
+
+        ('m':[])   -> Just MoveToTrash
+
+        ('d':rest) -> case parseInts rest of
+                           []           -> if numFiles == 1 then Just ShowDiff else Nothing
+                           [num]        -> if validFileNum num
+                                              then Just $ ShowDiffWith num
+                                              else Nothing
+                           [num1, num2] -> if validFileNum num1 && validFileNum num2
+                                              then Just $ ShowDiffBetween num1 num2
+                                              else Nothing
+                           _            -> Nothing
+
+        ('s':[])   -> Just Skip
+        ('q':[])   -> Just Quit
+        ('h':[])   -> Just Help
+        ('?':[])   -> Just Help
+
+        _          -> Nothing
+
+   where
+      validFileNum num = num > 0 && num <= numFiles
+
+
+parseInts :: String -> [Int]
+parseInts str = go str []
+   where
+      go str ints
+         | [(int, rest)] <- reads str :: [(Int, String)] = go rest (ints ++ [int])
+         | otherwise                                     = ints
diff --git a/Utils.hs b/Utils.hs
new file mode 100644
--- /dev/null
+++ b/Utils.hs
@@ -0,0 +1,73 @@
+{-# LANGUAGE ScopedTypeVariables, OverloadedStrings #-}
+
+module Utils where
+import System.IO
+import System.Environment
+import Data.Time.Clock
+import Data.Time.Calendar
+import Control.Exception.Base (try)
+import qualified Data.Text as T
+import qualified Filesystem as FS
+import qualified Filesystem.Path.CurrentOS as FP
+import Filesystem.Path ((</>), (<.>))
+
+errorsToStderr :: IO () -> IO ()
+errorsToStderr action =
+   catch action (\e -> appPutStrLn stderr (show e))
+
+appPutStrLn :: Handle -> String -> IO ()
+appPutStrLn handle string = do
+   progName <- normalizedProgName
+   hPutStrLn handle ("\n" ++ T.unpack progName ++ ": " ++ string)
+
+normalizedProgName :: IO T.Text
+normalizedProgName = do
+   pn <- getProgName
+   return (T.pack $ takeWhile (/= '.') pn)
+
+appDataDirectory = do
+   pn <- normalizedProgName
+   hd <- FS.getHomeDirectory
+   return $ hd </> FP.fromText (T.append "." pn)
+
+trashDirectory = do
+   ad <- appDataDirectory
+   return $ ad </> "trash"
+
+defaultDiff = "gvimdiff -f"
+
+getCurrentDate :: IO (Integer,Int,Int) -- :: (year,month,day)
+getCurrentDate = getCurrentTime >>= return . toGregorian . utctDay
+
+getEnvOrDefault :: String -> String -> IO String
+getEnvOrDefault envVar defaultValue = do
+   result <- try $ getEnv envVar
+   case result of
+	Right value          -> return value
+	Left  (_ :: IOError) -> return defaultValue
+
+toText :: FP.FilePath -> T.Text
+toText filePath =
+   either id id (FP.toText filePath)
+
+toString :: FP.FilePath -> String
+toString = T.unpack . toText
+
+-- splits filePath into (directory, baseName, extension)
+splitFilePath :: FP.FilePath -> (FP.FilePath, FP.FilePath, T.Text)
+splitFilePath filePath = (dir, bname, ext)
+   where
+      dir   = FP.directory filePath
+      bname = FP.basename filePath
+      ext   = allExtensions filePath
+
+allExtensions :: FP.FilePath -> T.Text
+allExtensions = (T.intercalate ".") . FP.extensions
+
+replaceBaseName :: FP.FilePath -> FP.FilePath -> FP.FilePath
+replaceBaseName filePath newBaseName
+   | T.null ext = dir </> newBaseName
+   | otherwise  = dir </> newBaseName <.> ext
+   where
+      dir = FP.directory filePath
+      ext = allExtensions filePath
diff --git a/Wuala/Conflict.hs b/Wuala/Conflict.hs
new file mode 100644
--- /dev/null
+++ b/Wuala/Conflict.hs
@@ -0,0 +1,18 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Wuala.Conflict where
+import FileConflict
+import qualified Wuala.FileNameParser as P
+import qualified Data.Text as T
+
+data Parser = Parser
+
+(<++>) = T.append
+
+instance ConflictParser Parser where
+   parseConflict _ baseName =
+      case P.parse baseName of
+           Just (P.FileInfo realBaseName version host) ->
+              Just (realBaseName, "Version " <++> version <++> " from " <++> host)
+
+           Nothing -> Nothing
diff --git a/Wuala/FileNameParser.hs b/Wuala/FileNameParser.hs
new file mode 100644
--- /dev/null
+++ b/Wuala/FileNameParser.hs
@@ -0,0 +1,30 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Wuala.FileNameParser where
+
+import Control.Applicative ((<*>), (<$>), (*>))
+import qualified Data.Attoparsec.Text as P
+import qualified Data.Text as T
+import Data.Char (isNumber)
+
+parse :: T.Text -> Maybe FileInfo
+parse fileName =
+   case P.feed (P.parse fileInfo fileName) T.empty of
+        P.Fail _ _ _ -> Nothing
+        P.Partial _  -> Nothing
+        P.Done _ r   -> Just r
+
+fileInfo :: P.Parser FileInfo
+fileInfo = FileInfo <$> (T.strip <$> tillLeftPar)
+                    <*> (P.string "(conflicting version " *> version)
+                    <*> (P.string " from " *> tillRightPar)
+   where
+      version      = P.takeWhile1 isNumber
+      tillLeftPar  = P.takeWhile1 (/= '(')
+      tillRightPar = P.takeWhile1 (/= ')')
+
+data FileInfo = FileInfo {
+   fileName :: T.Text,
+   version  :: T.Text,
+   host     :: T.Text
+   } deriving (Show)
diff --git a/confsolve.cabal b/confsolve.cabal
--- a/confsolve.cabal
+++ b/confsolve.cabal
@@ -1,5 +1,5 @@
 Name:          confsolve
-Version:       0.3.6
+Version:       0.3.7
 License:       BSD3
 License-file:  LICENSE
 Author:        Daniel Trstenjak
@@ -16,5 +16,13 @@
 
 Executable confsolve
   Main-is:       confsolve.hs
+  other-modules: FileConflict
+                 ParseInput
+                 Dropbox.Conflict
+                 Dropbox.FileNameParser
+                 Utils
+                 Wuala.Conflict
+                 Wuala.FileNameParser
+                 ConfsolveArgs
   ghc-options:   -funbox-strict-fields
   Build-Depends: base >= 3 && < 5, time, process, unordered-containers, text, attoparsec, system-filepath, system-fileio, cmdargs
