// crea un post di risposta al post invocato (con la struttura dati già
// inizializzata per bene)
func (post *Post) Respond(conv *Conversation, user *User) (*Post, error) {
	registra := func(post *Post) {
		err := Data.creaPostSQL(conv, post)
		if err != nil {
			logs.Error("Impossibile creare il post sul database\nmotivo:", err.Error())
		}
		err = Data.salvaPostSQL(conv, post.padre)
		if err != nil {
			logs.Error("Impossibile aggiornare il post padre sul database\nmotivo:", err.Error())
		}
	}

	response := conv.NewPost(user, post)
	if post.rispostaPrincipale == nil {
		post.rispostaPrincipale = response
		registra(response)
		return response, nil
	}
	if post.rispostaSecondaria == nil {
		post.rispostaSecondaria = response
		registra(response)
		return response, nil
	}
	return nil, NewConversationError("Impossibile attaccare più di due risposte ad un solo post", conv)
}
Example #2
0
// Inizializzare il server in ascolto per le Sincronizzazione delle conversazioni
//
// "laddress string" indica su quali indirizza ascoltare e su quale porta
func StartServer(laddress string) {
	ln, err := net.Listen("tcp", laddress)
	logs.Log("Server in ascolto su: \"", laddress, "\"")
	if err != nil {
		logs.Error("Errore nell'aprire la connessione: ", err.Error())
		return
		//TODO handle error
	}

	//database.MainConv = database.NewConversation(database.ServerFakeUser) //duplicato!

	//canale := make(chan byte, 256)
	codaReadiness := make(chan *Client, 64)
	codaAccettazioni := make(chan *Client, 64)
	logs.AggiungiAzioneDiChiusura(func() {
		close(codaReadiness)
		close(codaAccettazioni)
	})

	go spedisci(codaAccettazioni, codaReadiness)

	var spegniti bool = false

	logs.AggiungiAzioneDiChiusura(func() {
		spegniti = true
		ln.Close()
	})

	for !spegniti {
		conn, err := ln.Accept()
		if spegniti {
			return
		}
		if err != nil {
			//TODO fare un pacchetto per la raccolta degli errori
			logs.Error("Tentativo di connessione non andato a buon fine: ", err.Error())
		}

		go func() {
			client, gestore := GestisciClient(conn)
			if client != nil {
				go gestore(codaReadiness)
				codaAccettazioni <- client
			} else {
				//L'handshaking non è andato a buon fine
				conn.Close()
			}
		}()

	}
}
// Inizializza tutte le operazioni necessarie per sul database
func init() {
	logs.Log("init del database")

	ServerFakeUser = new(User)
	ServerFakeUser.ID = 0
	ServerFakeUser.Username = "******"
	serverPassword := sha256.New()
	serverPassword.Write([]byte("toor"))
	ServerFakeUser.password = serverPassword.Sum([]byte{})

	err := CreateDataBaseSQL()
	if err != nil {
		logs.Error(err.Error())
	}

	//	MainConv = NewConversation(ServerFakeUser)
	err = Data.caricaConversazioni()
	if err != nil {
		logs.Error("Impossibile caricare le conversazioni vecchie\nmotivo: ", err.Error())
	}

	//	err = salvaUtente(ServerFakeUser)
	//	if err != nil {
	//		logs.Error("Impossibile salvare l'utente server nel database\nmotivo: ", err.Error())
	//	}()

	Data.CaricaUtentiSQL()
	if err != nil {
		logs.Error("Impossibile caricare gli utenti dal database\nmotivo: ", err.Error())
	}

	go func() {
		var spegniti bool = false
		logs.AggiungiAzioneDiChiusura(func() { spegniti = true })

		tk := time.NewTicker(10 * time.Minute)
		for !spegniti {
			<-tk.C
			err := MainConv.salvaTutteLeConversazioniSQL()
			if err != nil {
				logs.Error("Impossibile salvare tutti i post\nmotivo: ", err.Error())
			}
			logs.Log("salvate tutte le conversazioni sul database")
			//err := MainConv.salvaTuttiIPostSQL(&Data)
			//if err != nil {
			//	logs.Error("Impossibile salvare tutti i post\nmotivo: ", err.Error())
			//}
		}
		tk.Stop()
	}()
}
// Questa funzione registra un nuovo utente nel database
func (datab *DatabaseRegistration) RegisterNewUser(username, password string) (user *User, success bool) {

	var present bool
	success = false

	if user, present = datab.userNameToId[username]; !present {
		datab.contatore++
		user = new(User)
		user.ID = datab.contatore
		user.Username = username
		hashpassword := sha256.New()
		hashpassword.Write([]byte(password))
		user.password = hashpassword.Sum([]byte{})

		err := salvaUtenteSQL(user)
		if err != nil {
			logs.Error(err.Error())
			return nil, false
		}

		datab.userNameToId[username] = user
		datab.userIDtoUser[user.ID] = user

		success = true

	} else {
		return nil, false
	}

	return

}
Example #5
0
// Fa il flush di tutte le conversazioni sul database
//
// Ancora finto: salva solamente la conversazione principale
func (conv *Conversation) salvaTutteLeConversazioniSQL() error {
	_, err := updateConvOp.Exec(0)
	if err != nil {
		logs.Error("Impossibile salvare la conversazione ", strconv.Itoa(conv.ID), " nel database")
		return err
	}
	return conv.salvaTuttiIPostSQL(&Data)
}
Example #6
0
// salva un utente nel database
func salvaUtenteSQL(user *User) error {

	_, err := insertUserOp.Exec(user.ID, user.Username, user.password)
	if err != nil {
		logs.Error("Impossibile salvare ", user.Username, " nel database")
		return err
	}

	return nil
}
Example #7
0
// Inizializza la struttura dati del client e crea la funzione per gestire il client
// (da partire come goroutine)
func GestisciClient(conn net.Conn) (*Client, func(chan *Client)) {
	fmt.Print("Nuova connessione: ")
	fmt.Println(conn.RemoteAddr())

	client := NewClient(&conn)
	if client == nil {
		conn.Close()
		return nil, nil
	}

	return client, func(readiness chan *Client) {

		var spegniti bool = false

		logs.AggiungiAzioneDiChiusura(func() {
			spegniti = true
		})

		for !spegniti {
			_, size, err := client.stream.ReadRune()
			if err != nil {
				logs.Error("connessione interrotta: ", conn.RemoteAddr().String(), "\n\tmotivazione: ", err.Error())
				client.gestisciDisconnessione(database.MainConv)
				return
			}
			//			for i := 0; i < size; i++ {
			fmt.Printf("letto carattere di %v byte\n", size)
			err = client.stream.UnreadRune()
			if err != nil {
				logs.Error("impossibile fare UnreadByte: ", conn.RemoteAddr().String(), "\n\tmotivazione: ", err.Error())
				client.gestisciDisconnessione(database.MainConv)
				return
			}
			//			}

			readiness <- client
			<-client.blocco
		}

		(*client.conn).Close()
	}
}
Example #8
0
// Fa il flush sul database di tutti i post relativi alla conversazione
func (conv *Conversation) salvaTuttiIPostSQL(data *DatabaseRegistration) error {
	messaggio := "salvataggio di tutti i post sul database"
	logs.Log(messaggio)
	err := conv.testaPost.salvaPostRicSQL(conv, data)

	if err != nil {
		logs.Error(messaggio + " fallito")
	} else {
		logs.Log(messaggio + " riuscito")
	}

	return err
}
Example #9
0
// Svolge l'handshake con il client. Ritorna true se l'handshake ha avuto successo
func (client *Client) handshake() bool {

	keepAlive, err := client.leggiEseguiComando()
	if err != nil {
		logs.Error(err.Error())
		return false
	}

	if !keepAlive {
		return false
	}

	//REGISTRAZIONE DELL'UTENTE ALLA CONVERSAZIONE
	//TODO scegliere la conversazione a cui connettersi -.-
	err2 := database.MainConv.NewUserConnection(client.user)
	if err2 != nil {
		//CONTROLLO SE L'UTENTE È GIÀ CONNESSO
		logs.Error("impossibile connettere: ", err2.Error())
		return false
	}

	client.stream.WriteString(strconv.Itoa(client.user.ID))
	client.stream.WriteRune('\\')

	//spedisci lo stato attuale della conversazione
	client.stream.WriteString("\\U0\\")
	client.stream.WriteString(database.MainConv.GetComplete(false))

	//Invia le posizioni di tutte quante le persone connesse
	client.stream.WriteString(database.MainConv.GetAllPositionString())
	//invia l'utente attivo
	client.stream.WriteString("\\U" + strconv.Itoa(database.MainConv.UtenteAttivo) + "\\")

	client.stream.Flush()
	return true
}
Example #10
0
// Fa il flush del post nel database
func (datab *DatabaseRegistration) salvaPostSQL(conv *Conversation, post *Post) error {
	//logs.Log("salvo post ", strconv.Itoa(post.idPost))
	var idPadre, idRPri, idRSec int = -1, -1, -1
	if post.padre != nil {
		idPadre = post.padre.idPost
	}
	if post.rispostaPrincipale != nil {
		idRPri = post.rispostaPrincipale.idPost
	}
	if post.rispostaSecondaria != nil {
		idRSec = post.rispostaSecondaria.idPost
	}

	//campi:					id		padre	first_response 		second_response 	text
	_, err := updatePostOp.Exec(post.idPost, idPadre, idRPri, idRSec, post.testo.GetComplete(false))
	if err != nil {
		logs.Error("Impossibile salvare il post ", strconv.Itoa(post.idPost), " nel database")
		return err
	}

	return nil
}
Example #11
0
// crea il post dentro il database. Ogni post deve essere creato prima di
// potere essere salvato.
func (datab *DatabaseRegistration) creaPostSQL(conv *Conversation, post *Post) error {
	logs.Log("creo post ", strconv.Itoa(post.idPost))

	var idPadre, idRPri, idRSec int = -1, -1, -1
	if post.padre != nil {
		idPadre = post.padre.idPost
	}
	if post.rispostaPrincipale != nil {
		idRPri = post.rispostaPrincipale.idPost
	}
	if post.rispostaSecondaria != nil {
		idRSec = post.rispostaSecondaria.idPost
	}

	_, err := insertPostOp.Exec(post.idPost, conv.ID, post.testo.GetComplete(false), idPadre, idRPri, idRSec)
	if err != nil {
		logs.Error("Impossibile creare il post ", strconv.Itoa(post.idPost), " nel database")
		return err
	}

	return nil

}
Example #12
0
// Elimina dalla Superstringa un tot caratteri nella posizione data.
// Elimina a partire dalla posizione data in poi
func (lista *SuperString) DelElem(pos int, howmany int) {
	//	fmt.Println("Cercando di eliminare: pos=", pos, " howmany=", howmany)
	if pos < 0 {
		logs.Error("che cazzo stai cercando di eliminare???")
		return
	}
	//roRebuildTotal = true

	posAttuale := lista.testa

	for pos > posAttuale.size {
		pos -= posAttuale.size
		posAttuale = posAttuale.succ
		if posAttuale == nil {
			logs.Error("Cercando di eliminare fuori dalla stringa, l'eliminazione verrà ignorata")
			return
		}
	}

	//elimina prima stringa
	quanti_eliminare := howmany
	if pos+howmany <= posAttuale.size { //elimina solo posizione attuale
		//		fmt.Println("Elimina solo posizione attuale") //DEBUG
		//vecchio := posAttuale.elemento
		//posAttuale.elemento = strings.Join([]string{vecchio[0:pos], vecchio[pos+howmany : posAttuale.size]}, "") //remove dalla stringa corrente
		if (posAttuale.size - quanti_eliminare) == 0 {
			lista.delSingleElem(posAttuale)
			return
		}
		posAttuale.elemento = removeFromRunes(posAttuale.elemento, posAttuale.size, pos, howmany)
		posAttuale.size -= quanti_eliminare
		return
	}

	quanti_eliminare = posAttuale.size - pos
	posAttuale.elemento = removeFromRunes(posAttuale.elemento, posAttuale.size, pos, quanti_eliminare)
	posAttuale.size -= quanti_eliminare

	if posAttuale.size == 0 {
		tmp := posAttuale.succ
		lista.delSingleElem(posAttuale)
		posAttuale = tmp
		if posAttuale == nil {
			posAttuale = lista.testa
		}
	} else {
		posAttuale = posAttuale.succ
	}

	//elimina le stringhe di mezzo
	quanti_eliminare = howmany - quanti_eliminare

	if posAttuale == nil {
		logs.Error("ma che cazzo succede? posAttuale == nil?")
		return
	}

	for posAttuale.succ != nil && quanti_eliminare >= posAttuale.size {
		//		fmt.Println("Elimino stringa di mezzo") //DEBUG
		quanti_eliminare -= posAttuale.size

		tmp := posAttuale.succ
		lista.delSingleElem(posAttuale)
		posAttuale = tmp
	}

	//elimina ultima stringa
	if posAttuale.size == quanti_eliminare {
		lista.delSingleElem(posAttuale)
	} else {
		//		fmt.Println("Elimino da ultima stringa") //DEBUG
		posAttuale.elemento = removeFromRunes(posAttuale.elemento, posAttuale.size, 0, quanti_eliminare)
		posAttuale.size -= quanti_eliminare
	}
}
Example #13
0
// Questa funzione entra in un ciclo e non vi esce finché il canale di input non
// viene chiuso. Quello che fa è un parsing dei caratteri dal canale di input
// controllando le azioni da svolgere e riversare (intelligentemente) sul canale
// di output quello che deve essere spedito ai vari client.
//
// This function should run as goroutine
func gestisciTestoConversazione(input chan rune, output chan string) {
	activeUser := database.Data.GetUserByID(0)
	var errore error
	var versoClient []rune
	//	cursor := 0
	for c := range input {
		//		c := <-input
		//		fmt.Println("input",string(c))
		switch c {
		case '\\': //caso di carattere di controllo
			cc := <-input
			//			fmt.Println("input",string(cc))
			switch cc {
			case 'K':
				parent, cu := mangiaIntero(input)
				if cu != '\\' {
					errore = TranslationError{"ERRORE lettura stream: carattere di controllo mangiato != '\\'"}
					break
				}
				var postRID int
				postRID, errore = database.MainConv.Respond(parent, activeUser)
				versoClient = []rune{'\\', 'K'}
				versoClient = append(versoClient, ([]rune(strconv.Itoa(parent)))...)
				versoClient = append(versoClient, '\\')
				versoClient = append(versoClient, ([]rune(strconv.Itoa(postRID)))...)
				versoClient = append(versoClient, '\\')
			case 'P':
				postCursor, cu := mangiaIntero(input)
				if cu != '\\' {
					errore = TranslationError{"ERRORE lettura stream: carattere di controllo mangiato != '\\'"}
					break
				}
				errore = database.MainConv.ChangePost(activeUser, postCursor)
				versoClient = []rune{'\\', cc}
				versoClient = append(versoClient, ([]rune(strconv.Itoa(postCursor)))...)
				versoClient = append(versoClient, '\\')
			case 'C':
				cursor, cu := mangiaIntero(input)
				if cu != '\\' {
					errore = TranslationError{"ERRORE lettura stream: carattere di controllo mangiato != '\\'"}
					break
				}
				errore = database.MainConv.ChangePos(activeUser, cursor)
				versoClient = []rune{'\\', cc}
				versoClient = append(versoClient, ([]rune(strconv.Itoa(cursor)))...)
				versoClient = append(versoClient, '\\')
			case 'D':
				howmany, cu := mangiaIntero(input)
				if cu != '\\' {
					errore = TranslationError{"ERRORE lettura stream: carattere di controllo mangiato != '\\'"}
					break
				}
				versoClient = []rune{'\\', cc}
				versoClient = append(versoClient, ([]rune(strconv.Itoa(howmany)))...)
				versoClient = append(versoClient, '\\')
				//cursor -= howmany
				//database.MainConv.TestaPost.Text(activeUser).DelElem(cursor, howmany)
				errore = database.MainConv.DelElem(activeUser, howmany)
			case 'U':
				newUserID, cu := mangiaIntero(input)
				if cu != '\\' {
					errore = TranslationError{"ERRORE lettura stream: carattere di controllo mangiato != '\\'"}
					break
				}
				activeUser = database.Data.GetUserByID(newUserID)
				versoClient = []rune{'\\', cc}
				versoClient = append(versoClient, ([]rune(strconv.Itoa(newUserID)))...)
				versoClient = append(versoClient, '\\')

			case '\\':
				//database.MainConv.TestaPost.Text(activeUser).InsSingleElem('\\', cursor)
				errore = database.MainConv.InsElem(activeUser, []rune{'\\'})
				versoClient = []rune{'\\'}
				//cursor++

			default:
				fmt.Println("ERRORE azione", cc, "non disponibile")
			}

			if errore != nil {
				logs.Error("errore nel lavorare sulla superstringa\n\tultimo comando: ", string(cc), "\n\tmotivo: ", errore.Error())
				errore = nil
			} else {
				//				for i := range versoClient {
				//					output <- versoClient[i]
				//				}
				output <- string(versoClient)
			}

		default:
			//database.MainConv.TestaPost.Text(activeUser).InsSingleElem(c, cursor)
			errore = database.MainConv.InsElem(activeUser, []rune{c})
			output <- string(c)
			//cursor++

			//TODO anche qui si può pensare ad un flasher a tempo per minimizzare il lavoro su superstring
		}

		if errore != nil {
			logs.Error("errore nel lavorare sulla superstringa\n\t ultimo carattere: ", string(c), "\n\tmotivo: ", errore.Error())
			errore = nil
		}

		//		fmt.Println("---", database.MainConv.TestaPost.Text(activeUser).GetComplete(true), "---") //DEBUG
		fmt.Println(database.MainConv.GetComplete(true)) //DEBUG

	}
}
Example #14
0
// Funzione che innesta tutta la procedura per il parsing di quello ricevuto
// dai client.
//TODO migliora la documentazione
func flasher(codaCiclica *list.List, readiness chan *Client) {
	tempoDaAspettare := 30 * time.Millisecond
	var lastActiveUser int = -1
	input := make(chan rune, 256)
	output := make(chan string, 256)
	logs.AggiungiAzioneDiChiusura(func() {
		close(input)
		close(output)
	})

	go gestisciTestoConversazione(input, output)

	// semplice funzione che rispedisce tutto quello che passa
	// dal canale di output sui socket delle connessioni
	go func() {
		for buffer := range output {
			for e := codaCiclica.Front(); e != nil; e = e.Next() {
				client := e.Value.(*Client)
				client.stream.WriteString(buffer)
				client.stream.Flush()
			}
		}
	}()

	var spegniti bool = false

	logs.AggiungiAzioneDiChiusura(func() {
		spegniti = true
	})

	for !spegniti {
		start := time.Now()

		quanti := len(readiness)

		for i := 0; i < quanti; i++ {
			clientAttivo := <-readiness
			var chiSonoString string

			if lastActiveUser != clientAttivo.user.ID {
				chiSonoString = strings.Join([]string{"\\U", strconv.Itoa(clientAttivo.user.ID), "\\"}, "")
				lastActiveUser = clientAttivo.user.ID
				database.MainConv.UtenteAttivo = lastActiveUser
			} else {
				chiSonoString = ""
			}
			chiSonoSSize := len(chiSonoString)

			//leggi cosa spedire
			var daLeggere int = clientAttivo.stream.Reader.Buffered()
			buffer := make([]rune, chiSonoSSize, daLeggere+chiSonoSSize)
			var err error
			var letto rune
			var size, i, j int
			for i, j = chiSonoSSize, 0; i < chiSonoSSize+daLeggere && j < daLeggere; i++ {
				letto, size, err = clientAttivo.stream.ReadRune()
				buffer = append(buffer, letto)
				j += size
				//fmt.Printf("#####size:_%v_ carattere:_%v_%v_\n",strconv.Itoa(size),string(buffer[i]),buffer[i])
				if err != nil {
					//TODO gestisci errore
					logs.Error("Errore nel leggere dalla rete")
					clientAttivo.blocco <- 1 //TODO dovresti chiudere il canale e tutto quanto
					clientAttivo.gestisciDisconnessione(database.MainConv)
					break
				}
			}
			clientAttivo.blocco <- 0

			//prepara il buffer da spedire
			for i := 0; i < chiSonoSSize; i++ {
				buffer[i] = []rune(chiSonoString)[i]
			}
			//			buffer = buffer[:i]

			//spedisci
			for i := 0; i < len(buffer); i++ {
				input <- buffer[i]
			}
		}

		duration := time.Since(start)
		if duration <= tempoDaAspettare {
			time.Sleep(tempoDaAspettare - duration)
		}
	}
}
Example #15
0
// Funzione che inizializza tutte le cose necessarie per collegare il
// database SQL, e lo collega
func CreateDataBaseSQL() error {
	var err error

	db, err = sql.Open("postgres", "dbname=thinkzoneDB user=thinkzone")
	if err != nil {
		logs.Error("Impossibile aprire il database")
		return err
	}

	/*
		for i := range createDbSqlScript {
			_, err = db.Exec(createDbSqlScript[i])
			if err != nil {
				if "result error: ERROR:  relation \"t_user\" already exists\n" != err.Error() {
					logs.Error("Impossibile creare le tabelle del database\nmotivo: _", err.Error(), "_")
					return err
				}
				break
			}

		}
	*/

	insertUserOp, err = db.Prepare(insertUser)
	if err != nil {
		//		logs.Error("Impossibile salvare gli utenti nel database\nmotivo: ", err.Error())
		return err
	}

	insertPostOp, err = db.Prepare(insertPost)
	if err != nil {
		//		logs.Error("Impossibile creare i post nel database\nmotivo: ", err.Error())
		return err
	}

	updatePostOp, err = db.Prepare(updatePost)
	if err != nil {
		//		logs.Error("Impossibile salvare i post nel database\nmotivo: ", err.Error())
		return err
	}

	insertConvOp, err = db.Prepare(insertConv)
	if err != nil {
		//		logs.Error("Impossibile salvare le conversazioni nel database\nmotivo: ", err.Error())
		return err
	}

	updateConvOp, err = db.Prepare(updateConv)
	if err != nil {
		return err
	}

	logs.AggiungiAzioneDiChiusura(func() {
		if insertUserOp != nil {
			insertUserOp.Close()
		}
		if insertConvOp != nil {
			err := MainConv.salvaTutteLeConversazioniSQL()
			if err != nil {
				logs.Error("Impossibile salvare tutti i post\nmotivo: ", err.Error())
			}
			insertConvOp.Close()
		}
		if insertPostOp != nil {
			insertPostOp.Close()
		}
		if updatePostOp != nil {
			updatePostOp.Close()
		}
		if updateConvOp != nil {
			updateConvOp.Close()
		}
		return
	})

	return nil

}