/// <summary> /// Checks a single file and handles changes if /// any occured (modification, creation). /// </summary> /// <param name="file">Path to the file.</param> private void HandleFile(string file) { // Without this, used files would be treated as deleted. if (FileHelper.FileInUse(file)) { filesChecked.Add(file); return; } if (!System.IO.File.Exists(file) || FileHelper.FileInUse(file) || file.EndsWith(".tmp") || filesChecked.Contains(file) || Ignored(file)) { return; } filesChecked.Add(file); DB.File tmp = null; var files = db.GetFiles(); var date = System.IO.File.GetLastWriteTime(file); if (!files.TryGet(file, out tmp) && canCreateFiles) { var f = new DB.File(); f.Name = file; f.DateModified = date; f.Access = null; files.Add(f); filesCreated.Add(file); } else if (tmp != null && tmp.OlderThan(date)) { tmp.Update(date); filesChanged.Add(file); } }
/// <summary> /// Updates a file based on data sent by a client. /// </summary> /// <param name="file">Path to the file.</param> /// <param name="operation">Type of operation to be done with the file.</param> /// <param name="reader">StreamReader used as a source for the new file data.</param> /// <returns>True if the file was successfully updated, false otherwise.</returns> private bool WriteContentsToFile(DB.File file, Protocol operation, StreamReader reader) { lock (file) { if (operation == Protocol.FILE_CHANGED) { BackupFile(file.Name); } string tmpFile = file.Name + ".tmp"; try { string line; using (var stream = System.IO.File.OpenWrite(tmpFile)) using (var fileWriter = new StreamWriter(stream, encoding)) { while ((line = reader.ReadLine()) != null && line != "TRANSMISSION_END") { fileWriter.WriteLine(line); } fileWriter.BaseStream.SetLength(fileWriter.BaseStream.Position); } if (line != "TRANSMISSION_END") { FileHelper.Delete(tmpFile); Console.WriteLine("[ERROR] Wrong file transmission end token."); return(false); } else { FileHelper.Move(tmpFile, file.Name); } } catch (IOException e) { // Most probably time out. try { Console.WriteLine("[ERROR] Cannot write to file: {0}", e.Message); FileHelper.Delete(tmpFile); } catch (IOException) { /* Just in case Delete throws, which should not propagate. */ } return(false); } } return(true); }
/// <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); } } }
/// <summary> /// Updates a file based on the information sent by the user, this includes /// changing contents, creating new files and deleting existing files. /// </summary> /// <param name="socket">Socket used for the communication.</param> /// <param name="reader">StreamReader used to read data from the client.</param> /// <param name="writer">StreamWriter used to write data to the client.</param> /// <param name="operation">Type of operation to be done with the file.</param> /// <param name="user">Database entry of the user (used for authorization).</param> private void HandleFileUpdate(Socket socket, StreamReader reader, StreamWriter writer, Protocol operation, User user) { Group group; var groups = db.GetGroups(); if (user == null || !groups.TryGet(user.Group, out group)) { return; } string fileName = reader.ReadLine(); if (fileName == null) { return; } var files = db.GetFiles(); DB.File file = null; if (!AuthorizeFileAccess(fileName, out file, operation, group)) { writer.WriteLine(Protocol.FAIL); return; } else { writer.WriteLine(Protocol.SUCCESS); } fileName = null; if (operation == Protocol.FILE_DELETED) { DeleteFile(file); } else if (!WriteContentsToFile(file, operation, reader)) { return; } InformAll(user, operation, file.Name); file.DateModified = DateTime.Now; // So the clients don't all refresh the file contents. return; }
/// <summary> /// Backs up and deletes a file (both from the database /// and the directory tree). /// </summary> /// <param name="file">Path to the file.</param> /// <returns>True if the file was deleted, false otherwise.</returns> private bool DeleteFile(DB.File file) { lock (file) { try { BackupFile(file.Name); System.IO.File.Delete(file.Name); if (db.GetFiles().Contains(file.Name)) { db.GetFiles().Remove(file); } return(true); } catch (IOException) { return(false); } } }
/// <summary> /// Checks if the user is allowed to perform a given operation on a given file. /// </summary> /// <param name="name">Path to the file.</param> /// <param name="file">Database entry of the file which will be assigned to.</param> /// <param name="type">Type of operation to be done with the file.</param> /// <param name="group">Group the user belongs to.</param> /// <returns></returns> private bool AuthorizeFileAccess(string name, out DB.File file, Protocol type, Group group) { switch (type) { case Protocol.FILE_CHANGED: case Protocol.FILE_DELETED: return(db.GetFiles().TryGet(name, out file) && file.Test(group, AccessRight.WRITE)); case Protocol.FILE_CREATED: if (group.CanCreateFiles && !db.GetFiles().Contains(name)) { Directory.CreateDirectory(Path.GetDirectoryName(name)); file = db.CreateNewFile(name, group); return(true); } else { file = null; return(false); } case Protocol.REQUEST_FILE_CONTENTS: if (!db.GetFiles().TryGet(name, out file)) { return(false); } else { return(file.Test(group, AccessRight.READ)); } default: file = null; return(false); } }
/// <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(); } }