From 9faf43a6c4b933b5f97be4348d461bf91f5bf2e2 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Mon, 24 Oct 2022 01:09:32 +0300 Subject: Spring Boot 3 --- src/main/java/com/cliqset/xrd/Alias.java | 11 +- src/main/java/com/cliqset/xrd/Expires.java | 2 +- src/main/java/com/cliqset/xrd/Link.java | 14 +- src/main/java/com/cliqset/xrd/Property.java | 12 +- src/main/java/com/cliqset/xrd/Subject.java | 10 +- src/main/java/com/cliqset/xrd/Title.java | 2 +- src/main/java/com/cliqset/xrd/XRD.java | 6 +- src/main/java/com/cliqset/xrd/package-info.java | 6 +- src/main/java/com/juick/ActivityPubManager.java | 4 +- src/main/java/com/juick/EmailManager.java | 20 +- src/main/java/com/juick/TelegramBotManager.java | 2 +- src/main/java/com/juick/XMPPManager.java | 6 +- .../config/ActivityPubClientErrorHandler.java | 3 +- .../java/com/juick/config/ActivityPubConfig.java | 3 +- .../java/com/juick/config/HttpClientConfig.java | 51 ++- src/main/java/com/juick/config/SecurityConfig.java | 236 +++++------ src/main/java/com/juick/config/WebConfig.java | 13 +- src/main/java/com/juick/model/Message.java | 12 +- src/main/java/com/juick/model/Tag.java | 7 +- src/main/java/com/juick/model/User.java | 10 +- src/main/java/com/juick/model/package-info.java | 6 +- .../java/com/juick/service/UserServiceImpl.java | 2 +- .../HTTPSignatureAuthenticationFilter.java | 17 +- .../security/HashParamAuthenticationFilter.java | 54 +-- .../com/juick/util/adapters/SimpleDateAdapter.java | 3 +- .../java/com/juick/util/xmpp/JidConverter.java | 4 +- .../java/com/juick/util/xmpp/iq/MessageQuery.java | 2 +- .../java/com/juick/util/xmpp/iq/package-info.java | 8 +- src/main/java/com/juick/www/VaryHandler.java | 2 +- src/main/java/com/juick/www/WebApp.java | 7 +- .../java/com/juick/www/api/ApiSocialLogin.java | 2 +- src/main/java/com/juick/www/api/Post.java | 2 +- src/main/java/com/juick/www/api/Service.java | 9 +- src/main/java/com/juick/www/controllers/Login.java | 3 +- .../java/com/juick/www/controllers/Settings.java | 19 +- src/main/java/com/juick/www/controllers/Site.java | 4 +- .../com/juick/www/controllers/SocialLogin.java | 12 +- .../java/com/juick/www/filters/AnythingFilter.java | 16 +- src/main/java/com/juick/www/rss/MessagesView.java | 12 +- src/main/java/com/juick/www/rss/RepliesView.java | 14 +- .../pebble/extension/FormatterExtension.java | 3 + .../extension/filters/FormatMessageFilter.java | 8 +- .../pebble/extension/filters/PrettyTimeFilter.java | 6 +- .../pebble/extension/filters/TagsListFilter.java | 6 +- .../pebble/extension/filters/TimestampFilter.java | 6 +- .../commons/mail/util/MimeMessageParser.java | 452 +++++++++++++++++++++ 46 files changed, 785 insertions(+), 324 deletions(-) create mode 100644 src/main/java/org/apache/commons/mail/util/MimeMessageParser.java (limited to 'src/main/java') diff --git a/src/main/java/com/cliqset/xrd/Alias.java b/src/main/java/com/cliqset/xrd/Alias.java index 49e4052b..3a108f46 100644 --- a/src/main/java/com/cliqset/xrd/Alias.java +++ b/src/main/java/com/cliqset/xrd/Alias.java @@ -20,13 +20,14 @@ import java.net.URI; import java.util.HashMap; import java.util.Map; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAnyAttribute; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.XmlValue; import javax.xml.namespace.QName; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAnyAttribute; +import jakarta.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.XmlValue; + @XmlType(name="Alias", namespace=XRDConstants.XRD_NAMESPACE) @XmlAccessorType(XmlAccessType.FIELD) public class Alias { diff --git a/src/main/java/com/cliqset/xrd/Expires.java b/src/main/java/com/cliqset/xrd/Expires.java index b4bcdd24..bb6704f8 100644 --- a/src/main/java/com/cliqset/xrd/Expires.java +++ b/src/main/java/com/cliqset/xrd/Expires.java @@ -21,7 +21,7 @@ import java.util.HashMap; import java.util.Map; import javax.xml.namespace.QName; -import javax.xml.bind.annotation.*; +import jakarta.xml.bind.annotation.*; @XmlType(name="Expires", namespace=XRDConstants.XRD_NAMESPACE) @XmlAccessorType(XmlAccessType.FIELD) diff --git a/src/main/java/com/cliqset/xrd/Link.java b/src/main/java/com/cliqset/xrd/Link.java index ec8522f0..020defe6 100644 --- a/src/main/java/com/cliqset/xrd/Link.java +++ b/src/main/java/com/cliqset/xrd/Link.java @@ -22,13 +22,13 @@ import java.util.List; import java.util.Map; import javax.xml.namespace.QName; -import javax.xml.bind.annotation.XmlAnyElement; -import javax.xml.bind.annotation.XmlAnyAttribute; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAnyElement; +import jakarta.xml.bind.annotation.XmlAnyAttribute; +import jakarta.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import org.w3c.dom.Element; diff --git a/src/main/java/com/cliqset/xrd/Property.java b/src/main/java/com/cliqset/xrd/Property.java index 35c7d0cc..f7777c94 100644 --- a/src/main/java/com/cliqset/xrd/Property.java +++ b/src/main/java/com/cliqset/xrd/Property.java @@ -18,12 +18,12 @@ package com.cliqset.xrd; import java.net.URI; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAnyAttribute; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.XmlValue; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAnyAttribute; +import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.XmlValue; import javax.xml.namespace.QName; import java.util.HashMap; diff --git a/src/main/java/com/cliqset/xrd/Subject.java b/src/main/java/com/cliqset/xrd/Subject.java index f6815317..d2f6b333 100644 --- a/src/main/java/com/cliqset/xrd/Subject.java +++ b/src/main/java/com/cliqset/xrd/Subject.java @@ -16,11 +16,11 @@ package com.cliqset.xrd; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAnyAttribute; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.XmlValue; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAnyAttribute; +import jakarta.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.XmlValue; import javax.xml.namespace.QName; import java.net.URI; diff --git a/src/main/java/com/cliqset/xrd/Title.java b/src/main/java/com/cliqset/xrd/Title.java index 7d6597bd..4bb43c55 100644 --- a/src/main/java/com/cliqset/xrd/Title.java +++ b/src/main/java/com/cliqset/xrd/Title.java @@ -19,7 +19,7 @@ package com.cliqset.xrd; import java.util.HashMap; import java.util.Map; -import javax.xml.bind.annotation.*; +import jakarta.xml.bind.annotation.*; import javax.xml.namespace.QName; @XmlType(name="Title", namespace=XRDConstants.XRD_NAMESPACE) diff --git a/src/main/java/com/cliqset/xrd/XRD.java b/src/main/java/com/cliqset/xrd/XRD.java index 393e977b..8fc6f7de 100644 --- a/src/main/java/com/cliqset/xrd/XRD.java +++ b/src/main/java/com/cliqset/xrd/XRD.java @@ -17,9 +17,9 @@ package com.cliqset.xrd; import javax.xml.namespace.QName; -import javax.xml.bind.annotation.*; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; +import jakarta.xml.bind.annotation.*; +import jakarta.xml.bind.JAXBContext; +import jakarta.xml.bind.JAXBException; import org.w3c.dom.Element; diff --git a/src/main/java/com/cliqset/xrd/package-info.java b/src/main/java/com/cliqset/xrd/package-info.java index bd8f0146..dfc11f51 100644 --- a/src/main/java/com/cliqset/xrd/package-info.java +++ b/src/main/java/com/cliqset/xrd/package-info.java @@ -26,8 +26,8 @@ package com.cliqset.xrd; import org.apache.commons.lang3.StringUtils; -import javax.xml.bind.annotation.XmlNs; -import javax.xml.bind.annotation.XmlNsForm; -import javax.xml.bind.annotation.XmlSchema; +import jakarta.xml.bind.annotation.XmlNs; +import jakarta.xml.bind.annotation.XmlNsForm; +import jakarta.xml.bind.annotation.XmlSchema; import static com.cliqset.xrd.XRDConstants.XRD_NAMESPACE; \ No newline at end of file diff --git a/src/main/java/com/juick/ActivityPubManager.java b/src/main/java/com/juick/ActivityPubManager.java index 613de046..7691aa6c 100644 --- a/src/main/java/com/juick/ActivityPubManager.java +++ b/src/main/java/com/juick/ActivityPubManager.java @@ -71,8 +71,8 @@ import com.juick.www.api.activity.model.objects.Image; import com.juick.www.api.activity.model.objects.Mention; import com.juick.www.api.activity.model.objects.Note; import com.juick.www.api.activity.model.objects.Person; -import com.mitchellbosecke.pebble.PebbleEngine; -import com.mitchellbosecke.pebble.template.PebbleTemplate; +import io.pebbletemplates.pebble.PebbleEngine; +import io.pebbletemplates.pebble.template.PebbleTemplate; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; diff --git a/src/main/java/com/juick/EmailManager.java b/src/main/java/com/juick/EmailManager.java index 8ef1a270..a62d31f3 100644 --- a/src/main/java/com/juick/EmailManager.java +++ b/src/main/java/com/juick/EmailManager.java @@ -22,6 +22,16 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.juick.model.Message; import com.juick.model.User; import com.juick.www.api.SystemActivity; + +import jakarta.mail.MessagingException; +import jakarta.mail.Multipart; +import jakarta.mail.Session; +import jakarta.mail.Transport; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeBodyPart; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimeMultipart; + import com.juick.util.HttpBadRequestException; import com.juick.www.WebApp; import com.juick.service.EmailService; @@ -38,14 +48,6 @@ import org.springframework.scheduling.annotation.Async; import javax.annotation.Nonnull; import javax.inject.Inject; -import javax.mail.MessagingException; -import javax.mail.Multipart; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; import java.util.*; import static com.juick.util.formatters.PlainTextFormatter.formatPost; @@ -163,7 +165,7 @@ public class EmailManager implements NotificationListener { }; String fromAddress = StringUtils.isNotEmpty(from) ? from : "juick@juick.com"; message.setFrom(fromAddress); - message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to)); + message.addRecipient(jakarta.mail.Message.RecipientType.TO, new InternetAddress(to)); message.setSubject(subject); MimeBodyPart textBodyPart = new MimeBodyPart(); textBodyPart.setContent(textPart, "text/plain; charset=UTF-8"); diff --git a/src/main/java/com/juick/TelegramBotManager.java b/src/main/java/com/juick/TelegramBotManager.java index 20f31b1b..1dcc7edb 100644 --- a/src/main/java/com/juick/TelegramBotManager.java +++ b/src/main/java/com/juick/TelegramBotManager.java @@ -54,7 +54,7 @@ import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import javax.inject.Inject; import java.io.IOException; import java.net.URI; diff --git a/src/main/java/com/juick/XMPPManager.java b/src/main/java/com/juick/XMPPManager.java index 6343378c..0878c450 100644 --- a/src/main/java/com/juick/XMPPManager.java +++ b/src/main/java/com/juick/XMPPManager.java @@ -21,6 +21,10 @@ import com.juick.model.User; import com.juick.util.formatters.PlainTextFormatter; import com.juick.model.CommandResult; import com.juick.www.api.SystemActivity; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; + import com.juick.www.WebApp; import com.juick.util.xmpp.iq.MessageQuery; import com.juick.service.MessagesService; @@ -66,8 +70,6 @@ import rocks.xmpp.extensions.version.SoftwareVersionManager; import rocks.xmpp.extensions.version.model.SoftwareVersion; import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; import javax.inject.Inject; import java.io.IOException; import java.net.MalformedURLException; diff --git a/src/main/java/com/juick/config/ActivityPubClientErrorHandler.java b/src/main/java/com/juick/config/ActivityPubClientErrorHandler.java index ecb81e30..69958bbd 100644 --- a/src/main/java/com/juick/config/ActivityPubClientErrorHandler.java +++ b/src/main/java/com/juick/config/ActivityPubClientErrorHandler.java @@ -28,7 +28,6 @@ import org.springframework.http.client.ClientHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.client.DefaultResponseErrorHandler; -import javax.annotation.Nonnull; import javax.inject.Inject; import java.io.IOException; import java.net.URI; @@ -41,7 +40,7 @@ public class ActivityPubClientErrorHandler extends DefaultResponseErrorHandler { @Inject private ApplicationEventPublisher applicationEventPublisher; @Override - public void handleError(URI contextUri, HttpMethod method, @Nonnull ClientHttpResponse response) throws IOException { + public void handleError(URI contextUri, HttpMethod method, ClientHttpResponse response) throws IOException { logger.warn("HTTP ERROR {} {} : {}", response.getStatusCode().value(), response.getStatusText(), IOUtils.toString(response.getBody(), StandardCharsets.UTF_8)); if (response.getStatusCode().equals(HttpStatus.GONE)) { diff --git a/src/main/java/com/juick/config/ActivityPubConfig.java b/src/main/java/com/juick/config/ActivityPubConfig.java index 89aacff0..fb204aaa 100644 --- a/src/main/java/com/juick/config/ActivityPubConfig.java +++ b/src/main/java/com/juick/config/ActivityPubConfig.java @@ -20,7 +20,8 @@ package com.juick.config; import com.fasterxml.jackson.databind.ObjectMapper; import com.juick.ActivityPubManager; import com.juick.util.ActivityPubRequestInterceptor; -import org.apache.http.impl.client.CloseableHttpClient; + +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; diff --git a/src/main/java/com/juick/config/HttpClientConfig.java b/src/main/java/com/juick/config/HttpClientConfig.java index 22c06ecb..99351828 100644 --- a/src/main/java/com/juick/config/HttpClientConfig.java +++ b/src/main/java/com/juick/config/HttpClientConfig.java @@ -1,20 +1,19 @@ package com.juick.config; import java.util.concurrent.TimeUnit; - -import org.apache.http.HeaderElement; -import org.apache.http.HeaderElementIterator; -import org.apache.http.HttpResponse; -import org.apache.http.client.config.CookieSpecs; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.conn.ConnectionKeepAliveStrategy; - -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.http.message.BasicHeaderElementIterator; -import org.apache.http.protocol.HTTP; -import org.apache.http.protocol.HttpContext; + +import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.cookie.StandardCookieSpec; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; +import org.apache.hc.core5.http.HeaderElement; +import org.apache.hc.core5.http.HttpResponse; +import org.apache.hc.core5.http.message.BasicHeaderElementIterator; +import org.apache.hc.core5.http.protocol.HttpContext; +import org.apache.hc.core5.util.TimeValue; +import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; @@ -55,19 +54,19 @@ public class HttpClientConfig { public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() { return new ConnectionKeepAliveStrategy() { @Override - public long getKeepAliveDuration(HttpResponse response, HttpContext context) { - HeaderElementIterator it = new BasicHeaderElementIterator - (response.headerIterator(HTTP.CONN_KEEP_ALIVE)); + public TimeValue getKeepAliveDuration(HttpResponse response, HttpContext context) { + BasicHeaderElementIterator it = new BasicHeaderElementIterator + (response.headerIterator("Keep-Alive")); while (it.hasNext()) { - HeaderElement he = it.nextElement(); + HeaderElement he = it.next(); String param = he.getName(); String value = he.getValue(); if (value != null && param.equalsIgnoreCase("timeout")) { - return Long.parseLong(value) * 1000; + return TimeValue.of(Long.parseLong(value) * 1000, TimeUnit.MILLISECONDS); } } - return DEFAULT_KEEP_ALIVE_TIME_MILLIS; + return TimeValue.of(DEFAULT_KEEP_ALIVE_TIME_MILLIS, TimeUnit.MILLISECONDS); } }; } @@ -75,10 +74,10 @@ public class HttpClientConfig { @Bean public CloseableHttpClient httpClient() { RequestConfig requestConfig = RequestConfig.custom() - .setCookieSpec(CookieSpecs.IGNORE_COOKIES) - .setConnectionRequestTimeout(REQUEST_TIMEOUT) - .setConnectTimeout(CONNECT_TIMEOUT) - .setSocketTimeout(SOCKET_TIMEOUT).build(); + .setCookieSpec(StandardCookieSpec.IGNORE) + .setConnectionRequestTimeout(Timeout.of(REQUEST_TIMEOUT, TimeUnit.MILLISECONDS)) + .setConnectTimeout(Timeout.of(CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)) + .setResponseTimeout(Timeout.of(SOCKET_TIMEOUT, TimeUnit.MILLISECONDS)).build(); return HttpClients.custom() .setDefaultRequestConfig(requestConfig) @@ -96,8 +95,8 @@ public class HttpClientConfig { try { if (connectionManager != null) { LOGGER.trace("run IdleConnectionMonitor - Closing expired and idle connections..."); - connectionManager.closeExpiredConnections(); - connectionManager.closeIdleConnections(CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS, TimeUnit.SECONDS); + connectionManager.closeExpired(); + connectionManager.closeIdle(TimeValue.of(CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS, TimeUnit.SECONDS)); } else { LOGGER.trace("run IdleConnectionMonitor - Http Client Connection manager is not initialised"); } diff --git a/src/main/java/com/juick/config/SecurityConfig.java b/src/main/java/com/juick/config/SecurityConfig.java index 1907c7c5..b5c93906 100644 --- a/src/main/java/com/juick/config/SecurityConfig.java +++ b/src/main/java/com/juick/config/SecurityConfig.java @@ -23,20 +23,17 @@ import com.juick.service.security.HTTPSignatureAuthenticationFilter; import com.juick.service.security.HashParamAuthenticationFilter; import com.juick.service.security.JuickUserDetailsService; import com.juick.service.security.entities.JuickUser; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.authentication.NullRememberMeServices; +import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.RememberMeServices; import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices; import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; @@ -46,18 +43,18 @@ import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import javax.annotation.Resource; -import javax.inject.Inject; import java.util.Arrays; import java.util.Collections; -import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; /** * Created by aalexeev on 11/21/16. */ @EnableWebSecurity +@Configuration public class SecurityConfig { - @Resource + @Inject private UserService userService; private static final String COOKIE_NAME = "juick-remember-me"; @@ -66,6 +63,7 @@ public class SecurityConfig { public UserDetailsService userDetailsService() { return new JuickUserDetailsService(userService); } + @Bean static CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); @@ -81,132 +79,106 @@ public class SecurityConfig { return source; } - @Configuration + @Inject + private SignatureManager signatureManager; + + @Bean + public HashParamAuthenticationFilter apiAuthenticationFilter() { + return new HashParamAuthenticationFilter(userService, null); + } + + @Bean + public AuthenticationEntryPoint juickAuthenticationEntryPoint() { + var entryPoint = new BasicAuthenticationEntryPoint(); + entryPoint.setRealmName("Juick"); + return entryPoint; + } + + @Value("${auth_remember_me_key:secret}") + private String rememberMeKey; + @Value("${web_domain:localhost}") + private String webDomain; + + @Bean + public HashParamAuthenticationFilter wwwAuthenticationFilter() { + return new HashParamAuthenticationFilter(userService, hashCookieServices()); + } + + @Bean + public RememberMeServices hashCookieServices() { + TokenBasedRememberMeServices services = new TokenBasedRememberMeServices( + rememberMeKey, userDetailsService()); + + services.setCookieName(COOKIE_NAME); + services.setCookieDomain(webDomain); + services.setAlwaysRemember(true); + services.setTokenValiditySeconds(6 * 30 * 24 * 3600); + services.setUseSecureCookie(false); // TODO set true if https is supports + return services; + } + + @Bean @Order(1) - public static class ApiConfig extends WebSecurityConfigurerAdapter { - @Value("${auth_remember_me_key:secret}") - private String rememberMeKey; - @Resource - private UserService userService; - @Resource - private SignatureManager signatureManager; - ApiConfig() { - super(true); - } - @Bean - RememberMeServices apiTokenServices() { - return new NullRememberMeServices(); - } - @Bean - public HashParamAuthenticationFilter apiAuthenticationFilter() { - return new HashParamAuthenticationFilter(userService, apiTokenServices()); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - http.antMatcher("/api/**") - .addFilterBefore(apiAuthenticationFilter(), BasicAuthenticationFilter.class) - .addFilterBefore(new HTTPSignatureAuthenticationFilter(signatureManager, userService), BasicAuthenticationFilter.class) - .authorizeRequests() - .antMatchers(HttpMethod.OPTIONS).permitAll() - .antMatchers("/api/", "/api/messages", "/api/avatar", "/api/messages/discussions", - "/api/users", "/api/thread", "/api/tags", "/api/tlgmbtwbhk", "/api/fbwbhk", - "/api/skypebotendpoint", "/api/_fblogin", "/api/_vklogin", "/api/_tglogin", - "/api/_google", "/api/_applelogin", "/api/signup", "/api/inbox", "/api/events", "/api/info/**", - "/api/nodeinfo/2.0").permitAll() - .anyRequest().hasRole("USER") - .and() - .anonymous().principal(JuickUser.ANONYMOUS_USER).authorities(JuickUser.ANONYMOUS_AUTHORITY) - .and() - .httpBasic().authenticationEntryPoint(juickAuthenticationEntryPoint()) - .and().cors().configurationSource(corsConfigurationSource()) - .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and().exceptionHandling().authenticationEntryPoint(juickAuthenticationEntryPoint()) - .and() - .rememberMe() - .alwaysRemember(true) - .tokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(6 * 30)) - .rememberMeServices(apiTokenServices()) - .key(rememberMeKey) - .and() - .headers().defaultsDisabled().cacheControl(); - } - - @Bean - public AuthenticationEntryPoint juickAuthenticationEntryPoint() { - var entryPoint = new BasicAuthenticationEntryPoint(); - entryPoint.setRealmName("Juick"); - return entryPoint; - } + public SecurityFilterChain apiChain(HttpSecurity http) throws Exception { + http.securityMatcher("/api/**") + .addFilterBefore(apiAuthenticationFilter(), BasicAuthenticationFilter.class) + .addFilterBefore(new HTTPSignatureAuthenticationFilter(signatureManager, userService), + BasicAuthenticationFilter.class) + .authorizeHttpRequests(requests -> requests + .requestMatchers(HttpMethod.OPTIONS).permitAll() + .requestMatchers("/api/", "/api/messages", "/api/avatar", "/api/messages/discussions", + "/api/users", "/api/thread", "/api/tags", "/api/tlgmbtwbhk", "/api/fbwbhk", + "/api/skypebotendpoint", "/api/_fblogin", "/api/_vklogin", "/api/_tglogin", + "/api/_google", "/api/_applelogin", "/api/signup", "/api/inbox", "/api/events", + "/api/info/**", + "/api/nodeinfo/2.0") + .permitAll() + .anyRequest().hasRole("USER")) + .anonymous(anonymous -> anonymous.principal(JuickUser.ANONYMOUS_USER) + .authorities(JuickUser.ANONYMOUS_AUTHORITY)) + .httpBasic(httpBasic -> httpBasic.authenticationEntryPoint(juickAuthenticationEntryPoint())) + .cors(cors -> cors.configurationSource(corsConfigurationSource())) + .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .exceptionHandling(exceptionHandling -> exceptionHandling.authenticationEntryPoint(juickAuthenticationEntryPoint())) + .csrf().disable() + .headers().defaultsDisabled().cacheControl(); + return http.build(); } - @Configuration - public static class WebConfig extends WebSecurityConfigurerAdapter { - @Value("${auth_remember_me_key:secret}") - private String rememberMeKey; - @Value("${web_domain:localhost}") - private String webDomain; - @Resource - private UserService userService; - @Inject - private UserDetailsService userDetailsService; - @Bean - @Qualifier("www") - public HashParamAuthenticationFilter wwwAuthenticationFilter() { - return new HashParamAuthenticationFilter(userService, hashCookieServices()); - } - @Bean - @Qualifier("www") - public RememberMeServices hashCookieServices() { - TokenBasedRememberMeServices services = new TokenBasedRememberMeServices( - rememberMeKey, userDetailsService); - - services.setCookieName(COOKIE_NAME); - services.setCookieDomain(webDomain); - services.setAlwaysRemember(true); - services.setTokenValiditySeconds(6 * 30 * 24 * 3600); - services.setUseSecureCookie(false); // TODO set true if https is supports - - return services; - } - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .addFilterBefore(wwwAuthenticationFilter(), BasicAuthenticationFilter.class) - .authorizeRequests() - .antMatchers("/settings", "/pm/**", "/**/bl", "/_twitter", "/post", "/post2", "/comment") - .authenticated() - .antMatchers("/actuator/**").hasRole("ADMIN") - .anyRequest().permitAll() - .and() - .anonymous().principal(JuickUser.ANONYMOUS_USER).authorities(JuickUser.ANONYMOUS_AUTHORITY) - .and().cors().configurationSource(corsConfigurationSource()) - .and().sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .invalidSessionUrl("/") - .and() - .logout() - .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) - .invalidateHttpSession(true) - .logoutUrl("/logout") - .logoutSuccessUrl("/") - .deleteCookies("hash", COOKIE_NAME) - .and() - .formLogin() - .loginPage("/login") - .permitAll() - .defaultSuccessUrl("/") - .loginProcessingUrl("/login") - .usernameParameter("username") - .passwordParameter("password") - .failureUrl("/login?error=1") - .and() - .rememberMe() - .rememberMeCookieDomain(webDomain).key(rememberMeKey) - .rememberMeServices(hashCookieServices()) - .and() - .csrf().disable() - .headers().defaultsDisabled().cacheControl(); - } + @Bean + public SecurityFilterChain wwwChain(HttpSecurity http) throws Exception { + http.addFilterBefore(wwwAuthenticationFilter(), BasicAuthenticationFilter.class) + .authorizeHttpRequests(authorize -> authorize + .requestMatchers("/settings", "/pm/**", "/**/bl", "/_twitter", "/post", "/post2", + "/comment") + .authenticated() + .requestMatchers("/actuator/**").hasRole("ADMIN") + .anyRequest().permitAll()) + .anonymous(anonymous -> anonymous.principal(JuickUser.ANONYMOUS_USER) + .authorities(JuickUser.ANONYMOUS_AUTHORITY)) + .cors(cors -> cors + .configurationSource(corsConfigurationSource())) + .sessionManagement( + sessionManagement -> sessionManagement + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .invalidSessionUrl("/")) + .logout(logout -> logout + .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) + .invalidateHttpSession(true) + .logoutSuccessUrl("/") + .deleteCookies("hash", COOKIE_NAME)) + .formLogin(form -> form.loginPage("/login") + .defaultSuccessUrl("/") + .loginProcessingUrl("/login") + .usernameParameter("username") + .passwordParameter("password") + .failureUrl("/login?error=1") + .permitAll()) + .csrf(csrf -> csrf.ignoringRequestMatchers("/settings/unsubscribe")) + .rememberMe(rememberMe -> rememberMe + .rememberMeCookieDomain(webDomain).key(rememberMeKey) + .rememberMeServices(hashCookieServices())) + .headers().defaultsDisabled().cacheControl(); + return http.build(); } } diff --git a/src/main/java/com/juick/config/WebConfig.java b/src/main/java/com/juick/config/WebConfig.java index 59f9a99c..bfebd77f 100644 --- a/src/main/java/com/juick/config/WebConfig.java +++ b/src/main/java/com/juick/config/WebConfig.java @@ -23,14 +23,16 @@ import com.juick.service.HelpService; import com.juick.service.StorageService; import com.juick.service.FileSystemStorageService; import com.juick.service.UserService; -import com.mitchellbosecke.pebble.PebbleEngine; import com.mitchellbosecke.pebble.extension.FormatterExtension; -import com.mitchellbosecke.pebble.loader.ClasspathLoader; -import com.mitchellbosecke.pebble.loader.Loader; -import com.mitchellbosecke.pebble.spring.extension.SpringExtension; -import com.mitchellbosecke.pebble.spring.servlet.PebbleViewResolver; import com.overzealous.remark.Options; import com.overzealous.remark.Remark; + +import io.pebbletemplates.pebble.PebbleEngine; +import io.pebbletemplates.pebble.loader.ClasspathLoader; +import io.pebbletemplates.pebble.loader.Loader; +import io.pebbletemplates.spring.extension.SpringExtension; +import io.pebbletemplates.spring.servlet.PebbleViewResolver; + import org.apache.commons.codec.CharEncoding; import org.commonmark.ext.autolink.AutolinkExtension; import org.commonmark.node.Link; @@ -175,6 +177,7 @@ public class WebConfig implements WebMvcConfigurer { viewResolver.setPrefix("templates"); viewResolver.setSuffix(".html"); viewResolver.setCharacterEncoding(CharEncoding.UTF_8); + viewResolver.setExposeRequestAttributes(true); return viewResolver; } @Override diff --git a/src/main/java/com/juick/model/Message.java b/src/main/java/com/juick/model/Message.java index 2a5883ac..cfd2582b 100644 --- a/src/main/java/com/juick/model/Message.java +++ b/src/main/java/com/juick/model/Message.java @@ -20,11 +20,17 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.juick.util.adapters.SimpleDateAdapter; + +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; +import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + import org.apache.commons.lang3.builder.ToStringBuilder; import javax.annotation.Nonnull; -import javax.xml.bind.annotation.*; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import java.io.Serializable; import java.net.URI; import java.time.Instant; @@ -120,7 +126,7 @@ public class Message implements Comparable, Serializable { } @Override - public int compareTo(@Nonnull Message jmsg) throws ClassCastException { + public int compareTo(Message jmsg) throws ClassCastException { if (jmsg == this) return 0; diff --git a/src/main/java/com/juick/model/Tag.java b/src/main/java/com/juick/model/Tag.java index 75266303..e7fcde5d 100644 --- a/src/main/java/com/juick/model/Tag.java +++ b/src/main/java/com/juick/model/Tag.java @@ -18,7 +18,12 @@ package com.juick.model; import com.fasterxml.jackson.annotation.JsonValue; -import javax.xml.bind.annotation.*; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; +import jakarta.xml.bind.annotation.XmlValue; + import java.io.Serializable; import java.util.Comparator; import java.util.Objects; diff --git a/src/main/java/com/juick/model/User.java b/src/main/java/com/juick/model/User.java index b43257ed..5cc0d125 100644 --- a/src/main/java/com/juick/model/User.java +++ b/src/main/java/com/juick/model/User.java @@ -18,14 +18,16 @@ package com.juick.model; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; + import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.ToStringBuilder; import javax.annotation.Nonnull; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlTransient; import java.io.Serializable; import java.net.URI; import java.time.Instant; diff --git a/src/main/java/com/juick/model/package-info.java b/src/main/java/com/juick/model/package-info.java index be9e6c2b..9e8d4b34 100644 --- a/src/main/java/com/juick/model/package-info.java +++ b/src/main/java/com/juick/model/package-info.java @@ -30,6 +30,6 @@ package com.juick.model; import org.apache.commons.lang3.StringUtils; -import javax.xml.bind.annotation.XmlNs; -import javax.xml.bind.annotation.XmlNsForm; -import javax.xml.bind.annotation.XmlSchema; \ No newline at end of file +import jakarta.xml.bind.annotation.XmlNs; +import jakarta.xml.bind.annotation.XmlNsForm; +import jakarta.xml.bind.annotation.XmlSchema; \ No newline at end of file diff --git a/src/main/java/com/juick/service/UserServiceImpl.java b/src/main/java/com/juick/service/UserServiceImpl.java index 36f37d5e..71f2dc91 100644 --- a/src/main/java/com/juick/service/UserServiceImpl.java +++ b/src/main/java/com/juick/service/UserServiceImpl.java @@ -129,7 +129,7 @@ public class UserServiceImpl extends BaseJdbcService implements UserService { return stmt; }, holder); - } catch (DuplicateKeyException e) { + } catch (DataIntegrityViolationException e) { throw new UsernameTakenException(); } diff --git a/src/main/java/com/juick/service/security/HTTPSignatureAuthenticationFilter.java b/src/main/java/com/juick/service/security/HTTPSignatureAuthenticationFilter.java index 4eeb41b4..723cf576 100644 --- a/src/main/java/com/juick/service/security/HTTPSignatureAuthenticationFilter.java +++ b/src/main/java/com/juick/service/security/HTTPSignatureAuthenticationFilter.java @@ -28,11 +28,10 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; -import javax.annotation.Nonnull; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Collections; import java.util.Map; @@ -43,15 +42,16 @@ public class HTTPSignatureAuthenticationFilter extends OncePerRequestFilter { private final SignatureManager signatureManager; private final UserService userService; - public HTTPSignatureAuthenticationFilter( final SignatureManager signatureManager, final UserService userService) { this.signatureManager = signatureManager; this.userService = userService; } + @Override - protected void doFilterInternal(@Nonnull HttpServletRequest request, @Nonnull HttpServletResponse response, @Nonnull FilterChain filterChain) throws IOException, ServletException { + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws IOException, ServletException { if (authenticationIsRequired()) { Map headers = Collections.list(request.getHeaderNames()) .stream() @@ -63,7 +63,8 @@ public class HTTPSignatureAuthenticationFilter extends OncePerRequestFilter { if (userUri.length() == 0) { User userWithPassword = userService.getUserByName(user.getName()); userWithPassword.setAuthHash(userService.getHashByUID(userWithPassword.getUid())); - Authentication authentication = new UsernamePasswordAuthenticationToken(userWithPassword.getName(), userWithPassword.getCredentials()); + Authentication authentication = new UsernamePasswordAuthenticationToken( + new JuickUser(user), userWithPassword.getCredentials(), JuickUser.USER_AUTHORITY); SecurityContextHolder.getContext().setAuthentication(authentication); } else { Authentication authentication = new AnonymousAuthenticationToken(userUri, diff --git a/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java b/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java index 68ae91ee..0f4ac66f 100644 --- a/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java +++ b/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java @@ -21,41 +21,40 @@ import com.juick.model.User; import com.juick.service.UserService; import com.juick.service.security.entities.JuickUser; import org.apache.commons.lang3.StringUtils; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.RememberMeAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.NullRememberMeServices; import org.springframework.security.web.authentication.RememberMeServices; import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices; import org.springframework.util.Assert; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.WebUtils; -import javax.annotation.Nonnull; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; /** * Created by aalexeev on 4/5/17. */ public class HashParamAuthenticationFilter extends OncePerRequestFilter { + public static final String PARAM_NAME = "hash"; private final UserService userService; private final RememberMeServices rememberMeServices; - public HashParamAuthenticationFilter( - final UserService userService, - final RememberMeServices rememberMeServices) { + @NonNull final UserService userService, + @Nullable final RememberMeServices rememberMeServices) { Assert.notNull(userService, "userService should not be null"); - Assert.notNull(rememberMeServices, "rememberMeServices should not be null"); this.userService = userService; this.rememberMeServices = rememberMeServices; @@ -63,28 +62,31 @@ public class HashParamAuthenticationFilter extends OncePerRequestFilter { @Override protected void doFilterInternal( - @Nonnull HttpServletRequest request, - @Nonnull HttpServletResponse response, - @Nonnull FilterChain filterChain) throws ServletException, IOException { + HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { String hash = getHashFromRequest(request); - if (hash != null && authenticationIsRequired()) { User user = userService.getUserByHash(hash); - if (!user.isAnonymous()) { User userWithPassword = userService.getUserByName(user.getName()); userWithPassword.setAuthHash(userService.getHashByUID(userWithPassword.getUid())); - Authentication authentication = rememberMeServices instanceof NullRememberMeServices - ? new UsernamePasswordAuthenticationToken(userWithPassword.getName(), - userWithPassword.getCredentials()) - : new RememberMeAuthenticationToken( - ((AbstractRememberMeServices)rememberMeServices).getKey(), - new JuickUser(userWithPassword), JuickUser.USER_AUTHORITY); - - SecurityContextHolder.getContext().setAuthentication(authentication); - - rememberMeServices.loginSuccess(request, response, authentication); + if (rememberMeServices != null) { + // web login should create cookie + var authentication = new RememberMeAuthenticationToken( + ((AbstractRememberMeServices) rememberMeServices).getKey(), + new JuickUser(userWithPassword), JuickUser.USER_AUTHORITY); + SecurityContextHolder.getContext().setAuthentication(authentication); + rememberMeServices.loginSuccess(request, response, authentication); + } else { + Authentication authentication = new UsernamePasswordAuthenticationToken( + new JuickUser(userWithPassword), + userWithPassword.getCredentials(), + JuickUser.USER_AUTHORITY); + SecurityContextHolder.getContext().setAuthentication(authentication); + + } } } diff --git a/src/main/java/com/juick/util/adapters/SimpleDateAdapter.java b/src/main/java/com/juick/util/adapters/SimpleDateAdapter.java index 46d4cc47..664a37a0 100644 --- a/src/main/java/com/juick/util/adapters/SimpleDateAdapter.java +++ b/src/main/java/com/juick/util/adapters/SimpleDateAdapter.java @@ -19,7 +19,8 @@ package com.juick.util.adapters; import com.juick.util.DateFormattersHolder; -import javax.xml.bind.annotation.adapters.XmlAdapter; +import jakarta.xml.bind.annotation.adapters.XmlAdapter; + import java.time.Instant; /** diff --git a/src/main/java/com/juick/util/xmpp/JidConverter.java b/src/main/java/com/juick/util/xmpp/JidConverter.java index f2add967..4f457164 100644 --- a/src/main/java/com/juick/util/xmpp/JidConverter.java +++ b/src/main/java/com/juick/util/xmpp/JidConverter.java @@ -21,12 +21,10 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.lang.Nullable; import rocks.xmpp.addr.Jid; -import javax.annotation.Nonnull; - public class JidConverter implements Converter { @Nullable @Override - public Jid convert(@Nonnull String jidStr) { + public Jid convert(String jidStr) { return Jid.of(jidStr); } } diff --git a/src/main/java/com/juick/util/xmpp/iq/MessageQuery.java b/src/main/java/com/juick/util/xmpp/iq/MessageQuery.java index 8a8d5614..aa1c1573 100644 --- a/src/main/java/com/juick/util/xmpp/iq/MessageQuery.java +++ b/src/main/java/com/juick/util/xmpp/iq/MessageQuery.java @@ -17,7 +17,7 @@ package com.juick.util.xmpp.iq; -import javax.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "query") public class MessageQuery { diff --git a/src/main/java/com/juick/util/xmpp/iq/package-info.java b/src/main/java/com/juick/util/xmpp/iq/package-info.java index 6d0395b1..cf8dcd4d 100644 --- a/src/main/java/com/juick/util/xmpp/iq/package-info.java +++ b/src/main/java/com/juick/util/xmpp/iq/package-info.java @@ -19,7 +19,7 @@ @XmlSchema(namespace = "http://juick.com/", elementFormDefault = XmlNsForm.QUALIFIED) package com.juick.util.xmpp.iq; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlNsForm; -import javax.xml.bind.annotation.XmlSchema; \ No newline at end of file +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlNsForm; +import jakarta.xml.bind.annotation.XmlSchema; \ No newline at end of file diff --git a/src/main/java/com/juick/www/VaryHandler.java b/src/main/java/com/juick/www/VaryHandler.java index 372847a9..6910823d 100644 --- a/src/main/java/com/juick/www/VaryHandler.java +++ b/src/main/java/com/juick/www/VaryHandler.java @@ -20,7 +20,7 @@ package com.juick.www; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ModelAttribute; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponse; @ControllerAdvice public class VaryHandler { diff --git a/src/main/java/com/juick/www/WebApp.java b/src/main/java/com/juick/www/WebApp.java index cebedb9b..a7c5eb8b 100644 --- a/src/main/java/com/juick/www/WebApp.java +++ b/src/main/java/com/juick/www/WebApp.java @@ -27,15 +27,16 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Stream; -import javax.annotation.PostConstruct; import javax.inject.Inject; import com.juick.model.Message; import com.juick.model.Tag; import com.juick.model.User; import com.juick.service.TagService; -import com.mitchellbosecke.pebble.PebbleEngine; -import com.mitchellbosecke.pebble.template.PebbleTemplate; +import io.pebbletemplates.pebble.PebbleEngine; +import io.pebbletemplates.pebble.template.PebbleTemplate; + +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/com/juick/www/api/ApiSocialLogin.java b/src/main/java/com/juick/www/api/ApiSocialLogin.java index ccb9f923..38884cd6 100644 --- a/src/main/java/com/juick/www/api/ApiSocialLogin.java +++ b/src/main/java/com/juick/www/api/ApiSocialLogin.java @@ -48,7 +48,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.util.UriComponentsBuilder; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import javax.inject.Inject; import java.io.IOException; import java.security.GeneralSecurityException; diff --git a/src/main/java/com/juick/www/api/Post.java b/src/main/java/com/juick/www/api/Post.java index bced92bf..9faf883a 100644 --- a/src/main/java/com/juick/www/api/Post.java +++ b/src/main/java/com/juick/www/api/Post.java @@ -23,7 +23,7 @@ import java.util.List; import java.util.Optional; import javax.inject.Inject; -import javax.validation.constraints.NotNull; +import jakarta.validation.constraints.NotNull; import com.juick.ActivityPubManager; import com.juick.CommandsManager; diff --git a/src/main/java/com/juick/www/api/Service.java b/src/main/java/com/juick/www/api/Service.java index 81b24a23..3bb760ff 100644 --- a/src/main/java/com/juick/www/api/Service.java +++ b/src/main/java/com/juick/www/api/Service.java @@ -32,6 +32,11 @@ import com.juick.service.UserService; import com.juick.service.component.AccountVerificationEvent; import com.juick.service.security.annotation.Visitor; import io.swagger.v3.oas.annotations.Hidden; +import jakarta.mail.Session; +import jakarta.mail.internet.AddressException; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; + import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.RandomStringUtils; @@ -52,10 +57,6 @@ import org.springframework.web.context.request.async.AsyncRequestTimeoutExceptio import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import javax.inject.Inject; -import javax.mail.Session; -import javax.mail.internet.AddressException; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; diff --git a/src/main/java/com/juick/www/controllers/Login.java b/src/main/java/com/juick/www/controllers/Login.java index 41d902de..f78ccef0 100644 --- a/src/main/java/com/juick/www/controllers/Login.java +++ b/src/main/java/com/juick/www/controllers/Login.java @@ -20,6 +20,8 @@ import com.juick.model.User; import com.juick.service.UserService; import com.juick.service.security.annotation.Visitor; +import jakarta.servlet.http.HttpSession; + import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.WebAttributes; import org.springframework.stereotype.Controller; @@ -28,7 +30,6 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import javax.inject.Inject; -import javax.servlet.http.HttpSession; /** * @author Ugnich Anton diff --git a/src/main/java/com/juick/www/controllers/Settings.java b/src/main/java/com/juick/www/controllers/Settings.java index 1dc5f1c3..1e40b9d1 100644 --- a/src/main/java/com/juick/www/controllers/Settings.java +++ b/src/main/java/com/juick/www/controllers/Settings.java @@ -25,15 +25,6 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import javax.inject.Inject; -import javax.mail.Message; -import javax.mail.MessagingException; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import com.juick.model.NotifyOpts; import com.juick.model.User; @@ -49,6 +40,16 @@ import com.juick.util.HttpBadRequestException; import com.juick.util.HttpUtils; import com.juick.www.WebApp; +import jakarta.mail.Message; +import jakarta.mail.MessagingException; +import jakarta.mail.Session; +import jakarta.mail.Transport; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; diff --git a/src/main/java/com/juick/www/controllers/Site.java b/src/main/java/com/juick/www/controllers/Site.java index 5f034b86..46440b2b 100644 --- a/src/main/java/com/juick/www/controllers/Site.java +++ b/src/main/java/com/juick/www/controllers/Site.java @@ -24,6 +24,9 @@ import com.juick.util.HttpForbiddenException; import com.juick.util.HttpNotFoundException; import com.juick.util.WebUtils; import com.juick.www.WebApp; + +import jakarta.servlet.http.HttpServletRequest; + import com.juick.service.*; import com.juick.service.security.annotation.Visitor; import com.juick.util.MessageUtils; @@ -42,7 +45,6 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; import javax.inject.Inject; -import javax.servlet.http.HttpServletRequest; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; diff --git a/src/main/java/com/juick/www/controllers/SocialLogin.java b/src/main/java/com/juick/www/controllers/SocialLogin.java index f4f8dbfd..dad3d969 100644 --- a/src/main/java/com/juick/www/controllers/SocialLogin.java +++ b/src/main/java/com/juick/www/controllers/SocialLogin.java @@ -29,6 +29,13 @@ import com.juick.service.TelegramService; import com.juick.service.UserService; import com.juick.service.security.annotation.Visitor; import com.juick.util.HttpBadRequestException; + +import jakarta.annotation.PostConstruct; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; + import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.HmacAlgorithms; import org.apache.commons.codec.digest.HmacUtils; @@ -46,12 +53,7 @@ import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.util.UriComponentsBuilder; -import javax.annotation.PostConstruct; import javax.inject.Inject; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; import java.io.IOException; import java.util.List; diff --git a/src/main/java/com/juick/www/filters/AnythingFilter.java b/src/main/java/com/juick/www/filters/AnythingFilter.java index 7f0950a4..a725a66f 100644 --- a/src/main/java/com/juick/www/filters/AnythingFilter.java +++ b/src/main/java/com/juick/www/filters/AnythingFilter.java @@ -19,6 +19,12 @@ package com.juick.www.filters; import com.juick.model.User; import com.juick.util.WebUtils; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import com.juick.service.MessagesService; import com.juick.service.UserService; import org.apache.commons.lang3.math.NumberUtils; @@ -27,11 +33,7 @@ import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import org.springframework.web.util.UriComponents; -import javax.annotation.Nonnull; import javax.inject.Inject; -import javax.servlet.*; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component @@ -42,9 +44,9 @@ public class AnythingFilter extends OncePerRequestFilter { private UserService userService; @Override - public void doFilterInternal(@Nonnull HttpServletRequest servletRequest, - @Nonnull HttpServletResponse servletResponse, - @Nonnull FilterChain filterChain) throws IOException, ServletException { + public void doFilterInternal(HttpServletRequest servletRequest, + HttpServletResponse servletResponse, + FilterChain filterChain) throws IOException, ServletException { String upgrade = servletRequest.getHeader("Connection"); if (upgrade != null && upgrade.equals("Upgrade")) { filterChain.doFilter(servletRequest, servletResponse); diff --git a/src/main/java/com/juick/www/rss/MessagesView.java b/src/main/java/com/juick/www/rss/MessagesView.java index 2c7d4a7d..dde67d7b 100644 --- a/src/main/java/com/juick/www/rss/MessagesView.java +++ b/src/main/java/com/juick/www/rss/MessagesView.java @@ -26,11 +26,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; import javax.inject.Inject; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import com.juick.model.Attachment; import com.juick.model.Message; @@ -57,6 +53,10 @@ import com.rometools.rome.feed.rss.Guid; import com.rometools.rome.feed.rss.Image; import com.rometools.rome.feed.rss.Item; +import jakarta.annotation.PostConstruct; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -81,8 +81,8 @@ public class MessagesView extends AbstractRssFeedView { @SuppressWarnings("unchecked") @Override - protected List buildFeedItems(@Nonnull Map model, @Nonnull HttpServletRequest request, - @Nonnull HttpServletResponse response) { + protected List buildFeedItems(Map model, HttpServletRequest request, + HttpServletResponse response) { List msgs = (List) model.get("messages"); return msgs.stream().map(this::createRssItem).collect(Collectors.toList()); } diff --git a/src/main/java/com/juick/www/rss/RepliesView.java b/src/main/java/com/juick/www/rss/RepliesView.java index 9cb98c94..83c8e104 100644 --- a/src/main/java/com/juick/www/rss/RepliesView.java +++ b/src/main/java/com/juick/www/rss/RepliesView.java @@ -30,15 +30,17 @@ import com.rometools.rome.feed.rss.Channel; import com.rometools.rome.feed.rss.Description; import com.rometools.rome.feed.rss.Guid; import com.rometools.rome.feed.rss.Item; + +import jakarta.annotation.PostConstruct; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.view.feed.AbstractRssFeedView; import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.net.URI; import java.net.URISyntaxException; import java.util.List; @@ -59,9 +61,9 @@ public class RepliesView extends AbstractRssFeedView { @SuppressWarnings("unchecked") @Override - protected @Nonnull List buildFeedItems(@Nonnull Map model, - @Nonnull HttpServletRequest request, - @Nonnull HttpServletResponse response) { + protected @Nonnull List buildFeedItems(Map model, + HttpServletRequest request, + HttpServletResponse response) { List msgs = (List)model.get("messages"); return msgs.stream().map(this::createRssItem).collect(Collectors.toList()); } diff --git a/src/main/java/com/mitchellbosecke/pebble/extension/FormatterExtension.java b/src/main/java/com/mitchellbosecke/pebble/extension/FormatterExtension.java index 9feca15b..bd8ba19d 100644 --- a/src/main/java/com/mitchellbosecke/pebble/extension/FormatterExtension.java +++ b/src/main/java/com/mitchellbosecke/pebble/extension/FormatterExtension.java @@ -19,6 +19,9 @@ package com.mitchellbosecke.pebble.extension; import com.mitchellbosecke.pebble.extension.filters.*; +import io.pebbletemplates.pebble.extension.AbstractExtension; +import io.pebbletemplates.pebble.extension.Filter; + import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatMessageFilter.java b/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatMessageFilter.java index 41c80691..0f4ba034 100644 --- a/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatMessageFilter.java +++ b/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatMessageFilter.java @@ -19,10 +19,10 @@ package com.mitchellbosecke.pebble.extension.filters; import com.juick.model.Message; import com.juick.util.MessageUtils; -import com.mitchellbosecke.pebble.extension.Filter; -import com.mitchellbosecke.pebble.extension.escaper.SafeString; -import com.mitchellbosecke.pebble.template.EvaluationContext; -import com.mitchellbosecke.pebble.template.PebbleTemplate; +import io.pebbletemplates.pebble.extension.Filter; +import io.pebbletemplates.pebble.extension.escaper.SafeString; +import io.pebbletemplates.pebble.template.EvaluationContext; +import io.pebbletemplates.pebble.template.PebbleTemplate; import org.apache.commons.lang3.StringUtils; import java.util.List; diff --git a/src/main/java/com/mitchellbosecke/pebble/extension/filters/PrettyTimeFilter.java b/src/main/java/com/mitchellbosecke/pebble/extension/filters/PrettyTimeFilter.java index 9c79e563..f5d82bfe 100644 --- a/src/main/java/com/mitchellbosecke/pebble/extension/filters/PrettyTimeFilter.java +++ b/src/main/java/com/mitchellbosecke/pebble/extension/filters/PrettyTimeFilter.java @@ -18,9 +18,9 @@ package com.mitchellbosecke.pebble.extension.filters; import com.juick.util.PrettyTimeFormatter; -import com.mitchellbosecke.pebble.extension.Filter; -import com.mitchellbosecke.pebble.template.EvaluationContext; -import com.mitchellbosecke.pebble.template.PebbleTemplate; +import io.pebbletemplates.pebble.extension.Filter; +import io.pebbletemplates.pebble.template.EvaluationContext; +import io.pebbletemplates.pebble.template.PebbleTemplate; import java.time.Instant; import java.util.Date; diff --git a/src/main/java/com/mitchellbosecke/pebble/extension/filters/TagsListFilter.java b/src/main/java/com/mitchellbosecke/pebble/extension/filters/TagsListFilter.java index 62518a09..d3a374a2 100644 --- a/src/main/java/com/mitchellbosecke/pebble/extension/filters/TagsListFilter.java +++ b/src/main/java/com/mitchellbosecke/pebble/extension/filters/TagsListFilter.java @@ -18,9 +18,9 @@ package com.mitchellbosecke.pebble.extension.filters; import com.juick.model.Tag; -import com.mitchellbosecke.pebble.extension.Filter; -import com.mitchellbosecke.pebble.template.EvaluationContext; -import com.mitchellbosecke.pebble.template.PebbleTemplate; +import io.pebbletemplates.pebble.extension.Filter; +import io.pebbletemplates.pebble.template.EvaluationContext; +import io.pebbletemplates.pebble.template.PebbleTemplate; import java.util.Collection; import java.util.List; diff --git a/src/main/java/com/mitchellbosecke/pebble/extension/filters/TimestampFilter.java b/src/main/java/com/mitchellbosecke/pebble/extension/filters/TimestampFilter.java index 5fc93805..dfe93673 100644 --- a/src/main/java/com/mitchellbosecke/pebble/extension/filters/TimestampFilter.java +++ b/src/main/java/com/mitchellbosecke/pebble/extension/filters/TimestampFilter.java @@ -17,9 +17,9 @@ package com.mitchellbosecke.pebble.extension.filters; -import com.mitchellbosecke.pebble.extension.Filter; -import com.mitchellbosecke.pebble.template.EvaluationContext; -import com.mitchellbosecke.pebble.template.PebbleTemplate; +import io.pebbletemplates.pebble.extension.Filter; +import io.pebbletemplates.pebble.template.EvaluationContext; +import io.pebbletemplates.pebble.template.PebbleTemplate; import java.time.Instant; import java.util.Date; diff --git a/src/main/java/org/apache/commons/mail/util/MimeMessageParser.java b/src/main/java/org/apache/commons/mail/util/MimeMessageParser.java new file mode 100644 index 00000000..ec36b7cb --- /dev/null +++ b/src/main/java/org/apache/commons/mail/util/MimeMessageParser.java @@ -0,0 +1,452 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.mail.util; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jakarta.activation.DataHandler; +import jakarta.activation.DataSource; +import jakarta.mail.Message; +import jakarta.mail.MessagingException; +import jakarta.mail.Multipart; +import jakarta.mail.Part; +import jakarta.mail.internet.ContentType; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeBodyPart; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimePart; +import jakarta.mail.internet.MimeUtility; +import jakarta.mail.internet.ParseException; +import jakarta.mail.util.ByteArrayDataSource; + +/** + * Parses a MimeMessage and stores the individual parts such a plain text, + * HTML text and attachments. + * + * @since 1.3 + */ +public class MimeMessageParser +{ + /** The MimeMessage to convert */ + private final MimeMessage mimeMessage; + + /** Plain mail content from MimeMessage */ + private String plainContent; + + /** Html mail content from MimeMessage */ + private String htmlContent; + + /** List of attachments of MimeMessage */ + private final List attachmentList; + + /** Attachments stored by their content-id */ + private final Map cidMap; + + /** Is this a Multipart email */ + private boolean isMultiPart; + + /** + * Constructs an instance with the MimeMessage to be extracted. + * + * @param message the message to parse + */ + public MimeMessageParser(final MimeMessage message) + { + attachmentList = new ArrayList<>(); + cidMap = new HashMap<>(); + this.mimeMessage = message; + this.isMultiPart = false; + } + + /** + * Does the actual extraction. + * + * @return this instance + * @throws Exception parsing the mime message failed + */ + public MimeMessageParser parse() throws Exception + { + this.parse(null, mimeMessage); + return this; + } + + /** + * @return the 'to' recipients of the message + * @throws Exception determining the recipients failed + */ + public List getTo() throws Exception + { + final jakarta.mail.Address[] recipients = this.mimeMessage.getRecipients(Message.RecipientType.TO); + return recipients != null ? Arrays.asList(recipients) : new ArrayList<>(); + } + + /** + * @return the 'cc' recipients of the message + * @throws Exception determining the recipients failed + */ + public List getCc() throws Exception + { + final jakarta.mail.Address[] recipients = this.mimeMessage.getRecipients(Message.RecipientType.CC); + return recipients != null ? Arrays.asList(recipients) : new ArrayList<>(); + } + + /** + * @return the 'bcc' recipients of the message + * @throws Exception determining the recipients failed + */ + public List getBcc() throws Exception + { + final jakarta.mail.Address[] recipients = this.mimeMessage.getRecipients(Message.RecipientType.BCC); + return recipients != null ? Arrays.asList(recipients) : new ArrayList<>(); + } + + /** + * @return the 'from' field of the message + * @throws Exception parsing the mime message failed + */ + public String getFrom() throws Exception + { + final jakarta.mail.Address[] addresses = this.mimeMessage.getFrom(); + if (addresses == null || addresses.length == 0) + { + return null; + } + return ((InternetAddress) addresses[0]).getAddress(); + } + + /** + * @return the 'replyTo' address of the email + * @throws Exception parsing the mime message failed + */ + public String getReplyTo() throws Exception + { + final jakarta.mail.Address[] addresses = this.mimeMessage.getReplyTo(); + if (addresses == null || addresses.length == 0) + { + return null; + } + return ((InternetAddress) addresses[0]).getAddress(); + } + + /** + * @return the mail subject + * @throws Exception parsing the mime message failed + */ + public String getSubject() throws Exception + { + return this.mimeMessage.getSubject(); + } + + /** + * Extracts the content of a MimeMessage recursively. + * + * @param parent the parent multi-part + * @param part the current MimePart + * @throws MessagingException parsing the MimeMessage failed + * @throws IOException parsing the MimeMessage failed + */ + protected void parse(final Multipart parent, final MimePart part) + throws MessagingException, IOException + { + if (isMimeType(part, "text/plain") && plainContent == null + && !Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) + { + plainContent = (String) part.getContent(); + } + else + { + if (isMimeType(part, "text/html") && htmlContent == null + && !Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) + { + htmlContent = (String) part.getContent(); + } + else + { + if (isMimeType(part, "multipart/*")) + { + this.isMultiPart = true; + final Multipart mp = (Multipart) part.getContent(); + final int count = mp.getCount(); + + // iterate over all MimeBodyPart + + for (int i = 0; i < count; i++) + { + parse(mp, (MimeBodyPart) mp.getBodyPart(i)); + } + } + else + { + final String cid = stripContentId(part.getContentID()); + final DataSource ds = createDataSource(parent, part); + if (cid != null) + { + this.cidMap.put(cid, ds); + } + this.attachmentList.add(ds); + } + } + } + } + + /** + * Strips the content id of any whitespace and angle brackets. + * @param contentId the string to strip + * @return a stripped version of the content id + */ + private String stripContentId(final String contentId) + { + if (contentId == null) + { + return null; + } + return contentId.trim().replaceAll("[\\<\\>]", ""); + } + + /** + * Checks whether the MimePart contains an object of the given mime type. + * + * @param part the current MimePart + * @param mimeType the mime type to check + * @return {@code true} if the MimePart matches the given mime type, {@code false} otherwise + * @throws MessagingException parsing the MimeMessage failed + * @throws IOException parsing the MimeMessage failed + */ + private boolean isMimeType(final MimePart part, final String mimeType) + throws MessagingException, IOException + { + // Do not use part.isMimeType(String) as it is broken for MimeBodyPart + // and does not really check the actual content type. + + try + { + final ContentType ct = new ContentType(part.getDataHandler().getContentType()); + return ct.match(mimeType); + } + catch (final ParseException ex) + { + return part.getContentType().equalsIgnoreCase(mimeType); + } + } + + /** + * Parses the MimePart to create a DataSource. + * + * @param parent the parent multi-part + * @param part the current part to be processed + * @return the DataSource + * @throws MessagingException creating the DataSource failed + * @throws IOException creating the DataSource failed + */ + protected DataSource createDataSource(final Multipart parent, final MimePart part) + throws MessagingException, IOException + { + final DataHandler dataHandler = part.getDataHandler(); + final DataSource dataSource = dataHandler.getDataSource(); + final String contentType = getBaseMimeType(dataSource.getContentType()); + byte[] content; + try (InputStream inputStream = dataSource.getInputStream()) + { + content = this.getContent(inputStream); + } + final ByteArrayDataSource result = new ByteArrayDataSource(content, contentType); + final String dataSourceName = getDataSourceName(part, dataSource); + result.setName(dataSourceName); + return result; + } + + /** @return Returns the mimeMessage. */ + public MimeMessage getMimeMessage() + { + return mimeMessage; + } + + /** @return Returns the isMultiPart. */ + public boolean isMultipart() + { + return isMultiPart; + } + + /** @return Returns the plainContent if any */ + public String getPlainContent() + { + return plainContent; + } + + /** @return Returns the attachmentList. */ + public List getAttachmentList() + { + return attachmentList; + } + + /** + * Returns a collection of all content-ids in the parsed message. + *

+ * The content-ids are stripped of any angle brackets, i.e. "part1" instead + * of "<part1>". + * + * @return the collection of content ids. + * @since 1.3.4 + */ + public Collection getContentIds() + { + return Collections.unmodifiableSet(cidMap.keySet()); + } + + /** @return Returns the htmlContent if any */ + public String getHtmlContent() + { + return htmlContent; + } + + /** @return true if a plain content is available */ + public boolean hasPlainContent() + { + return this.plainContent != null; + } + + /** @return true if HTML content is available */ + public boolean hasHtmlContent() + { + return this.htmlContent != null; + } + + /** @return true if attachments are available */ + public boolean hasAttachments() + { + return !this.attachmentList.isEmpty(); + } + + /** + * Find an attachment using its name. + * + * @param name the name of the attachment + * @return the corresponding datasource or null if nothing was found + */ + public DataSource findAttachmentByName(final String name) + { + DataSource dataSource; + + for (final DataSource element : getAttachmentList()) { + dataSource = element; + if (name.equalsIgnoreCase(dataSource.getName())) + { + return dataSource; + } + } + + return null; + } + + /** + * Find an attachment using its content-id. + *

+ * The content-id must be stripped of any angle brackets, + * i.e. "part1" instead of "<part1>". + * + * @param cid the content-id of the attachment + * @return the corresponding datasource or null if nothing was found + * @since 1.3.4 + */ + public DataSource findAttachmentByCid(final String cid) + { + final DataSource dataSource = cidMap.get(cid); + return dataSource; + } + + /** + * Determines the name of the data source if it is not already set. + * + * @param part the mail part + * @param dataSource the data source + * @return the name of the data source or {@code null} if no name can be determined + * @throws MessagingException accessing the part failed + * @throws UnsupportedEncodingException decoding the text failed + */ + protected String getDataSourceName(final Part part, final DataSource dataSource) + throws MessagingException, UnsupportedEncodingException + { + String result = dataSource.getName(); + + if (result == null || result.isEmpty()) + { + result = part.getFileName(); + } + + if (result != null && !result.isEmpty()) + { + result = MimeUtility.decodeText(result); + } + else + { + result = null; + } + + return result; + } + + /** + * Read the content of the input stream. + * + * @param is the input stream to process + * @return the content of the input stream + * @throws IOException reading the input stream failed + */ + private byte[] getContent(final InputStream is) + throws IOException + { + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + final BufferedInputStream isReader = new BufferedInputStream(is); + try (BufferedOutputStream osWriter = new BufferedOutputStream(os)) { + int ch; + while ((ch = isReader.read()) != -1) + { + osWriter.write(ch); + } + osWriter.flush(); + return os.toByteArray(); + } + } + + /** + * Parses the mimeType. + * + * @param fullMimeType the mime type from the mail api + * @return the real mime type + */ + private String getBaseMimeType(final String fullMimeType) + { + final int pos = fullMimeType.indexOf(';'); + if (pos >= 0) + { + return fullMimeType.substring(0, pos); + } + return fullMimeType; + } +} -- cgit v1.2.3