/// <summary> /// La funzione deve gestire la creazione di un nuovo file. /// </summary> public override bool Handle() { if(this.Stopped) return false; if (String.IsNullOrEmpty(base.FullPath)){ throw new Exception("Path e nome del file non validi."); } using(TcpClient connection = new TcpClient()){ connection.Connect(UserCredentials.Instance.Host, UserCredentials.Instance.Port); using (StreamReader sr = new StreamReader(connection.GetStream())) using (StreamWriter sw = new StreamWriter(connection.GetStream())) { //sr.BaseStream.ReadTimeout = 10000; //sw.BaseStream.WriteTimeout = 10000; /************************************ Inizio della funzione ***************************************/ string username = UserCredentials.Instance.Username; string accesstoken = UserCredentials.Instance.AccessToken; string allPath = this.FullPath; string subcmd = ProtocolStrings.SUBCMD_CREATE_MODIFY; long client_vfsversion = SyncToken.Instance.VFSVersion; try { string syncroPath = UserUtils.GetRootPath(); if (!allPath.StartsWith(syncroPath)) throw new Exception("File o root non validi."); string relativePath = allPath.Replace(syncroPath, "/"); //controlli if (String.IsNullOrEmpty(username) || String.IsNullOrEmpty(accesstoken)) { throw new Exception("Lo username o l'accesstoken non può essere nullo."); } if (!relativePath.StartsWith("/")) { throw new Exception("Il path deve iniziare con uno slash."); } if (!File.Exists(allPath)) { throw new Exception("Il path assoluto non esiste in questo file system"); } string name = Path.GetFileName(relativePath); string path = Path.GetDirectoryName(relativePath).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; DateTime lastUpdate = File.GetLastWriteTime(allPath); FileInfo file = new FileInfo(allPath); long dim = file.Length; string hashFile = UserUtils.GetFileHash(allPath); if (String.IsNullOrEmpty(name) || name.StartsWith("\\")) { throw new Exception("Il nome del file non può essere nullo e non può iniziare con uno slash."); } if (String.IsNullOrEmpty(hashFile)) { throw new Exception("L'hash del file non può essere vuoto o nullo."); } if (dim < 0) { throw new Exception("La dimensione del file non può essere negativa."); } long nchunkUser = (long)Math.Ceiling(((double)file.Length / (double)ProtocolStrings.CHUNK_SIZE)); if (nchunkUser < 0) { throw new Exception("Il numero di chunk del file non può essere negativo."); } //la lista di chunk da inviare al server List<VChunk> listChunk = new List<VChunk>(); PushFile_D pD = null; //primo controllo da fare if (this.Stopped) return false; //apro il file e calcolo il numero di chunk using (FileStream fs = new FileStream(allPath, FileMode.Open, FileAccess.Read, FileShare.Delete)) { byte[] c = new byte[ProtocolStrings.CHUNK_SIZE]; string hashChunk; VChunk vc = null; for (long i = 0; i < nchunkUser; i++) { if (this.Stopped) return false; int l = fs.Read(c, 0, c.Count()); hashChunk = UserUtils.GetHash(c, l); vc = new VChunk(i, -1, -1, hashChunk, l, lastUpdate); listChunk.Add(vc); } if (this.Stopped) return false; //invio la richiesta PushFile_A req = new PushFile_A(username, accesstoken, subcmd, name, path, lastUpdate, hashFile, dim, nchunkUser, client_vfsversion, listChunk); UserUtils.NetworkWriteLine(sw, req.ToJSON()); //attendo la risposta del server string rec = UserUtils.NetworkReadLine(sr); Response r = Response.fromJson(rec); if (r.Result == 401) { //problemi con la login T (token) throw new UserLoginException(BadLoginResponse.fromJson(rec).Msg); } else if (r.Result != 200) { throw new Exception(BadPushFileResponse.fromJson(rec).Msg); } if (this.Stopped) return false; PushFile_Abis pAbis = PushFile_Abis.fromJson(rec); //ora il server mi deve mandare la lista di chunk che gli devo inviare //N.B. se la lista è vuota devo andare direttamente all'ultima operazione. if (pAbis.NChunk > 0) { PushFile_B pB = null; byte[] filledChunk = new byte[ProtocolStrings.CHUNK_SIZE]; long id = -1; fs.Seek(0, SeekOrigin.Begin); do { if (this.Stopped) return false; pB = PushFile_B.fromJson(UserUtils.NetworkReadLine(sr)); id = pB.IdChunk; fs.Seek(id * (ProtocolStrings.CHUNK_SIZE), SeekOrigin.Begin); int l = fs.Read(filledChunk, 0, (int)ProtocolStrings.CHUNK_SIZE); //TODO con l'ultimo chunk sprechiamo byte(inviamo byte inutili). PushFile_C pC = new PushFile_C(filledChunk, l); UserUtils.NetworkWriteLine(sw, pC.toJSON()); } while (!pB.Done); } pD = PushFile_D.fromJson(UserUtils.NetworkReadLine(sr)); /** N.B. da qui in poi non dobbiamo più stoppare il thread perché il server ha committato **/ } //ora devo andare a salvare il synctoken (che poi sarebbe il vfsversion) SyncToken.Instance.VFSVersion = pD.NewVfsversion; } catch (SocketException e) { throw new NetworkException("Si è verificato un errore di rete.", e); } catch (UserLoginException e) { throw e; } catch (IOException e) { if ((uint)e.HResult == FILE_IN_USE) { throw new FileInUseException("Il file è in uso da un altro processo, verrà gestito più tardi."); } } catch (Exception e) { throw new PushFileExceptionU(e.Message); } /**************************************** Fine della funzione ***************************************/ } } return true; }
/// <summary> /// Dato un fileid, una versione del file partenza ed una di arrivo, questa funzione calcola tutti i chunk modificati /// che è necessario aggiornare e li ritorna in una lista. /// </summary> /// <param name="fileId"></param> /// <param name="startVersion"></param> /// <param name="targetVersion"></param> /// <returns></returns> public IEnumerable<VChunk> GetFileChunksUpdates(long fileId, long client_vfsversion, long fileTargetVersion) { if (fileTargetVersion < 0) { throw new JsonRequestException("TargetVersion non può essere negativo."); } /* if (startVersion > targetVersion) throw new JsonRequestException("Start version non può essere maggiore di targetVersion."); */ //startVersion = Math.Min(startVersion, targetVersion); lock (this) { List<VChunk> res = new List<VChunk>(); try { SQLiteConnection conn; string dir; string db = UserManager.Instance.GetUserDbPath(this.Id, out dir); using (conn = new SQLiteConnection(String.Format("Data Source={0};Version=3;", db))) { conn.Open(); using (SQLiteCommand cmd = conn.CreateCommand()) { // Recupero gli nchunk per il file id dato. cmd.CommandText = "SELECT ifnull(nchunk,0) FROM files WHERE id = $fileid AND version = $targetVersion AND iduser = $iduser"; cmd.Parameters.AddWithValue("fileid", fileId); cmd.Parameters.AddWithValue("targetVersion", fileTargetVersion); cmd.Parameters.AddWithValue("iduser", this.Id); long nchunk = -1; long clientFileVersion = -1; using (SQLiteDataReader r = cmd.ExecuteReader()) { if (!r.Read()) throw new UserDonwloadFilException("Non è stato trovato alcun match per i parametri specificati. Si prega di controllare la versione e l'id del file."); else { nchunk = r.GetInt64(0); } } cmd.Parameters.Clear(); // RECUPERIAMO LA VERSIONE DEL FILE CHE IL CLIENT HA, DATO IL SUO VFSVERSION cmd.CommandText = "SELECT IFNULL(MAX(version),-1) FROM files " +"WHERE iduser = $iduser AND id=$fileid " +"AND vfsversion<=$client_vfsversion"; cmd.Parameters.AddWithValue("iduser",this.Id); cmd.Parameters.AddWithValue("fileid",fileId); cmd.Parameters.AddWithValue("client_vfsversion", client_vfsversion); using (SQLiteDataReader r = cmd.ExecuteReader()) { if (r.Read()) clientFileVersion = r.GetInt64(0); else clientFileVersion = -1; } cmd.Parameters.Clear(); // Eseguimo la query TOP cmd.CommandText = "SELECT id, idfile,version,hash,dimension,lastupdate FROM chunks " + "WHERE idfile = $idfile AND version >$m AND version <= (SELECT MAX(C1.version) FROM chunks C1 WHERE C1.id = id AND version <=$n) " + "GROUP BY id " + "HAVING id<$nchunk"; cmd.Parameters.AddWithValue("idfile", fileId); cmd.Parameters.AddWithValue("m", clientFileVersion); cmd.Parameters.AddWithValue("n", fileTargetVersion); cmd.Parameters.AddWithValue("nchunk", nchunk); using (var r = cmd.ExecuteReader()) { while (r.Read()) { long id = r.GetInt64(0); long idfile = r.GetInt64(1); long version = r.GetInt64(2); string hash = r.GetString(3); long dim = r.GetInt64(4); DateTime lastupdate = DateTime.Parse(r.GetString(5)); VChunk tmp = new VChunk(id, idfile, version, hash, dim, lastupdate); res.Add(tmp); } } return res; } } } catch (Exception e) { throw new UserDonwloadFilException(e.Message); } } }
public static long DoPushFile(string username, string accesstoken, string allPath, string subcmd, long client_vfsversion, StreamWriter sw, StreamReader sr) { try { string syncroPath = GetRootPath(); if (!allPath.StartsWith(syncroPath)) throw new Exception("File o root non validi."); string relativePath = allPath.Replace(syncroPath, "/"); //controlli if(String.IsNullOrEmpty(username) || String.IsNullOrEmpty(accesstoken)){ throw new Exception("Lo username o l'accesstoken non può essere nullo."); } if (!relativePath.StartsWith("/")) { throw new Exception("Il path deve iniziare con uno slash."); } File.Exists(allPath); string name = Path.GetFileName(relativePath); string path = Path.GetDirectoryName(relativePath); path = path.TrimEnd(Path.DirectorySeparatorChar); path += Path.DirectorySeparatorChar; DateTime lastUpdate = File.GetLastWriteTime(allPath); FileInfo file = new FileInfo(allPath); long dim = file.Length; string hashFile = GetFileHash(allPath); if(String.IsNullOrEmpty(name) || name.StartsWith("\\")){ throw new Exception("Il nome del file non può essere nullo e non può iniziare con uno slash."); } if(String.IsNullOrEmpty(hashFile)){ throw new Exception("L'hash del file non può essere vuoto o nullo."); } if(dim<0){ throw new Exception("La dimensione del file non può essere negativa."); } FileInfo finfo = new FileInfo(allPath); long nchunkUser = (long)Math.Ceiling(((double)finfo.Length / (double)ProtocolStrings.CHUNK_SIZE)); if(nchunkUser<0){ throw new Exception("Il numero di chunk del file non può essere negativo."); } //la lista di chunk da inviare al server List<VChunk> listChunk = new List<VChunk>(); PushFile_D pD = null; //apro il file e calcolo il numero di chunk using (FileStream fs = File.OpenRead(allPath)) { byte[] c = new byte[ProtocolStrings.CHUNK_SIZE]; string hashChunk; VChunk vc = null; for (long i = 0; i < nchunkUser; i++) { int l = fs.Read(c, 0, c.Count()); hashChunk = GetHash(c,l); vc = new VChunk(i, -1, -1, hashChunk, l, lastUpdate); listChunk.Add(vc); } //invio la richiesta PushFile_A req = new PushFile_A(username, accesstoken, subcmd, name, path, lastUpdate, hashFile, dim, nchunkUser, client_vfsversion, listChunk); NetworkWriteLine(sw, req.ToJSON()); //attendo la risposta del server string rec = NetworkReadLine(sr); Response r = Response.fromJson(rec); if (r.Result == 401) { //problemi con la login T (token) throw new UserLoginException(BadLoginResponse.fromJson(rec).Msg); } else if(r.Result != 200){ throw new Exception(BadPushFileResponse.fromJson(rec).Msg); } PushFile_Abis pAbis = PushFile_Abis.fromJson(rec); //ora il server mi deve mandare la lista di chunk che gli devo inviare //N.B. se la lista è vuota devo andare direttamente all'ultima operazione. if (pAbis.NChunk > 0) { PushFile_B pB = null; byte[] filledChunk = new byte[ProtocolStrings.CHUNK_SIZE]; long id = -1; fs.Seek(0, SeekOrigin.Begin); do { pB = PushFile_B.fromJson(NetworkReadLine(sr)); id = pB.IdChunk; fs.Seek(id * (ProtocolStrings.CHUNK_SIZE), SeekOrigin.Begin); int l = fs.Read(filledChunk, 0, (int)ProtocolStrings.CHUNK_SIZE); //TODO con l'ultimo chunk sprechiamo byte(inviamo byte inutili). PushFile_C pC = new PushFile_C(filledChunk, l); NetworkWriteLine(sw, pC.toJSON()); } while (!pB.Done); } pD = PushFile_D.fromJson(NetworkReadLine(sr)); } //ora devo andare a salvare il synctoken (che poi sarebbe il vfsversion) return pD.NewVfsversion; } catch (NetworkException e) { throw e; } catch (UserLoginException e) { throw e; } catch (Exception e) { throw new PushFileExceptionU(e.Message); } }
/// <summary> /// Dato un fileid ed una versione del file da ottenere, questa funzione ritorna l'intera lista di chunk che descrive il file. /// Con questa lista è quindi possibile recuperare tutto il file alla versione specificata. /// </summary> /// <param name="fileId"></param> /// <param name="targetVersion"></param> /// <returns></returns> public IEnumerable<VChunk> GetFileChunks(long fid, long tVer, bool donotlock = false) { if (tVer < 0) { throw new JsonRequestException("TargetVersion non può essere negativo."); } Del d = delegate(long fileId, long targetVersion) #region { List<VChunk> res = new List<VChunk>(); try { SQLiteConnection conn; string dir; string db = UserManager.Instance.GetUserDbPath(this.Id, out dir); using (conn = new SQLiteConnection(String.Format("Data Source={0};Version=3;", db))) { conn.Open(); using (SQLiteCommand cmd = conn.CreateCommand()) { // Recupero gli nchunk per il file id dato. cmd.CommandText = "SELECT ifnull(nchunk,0) FROM files WHERE id = $fileid AND version = $targetVersion AND iduser = $iduser"; cmd.Parameters.AddWithValue("fileid", fileId); cmd.Parameters.AddWithValue("targetVersion", targetVersion); cmd.Parameters.AddWithValue("iduser", this.Id); long nchunk = (long)cmd.ExecuteScalar(); cmd.Parameters.Clear(); cmd.CommandText = "SELECT id,idfile,version,hash,dimension,lastupdate " + "FROM chunks " + "WHERE idfile = $fileid AND version <= (SELECT MAX(C1.version) FROM chunks C1 WHERE C1.id = id AND version <=$n) " + "GROUP BY id " + "HAVING id<$nchunk " + "ORDER BY id ASC "; cmd.Parameters.AddWithValue("fileid", fileId); cmd.Parameters.AddWithValue("n", targetVersion); cmd.Parameters.AddWithValue("nchunk", nchunk); using (var r = cmd.ExecuteReader()) { while (r.Read()) { long id = r.GetInt64(0); long idfile = r.GetInt64(1); long version = r.GetInt64(2); string hash = r.GetString(3); long dim = r.GetInt64(4); DateTime lastupdate = DateTime.Parse(r.GetString(5)); VChunk tmp = new VChunk(id, idfile, version, hash, dim, lastupdate); res.Add(tmp); } } return res; } } } catch (Exception e) { throw new UserDonwloadFilException(e.Message); } }; #endregion if (donotlock) { return d(fid, tVer); } else { lock (this) { return d(fid, tVer); } } }