[PD2] Server done?
Co-authored-by: tsousa111 <tiagao2001@hotmail.com>
This commit is contained in:
parent
08a73a4f76
commit
49a29e43a7
66 changed files with 2777 additions and 5 deletions
323
Projs/PD2/internal/client/client.go
Normal file
323
Projs/PD2/internal/client/client.go
Normal file
|
@ -0,0 +1,323 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"PD1/internal/protocol"
|
||||
"PD1/internal/utils/cryptoUtils"
|
||||
"PD1/internal/utils/networking"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func Run() {
|
||||
var userFile string
|
||||
flag.StringVar(&userFile, "user", "userdata.p12", "Specify user data file")
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() == 0 {
|
||||
log.Fatalln("No command provided. Use 'help' for instructions.")
|
||||
}
|
||||
//Get user KeyStore
|
||||
password := readStdin("Insert keystore passphrase")
|
||||
clientKeyStore, err := cryptoUtils.LoadKeyStore(userFile, password)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
command := flag.Arg(0)
|
||||
switch command {
|
||||
case "send":
|
||||
if flag.NArg() != 3 {
|
||||
printError("MSG SERVICE: command error!")
|
||||
showHelp()
|
||||
os.Exit(1)
|
||||
}
|
||||
uid := flag.Arg(1)
|
||||
plainSubject := flag.Arg(2)
|
||||
plainBody := readStdin("Enter message content (limited to 1000 bytes):")
|
||||
err := sendCommand(clientKeyStore, plainSubject, plainBody, uid)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
case "askqueue":
|
||||
if flag.NArg() > 3 {
|
||||
printError("MSG SERVICE: command error!")
|
||||
showHelp()
|
||||
os.Exit(1)
|
||||
}
|
||||
pageInput := flag.Arg(1)
|
||||
page := 1
|
||||
if pageInput != "" {
|
||||
if val, err := strconv.Atoi(pageInput); err == nil {
|
||||
page = max(1, val)
|
||||
}
|
||||
}
|
||||
pageSizeInput := flag.Arg(2)
|
||||
pageSize := 5
|
||||
if pageSizeInput != "" {
|
||||
if val, err := strconv.Atoi(pageSizeInput); err == nil {
|
||||
pageSize = max(1, val)
|
||||
}
|
||||
}
|
||||
|
||||
err := askQueueCommand(clientKeyStore, page, pageSize)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
case "getmsg":
|
||||
if flag.NArg() < 2 {
|
||||
printError("MSG SERVICE: command error!")
|
||||
showHelp()
|
||||
os.Exit(1)
|
||||
}
|
||||
numString := flag.Arg(1)
|
||||
num, err := strconv.Atoi(numString)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
err = getMsgCommand(clientKeyStore, num)
|
||||
if err != nil {
|
||||
printError(err.Error())
|
||||
}
|
||||
|
||||
case "help":
|
||||
showHelp()
|
||||
|
||||
default:
|
||||
printError("MSG SERVICE: command error!")
|
||||
showHelp()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func sendCommand(clientKeyStore cryptoUtils.KeyStore, plainSubject, plainBody, uid string) error {
|
||||
//Turn content to bytes
|
||||
plainSubjectBytes, err := Marshal(plainSubject)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plainBodyBytes, err := Marshal(plainBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cl, err := networking.NewClient[protocol.Packet](&clientKeyStore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cl.Connection.Conn.Close()
|
||||
|
||||
receiverCert, err := getUserCert(cl, clientKeyStore, uid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
subject, err := clientKeyStore.EncryptMessageContent(receiverCert, plainSubjectBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body, err := clientKeyStore.EncryptMessageContent(receiverCert, plainBodyBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sendMsgPacket := protocol.NewSendMsgPacket(uid, subject, body)
|
||||
if err := cl.Connection.Send(sendMsgPacket); err != nil {
|
||||
return err
|
||||
}
|
||||
answerSendMsg, err := cl.Connection.Receive()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if answerSendMsg.Flag == protocol.FlagReportError {
|
||||
reportError, err := protocol.UnmarshalReportError(answerSendMsg.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return errors.New(reportError.ErrorMessage)
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func getMsgCommand(clientKeyStore cryptoUtils.KeyStore, num int) error {
|
||||
cl, err := networking.NewClient[protocol.Packet](&clientKeyStore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cl.Connection.Conn.Close()
|
||||
packet := protocol.NewGetMsgPacket(num)
|
||||
if err := cl.Connection.Send(packet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
receivedMsgPacket, err := cl.Connection.Receive()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if receivedMsgPacket.Flag == protocol.FlagReportError {
|
||||
reportError, err := protocol.UnmarshalReportError(receivedMsgPacket.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return errors.New(reportError.ErrorMessage)
|
||||
}
|
||||
answerGetMsg, err := protocol.UnmarshalAnswerGetMsg(receivedMsgPacket.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
senderCert, err := getUserCert(cl, clientKeyStore, answerGetMsg.FromUID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decSubjectBytes, err := clientKeyStore.DecryptMessageContent(senderCert, answerGetMsg.Subject)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decBodyBytes, err := clientKeyStore.DecryptMessageContent(senderCert, answerGetMsg.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
subject, err := Unmarshal(decSubjectBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body, err := Unmarshal(decBodyBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
message := newClientMessage(answerGetMsg.FromUID, answerGetMsg.ToUID, subject, body, answerGetMsg.Timestamp)
|
||||
showMessage(message)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getUserCert(cl networking.Client[protocol.Packet], keyStore cryptoUtils.KeyStore, uid string) (*x509.Certificate, error) {
|
||||
getUserCertPacket := protocol.NewGetUserCertPacket(uid)
|
||||
if err := cl.Connection.Send(getUserCertPacket); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var answerGetUserCertPacket *protocol.Packet
|
||||
answerGetUserCertPacket, err := cl.Connection.Receive()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if answerGetUserCertPacket.Flag == protocol.FlagReportError {
|
||||
reportError, err := protocol.UnmarshalReportError(answerGetUserCertPacket.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, errors.New(reportError.ErrorMessage)
|
||||
}
|
||||
answerGetUserCert, err := protocol.UnmarshalAnswerGetUserCert(answerGetUserCertPacket.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userCert, err := x509.ParseCertificate(answerGetUserCert.Certificate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := keyStore.CheckCert(userCert, uid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return userCert, nil
|
||||
}
|
||||
|
||||
func getManyMessagesInfo(cl networking.Client[protocol.Packet], keyStore cryptoUtils.KeyStore) (protocol.AnswerGetUnreadMsgsInfo, map[string]*x509.Certificate, error) {
|
||||
answerGetUnreadMsgsInfoPacket, err := cl.Connection.Receive()
|
||||
if err != nil {
|
||||
return protocol.NewAnswerGetUnreadMsgsInfo(0, 0, nil), nil, err
|
||||
}
|
||||
if answerGetUnreadMsgsInfoPacket.Flag == protocol.FlagReportError {
|
||||
reportError, err := protocol.UnmarshalReportError(answerGetUnreadMsgsInfoPacket.Body)
|
||||
if err != nil {
|
||||
return protocol.AnswerGetUnreadMsgsInfo{}, nil, err
|
||||
}
|
||||
return protocol.NewAnswerGetUnreadMsgsInfo(0, 0, nil), nil, errors.New(reportError.ErrorMessage)
|
||||
}
|
||||
answerGetUnreadMsgsInfo, err := protocol.UnmarshalAnswerGetUnreadMsgsInfo(answerGetUnreadMsgsInfoPacket.Body)
|
||||
if err != nil {
|
||||
return protocol.AnswerGetUnreadMsgsInfo{}, nil, err
|
||||
}
|
||||
|
||||
//Create Set of needed certificates
|
||||
senderSet := map[string]bool{}
|
||||
for _, messageInfo := range answerGetUnreadMsgsInfo.MessagesInfo {
|
||||
senderSet[messageInfo.FromUID] = true
|
||||
}
|
||||
certificatesMap := map[string]*x509.Certificate{}
|
||||
//Get senders' certificates
|
||||
for senderUID := range senderSet {
|
||||
senderCert, err := getUserCert(cl, keyStore, senderUID)
|
||||
if err == nil {
|
||||
certificatesMap[senderUID] = senderCert
|
||||
}
|
||||
}
|
||||
return answerGetUnreadMsgsInfo, certificatesMap, nil
|
||||
}
|
||||
|
||||
func askQueueCommand(clientKeyStore cryptoUtils.KeyStore, page int, pageSize int) error {
|
||||
cl, err := networking.NewClient[protocol.Packet](&clientKeyStore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cl.Connection.Conn.Close()
|
||||
return askQueueRec(cl, clientKeyStore, page, pageSize)
|
||||
}
|
||||
|
||||
func askQueueRec(cl networking.Client[protocol.Packet], clientKeyStore cryptoUtils.KeyStore, page int, pageSize int) error {
|
||||
|
||||
requestUnreadMsgsQueuePacket := protocol.NewGetUnreadMsgsInfoPacket(page, pageSize)
|
||||
if err := cl.Connection.Send(requestUnreadMsgsQueuePacket); err != nil {
|
||||
return err
|
||||
}
|
||||
unreadMsgsInfo, certificates, err := getManyMessagesInfo(cl, clientKeyStore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var clientMessages []ClientMessageInfo
|
||||
for _, message := range unreadMsgsInfo.MessagesInfo {
|
||||
var clientMessageInfo ClientMessageInfo
|
||||
senderCert, ok := certificates[message.FromUID]
|
||||
if !ok {
|
||||
clientMessageInfo = newClientMessageInfo(message.Num,
|
||||
message.FromUID,
|
||||
"",
|
||||
message.Timestamp,
|
||||
errors.New("certificate needed to decrypt not received"))
|
||||
clientMessages = append(clientMessages, clientMessageInfo)
|
||||
continue
|
||||
}
|
||||
decryptedSubjectBytes, err := clientKeyStore.DecryptMessageContent(senderCert, message.Subject)
|
||||
if err != nil {
|
||||
clientMessageInfo = newClientMessageInfo(message.Num, message.FromUID, "", message.Timestamp, err)
|
||||
clientMessages = append(clientMessages, clientMessageInfo)
|
||||
continue
|
||||
}
|
||||
subject, err := Unmarshal(decryptedSubjectBytes)
|
||||
if err != nil {
|
||||
clientMessageInfo = newClientMessageInfo(message.Num, message.FromUID, "", message.Timestamp, err)
|
||||
clientMessages = append(clientMessages, clientMessageInfo)
|
||||
continue
|
||||
}
|
||||
clientMessageInfo = newClientMessageInfo(message.Num, message.FromUID, subject, message.Timestamp, nil)
|
||||
clientMessages = append(clientMessages, clientMessageInfo)
|
||||
}
|
||||
//Sort the messages
|
||||
sort.Slice(clientMessages, func(i, j int) bool {
|
||||
return clientMessages[i].Num > clientMessages[j].Num
|
||||
})
|
||||
|
||||
action := showMessagesInfo(unreadMsgsInfo.Page, unreadMsgsInfo.NumPages, clientMessages)
|
||||
switch action {
|
||||
case -1:
|
||||
return askQueueRec(cl, clientKeyStore, max(1, unreadMsgsInfo.Page-1), pageSize)
|
||||
case 1:
|
||||
return askQueueRec(cl, clientKeyStore, max(1, unreadMsgsInfo.Page+1), pageSize)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
47
Projs/PD2/internal/client/datastore.go
Normal file
47
Projs/PD2/internal/client/datastore.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ClientMessage struct {
|
||||
FromUID string
|
||||
ToUID string
|
||||
Subject string
|
||||
Body string
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
type ClientMessageInfo struct {
|
||||
Num int
|
||||
FromUID string
|
||||
Timestamp time.Time
|
||||
Subject string
|
||||
decryptError error
|
||||
}
|
||||
|
||||
func newClientMessage(fromUID string, toUID string, subject string, body string, timestamp time.Time) ClientMessage {
|
||||
return ClientMessage{FromUID: fromUID, ToUID: toUID, Subject: subject, Body: body, Timestamp: timestamp}
|
||||
}
|
||||
|
||||
func newClientMessageInfo(num int, fromUID string, subject string, timestamp time.Time, err error) ClientMessageInfo {
|
||||
return ClientMessageInfo{Num: num, FromUID: fromUID, Subject: subject, Timestamp: timestamp, decryptError: err}
|
||||
}
|
||||
|
||||
func Marshal(data any) ([]byte, error) {
|
||||
subject, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return subject, nil
|
||||
}
|
||||
|
||||
func Unmarshal(data []byte) (string, error) {
|
||||
var c string
|
||||
err := json.Unmarshal(data, &c)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return c, nil
|
||||
}
|
92
Projs/PD2/internal/client/interface.go
Normal file
92
Projs/PD2/internal/client/interface.go
Normal file
|
@ -0,0 +1,92 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func readStdin(message string) string {
|
||||
fmt.Println(message)
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
scanner.Scan()
|
||||
return scanner.Text()
|
||||
}
|
||||
|
||||
func printError(err string) {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
|
||||
func showHelp() {
|
||||
fmt.Println("Comandos da aplicação cliente:")
|
||||
fmt.Println("-user <FNAME>: Especifica o ficheiro com dados do utilizador. Por omissão, será assumido que esse ficheiro é userdata.p12.")
|
||||
fmt.Println("send <UID> <SUBJECT>: Envia uma mensagem com assunto <SUBJECT> destinada ao utilizador com identificador <UID>. O conteúdo da mensagem será lido do stdin, e o tamanho deve ser limitado a 1000 bytes.")
|
||||
fmt.Println("askqueue: Solicita ao servidor que lhe envie a lista de mensagens não lidas da queue do utilizador.")
|
||||
fmt.Println("getmsg <NUM>: Solicita ao servidor o envio da mensagem da sua queue com número <NUM>.")
|
||||
fmt.Println("help: Imprime instruções de uso do programa.")
|
||||
}
|
||||
|
||||
func showMessagesInfo(page int, numPages int, messages []ClientMessageInfo) int {
|
||||
if messages == nil {
|
||||
fmt.Println("No unread messages in the queue")
|
||||
return 0
|
||||
}
|
||||
for _, message := range messages {
|
||||
if message.decryptError != nil {
|
||||
fmt.Printf("ERROR: %v:%v:%v:", message.Num, message.FromUID, message.Timestamp)
|
||||
fmt.Println(message.decryptError)
|
||||
} else {
|
||||
fmt.Printf("%v:%v:%v:%v\n", message.Num, message.FromUID, message.Timestamp, message.Subject)
|
||||
}
|
||||
}
|
||||
fmt.Printf("Page %v/%v\n", page, numPages)
|
||||
return messagesInfoPageNavigation(page, numPages)
|
||||
}
|
||||
|
||||
func messagesInfoPageNavigation(page int, numPages int) int {
|
||||
var action string
|
||||
|
||||
switch page {
|
||||
case 1:
|
||||
if page == numPages {
|
||||
return 0
|
||||
} else {
|
||||
action = readStdin("Actions: quit/next")
|
||||
}
|
||||
case numPages:
|
||||
action = readStdin("Actions: prev/quit")
|
||||
default:
|
||||
action = readStdin("prev/quit/next")
|
||||
}
|
||||
|
||||
switch strings.ToLower(action) {
|
||||
case "prev":
|
||||
if page == 1 {
|
||||
fmt.Println("Unavailable action: Already in first page")
|
||||
messagesInfoPageNavigation(page, numPages)
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
case "quit":
|
||||
return 0
|
||||
case "next":
|
||||
if page == numPages {
|
||||
fmt.Println("Unavailable action: Already in last page")
|
||||
messagesInfoPageNavigation(page, numPages)
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
default:
|
||||
fmt.Println("Unknown action")
|
||||
messagesInfoPageNavigation(page, numPages)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func showMessage(message ClientMessage) {
|
||||
fmt.Printf("From: %s\n", message.FromUID)
|
||||
fmt.Printf("To: %s\n", message.ToUID)
|
||||
fmt.Printf("Subject: %s\n", message.Subject)
|
||||
fmt.Printf("Body: %s\n", message.Body)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue