예제 #1
0
        /// <summary>
        /// Sends contents of a file to a client.
        /// </summary>
        /// <param name="reader">StreamReader used to ask the client for acknowledgement.</param>
        /// <param name="writer">StreamWriter used to send the data.</param>
        /// <param name="name">Path to the file.</param>
        /// <returns></returns>
        private bool SendFileContents(StreamReader reader, StreamWriter writer, string name)
        {
            try
            {
                using (var fileReader = new StreamReader(System.IO.File.OpenRead(name), encoding))
                {
                    string line;
                    writer.WriteLine(name);

                    if (ProtocolHelper.ExtractProtocol(reader.ReadLine()) != Protocol.SUCCESS)
                    {
                        return(true);
                    }

                    while ((line = fileReader.ReadLine()) != null)
                    {
                        writer.WriteLine(line);
                    }
                    writer.WriteLine(Protocol.TRANSMISSION_END);
                }
                return(true);
            }
            catch (IOException)
            {
                try
                {
                    writer.WriteLine(Protocol.FAIL);
                    return(true);
                }
                catch (IOException)
                {
                    return(false);
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Requests (and reads if authorized) contents of a given file from
        /// the server.
        /// </summary>
        /// <param name="reader">StreamReader used to read authorization result and the contents of the file.</param>
        /// <param name="writer">StreamWriter used to send the request.</param>
        /// <param name="file">Path to the file.</param>
        private void RequestFileContents(StreamReader reader, StreamWriter writer, string file)
        {
            writer.WriteLine(Protocol.REQUEST_FILE_CONTENTS);
            writer.WriteLine(subTreePrefix + file);
            string line    = reader.ReadLine();
            string tmpFile = file + ".tmp";

            if (ProtocolHelper.ExtractProtocol(line) == Protocol.SUCCESS)
            {
                DB.File tmp = null;
                if (!db.GetFiles().TryGet(file, out tmp))
                {
                    tmp = new DB.File(file);
                    db.GetFiles().Add(tmp);
                }

                try
                {
                    reader.ReadLine();                     // File name resent, here we can discard it.
                    writer.WriteLine(Protocol.SUCCESS);

                    Directory.CreateDirectory(Path.GetDirectoryName(file));

                    using (var stream = System.IO.File.OpenWrite(tmpFile))
                        using (var fileWriter = new StreamWriter(stream, encoding))
                        {
                            while ((line = reader.ReadLine()) != null && line != "TRANSMISSION_END" && line != "FAIL")
                            {
                                fileWriter.WriteLine(line);
                            }
                        }

                    FileHelper.Move(tmpFile, file);
                    tmp.DateModified = System.IO.File.GetLastWriteTime(file);
                }
                catch (SocketException)
                {                 // This should be connection interruption or time out.
                    FileHelper.Delete(tmpFile);
                }
                catch (IOException)
                {                 // Target file in use or nonexistent.
                    FileHelper.Delete(tmpFile);
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Asks the server for information (modification time) about all files
        /// this client has read access to and requests contents if necessary.
        /// </summary>
        private void PerformInitialRequest()
        {
            var       filesToRequest = new List <string>();
            var       filesOnServer  = new List <string>();
            TcpClient client         = null;

            try
            {
                client = ConnectToServer();

                if (client == null)
                {
                    return;
                }

                using (var stream = client.GetStream())
                    using (var reader = new StreamReader(stream, encoding))
                        using (var writer = new StreamWriter(stream, encoding))
                        {
                            writer.AutoFlush = true;
                            reader.BaseStream.ReadTimeout = 10000;
                            string   line;
                            string   fileName;
                            DB.File  file;
                            Protocol message;

                            writer.WriteLine(Protocol.AUTHENTICATE);
                            writer.WriteLine(name);
                            writer.WriteLine(passwordHash);
                            line    = reader.ReadLine();
                            message = ProtocolHelper.ExtractProtocol(line);
                            if (message != Protocol.SUCCESS)
                            {
                                return;
                            }

                            writer.WriteLine(Protocol.REQUEST_INITIAL_INFO);
                            line    = reader.ReadLine();
                            message = ProtocolHelper.ExtractProtocol(line);

                            if (message == Protocol.SUCCESS)
                            {
                                var files = db.GetFiles();
                                while (true)
                                {
                                    fileName = reader.ReadLine();

                                    if (fileName.StartsWith(subTreePrefix + TrackedDirectory + @"\"))
                                    {
                                        fileName = fileName.Substring(subTreePrefix.Length);
                                    }
                                    else if (fileName == "TRANSMISSION_END")
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        reader.ReadLine();                         // Skip modification time.
                                        continue;
                                    }
                                    filesOnServer.Add(fileName);

                                    line = reader.ReadLine();

                                    if (!files.TryGet(fileName, out file) || file.OlderThan(DateTime.Parse(line)))
                                    {
                                        filesToRequest.Add(fileName);
                                    }
                                }

                                foreach (var f in filesToRequest)
                                {
                                    RequestFileContents(reader, writer, f);
                                }
                            }
                            writer.WriteLine(Protocol.TRANSMISSION_END);
                            client.Close();

                            var filesToDelete = db.GetFiles().GetDictionary().Keys.ToList().Except(filesOnServer);
                            foreach (var f in filesToDelete)
                            {
                                db.GetFiles().Remove(f);
                                FileHelper.Delete(f);
                            }
                        }
            }
            catch (SocketException)
            { /* Time out. */ }
            catch (IOException)
            { /* File probably already deleted as RequestFileContents does not throw. */ }
            finally
            {
                db.Save();
                if (client != null)
                {
                    client.Close();
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Informs server about all changes found during the last
        /// directory checks.
        /// </summary>
        private void InformServer()
        {
            if ((filesCreated.Count == 0 || !canCreateFiles) &&
                filesChecked.Count == db.GetFiles().Count&& filesChanged.Count == 0)
            {
                // If checked == db files, we still need to clear the checked files otherwise deletes would be ignored
                // next time we check.
                lock (filesChecked)
                {
                    if (filesChecked.Count > 0)
                    {
                        filesChecked.Clear();
                    }
                }
                return;
            }

            try
            {
                var client = ConnectToServer();
                if (client == null)
                {
                    return;
                }
                var files = db.GetFiles();
                using (var stream = client.GetStream())
                    using (var writer = new StreamWriter(stream, encoding))
                        using (var reader = new StreamReader(stream, encoding))
                        {
                            writer.AutoFlush = true;

                            // Authentication of this session.
                            writer.WriteLine(Protocol.AUTHENTICATE);
                            writer.WriteLine(name);
                            writer.WriteLine(passwordHash);
                            if (ProtocolHelper.ExtractProtocol(reader.ReadLine()) != Protocol.SUCCESS)
                            {
                                writer.WriteLine(Protocol.TRANSMISSION_END);
                                Console.WriteLine("[ERROR] Refused because of wrong credentials.");
                                ClearLists();
                                return;
                            }

                            lock (filesCreated)
                            {
                                bool removeCreatedFiles = false;
                                foreach (var file in filesCreated)
                                {
                                    writer.WriteLine(Protocol.FILE_CREATED);
                                    writer.WriteLine(subTreePrefix + file);
                                    if (ProtocolHelper.ExtractProtocol(reader.ReadLine()) == Protocol.SUCCESS)
                                    {
                                        SendFileContents(writer, file);
                                    }
                                    else
                                    {
                                        writer.WriteLine(Protocol.TRANSMISSION_END);
                                        canCreateFiles     = false;
                                        removeCreatedFiles = true;
                                        break;
                                    }
                                }
                                if (removeCreatedFiles)
                                {                 // This removes those recently tracked and canCreateFiles == false will prevent new tracking.
                                    foreach (var file in filesCreated)
                                    {
                                        files.Remove(file);
                                    }
                                }
                                filesCreated.Clear();
                            }

                            lock (filesChanged)
                            {
                                foreach (var file in filesChanged)
                                {
                                    writer.WriteLine(Protocol.FILE_CHANGED);
                                    writer.WriteLine(subTreePrefix + file);
                                    if (ProtocolHelper.ExtractProtocol(reader.ReadLine()) == Protocol.SUCCESS)
                                    {
                                        SendFileContents(writer, file);
                                    }
                                    else
                                    {
                                        files.Remove(file);                         // Leave it physically on the disk, but do not track it.
                                    }
                                }
                                filesChanged.Clear();
                            }

                            lock (filesDeleted)
                            {
                                filesDeleted = db.GetFiles().GetDictionary().Keys.ToList().Except(filesChecked).ToList();

                                lock (filesChecked)
                                {
                                    filesChecked.Clear();
                                }
                                foreach (var file in filesDeleted)
                                {
                                    writer.WriteLine(Protocol.FILE_DELETED);
                                    writer.WriteLine(subTreePrefix + file);
                                    db.GetFiles().Remove(file);

                                    /*
                                     * Note: This is either SUCCESS or FAIL (depending of authorization).
                                     *       Currently is ignored, assuming the user just wants to get rid of the file,
                                     *       but it may be useful to add a config option to request the data of deleted
                                     *       files to recover them if the deletion fails on the server.
                                     */
                                    reader.ReadLine();
                                }
                                filesDeleted.Clear();
                            }

                            writer.WriteLine(Protocol.TRANSMISSION_END);
                        }
            }
            catch (IOException e)
            {
                Console.WriteLine("[ERROR] Cannot inform the server: {0}", e.Message);
                ClearLists();
            }
            finally
            {
                db.Save();
            }
        }
예제 #5
0
        /// <summary>
        /// Performs initial authentication either from config file, or
        /// (if it's missing or manual authentication is forced) from
        /// the console (and saves info on success).
        /// </summary>
        /// <returns>True if authentication succeeded, false otherwise.</returns>
        private bool Authenticate()
        {
            bool register = false;
            bool consoleAuthentication = false;

            if (forceManualAuthentization || !System.IO.File.Exists(userData))
            {             // Console authentication.
                consoleAuthentication = true;
                register = GetCredentialsFromConsole(out name, out passwordHash);
            }
            else
            {             // Saved authentication.
                using (var stream = System.IO.File.OpenRead(userData))
                    using (var reader = new StreamReader(stream))
                    {
                        name         = reader.ReadLine();
                        passwordHash = reader.ReadLine();
                    }
            }

            bool authenticated = false;

            using (var client = ConnectToServer())
            {
                if (client == null)
                {
                    return(false);
                }
                using (var stream = client.GetStream())
                    using (var writer = new StreamWriter(stream, encoding))
                        using (var reader = new StreamReader(stream, encoding))
                        {         // Actual authentication communication.
                            writer.AutoFlush = true;
                            writer.WriteLine(register ? Protocol.REGISTER : Protocol.AUTHENTICATE);
                            writer.WriteLine(name);
                            writer.WriteLine(passwordHash);

                            Protocol response = ProtocolHelper.ExtractProtocol(reader.ReadLine());
                            authenticated = (response == Protocol.SUCCESS);

                            if (authenticated)
                            {
                                writer.WriteLine(Protocol.NEW_CONNECTION);
                                writer.WriteLine(listenPort);
                                writer.WriteLine(Protocol.TRANSMISSION_END);
                            }
                        }
            }

            if (consoleAuthentication && authenticated)
            {             // Save the credentials for later use.
                using (var stream = System.IO.File.Open(userData, FileMode.Create))
                    using (var writer = new StreamWriter(stream, encoding))
                    {
                        writer.WriteLine(name);
                        writer.WriteLine(passwordHash);
                        writer.WriteLine(listenPort);
                    }
            }

            return(authenticated);
        }
예제 #6
0
        /// <summary>
        /// Handles a notification from the server (mostly
        /// regardin file changes).
        /// </summary>
        /// <param name="s">Socket used for the communication.</param>
        private void HandleServerNotice(Socket s)
        {
            try
            {
                using (var stream = new NetworkStream(s))
                    using (var reader = new StreamReader(stream, encoding))
                        using (var writer = new StreamWriter(stream, encoding))
                        {
                            writer.AutoFlush = true;
                            //reader.BaseStream.ReadTimeout = 10000;

                            var    message = ProtocolHelper.ExtractProtocol(reader.ReadLine());
                            string name    = reader.ReadLine();
                            if (name.StartsWith(subTreePrefix + TrackedDirectory + @"\"))
                            {
                                name = name.Substring(subTreePrefix.Length);

                                if (message != Protocol.FILE_DELETED)
                                {
                                    writer.WriteLine(Protocol.SUCCESS);
                                }
                                else
                                {
                                    reader.ReadLine();                     // T_E token, can be ignored here.
                                }
                            }
                            else
                            {
                                if (message != Protocol.FILE_DELETED)
                                {
                                    writer.WriteLine(Protocol.FAIL);
                                }
                                else
                                {
                                    reader.ReadLine();                     // T_E token, can be ignored here.
                                }
                                writer.WriteLine(Protocol.TRANSMISSION_END);
                                return;
                            }

                            if (message == Protocol.NONE || name == null)
                            {
                                return;
                            }

                            DB.File file = null;
                            if (message == Protocol.FILE_CREATED ||
                                (name != null && !db.GetFiles().TryGet(name, out file) && message != Protocol.FILE_DELETED))
                            {
                                file = new DB.File(name);
                                file.DateModified = FileHelper.Create(file.Name);
                                if (file.DateModified != DateTime.MaxValue)
                                {
                                    db.GetFiles().Add(file);
                                }
                            }

                            if (message == Protocol.FILE_DELETED)
                            {
                                FileHelper.Delete(name);
                                if (file != null)
                                {
                                    db.GetFiles().Remove(file);
                                }
                                reader.ReadLine();                 // T_E token, can be ignored here.
                            }
                            else
                            {
                                lock (file)
                                {
                                    string tmpName = name + ".tmp";
                                    try
                                    {
                                        string line;
                                        using (var fstream = System.IO.File.OpenWrite(tmpName))
                                            using (var fileWriter = new StreamWriter(fstream, encoding))
                                            {
                                                while ((line = reader.ReadLine()) != null && line != "TRANSMISSION_END")
                                                {
                                                    fileWriter.WriteLine(line);
                                                }
                                                fileWriter.BaseStream.SetLength(fileWriter.BaseStream.Position);
                                            }

                                        FileHelper.Move(tmpName, file.Name);
                                        file.DateModified = System.IO.File.GetLastWriteTime(file.Name);
                                    }
                                    catch (IOException e)
                                    {
                                        Console.WriteLine(e);
                                        // TODO:
                                        FileHelper.Delete(tmpName);
                                    }
                                }
                            }
                        }
            }
            catch (IOException e)
            {
                // TODO:
                Console.WriteLine("HUH? {0}", e.Message);
            }
            finally
            {
                db.Save();
            }
        }
예제 #7
0
        /// <summary>
        /// Handles communication with a single client.
        /// </summary>
        /// <param name="s">Socket used for communication with the client.</param>
        private void HandleConnection(Socket s)
        {
            int newPort = 0;

            try
            {
                // Resend the client to another port.
                using (var writer = new StreamWriter(new NetworkStream(s), encoding))
                {
                    writer.AutoFlush = true;

                    newPort = GetNewPort();
                    writer.WriteLine(newPort);
                    var tmpListener = new TcpListener(IPAddress.Parse(serverAddress), newPort);
                    tmpListener.Start();

                    // Just to be sure, close the original after establishing a new connection.
                    var tmpSocket = tmpListener.AcceptSocket();
                    tmpListener.Stop();
                    s.Close();

                    s = tmpSocket;
                }

                using (var stream = new NetworkStream(s))
                    using (var reader = new StreamReader(stream, encoding))
                        using (var writer = new StreamWriter(stream, encoding))
                        {
                            writer.AutoFlush = true;
                            reader.BaseStream.ReadTimeout = 10000;
                            string msg;
                            User   user = null;

                            bool running = true;
                            while (running)
                            {
                                msg = reader.ReadLine();
                                Protocol protocol = ProtocolHelper.ExtractProtocol(msg);
                                if (protocol == Protocol.NONE)                // Client crash/hang.
                                {
                                    return;
                                }

                                switch (protocol)
                                {
                                case Protocol.AUTHENTICATE:
                                    user = HandleUserAuthentication(s, reader, writer);
                                    if (user == null)
                                    {
                                        running = false;
                                    }
                                    break;

                                case Protocol.REGISTER:
                                    user = HandleUserRegistration(s, reader, writer);
                                    if (user == null)
                                    {
                                        running = false;
                                    }
                                    break;

                                case Protocol.FILE_CHANGED:
                                case Protocol.FILE_CREATED:
                                case Protocol.FILE_DELETED:
                                    HandleFileUpdate(s, reader, writer, protocol, user);
                                    break;

                                case Protocol.REQUEST_INITIAL_INFO:
                                    if (!SendInitialInfo(writer, user))
                                    {
                                        running = false;
                                    }
                                    break;

                                case Protocol.REQUEST_FILE_CONTENTS:
                                    HandleFileRequest(reader, writer, user);
                                    break;

                                case Protocol.TRANSMISSION_END:
                                    running = false;
                                    break;

                                case Protocol.NEW_CONNECTION:
                                    GetClientListenPort(reader, user);
                                    break;
                                }
                            }
                        }
            }
            catch (IOException)
            { /* This should be timeout or connection close. */ }
            finally
            {
                ReleasePort(newPort);
                s.Close();
            }
        }