From 35674570265c9b97672c7f1ff8e8e2498a9b0b69 Mon Sep 17 00:00:00 2001 From: heyethereum Date: Sun, 28 Jul 2024 03:07:23 +0800 Subject: [PATCH] Initial completion of scanning of qr code from gmail --- pom.xml | 12 ++ .../app/gmail/controller/GmailController.java | 3 +- .../gmail/dto/ScannedGmailResponseDto.java | 15 ++ .../safeqr/app/gmail/model/EmailMessage.java | 38 ++++ .../app/gmail/model/QRCodeByContentId.java | 17 ++ .../safeqr/app/gmail/model/QRCodeByURL.java | 17 ++ .../app/gmail/service/GmailService.java | 164 +++++++++++------- 7 files changed, 206 insertions(+), 60 deletions(-) create mode 100644 src/main/java/com/safeqr/app/gmail/dto/ScannedGmailResponseDto.java create mode 100644 src/main/java/com/safeqr/app/gmail/model/EmailMessage.java create mode 100644 src/main/java/com/safeqr/app/gmail/model/QRCodeByContentId.java create mode 100644 src/main/java/com/safeqr/app/gmail/model/QRCodeByURL.java diff --git a/pom.xml b/pom.xml index 99309cc..0a8cddd 100644 --- a/pom.xml +++ b/pom.xml @@ -109,6 +109,18 @@ jsoup 1.18.1 + + + com.fasterxml.jackson.core + jackson-databind + 2.17.2 + + + + com.fasterxml.jackson.core + jackson-annotations + 2.17.2 + diff --git a/src/main/java/com/safeqr/app/gmail/controller/GmailController.java b/src/main/java/com/safeqr/app/gmail/controller/GmailController.java index 08b55e2..de72cd3 100644 --- a/src/main/java/com/safeqr/app/gmail/controller/GmailController.java +++ b/src/main/java/com/safeqr/app/gmail/controller/GmailController.java @@ -1,6 +1,7 @@ package com.safeqr.app.gmail.controller; import com.google.api.services.gmail.model.*; +import com.safeqr.app.gmail.dto.ScannedGmailResponseDto; import org.apache.commons.codec.binary.Base64; import org.json.JSONObject; import com.google.api.client.auth.oauth2.AuthorizationCodeRequestUrl; @@ -106,7 +107,7 @@ public class GmailController { return new ResponseEntity<>("Access token is missing", HttpStatus.BAD_REQUEST); } - return new ResponseEntity<>(gmailService.getEmail(accessToken).toString(), HttpStatus.OK); + return new ResponseEntity<>(gmailService.getEmail(accessToken), HttpStatus.OK); } } diff --git a/src/main/java/com/safeqr/app/gmail/dto/ScannedGmailResponseDto.java b/src/main/java/com/safeqr/app/gmail/dto/ScannedGmailResponseDto.java new file mode 100644 index 0000000..07e2f85 --- /dev/null +++ b/src/main/java/com/safeqr/app/gmail/dto/ScannedGmailResponseDto.java @@ -0,0 +1,15 @@ +package com.safeqr.app.gmail.dto; + +import com.safeqr.app.gmail.model.EmailMessage; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Builder +@Data +@AllArgsConstructor +public class ScannedGmailResponseDto { + List messages; +} diff --git a/src/main/java/com/safeqr/app/gmail/model/EmailMessage.java b/src/main/java/com/safeqr/app/gmail/model/EmailMessage.java new file mode 100644 index 0000000..5dfb87d --- /dev/null +++ b/src/main/java/com/safeqr/app/gmail/model/EmailMessage.java @@ -0,0 +1,38 @@ +package com.safeqr.app.gmail.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +@Data +public class EmailMessage { + private String messageId; + private String subject; + private String historyId; + + @JsonInclude(JsonInclude.Include.NON_EMPTY) + List qrCodeByContentId; + + @JsonInclude(JsonInclude.Include.NON_EMPTY) + List qrCodeByURL; + + public EmailMessage(String messageId, String subject, String historyId) { + this.messageId = messageId; + this.subject = subject; + this.historyId = historyId; + this.qrCodeByContentId = new ArrayList<>(); + this.qrCodeByURL = new ArrayList<>(); + } + public void addQRCodeByContentId(QRCodeByContentId qrCode) { + this.qrCodeByContentId.add(qrCode); + } + + public void addQRCodeByURL(QRCodeByURL qrCode) { + this.qrCodeByURL.add(qrCode); + } + + public boolean hasQRCodes() { + return !qrCodeByContentId.isEmpty() || !qrCodeByURL.isEmpty(); + } +} diff --git a/src/main/java/com/safeqr/app/gmail/model/QRCodeByContentId.java b/src/main/java/com/safeqr/app/gmail/model/QRCodeByContentId.java new file mode 100644 index 0000000..d26e8cd --- /dev/null +++ b/src/main/java/com/safeqr/app/gmail/model/QRCodeByContentId.java @@ -0,0 +1,17 @@ +package com.safeqr.app.gmail.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Data +@Builder +@AllArgsConstructor +public class QRCodeByContentId { + private String cid; + private String attachmentId; + private List decodedContent; + private int totalQRCodeFound; +} diff --git a/src/main/java/com/safeqr/app/gmail/model/QRCodeByURL.java b/src/main/java/com/safeqr/app/gmail/model/QRCodeByURL.java new file mode 100644 index 0000000..1a5db08 --- /dev/null +++ b/src/main/java/com/safeqr/app/gmail/model/QRCodeByURL.java @@ -0,0 +1,17 @@ +package com.safeqr.app.gmail.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Data +@Builder +@AllArgsConstructor +public class QRCodeByURL { + private String url; + private List decodedContent; + private int totalQRCodeFound; + +} diff --git a/src/main/java/com/safeqr/app/gmail/service/GmailService.java b/src/main/java/com/safeqr/app/gmail/service/GmailService.java index c1ba153..c3b8599 100644 --- a/src/main/java/com/safeqr/app/gmail/service/GmailService.java +++ b/src/main/java/com/safeqr/app/gmail/service/GmailService.java @@ -7,9 +7,11 @@ import com.google.zxing.*; import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.common.HybridBinarizer; import com.google.zxing.multi.qrcode.QRCodeMultiReader; +import com.safeqr.app.gmail.dto.ScannedGmailResponseDto; +import com.safeqr.app.gmail.model.EmailMessage; +import com.safeqr.app.gmail.model.QRCodeByContentId; +import com.safeqr.app.gmail.model.QRCodeByURL; import org.apache.commons.codec.binary.Base64; -import org.json.JSONArray; -import org.json.JSONObject; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.select.Elements; @@ -25,11 +27,14 @@ import com.google.api.services.gmail.Gmail; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.*; +import java.lang.Thread; +import java.net.ConnectException; import java.net.URI; import java.net.URISyntaxException; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.net.http.HttpTimeoutException; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -49,55 +54,82 @@ public class GmailService { .build(); } - public JSONObject getEmail(String accessToken) throws IOException, InterruptedException { - JSONObject json = new JSONObject(); - JSONArray emailArray = new JSONArray(); + private static final long MAX_RESULTS = 100L; - // Build the Gmail service + public ScannedGmailResponseDto getEmail(String accessToken) throws IOException, InterruptedException { Gmail service = getGmailService(accessToken); - logger.info("service-> {}", service); + logger.info("Gmail service initialized: {}", service); - // Get the list of messages - ListMessagesResponse listResponse = service.users().messages().list("me").execute(); - List messages = listResponse.getMessages(); + List emailMessagesList = new ArrayList<>(); + String userId = "me"; + String nextPageToken = null; - for (Message message : messages) { - message = service.users().messages().get("me", message.getId()).setFormat("full").execute(); - List parts = message.getPayload().getParts(); - Set attachmentIds = new HashSet<>(); - Set imageUrls = new HashSet<>(); - processPartsRecursively(parts, attachmentIds, imageUrls); + do { + ListMessagesResponse listResponse = fetchMessages(service, userId, nextPageToken); + List messages = listResponse.getMessages(); + nextPageToken = listResponse.getNextPageToken(); - // Extract and log the email subject - String subject = getSubject(message); - logger.info("Email Subject-> {}", subject); - - if (attachmentIds.isEmpty() && imageUrls.isEmpty()) - continue; - - String messageId = message.getId(); - logger.info("messageId-> {}", messageId); - String historyId = String.valueOf(message.getHistoryId()); - logger.info("historyId-> {}", historyId); - - for (String attachmentId : attachmentIds) { - Optional attachment = findAttachmentIdByCid(parts, attachmentId); - logger.info("attachment-> {}", attachment); - if (attachment.isPresent()) { - List qrCodeValue = processAttachment(service, messageId, attachment.get()); - emailArray.put(qrCodeValue); + for (Message message : messages) { + EmailMessage emailMessage = processMessage(service, userId, message); + if (emailMessage != null) { + emailMessagesList.add(emailMessage); } } - for (String imageUrl : imageUrls) { - List qrCodeValue = scanQRCodeFromUrl(imageUrl); - if (qrCodeValue != null) { - emailArray.put(qrCodeValue); + } while (nextPageToken != null); + + return new ScannedGmailResponseDto(emailMessagesList); + } + + private ListMessagesResponse fetchMessages(Gmail service, String userId, String pageToken) throws IOException { + return service.users().messages().list(userId) + .setPageToken(pageToken) + .setMaxResults(MAX_RESULTS) + .execute(); + } + + private EmailMessage processMessage(Gmail service, String userId, Message message) throws IOException, InterruptedException { + message = service.users().messages().get(userId, message.getId()).setFormat("full").execute(); + List parts = message.getPayload().getParts(); + Set attachmentIds = new HashSet<>(); + Set imageUrls = new HashSet<>(); + processPartsRecursively(parts, attachmentIds, imageUrls); + + if (attachmentIds.isEmpty() && imageUrls.isEmpty()) { + return null; + } + + String subject = getSubject(message); + logger.info("Email Subject: {}", subject); + logger.info("Message ID: {}", message.getId()); + logger.info("History ID: {}", message.getHistoryId()); + + EmailMessage emailMessage = new EmailMessage(message.getId(), subject, String.valueOf(message.getHistoryId())); + + processAttachments(service, message.getId(), parts, attachmentIds, emailMessage); + processImageUrls(imageUrls, emailMessage); + + return emailMessage.hasQRCodes() ? emailMessage : null; + } + + private void processAttachments(Gmail service, String messageId, List parts, Set attachmentIds, EmailMessage emailMessage) throws IOException { + for (String attachmentId : attachmentIds) { + Optional attachment = findAttachmentIdByCid(parts, attachmentId); + if (attachment.isPresent()) { + List qrCodeValue = processAttachment(service, messageId, attachment.get()); + if (!qrCodeValue.isEmpty()) { + emailMessage.addQRCodeByContentId(new QRCodeByContentId(attachmentId, attachment.get(), qrCodeValue, qrCodeValue.size())); } } } - logger.info("Total Emails-> {}", messages.size()); - json.put("qr_codes", emailArray); - return json; + } + + private void processImageUrls(Set imageUrls, EmailMessage emailMessage) throws IOException { + for (String imageUrl : imageUrls) { + List qrCodeValue = scanQRCodeFromUrl(imageUrl); + if (!qrCodeValue.isEmpty()) { + emailMessage.addQRCodeByURL(new QRCodeByURL(imageUrl, qrCodeValue, qrCodeValue.size())); + } + } } private String getSubject(Message message) { @@ -140,35 +172,49 @@ public class GmailService { } } } - private List scanQRCodeFromUrl(String imageUrl) throws IOException, InterruptedException { + private List scanQRCodeFromUrl(String imageUrl) { try { BufferedImage image = downloadImageFromUrl(imageUrl); if (image != null) { return decodeQRCodes(image); } + } catch (IllegalArgumentException e) { + logger.error("Invalid URI scheme for URL: {} -> {}", imageUrl, e.getMessage()); } catch(URISyntaxException e) { logger.error("Error while scanning QR code from URL", e); } - return null; + return Collections.emptyList(); } // Download the image from the given URL - private BufferedImage downloadImageFromUrl(String imageUrl) throws IOException, InterruptedException, URISyntaxException { - HttpClient client = HttpClient.newBuilder() - .followRedirects(HttpClient.Redirect.ALWAYS) - .build(); - logger.info("imageUrl-> {}", imageUrl); - // Encode the URL - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(imageUrl.replace(" ", "%20"))) - .GET() - .build(); + private BufferedImage downloadImageFromUrl(String imageUrl) throws URISyntaxException { + try { + imageUrl = imageUrl.replace(" ", "%20"); + HttpClient client = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.ALWAYS) + .build(); + logger.info("Downloading image from URL: {}", imageUrl); + HttpRequest request = HttpRequest.newBuilder() + .uri(new URI(imageUrl)) + .GET() + .build(); - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); - if (response.statusCode() == 200) { - byte[] imageBytes = response.body(); - return ImageIO.read(new ByteArrayInputStream(imageBytes)); - } else { - logger.error("Failed to download image. HTTP response code: {}", response.statusCode()); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); + + if (response.statusCode() == 200) { + byte[] imageBytes = response.body(); + return ImageIO.read(new ByteArrayInputStream(imageBytes)); + } else { + logger.warn("Failed to download image. HTTP response code: {}", response.statusCode()); + } + } catch (URISyntaxException e) { + logger.warn("Invalid URL: {} -> {}", imageUrl, e.getMessage()); + } catch (HttpTimeoutException e) { + logger.warn("Request timed out for URL: {} -> {}", imageUrl, e.getMessage()); + } catch (ConnectException e) { + logger.warn("Failed to connect to URL: {} -> {}", imageUrl, e.getMessage()); + } catch (IOException | InterruptedException e) { + logger.warn("Error downloading image from URL: {} -> {}", imageUrl, e.getMessage()); + Thread.currentThread().interrupt(); } return null; }