u
This commit is contained in:
1 parent
5136bb90bd
commit
603f938e96
17 files changed
+334
-654
No files matched your search
@@ -1,38 +0,0 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
Generated
-10
@@ -1,10 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Environment-dependent path to Maven home directory
|
||||
/mavenHomeManager.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
-14
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
-6
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/../../.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.email</groupId>
|
||||
<artifactId>Email-Netty</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.1.106.Final</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,144 +0,0 @@
|
||||
package com.email;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Base64;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ImapClientHandler extends SimpleChannelInboundHandler<String> {
|
||||
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
private enum State { LOGIN, SELECT, FETCH, DONE }
|
||||
private State state = State.LOGIN;
|
||||
|
||||
private int tagCounter = 1;
|
||||
private StringBuilder headerBuffer = new StringBuilder();
|
||||
private StringBuilder bodyBuffer = new StringBuilder();
|
||||
private boolean inHeader = true;
|
||||
private String boundary = null;
|
||||
|
||||
private boolean inAttachment = false;
|
||||
private String currentFilename = null;
|
||||
|
||||
public ImapClientHandler(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
private String nextTag() {
|
||||
return "a" + (tagCounter++);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) {
|
||||
System.out.println("Connected to IMAP server...");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, String line) {
|
||||
line = line.trim();
|
||||
if (line.isEmpty()) return;
|
||||
|
||||
System.out.println("S: " + line);
|
||||
|
||||
switch (state) {
|
||||
case LOGIN:
|
||||
if (line.startsWith("* OK")) {
|
||||
ctx.writeAndFlush(nextTag() + " LOGIN " + username + " " + password + "\r\n");
|
||||
} else if (line.contains("OK LOGIN")) {
|
||||
state = State.SELECT;
|
||||
ctx.writeAndFlush(nextTag() + " SELECT INBOX\r\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case SELECT:
|
||||
if (line.contains("OK [READ-WRITE]") || line.contains("OK [READ-ONLY]")) {
|
||||
state = State.FETCH;
|
||||
ctx.writeAndFlush(nextTag() + " FETCH 1:* (BODY.PEEK[])\r\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case FETCH:
|
||||
if (line.contains("OK FETCH completed")) {
|
||||
state = State.DONE;
|
||||
System.out.println("All emails fetched!");
|
||||
ctx.close();
|
||||
} else if (line.startsWith("*") && line.contains("FETCH")) {
|
||||
// 新邮件开始
|
||||
headerBuffer.setLength(0);
|
||||
bodyBuffer.setLength(0);
|
||||
inHeader = true;
|
||||
inAttachment = false;
|
||||
currentFilename = null;
|
||||
boundary = null;
|
||||
} else {
|
||||
if (inHeader) {
|
||||
headerBuffer.append(line).append("\r\n");
|
||||
if (line.isEmpty()) { // header结束
|
||||
inHeader = false;
|
||||
|
||||
// 解析邮件标题
|
||||
Matcher subjectMatcher = Pattern.compile("(?i)^Subject: (.+)$", Pattern.MULTILINE).matcher(headerBuffer);
|
||||
if (subjectMatcher.find()) {
|
||||
String subject = subjectMatcher.group(1).trim();
|
||||
System.out.println("Email Subject: " + subject);
|
||||
}
|
||||
|
||||
// 解析 boundary
|
||||
Matcher m = Pattern.compile("boundary=\"([^\"]+)\"").matcher(headerBuffer);
|
||||
if (m.find()) boundary = m.group(1);
|
||||
}
|
||||
} else {
|
||||
// body部分
|
||||
if (boundary != null && line.contains(boundary)) {
|
||||
// attachment结束
|
||||
if (inAttachment && currentFilename != null) {
|
||||
saveAttachment(bodyBuffer.toString(), currentFilename);
|
||||
System.out.println("Attachment saved: " + currentFilename);
|
||||
bodyBuffer.setLength(0);
|
||||
inAttachment = false;
|
||||
currentFilename = null;
|
||||
}
|
||||
} else {
|
||||
// 检测附件开始
|
||||
Matcher m = Pattern.compile("Content-Disposition: attachment; filename=\"([^\"]+)\"").matcher(line);
|
||||
if (m.find()) {
|
||||
currentFilename = m.group(1);
|
||||
inAttachment = true;
|
||||
bodyBuffer.setLength(0);
|
||||
} else if (inAttachment) {
|
||||
bodyBuffer.append(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DONE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void saveAttachment(String base64Data, String filename) {
|
||||
try {
|
||||
byte[] data = Base64.getDecoder().decode(base64Data);
|
||||
try (OutputStream os = new FileOutputStream(filename)) {
|
||||
os.write(data);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package com.email;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.LineBasedFrameDecoder;
|
||||
import io.netty.handler.codec.string.StringDecoder;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
|
||||
public class NettyImapClient {
|
||||
|
||||
private final String host;
|
||||
private final int port;
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
public NettyImapClient(String host, int port, String username, String password) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void start() throws Exception {
|
||||
EventLoopGroup group = new NioEventLoopGroup();
|
||||
SslContext sslCtx = SslContextBuilder.forClient().build();
|
||||
|
||||
try {
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(group)
|
||||
.channel(NioSocketChannel.class)
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
p.addLast(sslCtx.newHandler(ch.alloc(), host, port));
|
||||
p.addLast(new LineBasedFrameDecoder(8192));
|
||||
p.addLast(new StringDecoder());
|
||||
p.addLast(new ImapClientHandler(username, password));
|
||||
}
|
||||
});
|
||||
|
||||
ChannelFuture f = b.connect(host, port).sync();
|
||||
f.channel().closeFuture().sync();
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String host = "imap.qq.com"; // IMAP服务器
|
||||
int port = 993; // SSL端口
|
||||
String user = "717406575@qq.com";
|
||||
String pass = "dhqesrwzgblrbcac"; // 授权码
|
||||
|
||||
new NettyImapClient(host, port, user, pass).start();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user