commit e0dd0c850ade81669a3fd794d644a77390881950 Author: Hizenberg469 Date: Mon Nov 4 21:51:00 2024 +0530 final implementation diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..400e7c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.classpath +/.metadata/ +.project +/.settings/ +/target/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..11e4304 --- /dev/null +++ b/pom.xml @@ -0,0 +1,157 @@ + + 4.0.0 + + com + secure_mailer + 0.0.1-SNAPSHOT + jar + + secure_mailer + http://maven.apache.org + + + com.secure_mailer.backend.MainClass + UTF-8 + + + + + + + src/main/resources + + + **/*.png + **/*.css + **/*.fxml + **/*.ttf + **/*.otf + **/*.default.address.map + + + + **/test/** + + + + + + + org.openjfx + javafx-maven-plugin + 0.0.5 + + + + run + + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.5.0 + + com.secure_mailer.App + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 17 + 17 + + + + + + + + + junit + junit + 3.8.1 + test + + + + commons-codec + commons-codec + 1.15 + + + + com.h2database + h2 + 2.1.214 + + + + com.gluonhq + gluonfx-maven-plugin + 1.0.24 + + + + + + org.openjfx + javafx-controls + 23 + + + org.openjfx + javafx-fxml + 23 + + + org.openjfx + javafx-graphics + 23 + + + + org.openjfx + javafx-media + 23 + + + org.openjfx + javafx-swing + 23 + + + org.openjfx + javafx-web + 23 + + + + + javax.mail + javax.mail-api + 1.6.2 + + + com.sun.mail + javax.mail + 1.6.2 + + + + + javax.activation + activation + 1.1.1 + + + + + + diff --git a/src/main/java/com/secure_mailer/App.java b/src/main/java/com/secure_mailer/App.java new file mode 100644 index 0000000..3b0ed79 --- /dev/null +++ b/src/main/java/com/secure_mailer/App.java @@ -0,0 +1,19 @@ +package com.secure_mailer; + +import com.secure_mailer.backend.EmailManager; +import com.secure_mailer.frontend.ViewGenerator; +import javafx.application.Application; +import javafx.stage.Stage; + +public class App extends Application { + + public static void main(String... args) { + launch(args); + } + + @Override + public void start(Stage primaryStage) throws Exception { + EmailManager em = new EmailManager(); + ViewGenerator.initialize(em); + } +} diff --git a/src/main/java/com/secure_mailer/backend/BaseController.java b/src/main/java/com/secure_mailer/backend/BaseController.java new file mode 100644 index 0000000..920d206 --- /dev/null +++ b/src/main/java/com/secure_mailer/backend/BaseController.java @@ -0,0 +1,18 @@ +package com.secure_mailer.backend; + +public class BaseController { + + private String fxml; + private EmailManager emailManager; + protected String emailID; + + public BaseController(String fxml, EmailManager emailManager) { + this.fxml = fxml; + this.emailManager = emailManager; + } + + public String getEmailID() { return this.emailID; } + + public String getFXML() { return fxml; } + public EmailManager getEmailManager() { return emailManager; } +} \ No newline at end of file diff --git a/src/main/java/com/secure_mailer/backend/DatabaseSetup.java b/src/main/java/com/secure_mailer/backend/DatabaseSetup.java new file mode 100644 index 0000000..c64220b --- /dev/null +++ b/src/main/java/com/secure_mailer/backend/DatabaseSetup.java @@ -0,0 +1,37 @@ +package com.secure_mailer.backend; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; + +public class DatabaseSetup { + private static final String DB_URL = "jdbc:h2:~/email_client_db"; // URL for H2 database + private static final String USER = "sa"; // Default user for H2 + private static final String PASSWORD = ""; // Default password for H2 + + /** + * Sets up the database and creates the SecretCodes table if it does not exist. + */ + public static void setupDatabase() { + // Using try-with-resources to ensure resources are closed properly + try (Connection connection = DriverManager.getConnection(DB_URL, USER, PASSWORD); + Statement stmt = connection.createStatement()) { + + // SQL statement to create the SecretCodes table if it doesn't exist + String createTable = "CREATE TABLE IF NOT EXISTS SecretCodes (" + + "email VARCHAR(255) PRIMARY KEY, " + + "secretCode VARCHAR(255) NOT NULL)"; + + stmt.execute(createTable); // Execute the create table statement + System.out.println("Database setup completed successfully."); + + } catch (SQLException e) { + System.err.println("Database setup failed: " + e.getMessage()); + } + } + + public static void main(String[] args) { + setupDatabase(); // Call the setupDatabase method + } +} \ No newline at end of file diff --git a/src/main/java/com/secure_mailer/backend/EmailAccount.java b/src/main/java/com/secure_mailer/backend/EmailAccount.java new file mode 100644 index 0000000..ccf59e3 --- /dev/null +++ b/src/main/java/com/secure_mailer/backend/EmailAccount.java @@ -0,0 +1,52 @@ +package com.secure_mailer.backend; + +import java.util.ArrayList; +import java.util.Properties; +import javax.mail.Folder; +import javax.mail.Session; +import javax.mail.Store; + +public class EmailAccount { + + private String address; + private String password; + private Properties properties; + private Store store; + private Session session; + private ArrayList folders = new ArrayList(); + + public EmailAccount(String address, String password) { + this.address = address; + this.password = password; + properties = new Properties(); + + properties.put("incomingHost", "peachy.in.net"); + properties.put("mail.store.protocol", "imaps"); + properties.put("mail.transport.protocol", "smtps"); + properties.put("mail.smtps.host", "peachy.in.net"); + properties.put("mail.smtps.auth", "true"); + properties.put("outgoingHost", "peachy.in.net"); + + //properties.put("mail.imap.host", "peachy.in.net"); // Replace with your IMAP server + properties.put("mail.imaps.port", "993"); // Common port for IMAP SSL + properties.put("mail.imaps.ssl.enable", "true"); + properties.put("mail.imaps.auth", "true"); + + //properties.put("mail.smtp.host", "peachy.in.net"); // Replace with your SMTP server + properties.put("mail.smtps.port", "465"); // Common port for SMTP SSL; check with your server if different + properties.put("mail.smtps.auth", "true"); + properties.put("mail.smtps.ssl.enable", "true"); + //properties.put("mail.smtp.starttls.enable", "true"); // Optional if the server supports STARTTLS + } + + public String getAddress() {return address;} + public String getPassword() {return password;} + public Properties getProperties() {return properties;} + public Store getStore() {return store;} + public Session getSession() {return session;} + public ArrayList getFolders() {return folders;} + public void setProperties(Properties properties) {this.properties = properties;} + public void setStore(Store store) {this.store = store;} + public void setSession(Session session) {this.session = session;} + @Override public String toString() {return address;} +} \ No newline at end of file diff --git a/src/main/java/com/secure_mailer/backend/EmailManager.java b/src/main/java/com/secure_mailer/backend/EmailManager.java new file mode 100644 index 0000000..2c92d29 --- /dev/null +++ b/src/main/java/com/secure_mailer/backend/EmailManager.java @@ -0,0 +1,36 @@ +package com.secure_mailer.backend; + +import com.secure_mailer.backend.service.FetchFolderService; +import com.secure_mailer.backend.service.LoginService; +import com.secure_mailer.frontend.FolderTreeItem; +import com.secure_mailer.frontend.EmailMessage; +import com.secure_mailer.frontend.ViewGenerator; + +public class EmailManager { + + private static final String LOCATION_OF_DOWNLOADS = System.getProperty("user.home") + "/Downloads/"; + private EmailAccount emailAccount; + private FolderTreeItem foldersRoot = new FolderTreeItem(); + private FolderTreeItem selectedFolder; + private EmailMessage selectedMessage; + + public void login(String account, String password) { + emailAccount = new EmailAccount(account, password); + new LoginService(emailAccount); + } + + public void fetchFolders() { + FetchFolderService ffs = new FetchFolderService(emailAccount.getStore(), foldersRoot, emailAccount.getFolders()); + ffs.setOnSucceeded(e -> ViewGenerator.showMainWindow(this)); + ffs.start(); + } + + public static String getDownloadPath() { return LOCATION_OF_DOWNLOADS; } + public FolderTreeItem getFolderRoot() { return foldersRoot; } + public void setSelectedFolder(FolderTreeItem item) { selectedFolder = item; } + public void setSelectedMessage(EmailMessage emailMessage) { this.selectedMessage = emailMessage; } + public EmailMessage getSelectedMessage() { return selectedMessage; } + public FolderTreeItem getSelectedFolder() { return selectedFolder; } + public EmailAccount getEmailAccount() { return emailAccount; } + +} \ No newline at end of file diff --git a/src/main/java/com/secure_mailer/backend/EmailSecretCode.java b/src/main/java/com/secure_mailer/backend/EmailSecretCode.java new file mode 100644 index 0000000..7f2214f --- /dev/null +++ b/src/main/java/com/secure_mailer/backend/EmailSecretCode.java @@ -0,0 +1,30 @@ +package com.secure_mailer.backend; + +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; + +public class EmailSecretCode { + private final StringProperty email; + private final StringProperty secretCode; + + public EmailSecretCode(String email, String secretCode) { + this.email = new SimpleStringProperty(email); + this.secretCode = new SimpleStringProperty(secretCode); + } + + public String getEmail() { + return email.get(); + } + + public StringProperty emailProperty() { + return email; + } + + public String getSecretCode() { + return secretCode.get(); + } + + public StringProperty secretCodeProperty() { + return secretCode; + } +} diff --git a/src/main/java/com/secure_mailer/backend/LoginController.java b/src/main/java/com/secure_mailer/backend/LoginController.java new file mode 100644 index 0000000..cf38fa5 --- /dev/null +++ b/src/main/java/com/secure_mailer/backend/LoginController.java @@ -0,0 +1,31 @@ +package com.secure_mailer.backend; + +import javafx.fxml.FXML; +import javafx.scene.control.PasswordField; +import javafx.scene.control.TextField; + +public class LoginController extends BaseController{ + + @FXML private TextField emailField; + @FXML private PasswordField passwordField; + private EmailManager em; + + public LoginController(EmailManager emailManager) { + super("/fxml/login.fxml", emailManager); + em = emailManager; + } + + @FXML void loginAction() { + String email = emailField.getText(); + String password = passwordField.getText(); + + saveEmailID(email); + + em.login(email, password); + em.fetchFolders(); + } + + private void saveEmailID(String username) { + this.emailID = username; + } +} \ No newline at end of file diff --git a/src/main/java/com/secure_mailer/backend/SecretCodeController.java b/src/main/java/com/secure_mailer/backend/SecretCodeController.java new file mode 100644 index 0000000..df050ea --- /dev/null +++ b/src/main/java/com/secure_mailer/backend/SecretCodeController.java @@ -0,0 +1,96 @@ +package com.secure_mailer.backend; + +import javafx.fxml.FXML; +import javafx.scene.control.*; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import java.sql.SQLException; + +public class SecretCodeController { + + @FXML private TextField emailField; + @FXML private TextField secretCodeField; + @FXML private TableView secretCodesTable; + @FXML private TableColumn emailColumn; + @FXML private TableColumn secretCodeColumn; + + private ObservableList dataList; + + public void initialize() { + dataList = FXCollections.observableArrayList(); + secretCodesTable.setItems(dataList); + emailColumn.setCellValueFactory(cellData -> cellData.getValue().emailProperty()); + secretCodeColumn.setCellValueFactory(cellData -> cellData.getValue().secretCodeProperty()); + loadAllEntries(); + } + + @FXML + private void handleAdd() { + String email = emailField.getText(); + String secretCode = secretCodeField.getText(); + try { + SecretCodeDAO.addSecretCode(email, secretCode); + //dataList.add(new EmailSecretCode(email, secretCode)); + clearFields(); + } catch (SQLException e) { + showAlert("Error", "Could not add secret code."); + e.printStackTrace(); + } + } + + @FXML + private void handleUpdate() { + String email = emailField.getText(); + String secretCode = secretCodeField.getText(); + try { + SecretCodeDAO.updateSecretCode(email, secretCode); + //loadAllEntries(); + clearFields(); + } catch (SQLException e) { + showAlert("Error", "Could not update secret code."); + e.printStackTrace(); + } + } + + @FXML + private void handleDelete() { + String email = emailField.getText(); + try { + SecretCodeDAO.deleteSecretCode(email); + //loadAllEntries(); + clearFields(); + } catch (SQLException e) { + showAlert("Error", "Could not delete secret code."); + e.printStackTrace(); + } + } + + @FXML + private void handleViewAll() { + loadAllEntries(); + } + + private void loadAllEntries() { + try { + dataList.clear(); + for (EmailSecretCode entry : SecretCodeDAO.getAllEntries()) { + dataList.add(entry); + } + } catch (SQLException e) { + showAlert("Error", "Could not load entries."); + e.printStackTrace(); + } + } + + private void clearFields() { + emailField.clear(); + secretCodeField.clear(); + } + + private void showAlert(String title, String message) { + Alert alert = new Alert(Alert.AlertType.INFORMATION); + alert.setTitle(title); + alert.setContentText(message); + alert.show(); + } +} \ No newline at end of file diff --git a/src/main/java/com/secure_mailer/backend/SecretCodeDAO.java b/src/main/java/com/secure_mailer/backend/SecretCodeDAO.java new file mode 100644 index 0000000..04426ff --- /dev/null +++ b/src/main/java/com/secure_mailer/backend/SecretCodeDAO.java @@ -0,0 +1,73 @@ +package com.secure_mailer.backend; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public class SecretCodeDAO { + private static final String DB_URL = "jdbc:h2:~/email_client_db"; + + public static void addSecretCode(String email, String secretCode) throws SQLException { + String sql = "INSERT INTO SecretCodes (email, secretCode) VALUES (?, ?)"; + + try (Connection connection = DriverManager.getConnection(DB_URL, "sa", ""); + PreparedStatement stmt = connection.prepareStatement(sql)) { + stmt.setString(1, email); + stmt.setString(2, secretCode); + stmt.executeUpdate(); + System.out.println("Secret code added."); + } + } + + public static String getSecretCode(String email) throws SQLException { + String sql = "SELECT secretCode FROM SecretCodes WHERE email = ?"; + try (Connection connection = DriverManager.getConnection(DB_URL, "sa", ""); + PreparedStatement stmt = connection.prepareStatement(sql)) { + stmt.setString(1, email); + ResultSet rs = stmt.executeQuery(); + return rs.next() ? rs.getString("secretCode") : null; + } + } + + public static void updateSecretCode(String email, String newSecretCode) throws SQLException { + String sql = "UPDATE SecretCodes SET secretCode = ? WHERE email = ?"; + try (Connection connection = DriverManager.getConnection(DB_URL, "sa", ""); + PreparedStatement stmt = connection.prepareStatement(sql)) { + stmt.setString(1, newSecretCode); + stmt.setString(2, email); + stmt.executeUpdate(); + System.out.println("Secret code updated."); + } + } + + public static void deleteSecretCode(String email) throws SQLException { + String sql = "DELETE FROM SecretCodes WHERE email = ?"; + try (Connection connection = DriverManager.getConnection(DB_URL, "sa", ""); + PreparedStatement stmt = connection.prepareStatement(sql)) { + stmt.setString(1, email); + stmt.executeUpdate(); + System.out.println("Secret code deleted."); + } + } + + public static List getAllEntries() throws SQLException { + String sql = "SELECT * FROM SecretCodes"; + List list = new ArrayList<>(); + + try (Connection connection = DriverManager.getConnection(DB_URL, "sa", ""); + PreparedStatement stmt = connection.prepareStatement(sql); + ResultSet rs = stmt.executeQuery()) { + + while (rs.next()) { + String email = rs.getString("email"); + String secretCode = rs.getString("secretCode"); + list.add(new EmailSecretCode(email, secretCode)); + } + } + return list; + } +} diff --git a/src/main/java/com/secure_mailer/backend/ValidatorClient.java b/src/main/java/com/secure_mailer/backend/ValidatorClient.java new file mode 100644 index 0000000..d237f49 --- /dev/null +++ b/src/main/java/com/secure_mailer/backend/ValidatorClient.java @@ -0,0 +1,260 @@ +package com.secure_mailer.backend; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.*; +import java.sql.SQLException; +import java.io.*; + +import org.apache.commons.codec.binary.Base32; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.time.Instant; + +import javafx.animation.PauseTransition; +import javafx.application.Platform; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.util.Duration; + +public class ValidatorClient { + + private Socket skt; + + private String fromEmailId; + private String toEmailId; + private String emlHash; + private String timeStamp; + private String authCode; + + + public ValidatorClient() { + try { + skt = new Socket(InetAddress.getByName(null), 2000); + } catch (IOException e) { + // TODO Auto-generated catch block + Platform.runLater(() -> showAlert("Error", "Failed to connet with server.")); + e.printStackTrace(); + } + + } + + + public ValidatorClient(String host, int port) { + try { + skt = new Socket(host, port); + } catch (IOException e) { + // TODO Auto-generated catch block + Platform.runLater(() -> showAlert("Error", "Failed to connet with server.")); + e.printStackTrace(); + } + } + + public void closeValidatorClient() { + try { + skt.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + + public void sendRecord(String fromEmailId, String toEmailId, + String emlHash ) throws SQLException { + + this.fromEmailId = fromEmailId; + this.toEmailId = toEmailId; + this.emlHash = emlHash; + + PrintStream ps = null; + + try { + ps=new PrintStream(skt.getOutputStream()); + + + String secretKey = SecretCodeDAO.getSecretCode(toEmailId); + long timeStep = Instant.now().getEpochSecond() / 30; + this.timeStamp = Long.toString(timeStep); + this.authCode = generateTOTP(secretKey, timeStep); + + String[] data = new String[] { + this.fromEmailId, + this.toEmailId, + this.timeStamp, + this.authCode, + this.emlHash + }; + + System.out.println(this.fromEmailId); + System.out.println(this.toEmailId); + System.out.println(this.timeStamp); + System.out.println(this.authCode); + System.out.println(this.emlHash); + + String packet = formPacket(data); + + //Packet ready. + packet = String.valueOf(0) + packet; + + //Send the packet. + ps.println(packet); + + + // If successful, show the alert on the JavaFX thread + Platform.runLater(() -> showAlert("Message Sent", "Your message has been sent successfully!")); + + } catch(SQLException e) { + + Platform.runLater(() -> showAlert("Error", "Failed to retrieve Secret key.")); + e.printStackTrace(); + } + catch (IOException e) { + // TODO Auto-generated catch block + Platform.runLater(() -> showAlert("Error", "Failed to send the message.")); + e.printStackTrace(); + } + + + + } + + public boolean bringCode(String fromEmailId, String toEmailId, String emlHash) { + + this.fromEmailId = fromEmailId; + this.toEmailId = toEmailId; + this.emlHash = emlHash; + + PrintStream ps = null; + BufferedReader br= null; + + try { + ps=new PrintStream(skt.getOutputStream()); + br=new BufferedReader(new InputStreamReader(skt.getInputStream())); + + String[] data = new String[] { + this.emlHash + }; + + + String packet = formPacket(data); + + //Packet ready. + packet = String.valueOf(1) + packet; + + //Send the size of packet. + //ps.println(packet.length()); + + //Send the packet. + ps.println(packet); + + //Receive packet. + + String msg = br.readLine(); + + //Parse the packet. + int i = 0; + String num = ""; + for(; i < msg.length() ; i++ ) { + if( msg.charAt(i) != ':' ) + num += Character.toString(msg.charAt(i)); + else + break; + } + + + if( num.equals("420") ) { + Platform.runLater(() -> showAlert("Error", "Email record not found on server.")); + return false; + } + + this.timeStamp = num; + i = i + 1; + num = ""; + for(; i < msg.length() ; i++ ) { + num += Character.toString(msg.charAt(i)); + } + this.authCode = num; + + String secretKey = SecretCodeDAO.getSecretCode(fromEmailId); + String totp = generateTOTP( secretKey, Long.parseLong(this.timeStamp)); + + + + return (totp == this.authCode); + + } catch(SQLException e) { + + Platform.runLater(() -> showAlert("Error", "Failed to retrieve Secret key.")); + e.printStackTrace(); + return false; + } catch (IOException e) { + // TODO Auto-generated catch block + Platform.runLater(() -> showAlert("Error", "Failed to send communicate with server for authentication.")); + e.printStackTrace(); + return false; + } + + } + + private String formPacket(String[] data) { + + int dataSize = data.length; + + String packet = ""; + int stringSize; + for(int i = 0 ; i < dataSize ; i++ ) { + stringSize = data[i].length(); + packet += String.valueOf(stringSize) + ":" + data[i]; + } + + return packet; + } + + + public String generateTOTP(String secretKey, long timeStep) { + try { + Base32 base32 = new Base32(); + byte[] bytes = base32.decode(secretKey); + SecretKeySpec secretKeySpec = new SecretKeySpec(bytes, "HmacSHA1"); + Mac mac = Mac.getInstance("HmacSHA1"); + mac.init(secretKeySpec); + + // Current Unix time / 30 (TOTP time step) + //long timeStep = Instant.now().getEpochSecond() / 30; + byte[] data = new byte[8]; + for (int i = 7; timeStep > 0; i--) { + data[i] = (byte) (timeStep & 0xFF); + timeStep >>= 8; + } + + byte[] hash = mac.doFinal(data); + int offset = hash[hash.length - 1] & 0xF; + int otp = ((hash[offset] & 0x7F) << 24 | (hash[offset + 1] & 0xFF) << 16 | + (hash[offset + 2] & 0xFF) << 8 | (hash[offset + 3] & 0xFF)) % 1_000_000; + + return String.format("%06d", otp); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void showAlert(String title, String content) { + Alert alert = new Alert(AlertType.INFORMATION); + alert.setTitle(title); + alert.setHeaderText(null); + alert.setContentText(content); + alert.show(); + + // Create a PauseTransition for 10 seconds + PauseTransition delay = new PauseTransition(Duration.seconds(10)); + delay.setOnFinished(event -> alert.close()); // Close alert after 10 seconds + delay.play(); // Start the timer + } + +} + + + + diff --git a/src/main/java/com/secure_mailer/backend/service/EmailSendingService.java b/src/main/java/com/secure_mailer/backend/service/EmailSendingService.java new file mode 100644 index 0000000..a00aa23 --- /dev/null +++ b/src/main/java/com/secure_mailer/backend/service/EmailSendingService.java @@ -0,0 +1,106 @@ +package com.secure_mailer.backend.service; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import javax.activation.DataHandler; +import javax.activation.DataSource; +import javax.activation.FileDataSource; +import javax.mail.BodyPart; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.Multipart; +import javax.mail.Transport; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; +import com.secure_mailer.backend.EmailAccount; +import javafx.concurrent.Service; +import javafx.concurrent.Task; + +import javax.crypto.SecretKey; +import java.util.Base64; + +public class EmailSendingService extends Service { + + private EmailAccount emailAccount; + private String subject; + private String recipient; + private String content; + private List attachments = new ArrayList(); + //private SecretKey sKey; + + class SecretKeyToString { + public static String secretKeyToString(SecretKey secretKey) { + // Convert SecretKey to byte array and then to Base64 string + byte[] encodedKey = secretKey.getEncoded(); + return Base64.getEncoder().encodeToString(encodedKey); + } + } + + public EmailSendingService(EmailAccount emailAccount, String subject, String recipient, String content, List list + ,SecretKey sKey + ) { + this.emailAccount = emailAccount; + this.subject = subject; + this.recipient = recipient; + this.content = content; + this.attachments = list; + //this.sKey = sKey; + } + + @Override + protected Task createTask() { + return new Task() { + @Override + protected Void call() throws Exception { + try { + + MimeMessage mimeMessage = new MimeMessage(emailAccount.getSession()); + mimeMessage.setFrom(emailAccount.getAddress()); + mimeMessage.addRecipients(Message.RecipientType.TO, recipient); + mimeMessage.setSubject(subject); + + //Embedding Secret Key. +// MimeBodyPart customDataPart = new MimeBodyPart(); +// customDataPart.setText(SecretKeyToString.secretKeyToString(sKey), "UTF-8"); +// customDataPart.setHeader("Secret-Key", ""); + + Multipart multipart = new MimeMultipart(); + BodyPart messageBodyPart = new MimeBodyPart(); + messageBodyPart.setContent(content, "text/html"); + multipart.addBodyPart(messageBodyPart); + + + //Adding Secret Key to message. + //multipart.addBodyPart(customDataPart); + + mimeMessage.setContent(multipart); + + if(attachments.size()>0) { + for (File file: attachments) { + MimeBodyPart mimeBodyPart = new MimeBodyPart(); + DataSource source = new FileDataSource(file.getAbsolutePath()); + mimeBodyPart.setDataHandler(new DataHandler(source)); + mimeBodyPart.setFileName(file.getName()); + multipart.addBodyPart(mimeBodyPart); + } + } + + Transport transport = emailAccount.getSession().getTransport(); + transport.connect( + emailAccount.getProperties().getProperty("outgoingHost"), + emailAccount.getAddress(), + emailAccount.getPassword()); + transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients()); + transport.close(); + } catch(MessagingException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } return null; + } + }; + } + +} \ No newline at end of file diff --git a/src/main/java/com/secure_mailer/backend/service/FetchFolderService.java b/src/main/java/com/secure_mailer/backend/service/FetchFolderService.java new file mode 100644 index 0000000..f83a901 --- /dev/null +++ b/src/main/java/com/secure_mailer/backend/service/FetchFolderService.java @@ -0,0 +1,102 @@ +package com.secure_mailer.backend.service; + +import java.util.ArrayList; + +import javax.mail.Folder; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.Store; +import javax.mail.event.MessageCountEvent; +import javax.mail.event.MessageCountListener; +import com.secure_mailer.frontend.FolderTreeItem; +import com.secure_mailer.frontend.IconResolver; +import javafx.concurrent.Service; +import javafx.concurrent.Task; + +public class FetchFolderService extends Service{ + private ArrayList folderList; + private FolderTreeItem foldersRoot; + private Store store; + + + public FetchFolderService(Store store, FolderTreeItem foldersRoot, ArrayList folderList) { + this.store = store; + this.foldersRoot = foldersRoot; + this.folderList = folderList; + } + + @Override + protected Task createTask() { + return new Task() { + @Override + protected Void call() throws Exception { + fetchFolders(); + return null; + } + }; + } + + private void fetchFolders() throws MessagingException { + Folder[] folders = store.getDefaultFolder().list(); + handleFolders(folders, foldersRoot); + } + + private void handleFolders(Folder[] folders, FolderTreeItem foldersRoot) throws MessagingException { + for(Folder folder: folders) { + folderList.add(folder); + FolderTreeItem folderTreeItem = new FolderTreeItem(folder); + folderTreeItem.setGraphic(IconResolver.getIcon(folder.getName())); + foldersRoot.getChildren().add(folderTreeItem); + foldersRoot.setExpanded(true); + fetchMessagesOnFolder(folderTreeItem); + addMessageListenerToFolder(folder, folderTreeItem); + if (folder.getType() == Folder.HOLDS_FOLDERS) { + Folder[] subFolders = folder.list(); + handleFolders(subFolders, folderTreeItem); + } + } + } + + private void addMessageListenerToFolder(Folder folder, FolderTreeItem folderTreeItem) { + folder.addMessageCountListener(new MessageCountListener() { + + @Override + public void messagesAdded(MessageCountEvent e) { + for(int i = 0; i < e.getMessages().length; i++) { + try { + Message message = folder.getMessage(folder.getMessageCount() - i); + folderTreeItem.addEmailToTop(message); + } catch (MessagingException ex) { + ex.printStackTrace(); + } + } + } + @Override + public void messagesRemoved(MessageCountEvent e) {} + }); + } + + private void fetchMessagesOnFolder(FolderTreeItem folderTreeItem) { + Folder folder = folderTreeItem.getFolder(); + Service fetchMessagesService = new Service() { + + @Override + protected Task createTask() { + return new Task() { + @Override + protected Void call() throws Exception { + if(folder.getType() != Folder.HOLDS_FOLDERS) { + folder.open(Folder.READ_WRITE); + int folderSize = folder.getMessageCount(); + for(int i = folderSize; i > 0; i--) { + folderTreeItem.addEmail(folder.getMessage(i)); + } + } + return null; + } + }; + } + }; + fetchMessagesService.start(); + } +} \ No newline at end of file diff --git a/src/main/java/com/secure_mailer/backend/service/LoginService.java b/src/main/java/com/secure_mailer/backend/service/LoginService.java new file mode 100644 index 0000000..15c3c4b --- /dev/null +++ b/src/main/java/com/secure_mailer/backend/service/LoginService.java @@ -0,0 +1,44 @@ +package com.secure_mailer.backend.service; + +import javax.mail.Authenticator; +import javax.mail.MessagingException; +import javax.mail.NoSuchProviderException; +import javax.mail.PasswordAuthentication; +import javax.mail.Session; +import javax.mail.Store; +import com.secure_mailer.backend.EmailAccount; + +public class LoginService { + + private EmailAccount emailAccount; + + public LoginService(EmailAccount emailAccount) { + this.emailAccount = emailAccount; + login(); + } + + private void login() { + Authenticator authenticator = new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(emailAccount.getAddress(), emailAccount.getPassword()); + } + }; + + Session session = Session.getInstance(emailAccount.getProperties(), authenticator); + emailAccount.setSession(session); + + try { + Store store = session.getStore("imaps"); + store.connect(emailAccount.getProperties().getProperty("incomingHost"), + emailAccount.getAddress(), + emailAccount.getPassword()); + emailAccount.setStore(store); + + } catch (NoSuchProviderException e) { + e.printStackTrace(); + } catch (MessagingException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/secure_mailer/backend/service/MessageRendererService.java b/src/main/java/com/secure_mailer/backend/service/MessageRendererService.java new file mode 100644 index 0000000..053d559 --- /dev/null +++ b/src/main/java/com/secure_mailer/backend/service/MessageRendererService.java @@ -0,0 +1,165 @@ +package com.secure_mailer.backend.service; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; + +import javax.mail.BodyPart; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.Multipart; +import javax.mail.internet.MimeBodyPart; +import com.secure_mailer.frontend.EmailMessage; +import javafx.concurrent.Service; +import javafx.concurrent.Task; + +public class MessageRendererService extends Service{ + private EmailMessage emailMessage; + private StringBuffer stringBuffer; +// private boolean isAuthenticated; + + public MessageRendererService(StringBuffer stringBuffer) { + this.stringBuffer = stringBuffer; +// this.isAuthenticated = false; + } + + public MessageRendererService(EmailMessage email) { + this.emailMessage = email; + this.stringBuffer = new StringBuffer(); +// this.isAuthenticated = false; + } + + @Override + protected Task createTask() { + try { loadMessage(); } + catch (Exception e) { e.printStackTrace(); } + return new Task() { + @Override + protected Object call() throws Exception { return null; } + }; + } + + private void loadMessage() throws MessagingException, IOException { + if (emailMessage.getMessage().isExpunged()) {this.cancel();} + stringBuffer.setLength(0); + Message message = emailMessage.getMessage(); + String contentType = message.getContentType(); + if(isSimpleType(contentType)){ + + if( emailMessage.getIsAuthenticated() ) + stringBuffer.append(message.getContent().toString()); + else + stringBuffer.append(encryptMessage(message.getContent().toString())); + + } else if (isMultipartType(contentType)) { + Multipart multipart = (Multipart) message.getContent(); + loadMultipart(multipart, stringBuffer); + } emailMessage.setDemoMessage(getDemoMessage()); + } + + private void loadMultipart(Multipart multipart, StringBuffer stringBuffer) throws MessagingException, IOException { + for (int i = multipart.getCount() - 1; i >=0; i--) { + BodyPart bodyPart = multipart.getBodyPart(i); + String contentType = bodyPart.getContentType(); + if (isSimpleType(contentType)) { + + if( emailMessage.getIsAuthenticated() ) + stringBuffer.append(bodyPart.getContent().toString()); + else + stringBuffer.append(encryptMessage(bodyPart.getContent().toString())); + + } else if (isMultipartType(contentType)){ + Multipart multipart2 = (Multipart) bodyPart.getContent(); + loadMultipart(multipart2,stringBuffer); + } else if (!isTextPlain(contentType)) { + MimeBodyPart mbp = (MimeBodyPart) bodyPart; + if (!emailMessage.isAttachmentLoaded())emailMessage.addAttachment(mbp); + } + } + } + + private boolean isTextPlain(String contentType) { + return contentType.contains("TEXT/PLAIN"); + } + + private boolean isSimpleType(String contentType) { + if(contentType.contains("TEXT/HTML") || + contentType.contains("mixed") || + contentType.contains("text")) { + return true; + } else return false; + } + + private boolean isMultipartType(String contentType) { + if (contentType.contains("multipart"))return true; + else return false; + } + + private String getDemoMessage() { + String content = stringBuffer.toString(); + content = content.replaceAll("\\<.*?\\>", ""); + content = content.replaceAll("[.].*[{].*[}]", ""); + content = content.replaceAll("\\ ", " "); + content = content.replaceAll("[/]", ""); + content = content.replaceAll("\s+|\\v+|\\h+", " "); + content = content.replaceAll("[<][!][-][-].*[-][-][>]", ""); + content = content.replaceAll("[@].*[{].*[}]", ""); + content = content.replaceAll("[a-z]+[.][a-zA-Z]+\\h*[{].*[}]", ""); + content = content.replaceAll("[a-z]+[{].*[}]",""); + content = content.replaceAll("[*]\\h*[{].*[}]",""); + content = content.strip(); + if (content.length() > 100)content = content.substring(0, 100); + if (content == null)content = ""; + return content; + } + + public StringBuffer getStringBuffer() { return stringBuffer; } + + public void setEmailMessage(EmailMessage emailMessage) { this.emailMessage = emailMessage; } + +// public void setIsAuthenticated(boolean isAuthenticated) { this.isAuthenticated = isAuthenticated; } + + private String encryptMessage(String message) { + // Generate AES key + KeyGenerator keyGen; + String encryptedText = ""; + try { + keyGen = KeyGenerator.getInstance("AES"); + + keyGen.init(128); // You can also use 192 or 256-bit key size + SecretKey secretKey = keyGen.generateKey(); + + // Initialize Cipher for encryption + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + + byte[] encryptedBytes; + + encryptedBytes = cipher.doFinal(message.getBytes()); + + // Convert encrypted bytes to a readable format + encryptedText = Base64.getEncoder().encodeToString(encryptedBytes); + + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return encryptedText; + // Initialize Cipher for decryption +// cipher.init(Cipher.DECRYPT_MODE, secretKey); +// byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText)); +// String decryptedText = new String(decryptedBytes); +// System.out.println("Decrypted Text: " + decryptedText); + + } + +} diff --git a/src/main/java/com/secure_mailer/frontend/Attachment.java b/src/main/java/com/secure_mailer/frontend/Attachment.java new file mode 100644 index 0000000..5b34fa2 --- /dev/null +++ b/src/main/java/com/secure_mailer/frontend/Attachment.java @@ -0,0 +1,50 @@ +package com.secure_mailer.frontend; + +import javax.mail.MessagingException; +import javax.mail.internet.MimeBodyPart; +import javafx.concurrent.Service; +import javafx.concurrent.Task; +import javafx.scene.image.Image; +import com.secure_mailer.backend.EmailManager; + +public class Attachment { + + private MimeBodyPart mbp; + private Image image; + private String name; + private String downloadPath; + + public Attachment(MimeBodyPart mbp) { + this.mbp = mbp; + name = getName(); + downloadPath = EmailManager.getDownloadPath() + name; + } + + public MimeBodyPart getMBP() { return mbp; } + public void setImage(Image image) { this.image = image; } + public Image getImage() { return image; } + public String getDownloadPath() { return downloadPath; } + + public String getName() { + String name = null; + try { name = mbp.getFileName(); } + catch (MessagingException e) { e.printStackTrace(); } + return name; + } + + public void downloadAttachment() { + Service service = new Service<>() { + @Override + protected Task createTask() { + return new Task() { + @Override + protected Void call() throws Exception { + mbp.saveFile(downloadPath); + return null; + } + }; + } + }; + service.start(); + } +} \ No newline at end of file diff --git a/src/main/java/com/secure_mailer/frontend/AttachmentCell.java b/src/main/java/com/secure_mailer/frontend/AttachmentCell.java new file mode 100644 index 0000000..626a634 --- /dev/null +++ b/src/main/java/com/secure_mailer/frontend/AttachmentCell.java @@ -0,0 +1,74 @@ +package com.secure_mailer.frontend; + +import java.io.IOException; +import javax.mail.MessagingException; +import javax.mail.internet.MimeBodyPart; +import javafx.concurrent.Service; +import javafx.concurrent.Task; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.control.ContentDisplay; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.Tooltip; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; + +public class AttachmentCell extends ListCell { + + @FXML private Label attachmentName; + @FXML private ImageView attachmentImg; + private Image thumbnail; + + public AttachmentCell() { loadFXML(); } + + private void loadFXML() { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/attachment_cell.fxml")); + loader.setController(this); + loader.setRoot(this); + loader.load(); + } + catch (IOException e) { + e.getMessage(); + e.printStackTrace(); + } + } + + @Override + protected void updateItem(Attachment item, boolean empty) { + super.updateItem(item, empty); + if(empty) { + setText(null); + setContentDisplay(ContentDisplay.TEXT_ONLY); + } else { + setContentDisplay(ContentDisplay.GRAPHIC_ONLY); + Attachment attachment = this.getItem(); + String name = attachment.getName(); + attachmentName.setText(name); + this.setTooltip( new Tooltip(name)); + + if (attachment.getImage() == null) { + MimeBodyPart mbp = attachment.getMBP(); + try { + if (mbp.getContentType().contains("IMAGE")) { + Service addImgService = new Service() { + @Override + protected Task createTask() { + return new Task() { + @Override + protected Void call() throws Exception { + thumbnail = new Image(mbp.getInputStream()); + attachment.setImage(thumbnail); + return null; + }}; + } + }; + addImgService.setOnSucceeded(e -> { if (thumbnail != null)attachmentImg.setImage(thumbnail); }); + addImgService.start(); + } + } catch (MessagingException e) { e.printStackTrace(); } + } else { attachmentImg.setImage(attachment.getImage()); } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/secure_mailer/frontend/EmailCell.java b/src/main/java/com/secure_mailer/frontend/EmailCell.java new file mode 100644 index 0000000..6846896 --- /dev/null +++ b/src/main/java/com/secure_mailer/frontend/EmailCell.java @@ -0,0 +1,89 @@ +package com.secure_mailer.frontend; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import com.secure_mailer.backend.service.MessageRendererService; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.control.ContentDisplay; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.MenuItem; +import javafx.scene.control.TextArea; +import javafx.scene.image.ImageView; +import javafx.scene.layout.Pane; + +public class EmailCell extends ListCell{ + + @FXML private Label sender; + @FXML private Label date; + @FXML private Label title; + @FXML private Pane selected; + @FXML private TextArea message; + @FXML private ImageView attachment; + private MenuItem markUnread = new MenuItem("mark as unread"); + private static SimpleDateFormat dateFormat = new SimpleDateFormat("MMM d"); + + public EmailCell() { loadFXML(); } + + private void loadFXML() { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/email_cell.fxml")); + loader.setController(this); + loader.setRoot(this); + loader.load(); + } + catch (IOException e) { + e.getMessage(); + e.printStackTrace(); + } + } + + @Override + protected void updateItem(EmailMessage item, boolean empty) { + super.updateItem(item, empty); + if(empty) { + setText(null); + setContentDisplay(ContentDisplay.TEXT_ONLY); + } else { + setContentDisplay(ContentDisplay.GRAPHIC_ONLY); + if (!item.getMessage().isExpunged()) { + MessageRendererService mrs = new MessageRendererService(item); + mrs.setOnSucceeded(e -> { + sender.setText(item.getSender().replaceAll("[<].*[>]", "")); + title.setText(item.getTitle()); + date.setText(dateFormat.format(item.getDate())); + makeBoldRows(!item.isRead()); + + if (item.hasAttachment())attachment.setVisible(true); + else attachment.setVisible(false); + + if(item.getDemoMessage() != null)message.setText(item.getDemoMessage()); + }); + mrs.start(); + } + + this.setOnMouseClicked(e -> { + item.setRead(true); + makeBoldRows(false); + }); + + this.setContextMenu(new ContextMenu(markUnread)); + markUnread.setOnAction(e -> { + item.setRead(false); + makeBoldRows(true); + }); + } + } + + private void makeBoldRows(boolean b) { + String style = ""; + if(b)style = "-fx-font-weight:bold;\n-fx-text-fill:black;"; + sender.setStyle(style); + date.setStyle(style); + message.setStyle(style); + } + + public void setSelectedIcon(boolean b) { this.selected.setVisible(b); } +} \ No newline at end of file diff --git a/src/main/java/com/secure_mailer/frontend/EmailMessage.java b/src/main/java/com/secure_mailer/frontend/EmailMessage.java new file mode 100644 index 0000000..016b9dd --- /dev/null +++ b/src/main/java/com/secure_mailer/frontend/EmailMessage.java @@ -0,0 +1,122 @@ +package com.secure_mailer.frontend; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.internet.MimeBodyPart; +import javax.mail.Address; +import javax.mail.internet.InternetAddress; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.util.Base64; + +import javax.mail.BodyPart; + +import javax.mail.Multipart; + +public class EmailMessage { + private SimpleStringProperty sender; + private SimpleObjectProperty date; + private SimpleStringProperty title; + private boolean selected = false; + private Message message; + private String demoMessage; + private List attachmentList = new ArrayList(); + private boolean attachmentLoaded = false; + private boolean isRead; + private SecretKey sKey; + private boolean isAuthenticated; + + class StringToSecretKey { + public static SecretKey stringToSecretKey(String encodedKey) { + byte[] decodedKey = Base64.getDecoder().decode(encodedKey); + return new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES"); // Replace "AES" with the appropriate algorithm + } + } + + public String extractCustomDataFromEmail(Message message) throws MessagingException, IOException { + if (message.isMimeType("multipart/*")) { + Multipart multipart = (Multipart) message.getContent(); + + for (int i = 0; i < multipart.getCount(); i++) { + BodyPart part = multipart.getBodyPart(i); + + if ("".equals(part.getHeader("Secret-Key")[0])) { + // Extract custom data + String customData = (String) part.getContent(); + return customData; + } + } + } + return null; // Return null if no custom data found + } + + public EmailMessage(String sender, Date date, String title, Message msg, boolean isRead) { + this.sender = new SimpleStringProperty(sender); + this.date = new SimpleObjectProperty(date); + this.title = new SimpleStringProperty(title); + + this.message = msg; + this.isRead = isRead; + + this.isAuthenticated = false; + } + + public String getFromEmailID() { + Address[] senderAddresses; + try { + senderAddresses = this.message.getFrom(); + String senderEmail = ""; + if (senderAddresses != null && senderAddresses.length > 0) { + InternetAddress senderAddress = (InternetAddress) senderAddresses[0]; + senderEmail = senderAddress.getAddress(); +// System.out.println("Sender Email: " + senderEmail); + } + + return senderEmail; + } catch (MessagingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return ""; + } // or message.getSender() + + + } + + public void setIsAuthenticated(boolean isAuth) { this.isAuthenticated = isAuth; } + public boolean getIsAuthenticated() { return this.isAuthenticated; } + public SecretKey getSecretKey() { return sKey; } + public String getSender() { return sender.get(); } + public Date getDate() { return date.get(); } + public String getTitle() { return title.get(); } + public Message getMessage() { return message; } + public String getDemoMessage() { return demoMessage; } + public List getAttachments() { return attachmentList; } + public boolean isAttachmentLoaded() { return attachmentLoaded; } + public void setAttachmentLoaded() { attachmentLoaded = true; } + public void setDemoMessage(String demo) { this.demoMessage = demo; } + public void setSelected(boolean b) { selected = b; } + public void setRead(boolean b) { isRead = b; } + public boolean isRead() { return isRead; } + public boolean isSelected() { return selected; } + public boolean hasAttachment() { return !attachmentList.isEmpty(); } + + public synchronized void addAttachment(MimeBodyPart mbp) { + Attachment attachment = new Attachment(mbp); + if (attachment.getName() != null) { + if (!attachmentList.isEmpty()) { + for (Attachment a : attachmentList) { + if (a == null || !a.getName().equals(attachment.getName()))continue; + else {return;} + } + } + attachmentList.add(attachment); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/secure_mailer/frontend/FolderTreeItem.java b/src/main/java/com/secure_mailer/frontend/FolderTreeItem.java new file mode 100644 index 0000000..dd15e2a --- /dev/null +++ b/src/main/java/com/secure_mailer/frontend/FolderTreeItem.java @@ -0,0 +1,62 @@ +package com.secure_mailer.frontend; + +import javax.mail.Flags; +import javax.mail.Folder; +import javax.mail.Message; +import javax.mail.MessagingException; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.scene.control.TreeItem; + +public class FolderTreeItem extends TreeItem{ + + private String name; + private Folder folder; + private ObservableList emails; + + public FolderTreeItem() { + super(""); + this.name = ""; + } + + public FolderTreeItem(String name) { + super(name); + this.name = name; + } + + public FolderTreeItem(Folder folder) { + super(folder.getName()); + emails = FXCollections.observableArrayList(); + this.name = folder.getName(); + this.folder = folder; + } + + public void addEmail(Message message) { + EmailMessage email = fetchMessage(message); + addEmail(email); + } + + private EmailMessage fetchMessage(Message message) { + EmailMessage email = null; + try { + boolean messageIsRead = message.getFlags().contains(Flags.Flag.SEEN); + email = new EmailMessage( + message.getFrom()[0].toString(), + message.getSentDate(), + message.getSubject(), + message, + messageIsRead); + } catch (MessagingException e) { e.printStackTrace(); } + return email; + } + + public void addEmailToTop(Message message) { + EmailMessage emailMessage = fetchMessage(message); + emails.add(0, emailMessage); + } + + public Folder getFolder() { return folder; } + public ObservableList getEmailMessages() { return emails; } + public String toString() { return name; } + private void addEmail(EmailMessage email) { emails.add(email); } +} \ No newline at end of file diff --git a/src/main/java/com/secure_mailer/frontend/IconResolver.java b/src/main/java/com/secure_mailer/frontend/IconResolver.java new file mode 100644 index 0000000..aba5319 --- /dev/null +++ b/src/main/java/com/secure_mailer/frontend/IconResolver.java @@ -0,0 +1,32 @@ +package com.secure_mailer.frontend; + +import javafx.scene.Node; +import javafx.scene.image.ImageView; + +public class IconResolver { + + public static Node getIcon(String name) { + name = name.toLowerCase(); + ImageView imageView = new ImageView(); + imageView.setFitWidth(16); + imageView.setFitHeight(16); + + try { + if(name.contains("gmail"))imageView.setId("gmail-folder"); + else if (name.contains("inbox"))imageView.setId("inbox-folder"); + else if (name.contains("all"))imageView.setId("all-folder"); + else if (name.contains("sent"))imageView.setId("sent-folder"); + else if (name.contains("spam"))imageView.setId("spam-folder"); + else if (name.contains("important"))imageView.setId("important-folder"); + else if (name.contains("draft"))imageView.setId("draft-folder"); + else if (name.contains("star"))imageView.setId("star-folder"); + else if (name.contains("trash"))imageView.setId("trash-folder"); + else if (name.contains("paper-clip")) { + imageView.setId("paper-clip"); + imageView.setFitWidth(23); + imageView.setFitHeight(23); + } else imageView.setId("mail-folder"); + } catch (Exception e) { e.printStackTrace(); return null; } + return imageView; + } +} \ No newline at end of file diff --git a/src/main/java/com/secure_mailer/frontend/MainWindowController.java b/src/main/java/com/secure_mailer/frontend/MainWindowController.java new file mode 100644 index 0000000..678b7e6 --- /dev/null +++ b/src/main/java/com/secure_mailer/frontend/MainWindowController.java @@ -0,0 +1,599 @@ +package com.secure_mailer.frontend; + +import java.awt.Desktop; + +import java.io.File; +import java.net.URL; +import java.security.NoSuchAlgorithmException; +import java.sql.SQLException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.ResourceBundle; +import com.secure_mailer.backend.service.EmailSendingService; +import com.secure_mailer.backend.service.MessageRendererService; + +import com.secure_mailer.backend.BaseController; +import com.secure_mailer.backend.DatabaseSetup; +import com.secure_mailer.backend.EmailManager; +import com.secure_mailer.backend.SecretCodeDAO; +import com.secure_mailer.backend.ValidatorClient; +import com.secure_mailer.backend.EmailAccount; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.control.TextField; +import javafx.scene.control.TreeView; +import javafx.scene.image.ImageView; +import javafx.scene.layout.AnchorPane; +import javafx.scene.shape.Line; +import javafx.scene.web.HTMLEditor; +import javafx.scene.web.WebView; +import javafx.util.Callback; + +import javax.activation.DataHandler; +import javax.activation.DataSource; +import javax.activation.FileDataSource; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import java.util.Base64; +import javafx.stage.FileChooser; + +import javafx.animation.PauseTransition; +//import javafx.scene.control.Alert; +//import javafx.scene.control.Alert.AlertType; +//import javafx.util.Duration; +//import javafx.application.Platform; + +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import javax.mail.BodyPart; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.Multipart; +import java.util.List; + +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.stage.Stage; + +import javafx.application.Platform; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.util.Duration; + +//import java.util.concurrent.atomic.AtomicBoolean; + + +public class MainWindowController extends BaseController implements Initializable { + + @FXML private TreeView folderSelection; + @FXML private ListView emailSelection; + @FXML private ListView attachmentList; + @FXML private AnchorPane messagePane; + @FXML private Label messageTitle; + @FXML private Label messageSenderName; + @FXML private Label messageAttachmentLabel; + @FXML private Label messageDate; + @FXML private WebView messageViewShort; + @FXML private WebView messageViewLong; + @FXML private ImageView defaultMessageViewBG; + @FXML private Label userNameLabel; + @FXML private AnchorPane composePane; + @FXML private TextField composeTo; + @FXML private TextField composeTitle; + @FXML private HTMLEditor htmlEditor; + @FXML private Line footerDiv; + @FXML private ImageView composeAttachIcon; + private static SimpleDateFormat dateFormat = new SimpleDateFormat("M'/'d'/'YYYY H:mm"); + private ArrayList attachments = new ArrayList(); + private MessageRendererService mrs; + private EmailManager emailManager; + private StringBuffer stringBuffer; + private String htmlBackup; + private SecretKey sKey; + private String fromEmailID; + + public MainWindowController(EmailManager emailManager, String fromEmailID) { + super("/fxml/main.fxml", emailManager); + this.emailManager = emailManager; + this.stringBuffer = new StringBuffer(); + this.fromEmailID = fromEmailID; + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + + emailSelection.setCellFactory(new Callback, ListCell>(){ + @Override + public ListCell call(ListView param) { return new EmailCell(); } + }); + + attachmentList.setCellFactory(new Callback, ListCell>(){ + @Override + public ListCell call(ListView param) { return new AttachmentCell(); } + }); + + htmlBackup = htmlEditor.getHtmlText(); + messageAttachmentLabel.setGraphic(IconResolver.getIcon("paper-clip")); + userNameLabel.setText(emailManager.getEmailAccount().getAddress()); + userNameLabel.setText("email.account@gmail.com"); + setUpMRS(); + setUpFolderSelection(); + setUpMessageSelection(); + setUpAttachmentSelection(); + + DatabaseSetup.setupDatabase(); + } + + private void setUpMRS() { mrs = new MessageRendererService(stringBuffer); } + + private void setUpFolderSelection() { + folderSelection.setRoot(emailManager.getFolderRoot()); + folderSelection.setShowRoot(false); + folderSelection.setOnMouseClicked(e -> { + FolderTreeItem item = (FolderTreeItem)folderSelection.getSelectionModel().getSelectedItem(); + if(item != null) { + emailManager.setSelectedFolder(item); + emailSelection.setItems(item.getEmailMessages()); + } + }); + } + +// private void setUpMessageSelection() { +// emailSelection.setOnMouseClicked(event -> { +// attachmentList.getItems().clear(); +// EmailMessage emailMessage = emailSelection.getSelectionModel().getSelectedItem(); +// +// if (emailMessage != null) { +// // Start a new thread for TCP communication +// +// new Thread(() -> { +// +// AtomicBoolean isAuthenticated = new AtomicBoolean(true); +// +// +// if( emailMessage.getFromEmailID().equals(this.fromEmailID + "@peachy.in.net") == false) { +// String fromEmailId = emailMessage.getSender(); +// String toEmailId = emailManager.getEmailAccount().getAddress(); +// String emlHash = generateEmlHash(emailMessage.getMessage(), emailMessage.getSecretKey()); +// +// ValidatorClient receiver = new ValidatorClient("peachy.in.net",2001); +// isAuthenticated.set(receiver.bringCode(fromEmailId, toEmailId, emlHash)); +// } +// // Check if the details are authenticated +//// if (isAuthenticated) { +// // Update the email manager and message view +// emailManager.setSelectedMessage(emailMessage); +// +// Platform.runLater(() -> { +// if (!composePane.isVisible()) setMessageViewVisible(true); +// mrs.setEmailMessage(emailMessage); +// mrs.setOnSucceeded(e -> { +// if (emailMessage.hasAttachment()) { +// messageViewShort.getEngine().loadContent(stringBuffer.toString()); +// setAttachmentView(true); +// emailMessage.setAttachmentLoaded(); +// +// if (isAuthenticated.get()) { +// +// loadAttachments(); +// +// } else { +// // Show authentication failure message +// Platform.runLater(() -> showAlert("Authentication Failed", "Unable to authenticate details from the server.")); +// } +// +// } else { +// +// if (isAuthenticated.get()) { +// messageViewLong.getEngine().loadContent(stringBuffer.toString()); +// setAttachmentView(false); +// +// } else { +// // Show authentication failure message +// Platform.runLater(() -> showAlert("Authentication Failed", "Unable to authenticate details from the server.")); +// } +// } +// }); +// mrs.restart(); +// messageSenderName.setText(emailMessage.getSender().replaceAll("lis.email.ttest@gmail.com", "email.account@gmail.com")); +// messageTitle.setText(emailMessage.getTitle()); +// messageDate.setText(dateFormat.format(emailMessage.getDate())); +// +// }); +//// } else { +//// // Show authentication failure message +//// Platform.runLater(() -> showAlert("Authentication Failed", "Unable to authenticate details from the server.")); +//// } +// }).start(); +// } +// }); +// } + + private void setUpMessageSelection() { + emailSelection.setOnMouseClicked(event -> { + attachmentList.getItems().clear(); + EmailMessage emailMessage = emailSelection.getSelectionModel().getSelectedItem(); + + if(emailMessage != null) { + emailManager.setSelectedMessage(emailMessage); + if (!composePane.isVisible())setMessageViewVisible(true); + mrs.setEmailMessage(emailMessage); + mrs.setOnSucceeded(e -> { + if(emailMessage.hasAttachment()) { + messageViewShort.getEngine().loadContent(stringBuffer.toString()); + setAttachmentView(true); + emailMessage.setAttachmentLoaded(); + loadAttachments(); + } else { + messageViewLong.getEngine().loadContent(stringBuffer.toString()); + setAttachmentView(false); + } + }); + mrs.restart(); + messageSenderName.setText(emailMessage.getSender().replaceAll("lis.email.ttest@gmail.com", "email.account@gmail.com")); + messageTitle.setText(emailMessage.getTitle()); + messageDate.setText(dateFormat.format(emailMessage.getDate())); + } + }); + } + + @FXML + void decryptEmail() { + + attachmentList.getItems().clear(); + EmailMessage emailMessage = emailSelection.getSelectionModel().getSelectedItem(); + + boolean isAuthenticated = false; + if( emailMessage.getFromEmailID().equals(this.fromEmailID + "@peachy.in.net") == false) { + String fromEmailId = emailMessage.getSender(); + String toEmailId = emailManager.getEmailAccount().getAddress(); + String emlHash = generateEmlHash(emailMessage.getMessage(), emailMessage.getSecretKey()); + + ValidatorClient receiver = new ValidatorClient("peachy.in.net",2001); + isAuthenticated = receiver.bringCode(fromEmailId, toEmailId, emlHash); + } + else { + isAuthenticated = true; + } + + emailMessage.setIsAuthenticated(isAuthenticated); + + if(emailMessage != null) { + if (isAuthenticated) { + emailManager.setSelectedMessage(emailMessage); + if (!composePane.isVisible())setMessageViewVisible(true); + mrs.setEmailMessage(emailMessage); + mrs.setOnSucceeded(e -> { + if(emailMessage.hasAttachment()) { + messageViewShort.getEngine().loadContent(stringBuffer.toString()); + setAttachmentView(true); + emailMessage.setAttachmentLoaded(); + loadAttachments(); + } else { + messageViewLong.getEngine().loadContent(stringBuffer.toString()); + setAttachmentView(false); + } + }); + mrs.restart(); + messageSenderName.setText(emailMessage.getSender().replaceAll("lis.email.ttest@gmail.com", "email.account@gmail.com")); + messageTitle.setText(emailMessage.getTitle()); + messageDate.setText(dateFormat.format(emailMessage.getDate())); + } + else { + //Show authentication failure message + Platform.runLater(() -> showAlert("Authentication Failed", "Unable to authenticate details from the server.")); + } + } + + emailMessage.setIsAuthenticated(false); + } + + private void showAlert(String title, String content) { + Alert alert = new Alert(AlertType.INFORMATION); + alert.setTitle(title); + alert.setHeaderText(null); + alert.setContentText(content); + alert.show(); + + // Create a PauseTransition for 10 seconds + PauseTransition delay = new PauseTransition(Duration.seconds(10)); + delay.setOnFinished(event -> alert.close()); // Close alert after 10 seconds + delay.play(); // Start the timer + } + + private void setUpAttachmentSelection() { + attachmentList.setOnMouseClicked(e -> { + Attachment selected = attachmentList.getSelectionModel().getSelectedItem(); + attachmentList.getSelectionModel().clearSelection(); + if (selected != null) { + File attachment = new File(selected.getDownloadPath()); + if (!attachment.exists())selected.downloadAttachment(); + else { + Desktop desktop = Desktop.getDesktop(); + try { desktop.open(attachment); } + catch (Exception exp) { exp.printStackTrace(); } + } + } + }); + } + + @FXML + void composeCancel() { + setComposeViewVisible(false); + if (!messageSenderName.getText().isBlank())setMessageViewVisible(true); + } + + @FXML + void composeKeyPressed() { + setMessageViewVisible(false); + setComposeViewVisible(true); + } + + @FXML + void composeSendPressed() { + + String toId = composeTo.getText(); + String secretKey = ""; + + try { + secretKey = SecretCodeDAO.getSecretCode(toId); + } catch (SQLException e) { + // TODO Auto-generated catch block + + KeyGenerator keyGen; + SecretKey stKey = null; + + try { + keyGen = KeyGenerator.getInstance("AES"); + keyGen.init(128); // You can also use 192 or 256-bit key size + stKey = keyGen.generateKey(); + } catch (NoSuchAlgorithmException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + + secretKey = SecretKeyToString.secretKeyToString(stKey); + e.printStackTrace(); + } + + String emlHash = generateEmlHash( + emailManager.getEmailAccount(), + composeTitle.getText(), + toId, + htmlEditor.getHtmlText(), + attachments, + secretKey); + + EmailSendingService emailSenderService = new EmailSendingService( + emailManager.getEmailAccount(), + composeTitle.getText(), + toId, + htmlEditor.getHtmlText(), + attachments, + this.sKey); + + + emailSenderService.setOnSucceeded(e -> { + + String fromEmailId = emailManager.getEmailAccount().getAddress(); + String toEmailId = toId; + + ValidatorClient sender = new ValidatorClient("peachy.in.net", 2000); + + try { + sender.sendRecord(fromEmailId, toEmailId, emlHash); + } catch (SQLException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + + }); + + + emailSenderService.start(); + setComposeViewVisible(false); + composeAttachIcon.setVisible(false); + composeTitle.clear(); + composeTo.clear(); + htmlEditor.setHtmlText(htmlBackup); + if (!messageSenderName.getText().isBlank())setMessageViewVisible(true); + } + + @FXML + void addAttachment() { + FileChooser fileChooser = new FileChooser(); + File selectedFile = fileChooser.showOpenDialog(null); + if(selectedFile != null){ + attachments.add(selectedFile); + composeAttachIcon.setVisible(true); + } + } + + @FXML + private void openCRUDWindow() { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/crud_window.fxml")); + Stage stage = new Stage(); + stage.setScene(new Scene(loader.load())); + stage.setTitle("Manage Secret Codes"); + stage.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void loadAttachments() { + EmailMessage emailMessage = emailManager.getSelectedMessage(); + if (emailMessage != null && emailMessage.hasAttachment()) { + ObservableList attachments = FXCollections.observableArrayList(emailMessage.getAttachments()); + attachmentList.getItems().addAll(attachments); + attachmentList.setVisible(true); + } else { attachmentList.setVisible(false); } + } + + private void setAttachmentView(boolean b) { + messageViewLong.setVisible(!b); + messageViewShort.setVisible(b); + footerDiv.setVisible(b); + messageAttachmentLabel.setVisible(b); + attachmentList.setVisible(b); + } + + private void setMessageViewVisible(boolean b) { + messagePane.setVisible(b); + defaultMessageViewBG.setVisible(!b); + } + + private void setComposeViewVisible(boolean b) { + composePane.setVisible(b); + defaultMessageViewBG.setVisible(!b); + } + + @FXML + void starKeyAction() {} + + @FXML + void trashKeyAction() {} + + public String MessageToString(Message Message) throws MessagingException, IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + Message.writeTo(outputStream); + return outputStream.toString("UTF-8"); // Specify encoding if needed + } + + private String generateEmlHash(Message message, SecretKey key) { + try { + + String msg = MessageToString(message); + String encryptMessage = encryptMessage(msg, key); + + return encryptMessage; + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return ""; + } + } + + + private String generateEmlHash(EmailAccount emailAccount, String subject, + String recipient, + String content, + List attachments, + String secretKey){ + + String message; + try { + + MimeMessage mimeMessage = new MimeMessage(emailAccount.getSession()); + mimeMessage.setFrom(emailAccount.getAddress()); + mimeMessage.addRecipients(Message.RecipientType.TO, recipient); + mimeMessage.setSubject(subject); + + Multipart multipart = new MimeMultipart(); + BodyPart messageBodyPart = new MimeBodyPart(); + messageBodyPart.setContent(content, "text/html"); + multipart.addBodyPart(messageBodyPart); + mimeMessage.setContent(multipart); + + if(attachments.size()>0) { + for (File file: attachments) { + MimeBodyPart mimeBodyPart = new MimeBodyPart(); + DataSource source = new FileDataSource(file.getAbsolutePath()); + mimeBodyPart.setDataHandler(new DataHandler(source)); + mimeBodyPart.setFileName(file.getName()); + multipart.addBodyPart(mimeBodyPart); + } + } + message = MessageToString(mimeMessage); + String encryptMessage = encryptMessage(message,StringToSecretKey.stringToSecretKey(secretKey)); + return encryptMessage; + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return ""; + } + + } + + +// private String encryptMessage(String message) throws Exception{ +// // Generate AES key +// KeyGenerator keyGen = KeyGenerator.getInstance("AES"); +// keyGen.init(128); // You can also use 192 or 256-bit key size +// SecretKey secretKey = keyGen.generateKey(); +// +// +// this.sKey = secretKey; +// +// // Initialize Cipher for encryption +// Cipher cipher = Cipher.getInstance("AES"); +// cipher.init(Cipher.ENCRYPT_MODE, secretKey); +// +// byte[] encryptedBytes = cipher.doFinal(message.getBytes()); +// +// // Convert encrypted bytes to a readable format +// String encryptedText = Base64.getEncoder().encodeToString(encryptedBytes); +// +// return encryptedText; +// // Initialize Cipher for decryption +//// cipher.init(Cipher.DECRYPT_MODE, secretKey); +//// byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText)); +//// String decryptedText = new String(decryptedBytes); +//// System.out.println("Decrypted Text: " + decryptedText); +// +// } + + private String encryptMessage(String message, SecretKey secretKey) throws Exception{ + // Generate AES key +// KeyGenerator keyGen = KeyGenerator.getInstance("AES"); +// keyGen.init(128); // You can also use 192 or 256-bit key size +// SecretKey secretKey = keyGen.generateKey(); + + // Initialize Cipher for encryption + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + + byte[] encryptedBytes = cipher.doFinal(message.getBytes()); + + // Convert encrypted bytes to a readable format + String encryptedText = Base64.getEncoder().encodeToString(encryptedBytes); + + return encryptedText; + // Initialize Cipher for decryption +// cipher.init(Cipher.DECRYPT_MODE, secretKey); +// byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText)); +// String decryptedText = new String(decryptedBytes); +// System.out.println("Decrypted Text: " + decryptedText); + + } + + class StringToSecretKey { + public static SecretKey stringToSecretKey(String encodedKey) { + byte[] decodedKey = Base64.getDecoder().decode(encodedKey); + return new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES"); // Replace "AES" with the appropriate algorithm + } + } + + class SecretKeyToString { + public static String secretKeyToString(SecretKey secretKey) { + // Convert SecretKey to byte array and then to Base64 string + byte[] encodedKey = secretKey.getEncoded(); + return Base64.getEncoder().encodeToString(encodedKey); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/secure_mailer/frontend/ViewGenerator.java b/src/main/java/com/secure_mailer/frontend/ViewGenerator.java new file mode 100644 index 0000000..db5fe5a --- /dev/null +++ b/src/main/java/com/secure_mailer/frontend/ViewGenerator.java @@ -0,0 +1,70 @@ +package com.secure_mailer.frontend; + +import java.io.IOException; +import java.io.InputStream; +import com.secure_mailer.backend.BaseController; +import com.secure_mailer.backend.LoginController; +import com.secure_mailer.backend.EmailManager; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.image.Image; +import javafx.scene.text.Font; +import javafx.stage.Stage; + +public class ViewGenerator { + + private static String theme = "/css/themeDefault.css"; + private static String fontFile1 = "/font/Myriad_Pro_Regular.ttf"; + private static String fontFile2 = "/font/Myriad_Pro_Light_SemiCondensed.otf"; + private static Stage stage; + private static BaseController logincontroller; + + public static void initialize(EmailManager em) { + loadFont(); + logincontroller = new LoginController(em); + stage = new Stage(); + stage.getIcons().add(new Image("/img/default/email-client.png")); + stage.setTitle("EmailClient"); + stage.setScene(initializeScene(logincontroller)); + stage.show(); + } + + public static void showMainWindow(EmailManager em) { + stage.close(); + BaseController controller = new MainWindowController(em, logincontroller.getEmailID()); + stage = new Stage(); + stage.getIcons().add(new Image("/img/default/email-client.png")); + stage.setTitle("EmailClient"); + stage.setScene(initializeScene(controller)); + stage.show(); + } + + public static void closeStage(Stage stage) { stage.close(); } + + private static Scene initializeScene(BaseController controller) { + FXMLLoader fxmlLoader = new FXMLLoader(ViewGenerator.class.getResource(controller.getFXML())); + fxmlLoader.setController(controller); + Parent parent; + try { parent = fxmlLoader.load(); } + catch (IOException e) { e.printStackTrace(); return null; } + Scene scene = new Scene(parent); + updateStyles(scene); + return scene; + } + + private static void updateStyles(Scene scene) { + scene.getStylesheets().clear(); + scene.getStylesheets().add(ViewGenerator.class.getResource(theme).toExternalForm()); + } + + private static void loadFont() { + try (InputStream in = ViewGenerator.class.getResourceAsStream(fontFile1); + InputStream in2 = ViewGenerator.class.getResourceAsStream(fontFile2)) { + if (in != null) { + Font.loadFont(in, 1); + Font.loadFont(in2, 1); + } + } catch (IOException e) { e.printStackTrace(); } + } +} diff --git a/src/main/resources/META-INF/javamail.default.address.map b/src/main/resources/META-INF/javamail.default.address.map new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/css/themeDefault.css b/src/main/resources/css/themeDefault.css new file mode 100644 index 0000000..dbc8992 --- /dev/null +++ b/src/main/resources/css/themeDefault.css @@ -0,0 +1,320 @@ +/*** Colors ***/ + +* { + mid-blue-color: #212d40; + dark-blue-color: #011826; + cell-unselected-color: #f2f2f2; + folder-selected-color: #495362; + light-grey-color: #cccccc; + mid-grey-color: #e0e0e0; + dark-grey-color: #999999; + white-color: white; + teal-color: #98c0bb; + selection-grey: #d1d1d1 +} + +/*** Text ***/ + +@font-face{ + font-family: 'Myriad Pro'; + src: url("../font/Myriad_Pro_Regular.ttf"); +} + +@font-face{ + font-family: 'Myriad Pro Light SemiCond'; + src: url("../font/Myriad_Pro_Light_SemiCondensed.otf"); +} + +.text { -fx-text-fill: black; } +.font-rockwell { -fx-font-family: 'Rockwell'; } +.font-myriad { -fx-font-family: 'Myriad Pro'; } +.font-bold { -fx-font-weight: bold; } +.font-grey { -fx-text-fill: dark-grey-color; } +.font-white { -fx-text-fill: white-color; } + + +/*** General ***/ + +#main-window-pane { -fx-background-color: white-color; } +#account-pane { -fx-background-color: dark-blue-color; } +#compose-key-pane { -fx-background-color: mid-blue-color; } + +/*** Login ***/ + +.login-field { + -fx-background-color: transparent; + -fx-font-size: 20; +} +#login-key { -fx-font-size: 35; } + +/*** Email Selection ***/ + +.list-view .scroll-bar:vertical, +.list-view .scroll-bar:horizontal { + -fx-scale-x: 0; + -fx-opacity: 0; + -fx-padding:-7; +} +#email-cell-view, #email-cell-view:selected { + -fx-focus-color: transparent; + -fx-faint-focus-color: transparent; +} +.email-cell { + -fx-graphic-text-gap: 0; + -fx-padding: 0px; + -fx-background-color: cell-unselected-color; + -fx-border-color: light-grey-color, light-grey-color, light-grey-color, transparent; + -fx-border-width: 0.5, 0.5, 0.5, 0; +} +.email-cell:selected { -fx-background-color: white-color; } +.email-cell:hover { -fx-background-color: white-color; } +.email-cell:empty { visibility: hidden; } +.email-cell:selected > #email-cell-pane #email-cell-focused { -fx-background-color: teal-color; } +#email-cell-title { -fx-font-size: 15; } +#email-cell-title { -fx-text-fill: black; } + +#email-cell-message *, #email-cell-message { + -fx-background-color: transparent; + -fx-border-color: transparent; +} + +/*** Message View ***/ + +#message-pane { -fx-background-color: white-color; } +#message-title { -fx-font-size: 15; } +#message-long-view .scroll-bar { visibility: hidden; } +.line { -fx-stroke: light-grey-color; } + +/*** Folder Selection ***/ + +#folder-view *, #folder-view, #folder-view:focused{ + -fx-background-color: mid-blue-color; + -fx-text-fill: white-color; + -fx-border-color: transparent; +} +#folder-view .tree-cell > .tree-disclosure-node { -fx-background-color: transparent;} +#folder-view .tree-cell:hover { -fx-text-fill: teal-color; } +#folder-view .tree-cell:focused { -fx-background-color: folder-selected-color; -fx-text-fill: teal-color; } + +/*** Composer Window ***/ + +#editor { -fx-background-color: white-color; } +#editor .top-toolbar, .bottom-toolbar {-fx-background-color: transparent} +#editor .grid, .combo-box *, .color-picker .arrow-button .arrow { -fx-background-color: transparent; } +.combo-box .arrow-button .arrow {-fx-background-color: black; } + +#editor * .button, .html-editor-align-justify, .html-editor-outdent, .html-editor-indent, +.html-editor-align-left, .html-editor-align-center, .html-editor-align-right, +.html-editor-bullets, .html-editor-numbers, .html-editor-foreground, .html-editor-background, .font-menu-button, +.font-menu-button *, .html-editor-underline, .html-editor-strike, .html-editor-italic, .html-editor-bold { + -fx-background-color: mid-grey-color; +} + +#editor * .button:hover, .html-editor-align-justify:hover, .html-editor-outdent:hover, .html-editor-indent:hover, +.html-editor-align-left:hover, .html-editor-align-center:hover, .html-editor-align-right:hover, +.html-editor-bullets:hover, .html-editor-numbers:hover, .html-editor-foreground:hover, .html-editor-background:hover, .font-menu-button:hover, +.font-menu-button *:hover, .html-editor-underline:hover, .html-editor-strike:hover, .html-editor-italic:hover, .html-editor-bold:hover { + -fx-background-color: selection-grey; +} + +/* Attachments */ + +#attach-label { -fx-font-family: 'Rockwell'; } +.attach-cell:empty { visibility: hidden; } +#attachments-label {} +#attachment-list { + -fx-background-color: transparent; + -fx-border-color: transparent; +} +.attach-cell { + -fx-background-color: transparent; + -fx-border-color: transparent; +} + +/*** Settings ***/ + +#settings-pane { -fx-background-color: dark-blue-color; } + +/* Icons */ + +#gmail-folder { -fx-image: url("../img/default/idle/at.png"); } +#inbox-folder { -fx-image: url("../img/default/idle/inbox.png"); } +#all-folder { -fx-image: url("../img/default/idle/mail.png"); } +#sent-folder { -fx-image: url("../img/default/idle/sent.png"); } +#spam-folder { -fx-image: url("../img/default/idle/spam.png"); } +#important-folder { -fx-image: url("../img/default/idle/flag.png"); } +#draft-folder { -fx-image: url("../img/default/idle/draft.png"); } +#star-folder { -fx-image: url("../img/default/idle/star-folder.png"); } +#trash-folder { -fx-image: url("../img/default/idle/trash-folder.png"); } + +.tree-cell:hover > #gmail-folder, .tree-cell:focused > #gmail-folder { -fx-image: url("../img/default/hover/at.png"); } +.tree-cell:hover > #inbox-folder, .tree-cell:focused > #inbox-folder { -fx-image: url("../img/default/hover/inbox.png"); } +.tree-cell:hover > #all-folder, .tree-cell:focused > #all-folder { -fx-image: url("../img/default/hover/mail.png"); } +.tree-cell:hover > #sent-folder, .tree-cell:focused > #sent-folder { -fx-image: url("../img/default/hover/sent.png"); } +.tree-cell:hover > #spam-folder, .tree-cell:focused > #spam-folder { -fx-image: url("../img/default/hover/spam.png"); } +.tree-cell:hover > #important-folder, .tree-cell:focused > #important-folder { -fx-image: url("../img/default/hover/flag.png"); } +.tree-cell:hover > #draft-folder, .tree-cell:focused > #draft-folder { -fx-image: url("../img/default/hover/draft.png"); } +.tree-cell:hover > #star-folder, .tree-cell:focused > #star-folder { -fx-image: url("../img/default/hover/star-folder.png"); } +.tree-cell:hover > #trash-folder, .tree-cell:focused > #trash-folder { -fx-image: url("../img/default/hover/trash-folder.png"); } + +#paper-clip { -fx-image: url("../img/default/idle/paper-clip.png"); } +#email-cell-clip { -fx-image: url("../img/default/idle/email-cell-clip.png"); } +#login-email-icon { -fx-image: url("../img/default/idle/login-email-icon.png"); } +#login-pass-icon { -fx-image: url("../img/default/idle/login-pass-icon.png"); } + +#login-key { + -fx-background-color: transparent; + -fx-background-image: url('../img/default/idle/login-key.png'); + -fx-background-size: 367px; + -fx-background-repeat: no-repeat; + -fx-background-position: center; +} + +#login-key:hover { + -fx-background-color: transparent; + -fx-background-image: url('../img/default/hover/login-key.png'); + -fx-background-size: 367px; + -fx-background-repeat: no-repeat; + -fx-background-position: center; +} + +#settings-key { + -fx-graphic: url('../img/default/idle/settings.png'); + -fx-scale-x: 0.04; + -fx-scale-y: 0.04; + -fx-translate-x: -203; + -fx-translate-y: -205; + -fx-background-color: transparent; +} + +#settings-key:hover { + -fx-graphic: url('../img/default/hover/settings.png'); + -fx-scale-x: 0.04; + -fx-scale-y: 0.04; + -fx-translate-x: -203; + -fx-translate-y: -205; + -fx-background-color: transparent; +} + +#key-trash { + -fx-graphic: url('../img/default/idle/trash.png'); + -fx-scale-x: 0.02; + -fx-scale-y: 0.02; + -fx-background-color: transparent; +} +#key-star { + -fx-graphic: url('../img/default/idle/star.png'); + -fx-scale-x: 0.018; + -fx-scale-y: 0.018; + -fx-background-color: transparent; +} +#key-trash:hover, #key-trash:focused { + -fx-graphic: url('../img/default/hover/trash.png'); + -fx-scale-x: 0.02; + -fx-scale-y: 0.02; + -fx-background-color: transparent; +} +#key-star:hover, #key-star:focused { + -fx-graphic: url('../img/default/hover/star.png'); + -fx-scale-x: 0.018; + -fx-scale-y: 0.018; + -fx-background-color: transparent; +} + +#send-key { + -fx-graphic: url('../img/default/idle/send-message.png'); + -fx-scale-x: 0.09; + -fx-scale-y: 0.09; + -fx-background-color: transparent; +} + +#send-key:hover { + -fx-graphic: url('../img/default/hover/send-message.png'); + -fx-scale-x: 0.09; + -fx-scale-y: 0.09; + -fx-background-color: transparent; +} + +#attach-key { + -fx-graphic: url('../img/default/idle/attach-message.png'); + -fx-scale-x: 0.08; + -fx-scale-y: 0.08; + -fx-background-color: transparent; +} + +#attach-key:hover { + -fx-graphic: url('../img/default/hover/attach-message.png'); + -fx-scale-x: 0.08; + -fx-scale-y: 0.08; + -fx-background-color: transparent; +} + +#compose-key { + -fx-background-color: transparent; + -fx-background-image: url('../img/default/idle/compose-key.png'); + -fx-background-size: 139px; + -fx-background-repeat: no-repeat; + -fx-background-position: center; +} + +#compose-key:hover { + -fx-background-color: transparent; + -fx-background-image: url('../img/default/hover/compose-key.png'); + -fx-background-size: 139px; + -fx-background-repeat: no-repeat; + -fx-background-position: center; +} + +#compose-cancel-key { + -fx-graphic: url('../img/default/idle/cancel-message.png'); + -fx-scale-x: 0.08; + -fx-scale-y: 0.08; + -fx-background-color: transparent; +} + +#compose-cancel-key:hover { + -fx-graphic: url('../img/default/hover/cancel-message.png'); + -fx-scale-x: 0.08; + -fx-scale-y: 0.08; + -fx-background-color: transparent; +} + +#compose-attached-icon { + -fx-image: url('../img/default/idle/attached-message.png'); + -fx-scale-x: 0.9; + -fx-scale-y: 0.9; +} + +#field-send-to, #field-title { + -fx-background-color: transparent; + -fx-background-image: url("../img/default/idle/composer-field.png"); + -fx-background-size: 469px; + -fx-background-repeat: no-repeat; + -fx-background-position: center; +} + +#field-send-to:hover, #field-title:hover, +#field-send-to:focused, #field-title:focused { + -fx-background-color: transparent; + -fx-background-image: url("../img/default/hover/composer-field.png"); + -fx-background-size: 469px; + -fx-background-repeat: no-repeat; + -fx-background-position: center; +} + +#search-bar { + -fx-background-color: transparent; + -fx-background-image: url('../img/default/idle/search-bar.png'); + -fx-background-size: 232px; + -fx-background-repeat: no-repeat; + -fx-background-position: center; +} + +#search-bar:hover { + -fx-background-color: transparent; + -fx-background-image: url('../img/default/hover/search-bar.png'); + -fx-background-size: 232px; + -fx-background-repeat: no-repeat; + -fx-background-position: center; +} + diff --git a/src/main/resources/font/Myriad_Pro_Light_SemiCondensed.otf b/src/main/resources/font/Myriad_Pro_Light_SemiCondensed.otf new file mode 100644 index 0000000..56e8515 Binary files /dev/null and b/src/main/resources/font/Myriad_Pro_Light_SemiCondensed.otf differ diff --git a/src/main/resources/font/Myriad_Pro_Regular.ttf b/src/main/resources/font/Myriad_Pro_Regular.ttf new file mode 100644 index 0000000..57a953b Binary files /dev/null and b/src/main/resources/font/Myriad_Pro_Regular.ttf differ diff --git a/src/main/resources/fxml/attachment_cell.fxml b/src/main/resources/fxml/attachment_cell.fxml new file mode 100644 index 0000000..b454bf8 --- /dev/null +++ b/src/main/resources/fxml/attachment_cell.fxml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/crud_window.fxml b/src/main/resources/fxml/crud_window.fxml new file mode 100644 index 0000000..5afbeca --- /dev/null +++ b/src/main/resources/fxml/crud_window.fxml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + +