From 7aaa3f9a29c280f01c677c918932620be45cdbd7 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 8 Nov 2018 21:38:27 +0300 Subject: Merge everything into single Spring Boot application --- .../src/main/java/com/juick/ApiServer.java | 16 - .../java/com/juick/server/ActivityPubManager.java | 331 ------ .../java/com/juick/server/CommandsManager.java | 540 --------- .../main/java/com/juick/server/EmailManager.java | 165 --- .../java/com/juick/server/KeystoreManager.java | 92 -- .../main/java/com/juick/server/ServerManager.java | 295 ----- .../java/com/juick/server/SignatureManager.java | 113 -- .../java/com/juick/server/TelegramBotManager.java | 412 ------- .../src/main/java/com/juick/server/TopManager.java | 54 - .../main/java/com/juick/server/TwitterManager.java | 125 --- .../src/main/java/com/juick/server/Utils.java | 45 - .../java/com/juick/server/WebsocketManager.java | 174 --- .../main/java/com/juick/server/XMPPConnection.java | 693 ------------ .../src/main/java/com/juick/server/XMPPServer.java | 429 -------- .../java/com/juick/server/api/ApiSocialLogin.java | 302 ------ .../src/main/java/com/juick/server/api/Index.java | 54 - .../main/java/com/juick/server/api/Messages.java | 201 ---- .../java/com/juick/server/api/Notifications.java | 221 ---- .../src/main/java/com/juick/server/api/PM.java | 116 -- .../src/main/java/com/juick/server/api/Post.java | 245 ----- .../main/java/com/juick/server/api/Service.java | 166 --- .../src/main/java/com/juick/server/api/Tags.java | 54 - .../src/main/java/com/juick/server/api/Users.java | 179 --- .../com/juick/server/api/activity/Profile.java | 379 ------- .../juick/server/api/activity/model/Activity.java | 23 - .../juick/server/api/activity/model/Context.java | 123 --- .../api/activity/model/activities/Accept.java | 6 - .../api/activity/model/activities/Announce.java | 6 - .../api/activity/model/activities/Block.java | 6 - .../api/activity/model/activities/Create.java | 6 - .../api/activity/model/activities/Delete.java | 6 - .../api/activity/model/activities/Follow.java | 6 - .../server/api/activity/model/activities/Like.java | 6 - .../server/api/activity/model/activities/Undo.java | 6 - .../server/api/activity/model/objects/Hashtag.java | 6 - .../server/api/activity/model/objects/Image.java | 15 - .../server/api/activity/model/objects/Key.java | 24 - .../server/api/activity/model/objects/Link.java | 15 - .../server/api/activity/model/objects/Mention.java | 12 - .../server/api/activity/model/objects/Note.java | 64 -- .../activity/model/objects/OrderedCollection.java | 25 - .../model/objects/OrderedCollectionPage.java | 58 - .../server/api/activity/model/objects/Person.java | 87 -- .../juick/server/api/apple/AppSiteAssociation.java | 49 - .../com/juick/server/api/hostmeta/HostMeta.java | 25 - .../main/java/com/juick/server/api/rss/Feeds.java | 75 -- .../com/juick/server/api/rss/MessagesView.java | 153 --- .../java/com/juick/server/api/rss/RepliesView.java | 111 -- .../server/api/rss/extension/JuickModule.java | 33 - .../api/rss/extension/JuickModuleGenerator.java | 70 -- .../server/api/rss/extension/JuickModuleImpl.java | 54 - .../api/rss/extension/JuickModuleParser.java | 42 - .../com/juick/server/api/webfinger/Resource.java | 51 - .../juick/server/api/webfinger/model/Account.java | 24 - .../com/juick/server/api/webfinger/model/Link.java | 31 - .../juick/server/api/webhooks/TelegramWebhook.java | 57 - .../java/com/juick/server/api/xnodeinfo2/Info.java | 51 - .../server/api/xnodeinfo2/model/NodeInfo.java | 54 - .../juick/server/api/xnodeinfo2/model/Server.java | 40 - .../server/api/xnodeinfo2/model/ServiceInfo.java | 24 - .../juick/server/api/xnodeinfo2/model/Usage.java | 31 - .../server/api/xnodeinfo2/model/UserStats.java | 31 - .../configuration/ActivityPubClientConfig.java | 22 - .../ActivityPubClientErrorHandler.java | 35 - .../server/configuration/ApiAppConfiguration.java | 76 -- .../server/configuration/BaseWebConfiguration.java | 63 -- .../server/configuration/SapeConfiguration.java | 39 - .../juick/server/configuration/SecurityConfig.java | 215 ---- .../server/configuration/StorageConfiguration.java | 20 - .../juick/server/configuration/TelegramConfig.java | 15 - .../server/configuration/WwwAppConfiguration.java | 120 -- .../com/juick/server/configuration/XMPPConfig.java | 55 - .../server/helpers/annotation/UserCommand.java | 50 - .../juick/server/util/HttpBadRequestException.java | 32 - .../juick/server/util/HttpForbiddenException.java | 33 - .../juick/server/util/HttpNotFoundException.java | 32 - .../main/java/com/juick/server/util/HttpUtils.java | 115 -- .../java/com/juick/server/util/ImageUtils.java | 175 --- .../main/java/com/juick/server/util/TagUtils.java | 42 - .../main/java/com/juick/server/util/UserUtils.java | 55 - .../main/java/com/juick/server/util/WebUtils.java | 62 -- .../java/com/juick/server/www/HelpService.java | 69 -- .../src/main/java/com/juick/server/www/WebApp.java | 71 -- .../server/www/controllers/AnythingFilter.java | 69 -- .../com/juick/server/www/controllers/Help.java | 93 -- .../com/juick/server/www/controllers/Login.java | 50 - .../juick/server/www/controllers/MessagesWWW.java | 593 ---------- .../juick/server/www/controllers/NewMessage.java | 59 - .../com/juick/server/www/controllers/Settings.java | 278 ----- .../com/juick/server/www/controllers/SignUp.java | 172 --- .../juick/server/www/controllers/SocialLogin.java | 329 ------ .../java/com/juick/server/xmpp/JidConverter.java | 13 - .../java/com/juick/server/xmpp/XMPPStatusPage.java | 32 - .../com/juick/server/xmpp/helpers/XMPPStatus.java | 48 - .../com/juick/server/xmpp/iq/MessageQuery.java | 10 - .../com/juick/server/xmpp/iq/package-info.java | 8 - .../com/juick/server/xmpp/router/Handshake.java | 39 - .../java/com/juick/server/xmpp/router/Stream.java | 202 ---- .../server/xmpp/router/StreamComponentServer.java | 57 - .../com/juick/server/xmpp/router/StreamError.java | 57 - .../juick/server/xmpp/router/StreamFeatures.java | 95 -- .../juick/server/xmpp/router/StreamHandler.java | 13 - .../juick/server/xmpp/router/StreamNamespaces.java | 10 - .../com/juick/server/xmpp/router/XMPPError.java | 73 -- .../com/juick/server/xmpp/router/XMPPRouter.java | 220 ---- .../com/juick/server/xmpp/router/XmlUtils.java | 88 -- .../juick/server/xmpp/s2s/BasicXmppSession.java | 68 -- .../java/com/juick/server/xmpp/s2s/CacheEntry.java | 40 - .../java/com/juick/server/xmpp/s2s/Connection.java | 158 --- .../com/juick/server/xmpp/s2s/ConnectionIn.java | 231 ---- .../juick/server/xmpp/s2s/ConnectionListener.java | 16 - .../com/juick/server/xmpp/s2s/ConnectionOut.java | 189 ---- .../java/com/juick/server/xmpp/s2s/DNSQueries.java | 65 -- .../com/juick/server/xmpp/s2s/StanzaListener.java | 28 - .../juick/server/xmpp/s2s/util/DialbackUtils.java | 37 - .../java/com/juick/service/ActivityPubService.java | 59 - .../java/com/juick/service/BaseJdbcService.java | 41 - .../com/juick/service/CrosspostServiceImpl.java | 282 ----- .../java/com/juick/service/EmailServiceImpl.java | 108 -- .../java/com/juick/service/ImagesServiceImpl.java | 82 -- .../com/juick/service/MessagesServiceImpl.java | 1143 -------------------- .../com/juick/service/MessengerServiceImpl.java | 71 -- .../com/juick/service/PMQueriesServiceImpl.java | 149 --- .../juick/service/PrivacyQueriesServiceImpl.java | 63 -- .../com/juick/service/PushQueriesServiceImpl.java | 143 --- .../com/juick/service/ShowQueriesServiceImpl.java | 62 -- .../com/juick/service/SphinxSearchService.java | 97 -- .../com/juick/service/SubscriptionServiceImpl.java | 229 ---- .../java/com/juick/service/TagServiceImpl.java | 277 ----- .../com/juick/service/TelegramServiceImpl.java | 84 -- .../java/com/juick/service/UserServiceImpl.java | 668 ------------ .../juick/service/activities/ActivityListener.java | 19 - .../service/activities/DeleteMessageEvent.java | 21 - .../juick/service/activities/DeleteUserEvent.java | 20 - .../com/juick/service/activities/FollowEvent.java | 21 - .../juick/service/activities/UndoFollowEvent.java | 26 - .../security/HashParamAuthenticationFilter.java | 103 -- .../service/security/JuickUserDetailsService.java | 53 - .../service/security/NullUserDetailsService.java | 33 - .../CookieSimpleHashRememberMeServices.java | 130 --- .../RequestParamHashRememberMeServices.java | 88 -- .../juick/service/security/entities/JuickUser.java | 93 -- 142 files changed, 15908 deletions(-) delete mode 100644 juick-server/src/main/java/com/juick/ApiServer.java delete mode 100644 juick-server/src/main/java/com/juick/server/ActivityPubManager.java delete mode 100644 juick-server/src/main/java/com/juick/server/CommandsManager.java delete mode 100644 juick-server/src/main/java/com/juick/server/EmailManager.java delete mode 100644 juick-server/src/main/java/com/juick/server/KeystoreManager.java delete mode 100644 juick-server/src/main/java/com/juick/server/ServerManager.java delete mode 100644 juick-server/src/main/java/com/juick/server/SignatureManager.java delete mode 100644 juick-server/src/main/java/com/juick/server/TelegramBotManager.java delete mode 100644 juick-server/src/main/java/com/juick/server/TopManager.java delete mode 100644 juick-server/src/main/java/com/juick/server/TwitterManager.java delete mode 100644 juick-server/src/main/java/com/juick/server/Utils.java delete mode 100644 juick-server/src/main/java/com/juick/server/WebsocketManager.java delete mode 100644 juick-server/src/main/java/com/juick/server/XMPPConnection.java delete mode 100644 juick-server/src/main/java/com/juick/server/XMPPServer.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/ApiSocialLogin.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/Index.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/Messages.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/Notifications.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/PM.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/Post.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/Service.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/Tags.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/Users.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/Profile.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/Activity.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/Context.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/activities/Accept.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/activities/Announce.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/activities/Block.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/activities/Create.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/activities/Delete.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/activities/Follow.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/activities/Like.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/activities/Undo.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/objects/Hashtag.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/objects/Image.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/objects/Key.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/objects/Link.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/objects/Mention.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/objects/Note.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/objects/OrderedCollection.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/objects/OrderedCollectionPage.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/objects/Person.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/apple/AppSiteAssociation.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/hostmeta/HostMeta.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/rss/Feeds.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/rss/MessagesView.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/rss/RepliesView.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModule.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleGenerator.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleImpl.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleParser.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/webfinger/Resource.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/webfinger/model/Account.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/webfinger/model/Link.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/webhooks/TelegramWebhook.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/xnodeinfo2/Info.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/NodeInfo.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/Server.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/ServiceInfo.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/Usage.java delete mode 100644 juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/UserStats.java delete mode 100644 juick-server/src/main/java/com/juick/server/configuration/ActivityPubClientConfig.java delete mode 100644 juick-server/src/main/java/com/juick/server/configuration/ActivityPubClientErrorHandler.java delete mode 100644 juick-server/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java delete mode 100644 juick-server/src/main/java/com/juick/server/configuration/BaseWebConfiguration.java delete mode 100644 juick-server/src/main/java/com/juick/server/configuration/SapeConfiguration.java delete mode 100644 juick-server/src/main/java/com/juick/server/configuration/SecurityConfig.java delete mode 100644 juick-server/src/main/java/com/juick/server/configuration/StorageConfiguration.java delete mode 100644 juick-server/src/main/java/com/juick/server/configuration/TelegramConfig.java delete mode 100644 juick-server/src/main/java/com/juick/server/configuration/WwwAppConfiguration.java delete mode 100644 juick-server/src/main/java/com/juick/server/configuration/XMPPConfig.java delete mode 100644 juick-server/src/main/java/com/juick/server/helpers/annotation/UserCommand.java delete mode 100644 juick-server/src/main/java/com/juick/server/util/HttpBadRequestException.java delete mode 100644 juick-server/src/main/java/com/juick/server/util/HttpForbiddenException.java delete mode 100644 juick-server/src/main/java/com/juick/server/util/HttpNotFoundException.java delete mode 100644 juick-server/src/main/java/com/juick/server/util/HttpUtils.java delete mode 100644 juick-server/src/main/java/com/juick/server/util/ImageUtils.java delete mode 100644 juick-server/src/main/java/com/juick/server/util/TagUtils.java delete mode 100644 juick-server/src/main/java/com/juick/server/util/UserUtils.java delete mode 100644 juick-server/src/main/java/com/juick/server/util/WebUtils.java delete mode 100644 juick-server/src/main/java/com/juick/server/www/HelpService.java delete mode 100644 juick-server/src/main/java/com/juick/server/www/WebApp.java delete mode 100644 juick-server/src/main/java/com/juick/server/www/controllers/AnythingFilter.java delete mode 100644 juick-server/src/main/java/com/juick/server/www/controllers/Help.java delete mode 100644 juick-server/src/main/java/com/juick/server/www/controllers/Login.java delete mode 100644 juick-server/src/main/java/com/juick/server/www/controllers/MessagesWWW.java delete mode 100644 juick-server/src/main/java/com/juick/server/www/controllers/NewMessage.java delete mode 100644 juick-server/src/main/java/com/juick/server/www/controllers/Settings.java delete mode 100644 juick-server/src/main/java/com/juick/server/www/controllers/SignUp.java delete mode 100644 juick-server/src/main/java/com/juick/server/www/controllers/SocialLogin.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/JidConverter.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/XMPPStatusPage.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/helpers/XMPPStatus.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/iq/MessageQuery.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/iq/package-info.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/router/Handshake.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/router/Stream.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/router/StreamComponentServer.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/router/StreamError.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/router/StreamFeatures.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/router/StreamHandler.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/router/StreamNamespaces.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/router/XMPPError.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/router/XMPPRouter.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/router/XmlUtils.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/s2s/BasicXmppSession.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/s2s/CacheEntry.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/s2s/Connection.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionIn.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionListener.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionOut.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/s2s/DNSQueries.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/s2s/StanzaListener.java delete mode 100644 juick-server/src/main/java/com/juick/server/xmpp/s2s/util/DialbackUtils.java delete mode 100644 juick-server/src/main/java/com/juick/service/ActivityPubService.java delete mode 100644 juick-server/src/main/java/com/juick/service/BaseJdbcService.java delete mode 100644 juick-server/src/main/java/com/juick/service/CrosspostServiceImpl.java delete mode 100644 juick-server/src/main/java/com/juick/service/EmailServiceImpl.java delete mode 100644 juick-server/src/main/java/com/juick/service/ImagesServiceImpl.java delete mode 100644 juick-server/src/main/java/com/juick/service/MessagesServiceImpl.java delete mode 100644 juick-server/src/main/java/com/juick/service/MessengerServiceImpl.java delete mode 100644 juick-server/src/main/java/com/juick/service/PMQueriesServiceImpl.java delete mode 100644 juick-server/src/main/java/com/juick/service/PrivacyQueriesServiceImpl.java delete mode 100644 juick-server/src/main/java/com/juick/service/PushQueriesServiceImpl.java delete mode 100644 juick-server/src/main/java/com/juick/service/ShowQueriesServiceImpl.java delete mode 100644 juick-server/src/main/java/com/juick/service/SphinxSearchService.java delete mode 100644 juick-server/src/main/java/com/juick/service/SubscriptionServiceImpl.java delete mode 100644 juick-server/src/main/java/com/juick/service/TagServiceImpl.java delete mode 100644 juick-server/src/main/java/com/juick/service/TelegramServiceImpl.java delete mode 100644 juick-server/src/main/java/com/juick/service/UserServiceImpl.java delete mode 100644 juick-server/src/main/java/com/juick/service/activities/ActivityListener.java delete mode 100644 juick-server/src/main/java/com/juick/service/activities/DeleteMessageEvent.java delete mode 100644 juick-server/src/main/java/com/juick/service/activities/DeleteUserEvent.java delete mode 100644 juick-server/src/main/java/com/juick/service/activities/FollowEvent.java delete mode 100644 juick-server/src/main/java/com/juick/service/activities/UndoFollowEvent.java delete mode 100644 juick-server/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java delete mode 100644 juick-server/src/main/java/com/juick/service/security/JuickUserDetailsService.java delete mode 100644 juick-server/src/main/java/com/juick/service/security/NullUserDetailsService.java delete mode 100644 juick-server/src/main/java/com/juick/service/security/deprecated/CookieSimpleHashRememberMeServices.java delete mode 100644 juick-server/src/main/java/com/juick/service/security/deprecated/RequestParamHashRememberMeServices.java delete mode 100644 juick-server/src/main/java/com/juick/service/security/entities/JuickUser.java (limited to 'juick-server/src/main/java/com/juick') diff --git a/juick-server/src/main/java/com/juick/ApiServer.java b/juick-server/src/main/java/com/juick/ApiServer.java deleted file mode 100644 index fb2d9701..00000000 --- a/juick-server/src/main/java/com/juick/ApiServer.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.juick; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration; -import org.springframework.context.annotation.ComponentScan; - -@SpringBootApplication -@EnableAutoConfiguration(exclude = { MailSenderAutoConfiguration.class }) -@ComponentScan(basePackages = {"com.juick.server", "com.juick.service"}) -public class ApiServer { - public static void main(String[] args) { - SpringApplication.run(ApiServer.class, args); - } -} diff --git a/juick-server/src/main/java/com/juick/server/ActivityPubManager.java b/juick-server/src/main/java/com/juick/server/ActivityPubManager.java deleted file mode 100644 index 4601f7d1..00000000 --- a/juick-server/src/main/java/com/juick/server/ActivityPubManager.java +++ /dev/null @@ -1,331 +0,0 @@ -package com.juick.server; - -import com.juick.Message; -import com.juick.User; -import com.juick.formatters.PlainTextFormatter; -import com.juick.server.api.activity.model.Context; -import com.juick.server.api.activity.model.activities.Accept; -import com.juick.server.api.activity.model.activities.Announce; -import com.juick.server.api.activity.model.activities.Create; -import com.juick.server.api.activity.model.activities.Delete; -import com.juick.server.api.activity.model.objects.Hashtag; -import com.juick.server.api.activity.model.objects.Image; -import com.juick.server.api.activity.model.objects.Mention; -import com.juick.server.api.activity.model.objects.Note; -import com.juick.server.api.activity.model.objects.Person; -import com.juick.server.util.HttpUtils; -import com.juick.service.SocialService; -import com.juick.service.UserService; -import com.juick.service.activities.*; -import com.juick.service.component.*; -import com.juick.util.MessageUtils; -import com.mitchellbosecke.pebble.PebbleEngine; -import com.mitchellbosecke.pebble.template.PebbleTemplate; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.net.URI; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -@Component -public class ActivityPubManager implements ActivityListener, NotificationListener { - private static final Logger logger = LoggerFactory.getLogger(ActivityPubManager.class); - @Inject - private SignatureManager signatureManager; - @Inject - private SocialService socialService; - @Inject - private UserService userService; - @Inject - private PebbleEngine pebbleEngine; - @Value("${ap_base_uri:http://localhost:8080/}") - private String baseUri; - @Value("${service_user:juick}") - private String serviceUsername; - - private User serviceUser; - - @PostConstruct - public void init() { - serviceUser = userService.getUserByName(serviceUsername); - } - - @Override - public void processFollowEvent(@Nonnull FollowEvent followEvent) { - String acct = (String)followEvent.getRequest().getObject(); - logger.info("received follower request to {}", acct); - User followedUser = socialService.getUserByAccountUri(acct); - if (!followedUser.isAnonymous()) { - // automatically accept follower requests - Person me = (Person) signatureManager.getContext(URI.create(acct)).get(); - Person follower = (Person) signatureManager.getContext(URI.create(followEvent.getRequest().getActor())).get(); - Accept accept = new Accept(); - accept.setActor(me.getId()); - accept.setObject(followEvent.getRequest()); - try { - signatureManager.post(me, follower, accept); - socialService.addFollower(followedUser, follower.getId()); - logger.info("Follower added for {}", followedUser.getName()); - } catch (IOException e) { - logger.info("activitypub exception", e); - } - } - } - - @Override - public void undoFollowEvent(UndoFollowEvent event) { - String actor = event.getActor(); - String me = event.getObject(); - logger.info("{} stopping to follow {}", actor, me); - User followedUser = socialService.getUserByAccountUri(me); - if (!followedUser.isAnonymous()) { - socialService.removeFollower(followedUser, actor); - } - } - - @Override - public void deleteUserEvent(DeleteUserEvent event) { - String acct = event.getUserUri(); - logger.info("Deleting {} from followers", acct); - socialService.removeAccount(acct); - } - - @Override - public void deleteMessageEvent(DeleteMessageEvent event) { - Message msg = event.getMessage(); - User user = msg.getUser(); - String userUri = personUri(user); - Note note = makeNote(msg); - Person me = (Person) signatureManager.getContext(URI.create(userUri)).get(); - socialService.getFollowers(user).forEach(acct -> { - Person follower = (Person) signatureManager.getContext(URI.create(acct)).get(); - Delete delete = new Delete(); - delete.setId(note.getId()); - delete.setActor(me.getId()); - delete.setPublished(note.getPublished()); - delete.setObject(note); - try { - logger.info("Deletion to follower {}", follower.getId()); - signatureManager.post(me, follower, delete); - } catch (IOException e) { - logger.warn("activitypub exception", e); - } - }); - } - - @Override - public void processMessageEvent(MessageEvent messageEvent) { - Message msg = messageEvent.getMessage(); - if (MessageUtils.isPM(msg)) { - return; - } - User user = msg.getUser(); - String userUri = personUri(user); - Note note = makeNote(msg); - Person me = (Person) signatureManager.getContext(URI.create(userUri)).get(); - Set subscribers = new HashSet<>(socialService.getFollowers(user)); - if (MessageUtils.isReply(msg) && msg.getTo().getUri().toASCIIString().length() > 0) { - String replier = msg.getTo().getUri().toASCIIString(); - subscribers.add(replier); - List cc = new ArrayList<>(note.getCc()); - cc.add(replier); - note.setCc(cc); - } - subscribers.forEach(acct -> { - Optional context = signatureManager.getContext(URI.create(acct)); - if (context.isPresent()) { - Person follower = (Person)context.get(); - Create create = new Create(); - create.setId(note.getId()); - create.setActor(me.getId()); - create.setPublished(note.getPublished()); - create.setObject(note); - try { - logger.info("Posting to subscriber {}", follower.getId()); - signatureManager.post(me, follower, create); - } catch (IOException e) { - logger.warn("activitypub exception", e); - } - } - }); - } - - public String inboxUri() { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath("/api/inbox").toUriString(); - } - - public String outboxUri(User user) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/u/%s/blog/toc", user.getName())).toUriString(); - } - - public String personUri(User user) { - if (user.getUri().toString().length() > 0) { - return user.getUri().toASCIIString(); - } - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/u/%s", user.getName())).toUriString(); - } - public String personWebUri(User user) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/%s/", user.getName())).toUriString(); - } - - public String followersUri(User user) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/u/%s/followers/toc", user.getName())).toUriString(); - } - - public String followingUri(User user) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/u/%s/following/toc", user.getName())).toUriString(); - } - public String messageUri(Message msg) { - return messageUri(msg.getMid(), msg.getRid()); - } - public String messageUri(int mid, int rid) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - uri.replacePath(String.format("/n/%d-%d", mid, rid)); - return uri.toUriString(); - } - public String tagUri(com.juick.Tag tag) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/t/%s", tag.getName())).toUriString(); - } - - public Note makeNote(Message msg) { - Note note = new Note(); - note.setId(messageUri(msg)); - note.setUrl(PlainTextFormatter.formatUrl(msg)); - note.setAttributedTo(personUri(msg.getUser())); - if (MessageUtils.isReply(msg)) { - if (msg.getReplyToUri().toASCIIString().length() > 0) { - note.setInReplyTo(msg.getReplyToUri().toASCIIString()); - } else { - note.setInReplyTo(messageUri(msg.getMid(), msg.getReplyto())); - } - } - if (MessageUtils.isPM(msg)) { - note.setTo(Collections.singletonList(personUri(msg.getTo()))); - } else { - note.setTo(Collections.singletonList("https://www.w3.org/ns/activitystreams#Public")); - note.setCc(Collections.singletonList(followersUri(msg.getUser()))); - } - note.setPublished(msg.getTimestamp()); - if (StringUtils.isNotBlank(msg.getAttachmentType())) { - Image attachment = new Image(); - attachment.setId(msg.getAttachment().getMedium().getUrl()); - attachment.setUrl(msg.getAttachment().getMedium().getUrl()); - attachment.setMediaType(HttpUtils.mediaType(msg.getAttachmentType())); - note.setAttachment(Collections.singletonList(attachment)); - } - note.setTags(msg.getTags().stream().map(t -> { - Hashtag hashtag = new Hashtag(); - hashtag.setId(tagUri(t)); - hashtag.setName(t.getName()); - return hashtag; - }).collect(Collectors.toList())); - if (msg.getReplyToUri() != null && msg.getReplyToUri().toASCIIString().length() > 0) { - Optional noteContext = signatureManager.getContext(msg.getReplyToUri()); - if (noteContext.isPresent()) { - Note activity = (Note) noteContext.get(); - Optional personContext = signatureManager.getContext(URI.create(activity.getAttributedTo())); - if (personContext.isPresent()) { - Person person = (Person) personContext.get(); - note.getTags().add(new Mention(person.getUrl(), person.getPreferredUsername())); - msg.getTo().setName(person.getPreferredUsername()); - note.setInReplyTo(activity.getInReplyTo()); - } - } - } else if (MessageUtils.isReply(msg)) { - note.getTags().add(new Mention(personWebUri(msg.getTo()), msg.getTo().getName())); - } - MessageUtils.getGlobalMentions(msg).forEach(m -> { - // @user@server.tld -> user@server.tld - Optional personContext = signatureManager.discoverPerson(m.substring(1)); - if (personContext.isPresent()) { - Person person = (Person) personContext.get(); - note.getTags().add(new Mention(person.getUrl(), person.getPreferredUsername())); - List cc = new ArrayList<>(note.getCc()); - cc.add(person.getUrl()); - note.setCc(cc); - } - }); - if (msg.isHtml()) { - note.setContent(msg.getText()); - } else { - PebbleTemplate noteTemplate = pebbleEngine.getTemplate("layouts/note"); - Map context = new HashMap<>(); - context.put("msg", msg); - context.put("baseUri", baseUri); - try { - Writer writer = new StringWriter(); - noteTemplate.evaluate(writer, context); - note.setContent(writer.toString()); - } catch (IOException e) { - logger.warn("template not rendered, falling back"); - note.setContent(MessageUtils.formatMessage(StringUtils.defaultString(msg.getText()))); - } - } - return note; - } - - @Override - public void processSubscribeEvent(SubscribeEvent subscribeEvent) { - - } - - @Override - public void processLikeEvent(LikeEvent likeEvent) { - - } - - @Override - public void processPingEvent(PingEvent pingEvent) { - - } - - @Override - public void processMessageReadEvent(MessageReadEvent messageReadEvent) { - - } - - @Override - public void processTopEvent(TopEvent topEvent) { - Message message = topEvent.getMessage(); - Note note = makeNote(message); - Announce announce = new Announce(); - announce.setId(note.getId() + "#top"); - announce.setActor(personUri(serviceUser)); - announce.setObject(note); - Person me = (Person) signatureManager.getContext(URI.create(announce.getActor())).get(); - socialService.getFollowers(serviceUser).forEach(acct -> { - Person follower = (Person) signatureManager.getContext(URI.create(acct)).get(); - try { - logger.info("Announcing top: {}", message.getMid()); - signatureManager.post(me, follower, announce); - } catch (IOException e) { - logger.warn("activitypub exception", e); - } - }); - } -} diff --git a/juick-server/src/main/java/com/juick/server/CommandsManager.java b/juick-server/src/main/java/com/juick/server/CommandsManager.java deleted file mode 100644 index 82143482..00000000 --- a/juick-server/src/main/java/com/juick/server/CommandsManager.java +++ /dev/null @@ -1,540 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server; - -import com.juick.Message; -import com.juick.Tag; -import com.juick.User; -import com.juick.formatters.PlainTextFormatter; -import com.juick.service.activities.DeleteMessageEvent; -import com.juick.service.component.*; -import com.juick.model.CommandResult; -import com.juick.model.TagStats; -import com.juick.server.helpers.annotation.UserCommand; -import com.juick.server.util.HttpUtils; -import com.juick.service.*; -import com.juick.util.MessageUtils; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.apache.commons.lang3.reflect.MethodUtils; -import org.apache.commons.lang3.tuple.Pair; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Component; - -import javax.annotation.Nonnull; -import javax.inject.Inject; -import java.lang.reflect.Method; -import java.net.URI; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -/** - * - * @author ugnich - */ -@Component -public class CommandsManager { - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - @Inject - private TagService tagService; - @Inject - private PMQueriesService pmQueriesService; - @Inject - private ShowQueriesService showQueriesService; - @Inject - private PrivacyQueriesService privacyQueriesService; - @Inject - private SubscriptionService subscriptionService; - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String imgDir; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - @Inject - private ImagesService imagesService; - - public CommandResult processCommand(User user, String data, @Nonnull URI attachment) throws Exception { - if (!user.isAnonymous()) { - userService.updateLastSeen(user); - } - String strippedData = StringUtils.stripStart(data, null); - if (strippedData.startsWith("?OTR")) { - return CommandResult.fromString("?OTR Error: we are not using OTR"); - } - String input = MessageUtils.stripNonSafeUrls(strippedData); - Optional cmd = MethodUtils.getMethodsListWithAnnotation(getClass(), UserCommand.class).stream() - .filter(m -> Pattern.compile(m.getAnnotation(UserCommand.class).pattern(), - m.getAnnotation(UserCommand.class).patternFlags()).matcher(input).matches()) - .findFirst(); - if (cmd.isPresent()) { - Matcher matcher = Pattern.compile(cmd.get().getAnnotation(UserCommand.class).pattern(), - cmd.get().getAnnotation(UserCommand.class).patternFlags()).matcher(input); - List groups = new ArrayList<>(); - while (matcher.find()) { - for (int i = 1; i <= matcher.groupCount(); i++) { - groups.add(matcher.group(i)); - } - } - CommandResult commandResult = (CommandResult) getClass().getMethod(cmd.get().getName(), User.class, URI.class, String[].class) - .invoke(this, user, attachment, groups.toArray(new String[groups.size()])); - if (StringUtils.isNotEmpty(commandResult.getText())) { - return commandResult; - } - } - Pair> tags = tagService.fromString(input); - if (tags.getRight().size() > 5) { - return CommandResult.fromString("Sorry, 5 tags maximum."); - } - // new message - String body = tags.getLeft().trim(); - boolean haveAttachment = StringUtils.isNotEmpty(attachment.toString()); - String attachmentFName = null; - String attachmentType = null; - if (haveAttachment) { - attachmentFName = attachment.getScheme().equals("juick") ? attachment.getHost() - : HttpUtils.downloadImage(attachment.toURL(), tmpDir).getHost(); - attachmentType = attachmentFName.substring(attachmentFName.length() - 3); - } - int mid = messagesService.createMessage(user.getUid(), body, attachmentType, tags.getRight()); - if (haveAttachment) { - String fname = String.format("%d.%s", mid, attachmentType); - imagesService.saveImageWithPreviews(attachmentFName, fname); - } - Message msg = messagesService.getMessage(mid); - subscriptionService.subscribeMessage(msg, user); - - applicationEventPublisher.publishEvent(new MessageReadEvent(this, user, msg)); - applicationEventPublisher.publishEvent(new MessageEvent(this, msg, subscriptionService.getSubscribedUsers(msg.getUser().getUid(), msg))); - return CommandResult.build(msg, "New message posted.\n#" + msg.getMid() + " https://juick.com/m/" + msg.getMid(), String.format("[New message](%s) posted", PlainTextFormatter.formatUrl(msg))); - } - - @UserCommand(pattern = "^ping$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "PING - returns you a PONG") - public CommandResult commandPing(User user, URI attachment, String[] input) { - applicationEventPublisher.publishEvent(new PingEvent(this, user)); - return CommandResult.fromString("PONG"); - } - - @UserCommand(pattern = "^help$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "HELP - returns this help message") - public CommandResult commandHelp(User user, URI attachment, String[] input) { - return CommandResult.fromString(Arrays.stream(getClass().getDeclaredMethods()) - .filter(m -> m.isAnnotationPresent(UserCommand.class)) - .map(m -> m.getAnnotation(UserCommand.class).help()) - .collect(Collectors.joining("\n"))); - } - - @UserCommand(pattern = "^login$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "LOGIN - log in to Juick website") - public CommandResult commandLogin(User user_from, URI attachment, String[] input) { - return CommandResult.fromString("http://juick.com/login?hash=" + userService.getHashByUID(user_from.getUid())); - } - @UserCommand(pattern = "^\\@(\\S+)\\s+([\\s\\S]+)$", help = "@username message - send PM to username") - public CommandResult commandPM(User user_from, URI attachment, String... arguments) { - String body = arguments[1]; - - User user_to = userService.getUserByName(arguments[0]); - - if (!user_to.isAnonymous()) { - if (!userService.isInBLAny(user_to.getUid(), user_from.getUid())) { - if (pmQueriesService.createPM(user_from.getUid(), user_to.getUid(), body)) { - com.juick.Message jmsg = new com.juick.Message(); - jmsg.setUser(user_from); - jmsg.setTo(user_to); - jmsg.setText(body); - applicationEventPublisher.publishEvent(new MessageEvent(this, jmsg, Collections.singletonList(user_to))); - return CommandResult.fromString("Private message sent"); - } - } - } - return CommandResult.fromString("Error"); - } - @UserCommand(pattern = "^bl$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "BL - Show your blacklist") - public CommandResult commandBLShow(User user_from, URI attachment, String... arguments) { - List blusers = userService.getUserBLUsers(user_from.getUid()); - List bltags = tagService.getUserBLTags(user_from.getUid()); - - String txt = StringUtils.EMPTY; - if (bltags.size() > 0) { - for (String bltag : bltags) { - txt += "*" + bltag + "\n"; - } - - if (blusers.size() > 0) { - txt += "\n"; - } - } - if (blusers.size() > 0) { - for (User bluser : blusers) { - txt += "@" + bluser.getName() + "\n"; - } - } - if (txt.isEmpty()) { - txt = "You don't have any users or tags in your blacklist."; - } - - return CommandResult.fromString(txt); - } - - @UserCommand(pattern = "^#\\+$", help = "#+ - Show last Juick messages") - public CommandResult commandLast(User user_from, URI attachment, String... arguments) { - return CommandResult.fromString("Last messages:\n" - + printMessages(user_from, messagesService.getAll(user_from.getUid(), 0), true)); - } - - @UserCommand(pattern = "@", help = "@ - Show recommendations and popular personal blogs") - public CommandResult commandUsers(User user_from, URI attachment, String... arguments) { - StringBuilder msg = new StringBuilder(); - msg.append("Recommended blogs"); - List recommendedUsers = showQueriesService.getRecommendedUsers(user_from); - if (recommendedUsers.size() > 0) { - for (String user : recommendedUsers) { - msg.append("\n@").append(user); - } - } else { - msg.append("\nNo recommendations now. Subscribe to more blogs. ;)"); - } - msg.append("\n\nTop 10 personal blogs:"); - List topUsers = showQueriesService.getTopUsers(); - if (topUsers.size() > 0) { - for (String user : topUsers) { - msg.append("\n@").append(user); - } - } else { - msg.append("\nNo top users. Empty DB? ;)"); - } - return CommandResult.fromString(msg.toString()); - } - @UserCommand(pattern = "^bl\\s+@([^\\s\\n\\+]+)", patternFlags = Pattern.CASE_INSENSITIVE, - help = "BL @username - add @username to your blacklist") - public CommandResult blacklistUser(User user_from, URI attachment, String... arguments) { - User blUser = userService.getUserByName(arguments[0]); - if (!blUser.isAnonymous()) { - PrivacyQueriesService.PrivacyResult result = privacyQueriesService.blacklistUser(user_from, blUser); - if (result == PrivacyQueriesService.PrivacyResult.Added) { - return CommandResult.fromString("User added to your blacklist"); - } else { - return CommandResult.fromString("User removed from your blacklist"); - } - } - return CommandResult.fromString("User not found"); - } - @UserCommand(pattern = "^bl\\s\\*(\\S+)$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "BL *tag - add *tag to your blacklist") - public CommandResult blacklistTag(User user_from, URI attachment, String... arguments) { - if (!user_from.isAnonymous()) { - Tag tag = tagService.getTag(arguments[0], false); - if (tag != null) { - PrivacyQueriesService.PrivacyResult result = privacyQueriesService.blacklistTag(user_from, tag); - if (result == PrivacyQueriesService.PrivacyResult.Added) { - return CommandResult.fromString("Tag added to your blacklist"); - } else { - return CommandResult.fromString("Tag removed from your blacklist"); - } - } - } - return CommandResult.fromString("Tag not found"); - } - @UserCommand(pattern = "\\*", help = "* - Show your tags") - public CommandResult commandTags(User currentUser, URI attachment, String... args) { - List tags = tagService.getUserTagStats(currentUser.getUid()); - String msg = "Your tags: (tag - messages)\n" + - tags.stream() - .map(t -> String.format("\n*%s - %d", t.getTag().getName(), t.getUsageCount())).collect(Collectors.joining()); - return CommandResult.fromString(msg); - } - @UserCommand(pattern = "S", help = "S - Show your subscriptions", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandSubscriptions(User currentUser, URI attachment, String... args) { - List friends = userService.getUserFriends(currentUser.getUid()); - List tags = subscriptionService.getSubscribedTags(currentUser); - String msg = friends.size() > 0 ? "You are subscribed to users:" + friends.stream().map(u -> "\n@" + u.getName()) - .collect(Collectors.joining()) - : "You are not subscribed to any user."; - msg += tags.size() > 0 ? "\nYou are subscribed to tags:" + tags.stream().map(t -> "\n*" + t) - .collect(Collectors.joining()) - : "\nYou are not subscribed to any tag."; - return CommandResult.fromString(msg); - } - @UserCommand(pattern = "!", help = "! - Show your favorite messages") - public CommandResult commandFavorites(User currentUser, URI attachment, String... args) { - List mids = messagesService.getUserRecommendations(currentUser.getUid(), 0); - if (mids.size() > 0) { - return CommandResult.fromString("Favorite messages: \n" + printMessages(currentUser, mids, false)); - } - return CommandResult.fromString("No favorite messages, try to \"like\" something ;)"); - } - @UserCommand(pattern = "^\\!\\s+#(\\d+)", help = "! #12345 - recommend message") - public CommandResult commandRecommend(User user, URI attachment, String... arguments) { - int mid = NumberUtils.toInt(arguments[0], 0); - if (mid > 0) { - com.juick.Message msg = messagesService.getMessage(mid); - if (msg != null) { - if (msg.getUser() == user) { - return CommandResult.fromString("You can't recommend your own messages."); - } - MessagesService.RecommendStatus status = messagesService.recommendMessage(mid, user.getUid()); - switch (status) { - case Added: - applicationEventPublisher.publishEvent(new LikeEvent(this, user, msg, - subscriptionService.getUsersSubscribedToUserRecommendations( - user.getUid(), msg))); - return CommandResult.fromString("Message is added to your recommendations"); - case Deleted: - return CommandResult.fromString("Message deleted from your recommendations."); - } - } - return CommandResult.fromString("Message not found"); - } - return CommandResult.fromString("Message not found"); - } - // TODO: target notification - @UserCommand(pattern = "^(s|u)\\s+\\@(\\S+)$", help = "S @username - subscribe to user" + - "\nU @username - unsubscribe from user", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandSubscribeUser(User user, URI attachment, String... args) { - boolean subscribe = args[0].equalsIgnoreCase("s"); - User toUser = userService.getUserByName(args[1]); - if (toUser.isAnonymous()) { - return CommandResult.fromString("User not found"); - } - if (subscribe) { - if (subscriptionService.subscribeUser(user, toUser)) { - // TODO: already subscribed case - applicationEventPublisher.publishEvent(new SubscribeEvent(this, user, toUser)); - return CommandResult.fromString("Subscribed to @" + toUser.getName()); - } - } else { - if (subscriptionService.unSubscribeUser(user, toUser)) { - return CommandResult.fromString("Unsubscribed from @" + toUser.getName()); - } - return CommandResult.fromString("You were not subscribed to @" + toUser.getName()); - } - return CommandResult.fromString("Error"); - } - @UserCommand(pattern = "^(s|u)\\s+\\*(\\S+)$", help = "S *tag - subscribe to tag" + - "\nU *tag - unsubscribe from tag", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandSubscribeTag(User user, URI attachment, String... args) { - boolean subscribe = args[0].equalsIgnoreCase("s"); - Tag tag = tagService.getTag(args[1], true); - if (subscribe) { - if (subscriptionService.subscribeTag(user, tag)) { - return CommandResult.fromString("Subscribed"); - } - } else { - if (subscriptionService.unSubscribeTag(user, tag)) { - return CommandResult.fromString("Unsubscribed from " + tag.getName()); - } - return CommandResult.fromString("You were not subscribed to " + tag.getName()); - } - return CommandResult.fromString("Error"); - } - @UserCommand(pattern = "^(s|u)\\s+#(\\d+)$", help = "S #1234 - subscribe to comments" + - "\nU #1234 - unsubscribe from comments", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandSubscribeMessage(User user, URI attachment, String... args) { - boolean subscribe = args[0].equalsIgnoreCase("s"); - int mid = NumberUtils.toInt(args[1], 0); - Message msg = messagesService.getMessage(mid); - if (msg != null) { - if (subscribe) { - if (subscriptionService.subscribeMessage(msg, user)) { - applicationEventPublisher.publishEvent( - new MessageReadEvent(this, user, msg)); - return CommandResult.fromString("Subscribed"); - } - } else { - if (subscriptionService.unSubscribeMessage(mid, user.getUid())) { - return CommandResult.fromString("Unsubscribed from #" + mid); - } - return CommandResult.fromString("You were not subscribed to #" + mid); - } - } - return CommandResult.fromString("Error"); - } - @UserCommand(pattern = "^(on|off)$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "ON/OFF - Enable/disable subscriptions delivery") - public CommandResult commandOnOff(User user, URI attachment, String[] input) { - UserService.ActiveStatus newStatus; - String retValUpdated; - if (input[0].toLowerCase().equals("on")) { - newStatus = UserService.ActiveStatus.Active; - retValUpdated = "XMPP notifications are activated"; - } else { - newStatus = UserService.ActiveStatus.Inactive; - retValUpdated = "XMPP notifications are disabled"; - } - if (userService.getAllJIDs(user).stream().allMatch(jid -> userService.setActiveStatusForJID(jid, newStatus))) { - return CommandResult.fromString(retValUpdated); - } - return CommandResult.fromString("Error"); - } - @UserCommand(pattern = "^\\@([^\\s\\n\\+]+)(\\+?)$", - help = "@username+ - Show user's info and last 20 messages") - public CommandResult commandUser(User user, URI attachment, String... arguments) { - User blogUser = userService.getUserByName(arguments[0]); - int page = arguments[1].length(); - if (!blogUser.isAnonymous()) { - List mids = messagesService.getUserBlog(blogUser.getUid(), 0, 0); - return CommandResult.fromString(String.format("Last messages from @%s:\n%s", arguments[0], - printMessages(user, mids, false))); - } - return CommandResult.fromString("User not found"); - } - @UserCommand(pattern = "^#(\\d+)(\\+?)$", help = "#1234 - Show message (#1234+ - message with replies)") - public CommandResult commandShow(User user, URI attachment, String... arguments) { - boolean showReplies = arguments[1].length() > 0; - int mid = NumberUtils.toInt(arguments[0], 0); - if (mid == 0) { - return CommandResult.fromString("Error"); - } - com.juick.Message msg = messagesService.getMessage(mid); - if (msg != null) { - if (showReplies) { - List replies = messagesService.getReplies(user, mid); - applicationEventPublisher.publishEvent( - new MessageReadEvent(this, user, msg)); - replies.add(0, msg); - return CommandResult.fromString(String.join("\n", - replies.stream().map(PlainTextFormatter::formatPostSummary).collect(Collectors.toList()))); - } - return CommandResult.fromString(PlainTextFormatter.formatPost(msg)); - } - return CommandResult.fromString("Message not found"); - } - @UserCommand(pattern = "^#(\\d+)\\/(\\d+)$", help = "#1234/5 - Show reply") - public CommandResult commandShowReply(User user, URI attachment, String... arguments) { - int mid = NumberUtils.toInt(arguments[0], 0); - int rid = NumberUtils.toInt(arguments[1], 0); - com.juick.Message reply = messagesService.getReply(mid, rid); - if (reply != null) { - return CommandResult.fromString(PlainTextFormatter.formatPost(reply)); - } - return CommandResult.fromString("Reply not found"); - } - @UserCommand(pattern = "^\\*(\\S+)(\\+?)$", help = "*tag - Show last messages with tag") - public CommandResult commandShowTag(User user, URI attachment, String... arguments) { - if (StringUtils.isNotEmpty(attachment.toString())) { - // new message with tag - return CommandResult.fromString(StringUtils.EMPTY); - } - Tag tag = tagService.getTag(arguments[0], false); - if (tag != null) { - // TODO: synonyms - List mids = messagesService.getTag(tag.TID, user.getUid(), 0, 10); - return CommandResult.fromString("Last messages with *" + tag.getName() + ":\n" + printMessages(user, mids, true)); - } - return CommandResult.fromString("Tag not found"); - } - @UserCommand(pattern = "^D #(\\d+)$", help = "D #1234 - Delete post", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandDeletePost(User user, URI attachment, String... args) { - int mid = Integer.valueOf(args[0]); - Message message = messagesService.getMessage(mid); - if (message != null && messagesService.deleteMessage(user.getUid(), mid)) { - applicationEventPublisher.publishEvent(new DeleteMessageEvent(this, message)); - return CommandResult.fromString("Message deleted"); - } - return CommandResult.fromString("This is not your message"); - } - @UserCommand(pattern = "^D #(\\d+)(\\.|\\-|\\/)(\\d+)$", help = "D #1234/5 - Delete comment", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandDeleteReply(User user, URI attachment, String... args) { - int mid = Integer.valueOf(args[0]); - int rid = Integer.valueOf(args[2]); - if (messagesService.deleteReply(user.getUid(), mid, rid)) { - return CommandResult.fromString("Reply deleted"); - } else { - return CommandResult.fromString("This is not your reply"); - } - } - @UserCommand(pattern = "^(D L|DL|D LAST)$", help = "D L - Delete last message", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandDeleteLast(User user, URI attachment, String... args) { - return CommandResult.fromString("Temporarily unavailable"); - } - @UserCommand(pattern = "^\\?\\s+\\@([a-zA-Z0-9\\-\\.\\@]+)\\s+([\\s\\S]+)$", help = "? @user string - search in user messages") - public CommandResult commandSearch(User user, URI attachment, String... args) { - return CommandResult.fromString("Temporarily unavailable"); - } - @UserCommand(pattern = "^\\?\\s+([\\s\\S]+)$", help = "? string - search in all messages") - public CommandResult commandSearchAll(User user, URI attachment, String... args) { - return CommandResult.fromString("Temporarily unavailable"); - } - @UserCommand(pattern = "^(#+)$", help = "# - Show last messages from your feed (## - second page, ...)") - public CommandResult commandMyFeed(User user, URI attachment, String... arguments) { - // number of # is the page count - int page = arguments[0].length() - 1; - List mids = messagesService.getMyFeed(user.getUid(), page, false); - if (mids.size() > 0) { - return CommandResult.fromString("Your feed: \n" + printMessages(user, mids, true)); - } - return CommandResult.fromString("Your feed is empty"); - } - @UserCommand(pattern = "^(#|\\.)(\\d+)((\\.|\\-|\\/)(\\d+))?\\s([\\s\\S]+)?", - help = "#1234 *tag *tag2 - edit tags\n#1234 text - reply to message") - public CommandResult EditOrReply(User user, @Nonnull URI attachment, String... args) throws Exception { - int mid = NumberUtils.toInt(args[1]); - int rid = NumberUtils.toInt(args[4], 0); - String txt = StringUtils.defaultString(args[5]); - Message msg = messagesService.getMessage(mid); - Pair> messageTags = tagService.fromString(txt); - if (messageTags.getRight().size() > 0) { - if (user.getUid() != msg.getUser().getUid()) { - return CommandResult.fromString("It is not your message"); - } - if (!CollectionUtils.isEqualCollection(tagService.updateTags(mid, messageTags.getRight()), msg.getTags())) { - return CommandResult.fromString("Tags are updated"); - } else { - return CommandResult.fromString("Tags are NOT updated (5 tags maximum?)"); - } - } else { - boolean haveAttachment = StringUtils.isNotEmpty(attachment.toString()); - String attachmentFName = null; - String attachmentType = null; - if (haveAttachment) { - attachmentFName = attachment.getScheme().equals("juick") ? attachment.getHost() - : HttpUtils.downloadImage(attachment.toURL(), tmpDir).getHost(); - attachmentType = attachmentFName.substring(attachmentFName.length() - 3); - } - int newrid = messagesService.createReply(mid, rid, user, txt, attachmentType); - if (haveAttachment) { - String fname = String.format("%d-%d.%s", mid, newrid, attachmentType); - imagesService.saveImageWithPreviews(attachmentFName, fname); - } - applicationEventPublisher.publishEvent( - new MessageReadEvent(this, user, msg)); - Message original = messagesService.getMessage(mid); - subscriptionService.subscribeMessage(original, user); - Message reply = messagesService.getReply(mid, newrid); - applicationEventPublisher.publishEvent(new MessageEvent(this, reply, subscriptionService.getUsersSubscribedToComments(original, reply))); - return CommandResult.build(reply,"Reply posted.\n#" + mid + "/" + newrid + " " - + "https://juick.com/m/" + mid + "#" + newrid, - String.format("[Reply](%s) posted", PlainTextFormatter.formatUrl(reply))); - } - } - - String printMessages(User visitor, List mids, boolean crop) { - return messagesService.getMessages(visitor, mids).stream() - .sorted(Collections.reverseOrder()) - .map(PlainTextFormatter::formatPostSummary).collect(Collectors.joining("\n\n")); - } -} diff --git a/juick-server/src/main/java/com/juick/server/EmailManager.java b/juick-server/src/main/java/com/juick/server/EmailManager.java deleted file mode 100644 index 1cdafac6..00000000 --- a/juick-server/src/main/java/com/juick/server/EmailManager.java +++ /dev/null @@ -1,165 +0,0 @@ -package com.juick.server; - -import com.juick.Message; -import com.juick.User; -import com.juick.service.EmailService; -import com.juick.service.MessagesService; -import com.juick.service.UserService; -import com.juick.service.component.*; -import com.juick.util.MessageUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -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.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import static com.juick.formatters.PlainTextFormatter.formatPost; -import static com.juick.formatters.PlainTextFormatter.formatUrl; - -@Component -public class EmailManager implements NotificationListener { - - public static final String MSGID_PATTERN = "\\.|@|<"; - - private static final Logger logger = LoggerFactory.getLogger(EmailManager.class); - @Inject - private EmailService emailService; - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - @Override - public void processMessageEvent(@Nonnull MessageEvent event) { - Message msg = event.getMessage(); - List subscribedUsers = event.getUsers(); - if (msg.isService()) { - return; - } - if (MessageUtils.isPM(msg)) { - String subject = String.format("Private message from %s", msg.getUser().getName()); - emailService.getEmails(msg.getTo().getUid(), true).forEach(email -> { - emailNotify(email, subject, msg); - }); - } else if (MessageUtils.isReply(msg)) { - Message originalMessage = messagesService.getMessage(msg.getMid()); - String subject = String.format("New reply to %s", originalMessage.getUser().getName()); - subscribedUsers.stream() - .flatMap(user -> emailService.getEmails(user.getUid(), true).stream()) - .forEach(email -> emailNotify(email, subject, msg)); - } else { - String subject = String.format("New message from %s", msg.getUser().getName()); - subscribedUsers - .forEach(user -> emailService.getEmails(user.getUid(), true) - .forEach(email -> emailNotify(email, subject, msg))); - } - } - - @Override - public void processSubscribeEvent(SubscribeEvent subscribeEvent) { - - } - - @Override - public void processLikeEvent(LikeEvent likeEvent) { - - } - - @Override - public void processPingEvent(PingEvent pingEvent) { - - } - - @Override - public void processMessageReadEvent(MessageReadEvent messageReadEvent) { - - } - - @Override - public void processTopEvent(TopEvent topEvent) { - - } - - private void emailNotify(String email, String subject, Message msg) { - Map headers = new HashMap<>(); - if (!MessageUtils.isPM(msg)) { - headers.put("Message-ID", String.format("<%d.%d@juick.com>", msg.getMid(), msg.getRid())); - } - if (MessageUtils.isReply(msg)) { - if (msg.getReplyto() > 0) { - Message replyto = messagesService.getReply(msg.getMid(), msg.getReplyto()); - headers.put("In-Reply-To", String.format("<%d.%d@juick.com>", replyto.getMid(), replyto.getRid())); - } else { - Message original = messagesService.getMessage(msg.getMid()); - headers.put("In-Reply-To", String.format("<%d.%d@juick.com>", original.getMid(), original.getRid())); - } - } - String plainText = String.format("%s\n\n--\nYou are receiving this because you are subscribed to this user," + - " discussion, tag or mentioned. Reply to this email directly or view it on Juick: %s.", - formatPost(msg), formatUrl(msg)); - String hash = userService.getHashByUID(userService.getUserByEmail(email).getUid()); - String htmlText = String.format("%s

--
You are receiving this because you are subscribed to this user" + - ", discussion, tag or mentioned. Reply to this email directly or view it on Juick." + - "
Configure or disable notifications", - msg.isHtml() ? msg.getText() : MessageUtils.formatHtml(msg), formatUrl(msg), - msg.getMid(), msg.getRid(), hash, hash); - sendEmail(email, subject, plainText, htmlText, headers); - } - public void sendEmail(String to, String subject, String textPart, String htmlPart, Map messageHeaders) { - Properties prop = System.getProperties(); - prop.put("mail.smtp.starttls.enable", "true"); - Session session = Session.getDefaultInstance(prop); - try { - Transport transport = session.getTransport("smtp"); - MimeMessage message = new MimeMessage(session) { - protected void updateMessageID() throws MessagingException { - for (Map.Entry entry: messageHeaders.entrySet()) { - setHeader(entry.getKey(), entry.getValue()); - } - } - }; - message.setFrom(new InternetAddress("juick@juick.com")); - message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to)); - message.setSubject(subject); - MimeBodyPart textBodyPart = new MimeBodyPart(); - textBodyPart.setContent(textPart, "text/plain; charset=UTF-8"); - - Multipart multipart = new MimeMultipart("alternative"); - multipart.addBodyPart(textBodyPart); - if (StringUtils.isNotBlank(htmlPart)) { - MimeBodyPart htmlBodyPart = new MimeBodyPart(); - htmlBodyPart.setContent(htmlPart, "text/html; charset=UTF-8"); - multipart.addBodyPart(htmlBodyPart); - } - message.setContent(multipart); - User emailUser = userService.getUserByEmail(to); - if (!emailUser.isAnonymous()) { - message.setHeader("List-Id", "Juick notifications "); - message.setHeader("List-Post", ""); - message.setHeader("List-Owner", ""); - message.setHeader("List-Archive", ""); - message.setHeader("List-Unsubscribe", String.format("https://juick.com/settings/unsubscribe?hash=%s", - userService.getHashByUID(emailUser.getUid()))); - message.setHeader("List-Unsubscribe-Post", "List-Unsubscribe=One-Click"); - } - message.saveChanges(); - transport.connect(); - transport.sendMessage(message, message.getAllRecipients()); - } catch (MessagingException ex) { - logger.error("mail exception", ex); - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/KeystoreManager.java b/juick-server/src/main/java/com/juick/server/KeystoreManager.java deleted file mode 100644 index 97c3a224..00000000 --- a/juick-server/src/main/java/com/juick/server/KeystoreManager.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.juick.server; - -import com.juick.server.api.activity.model.objects.Person; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.util.Base64Utils; - -import javax.annotation.PostConstruct; -import javax.net.ssl.KeyManagerFactory; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.*; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.spec.X509EncodedKeySpec; -import java.util.Arrays; -import java.util.stream.Collectors; - -@Component -public class KeystoreManager { - private static final Logger logger = LoggerFactory.getLogger("com.juick.server"); - @Value("${keystore:../juick.p12}") - private String keystore; - @Value("${keystore_password:secret}") - private String keystorePassword; - - private KeyStore ks; - - private KeyManagerFactory kmf; - - @PostConstruct - public void init() { - try (InputStream ksIs = new FileInputStream(keystore)) { - ks = KeyStore.getInstance("PKCS12"); - ks.load(ksIs, keystorePassword.toCharArray()); - kmf = KeyManagerFactory.getInstance(KeyManagerFactory - .getDefaultAlgorithm()); - kmf.init(ks, keystorePassword.toCharArray()); - } catch (IOException | KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException e) { - logger.error("Keystore error", e); - } - } - - public KeyStore getKeystore() { - return ks; - } - - public KeyManagerFactory getKeymanagerFactory() { - return kmf; - } - - private KeyPair getKeyPair() { - Key privateKey = null; - try { - privateKey = ks.getKey("1", keystorePassword.toCharArray()); - Certificate certificate = ks.getCertificate("1"); - return new KeyPair(certificate.getPublicKey(), (PrivateKey) privateKey); - } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) { - e.printStackTrace(); - } - return null; - } - public PrivateKey getPrivateKey() { - return getKeyPair().getPrivate(); - } - public PublicKey getPublicKey() { - return getKeyPair().getPublic(); - } - public String getPublicKeyPem() { - String[] key = Base64Utils.encodeToString(getKeyPair().getPublic().getEncoded()).split("(?<=\\G.{64})"); - return String.format("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n", - Arrays.asList(key).stream().collect(Collectors.joining("\n"))); - } - public static PublicKey publicKeyOf(Person person) { - String pubkeyPem = person.getPublicKey().getPublicKeyPem(); - String[] rawKey = pubkeyPem.split("\\n"); - String pubkeyData = String.join("", Arrays.asList(rawKey).subList(1, rawKey.length - 1)); - try{ - byte[] byteKey = Base64Utils.decodeFromString(pubkeyData); - X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(byteKey); - KeyFactory kf = KeyFactory.getInstance("RSA"); - return kf.generatePublic(X509publicKey); - } - catch(Exception e){ - e.printStackTrace(); - } - return null; - } -} diff --git a/juick-server/src/main/java/com/juick/server/ServerManager.java b/juick-server/src/main/java/com/juick/server/ServerManager.java deleted file mode 100644 index ef848526..00000000 --- a/juick-server/src/main/java/com/juick/server/ServerManager.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.server; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.juick.Message; -import com.juick.User; -import com.juick.model.AnonymousUser; -import com.juick.service.MessagesService; -import com.juick.service.SubscriptionService; -import com.juick.service.UserService; -import com.juick.service.component.LikeEvent; -import com.juick.service.component.MessageEvent; -import com.juick.service.component.MessageReadEvent; -import com.juick.service.component.NotificationListener; -import com.juick.service.component.PingEvent; -import com.juick.service.component.SubscribeEvent; -import com.juick.service.component.TopEvent; -import com.juick.util.MessageUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import org.springframework.web.socket.TextMessage; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.stream.Collectors; - -/** - * @author Ugnich Anton - */ -@Component -public class ServerManager implements NotificationListener { - private static Logger logger = LoggerFactory.getLogger(ServerManager.class); - - @Inject - private ObjectMapper jsonMapper; - @Inject - private MessagesService messagesService; - @Inject - private WebsocketManager wsHandler; - @Inject - private SubscriptionService subscriptionService; - @Inject - private UserService userService; - private CopyOnWriteArrayList sessions = new CopyOnWriteArrayList<>(); - - @Value("${service_user:juick}") - private String serviceUsername; - - private User serviceUser; - - @PostConstruct - public void init() { - serviceUser = userService.getUserByName(serviceUsername); - } - - - private void onJuickPM(final User to, final com.juick.Message jmsg) { - try { - String json = jsonMapper.writeValueAsString(jmsg); - synchronized (wsHandler.getClients()) { - wsHandler.getClients().stream().filter(c -> - (!c.legacy && c.visitor.getUid() == to.getUid()) || c.visitor.equals(serviceUser)) - .forEach(c -> { - try { - logger.debug("sending pm to {}", c.visitor.getUid()); - c.sendMessage(new TextMessage(json)); - } catch (IOException e) { - logger.warn("ws error", e); - } - }); - } - } catch (JsonProcessingException e) { - logger.warn("Invalid JSON", e); - } - messageEvent(jmsg, Collections.singletonList(to)); - } - - private void onJuickMessagePost(final com.juick.Message jmsg, List subscribedUsers) { - try { - String json = jsonMapper.writeValueAsString(jmsg); - List uids = subscribedUsers - .stream().map(User::getUid).collect(Collectors.toList()); - synchronized (wsHandler.getClients()) { - wsHandler.getClients().stream().filter(c -> - (!c.legacy && c.visitor.isAnonymous()) // anonymous users - || c.visitor.equals(serviceUser) // services - || (!c.legacy && uids.contains(c.visitor.getUid()))) // subscriptions - .forEach(c -> { - try { - logger.debug("sending message to {}", c.visitor.getUid()); - c.sendMessage(new TextMessage(json)); - } catch (IOException e) { - logger.warn("ws error", e); - } - }); - wsHandler.getClients().stream().filter(c -> - c.legacy && c.allMessages) // legacy all posts - .forEach(c -> { - try { - logger.debug("sending message to legacy client {}", c.visitor.getUid()); - c.sendMessage(new TextMessage(json)); - } catch (IOException e) { - logger.warn("ws error", e); - } - }); - } - } catch (JsonProcessingException e) { - logger.warn("Invalid JSON", e); - } - messageEvent(jmsg, subscribedUsers); - messageEvent(jmsg, Collections.singletonList(AnonymousUser.INSTANCE)); - } - - private void onJuickMessageReply(final com.juick.Message jmsg, final List subscribedUsers) { - try { - - String json = jsonMapper.writeValueAsString(jmsg); - com.juick.Message op = messagesService.getMessage(jmsg.getMid()); - List threadUsers = - subscribedUsers - .stream().map(User::getUid).collect(Collectors.toList()); - synchronized (wsHandler.getClients()) { - wsHandler.getClients().stream().filter(c -> - (!c.legacy && c.visitor.isAnonymous()) // anonymous users - || c.visitor.equals(serviceUser) // services - || (!c.legacy && threadUsers.contains(c.visitor.getUid()))) // subscriptions - .forEach(c -> { - try { - logger.debug("sending reply to {}", c.visitor.getUid()); - c.sendMessage(new TextMessage(json)); - } catch (IOException e) { - logger.warn("ws error", e); - } - }); - wsHandler.getClients().stream().filter(c -> - (c.legacy && c.allReplies) || (c.legacy && c.MID == jmsg.getMid())) // legacy replies - .forEach(c -> { - try { - logger.debug("sending reply to legacy client {}", c.visitor.getUid()); - c.sendMessage(new TextMessage(json)); - } catch (IOException e) { - logger.warn("ws error", e); - } - }); - } - } catch (JsonProcessingException e) { - logger.warn("Invalid JSON", e); - } - messageEvent(jmsg, subscribedUsers); - messageEvent(jmsg, Collections.singletonList(AnonymousUser.INSTANCE)); - } - - @Override - public void processMessageEvent(MessageEvent event) { - com.juick.Message jmsg = event.getMessage(); - List subscribedUsers = event.getUsers(); - if (jmsg.isService()) { - readEvent(jmsg, Collections.singletonList(serviceUser)); - return; - } - if (MessageUtils.isPM(jmsg)) { - onJuickPM(jmsg.getTo(), jmsg); - } else if (!MessageUtils.isReply(jmsg)) { - // to get full message with attachment, etc. - onJuickMessagePost(messagesService.getMessage(jmsg.getMid()), subscribedUsers); - } else { - // to get quote and attachment - Message op = messagesService.getMessage(jmsg.getMid()); - com.juick.Message reply = messagesService.getReply(jmsg.getMid(), jmsg.getRid()); - subscriptionService.getUsersSubscribedToComments(op, reply, true).stream() - .filter(u -> userService.isReplyToBL(u, reply)) - .forEach(b -> messagesService.setLastReadComment(b, reply.getMid(), reply.getRid())); - onJuickMessageReply(reply, subscribedUsers); - } - messageEvent(jmsg, Collections.singletonList(serviceUser)); - } - - @Override - public void processSubscribeEvent(SubscribeEvent subscribeEvent) { - - } - - @Override - public void processLikeEvent(LikeEvent likeEvent) { - - } - - @Override - public void processPingEvent(PingEvent pingEvent) { - - } - - @Override - public void processMessageReadEvent(MessageReadEvent messageReadEvent) { - User user = messageReadEvent.getUser(); - Message source = messageReadEvent.getMessage(); - - logger.info("Message read event from {} for {}", user.getUid(), source.getMid()); - Message serviceMessage = new Message(); - serviceMessage.setService(true); - serviceMessage.setUser(user); - serviceMessage.setMid(source.getMid()); - serviceMessage.setUnread(false); - wsHandler.getClients().stream().filter(c -> - (!c.legacy && c.visitor == user) || c.visitor.equals(serviceUser) - ).forEach(u -> { - try { - u.sendMessage(new TextMessage(jsonMapper.writeValueAsString(serviceMessage))); - } catch (IOException e) { - logger.error("JSON error", e); - } - }); - readEvent(serviceMessage, Collections.singletonList(serviceUser)); - } - - @Override - public void processTopEvent(TopEvent topEvent) { - User topUser = topEvent.getMessage().getUser(); - topEvent(topEvent.getMessage(), Arrays.asList(topUser, serviceUser)); - } - - public void topEvent(Message msg, List subscribers){ - sendSseEvent(msg, "top", subscribers); - } - - public void readEvent(Message msg, List subscribers){ - sendSseEvent(msg, "read", subscribers); - } - - public void messageEvent(Message msg, List subscribers){ - sendSseEvent(msg, "msg", subscribers); - } - - private void sendSseEvent(Message msg, String name, List subscribers) { - List deadEmitters = new ArrayList<>(); - this.sessions.stream().filter(s -> subscribers.contains(s.user)).forEach(session -> { - try { - SseEmitter.SseEventBuilder builder = SseEmitter.event() - .name(name) - .data(msg); - session.getEmitter().send(builder); - } catch (Exception e) { - deadEmitters.add(session); - } - }); - this.sessions.removeAll(deadEmitters); - } - - public static class EventSession { - private User user; - private SseEmitter emitter; - - public EventSession(User user, SseEmitter sseEmitter) { - this.user = user; - this.emitter = sseEmitter; - } - - public User getUser() { - return user; - } - - public SseEmitter getEmitter() { - return emitter; - } - } - - public CopyOnWriteArrayList getSessions() { - return sessions; - } -} diff --git a/juick-server/src/main/java/com/juick/server/SignatureManager.java b/juick-server/src/main/java/com/juick/server/SignatureManager.java deleted file mode 100644 index b3b7a301..00000000 --- a/juick-server/src/main/java/com/juick/server/SignatureManager.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.juick.server; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.juick.server.api.activity.model.Context; -import com.juick.server.api.activity.model.objects.Person; -import com.juick.server.api.webfinger.model.Account; -import com.juick.server.api.webfinger.model.Link; -import com.juick.util.DateFormattersHolder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; -import org.tomitribe.auth.signatures.Signature; -import org.tomitribe.auth.signatures.Signer; -import org.tomitribe.auth.signatures.Verifier; -import rocks.xmpp.addr.Jid; - -import javax.inject.Inject; -import java.io.IOException; -import java.net.URI; -import java.security.Key; -import java.security.NoSuchAlgorithmException; -import java.security.SignatureException; -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -import static com.juick.server.api.activity.model.Context.ACTIVITY_MEDIA_TYPE; - -@Component -public class SignatureManager { - private static final Logger logger = LoggerFactory.getLogger(ActivityPubManager.class); - @Inject - private KeystoreManager keystoreManager; - @Inject - private ObjectMapper jsonMapper; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - @Inject - private RestTemplate apClient; - - public void post(Person from, Person to, Context data) throws IOException { - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(to.getInbox()); - URI inbox = uriComponentsBuilder.build().toUri(); - Instant now = Instant.now(); - String requestDate = DateFormattersHolder.getHttpDateFormatter().format(now); - Signature templateSignature = new Signature(from.getPublicKey().getId(), "rsa-sha256", null, - "(request-target)", "host", "date"); - Signer signer = new Signer(keystoreManager.getPrivateKey(), templateSignature); - Map headers = new HashMap<>(); - headers.put("host", inbox.getHost()); - headers.put("date", requestDate); - Signature signature = signer.sign("POST", inbox.getPath(), headers); - HttpHeaders requestHeaders = new HttpHeaders(); - requestHeaders.add("Content-Type", Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE); - requestHeaders.add("Date", requestDate); - requestHeaders.add("Signature", signature.toString().substring(10)); - HttpEntity request = new HttpEntity<>(Context.build(data), requestHeaders); - //boolean valid = verifySignature(Signature.fromString(requestHeaders.getFirst("Signature")), - // keystoreManager.getPublicKey(), "POST", inbox.getPath(), headers); - logger.info("Sending context: {}", jsonMapper.writeValueAsString(data)); - logger.info("Request date: {}", requestDate); - ResponseEntity response = apClient.postForEntity(inbox, request, Void.class); - logger.info("accepted follower: {}", response.getStatusCodeValue()); - - } - public boolean verifySignature(String signatureString, URI actor, String method, String path, Map headers) { - Optional context = getContext(actor); - if (context.isPresent() && context.get() instanceof Person) { - Person person = (Person) context.get(); - Key key = KeystoreManager.publicKeyOf(person); - Verifier verifier = new Verifier(key, Signature.fromString(signatureString)); - try { - boolean result = verifier.verify(method, path, headers); - logger.info("signature is valid: {}", result); - return result; - } catch (NoSuchAlgorithmException | SignatureException | IOException e) { - logger.info("signature exception", e); - return false; - } - } - logger.info("person not found"); - return false; - } - public Optional getContext(URI contextUri) { - Context context = apClient.getForEntity(contextUri, Context.class).getBody(); - if (context == null) { - logger.warn("Cannot identify {}", contextUri); - return Optional.empty(); - } - return Optional.of(context); - } - public Optional discoverPerson(String acct) { - Jid acctId = Jid.of(acct); - URI resourceUri = UriComponentsBuilder.fromUriString( - String.format("https://%s/.well-known/webfinger?resource=acct:%s", acctId.getDomain(), acct)).build().toUri(); - Account acctData = apClient.getForEntity(resourceUri, Account.class).getBody(); - if (acctData != null) { - for (Link l : acctData.getLinks()) { - if (l.getRel().equals("self") && l.getType().equals(ACTIVITY_MEDIA_TYPE)) { - return getContext(URI.create(l.getHref())); - } - } - } - return Optional.empty(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/TelegramBotManager.java b/juick-server/src/main/java/com/juick/server/TelegramBotManager.java deleted file mode 100644 index 8e8d0104..00000000 --- a/juick-server/src/main/java/com/juick/server/TelegramBotManager.java +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server; - -import com.juick.User; -import com.juick.service.component.*; -import com.juick.model.AnonymousUser; -import com.juick.model.CommandResult; -import com.juick.server.util.HttpUtils; -import com.juick.service.MessagesService; -import com.juick.service.TelegramService; -import com.juick.service.UserService; -import com.juick.util.MessageUtils; -import com.pengrad.telegrambot.Callback; -import com.pengrad.telegrambot.TelegramBot; -import com.pengrad.telegrambot.UpdatesListener; -import com.pengrad.telegrambot.model.Message; -import com.pengrad.telegrambot.model.MessageEntity; -import com.pengrad.telegrambot.model.PhotoSize; -import com.pengrad.telegrambot.model.Update; -import com.pengrad.telegrambot.model.request.ParseMode; -import com.pengrad.telegrambot.request.GetFile; -import com.pengrad.telegrambot.request.SendMessage; -import com.pengrad.telegrambot.request.SendPhoto; -import com.pengrad.telegrambot.request.SetWebhook; -import com.pengrad.telegrambot.response.GetFileResponse; -import com.pengrad.telegrambot.response.SendResponse; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import java.io.IOException; -import java.net.URI; -import java.net.URL; -import java.util.*; - -import static com.juick.formatters.PlainTextFormatter.formatPost; -import static com.juick.formatters.PlainTextFormatter.formatUrl; - -/** - * Created by vt on 12/05/16. - */ -public class TelegramBotManager implements NotificationListener { - private static final Logger logger = LoggerFactory.getLogger(TelegramBotManager.class); - - private TelegramBot bot; - - @Value("${telegram_token:12345678}") - private String telegramToken; - @Value("${telegram_debug:false}") - private boolean telegramDebug; - @Inject - private TelegramService telegramService; - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - @Inject - private CommandsManager commandsManager; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - - private static final String MSG_LINK = "🔗"; - - @PostConstruct - public void init() { - if (StringUtils.isBlank(telegramToken)) { - logger.info("telegram token is not set, exiting"); - return; - } - bot = new TelegramBot(telegramToken); - if (!telegramDebug) { - try { - SetWebhook webhook = new SetWebhook().url("https://api.juick.com/tlgmbtwbhk"); - if (!bot.execute(webhook).isOk()) { - logger.error("error setting webhook"); - } - } catch (Exception e) { - logger.warn("couldn't initialize telegram bot", e); - } - } else { - bot.setUpdatesListener(updates -> { - updates.forEach(this::processUpdate); - return UpdatesListener.CONFIRMED_UPDATES_ALL; - }); - } - } - - public void processUpdate(Update update) { - Message message = update.message(); - if (update.message() == null) { - message = update.editedMessage(); - if (message == null) { - logger.error("error parsing telegram update: {}", update); - return; - } - } - User user_from = userService.getUserByUID(telegramService.getUser(message.chat().id())).orElse(AnonymousUser.INSTANCE); - logger.info("Found juick user {}", user_from.getUid()); - - String username = message.from().username(); - if (username == null) { - username = message.from().firstName(); - } - if (!user_from.isAnonymous()) { - URI attachment = URI.create(StringUtils.EMPTY); - if (message.photo() != null) { - String fileId = Arrays.stream(message.photo()).max(Comparator.comparingInt(PhotoSize::fileSize)) - .orElse(new PhotoSize()).fileId(); - if (StringUtils.isNotEmpty(fileId)) { - GetFile request = new GetFile(fileId); - GetFileResponse response = bot.execute(request); - logger.info("got file {}", response.file()); - try { - URL fileURL = new URL(bot.getFullFilePath(response.file())); - attachment = HttpUtils.downloadImage(fileURL, tmpDir); - } catch (Exception e) { - logger.warn("attachment exception", e); - } - logger.info("received {}", attachment); - } - } - String text = message.text(); - if (StringUtils.isBlank(text)) { - text = message.caption(); - } - if (StringUtils.isBlank(text)) { - text = StringUtils.EMPTY; - } - if (StringUtils.isNotEmpty(text) || StringUtils.isNotEmpty(attachment.toString())) { - if (text.equalsIgnoreCase("LOGIN") - || text.equalsIgnoreCase("PING") - || text.equalsIgnoreCase("HELP") - || text.equalsIgnoreCase("/login") - || text.equalsIgnoreCase("/logout") - || text.equalsIgnoreCase("/start") - || text.equalsIgnoreCase("/help")) { - String msgUrl = "http://juick.com/login?hash=" + userService.getHashByUID(user_from.getUid()); - String msg = String.format("Hi, %s!\nYou can post messages and images to Juick there.\n" + - "Tap to [log into website](%s) to get more info", user_from.getName(), msgUrl); - telegramNotify(message.from().id().longValue(), msg, new com.juick.Message()); - } else { - Message replyMessage = message.replyToMessage(); - if (replyMessage != null) { - MessageEntity[] entities = replyMessage.entities(); - if (entities == null) { - entities = replyMessage.captionEntities(); - } - if (entities != null) { - Optional juickLink = Arrays.stream(entities) - .filter(this::isJuickLink) - .findFirst(); - if (juickLink.isPresent()) { - if (StringUtils.isNotEmpty(juickLink.get().url())) { - UriComponents uriComponents = UriComponentsBuilder.fromUriString( - juickLink.get().url()).build(); - String path = uriComponents.getPath(); - if (StringUtils.isNotEmpty(path) && path.length() > 1) { - int mid = 0; - try { - mid = Integer.valueOf(path.substring(3)); - } catch (NumberFormatException e) { - logger.warn("wrong mid received"); - return; - } - String prefix = String.format("#%d ", mid); - if (StringUtils.isNotEmpty(uriComponents.getFragment())) { - int rid = Integer.valueOf(uriComponents.getFragment()); - prefix = String.format("#%d/%d ", mid, rid); - } - CommandResult result = null; - try { - result = commandsManager.processCommand(user_from, prefix + text, attachment); - String messageTxt = StringUtils.isNotEmpty(result.getMarkdown()) ? result.getMarkdown() - : "Unknown error or unsupported command"; - telegramNotify(message.from().id().longValue(), messageTxt, new com.juick.Message()); - } catch (Exception e) { - logger.warn("telegram exception", e); - } - } else { - logger.warn("invalid path: {}", path); - } - } else { - logger.warn("invalid entity: {}", juickLink); - } - } else { - telegramNotify(message.from().id().longValue(), - "Can not reply to this message", replyMessage.messageId(), new com.juick.Message()); - } - } else { - telegramNotify(message.from().id().longValue(), - "Can not reply to this message", replyMessage.messageId(), new com.juick.Message()); - } - } else { - try { - CommandResult result = commandsManager.processCommand(user_from, text, attachment); - String messageTxt = StringUtils.isNotEmpty(result.getMarkdown()) ? result.getMarkdown() - : "Unknown error or unsupported command"; - telegramNotify(message.from().id().longValue(), messageTxt, new com.juick.Message()); - } catch (Exception e) { - logger.warn("telegram reply exception", e); - } - } - } - } - } else { - List chats = telegramService.getAnonymous(); - if (!chats.contains(message.chat().id())) { - logger.info("added chat with {}", username); - telegramService.createTelegramUser(message.from().id(), username); - } - telegramSignupNotify(message.from().id().longValue(), userService.getSignUpHashByTelegramID(message.from().id().longValue(), username)); - } - } - - private boolean isJuickLink(MessageEntity e) { - return e.offset() == 0 && e.type().equals(MessageEntity.Type.text_link) && e.length() == 2; - } - - public void telegramNotify(Long chatId, String msg, @Nonnull com.juick.Message source) { - telegramNotify(chatId, msg, 0, source); - } - - public void telegramNotify(Long chatId, String msg, Integer replyTo, @Nonnull com.juick.Message source) { - String attachment = MessageUtils.attachmentUrl(source); - if (StringUtils.isEmpty(attachment)) { - SendMessage telegramMessage = new SendMessage(chatId, msg); - if (replyTo > 0) { - telegramMessage.replyToMessageId(replyTo); - } - telegramMessage.parseMode(ParseMode.Markdown).disableWebPagePreview(true); - bot.execute(telegramMessage, new Callback() { - @Override - public void onResponse(SendMessage request, SendResponse response) { - processTelegramResponse(chatId, response, source); - } - - @Override - public void onFailure(SendMessage request, IOException e) { - logger.warn("telegram failure", e); - } - }); - } else { - SendPhoto telegramPhoto = new SendPhoto(chatId, attachment); - String trimmedPost = msg.length() > 1024 ? msg.substring(0, 1023) + "..." : msg; - telegramPhoto.caption(trimmedPost); - if (replyTo > 0) { - telegramPhoto.replyToMessageId(replyTo); - } - telegramPhoto.parseMode(ParseMode.Markdown); - bot.execute(telegramPhoto, new Callback() { - @Override - public void onResponse(SendPhoto request, SendResponse response) { - processTelegramResponse(chatId, response, source); - } - - @Override - public void onFailure(SendPhoto request, IOException e) { - logger.warn("telegram failure", e); - } - }); - } - } - - private void processTelegramResponse(Long chatId, SendResponse response, com.juick.Message source) { - int userId = telegramService.getUser(chatId); - if (!response.isOk()) { - if (response.errorCode() == 403) { - // remove from anonymous users - telegramService.getAnonymous().stream().filter(c -> c.equals(chatId)).findFirst().ifPresent( - d -> { - telegramService.deleteAnonymous(d); - logger.info("deleted {} chat", d); - } - ); - if (userId > 0) { - User userToDelete = userService.getUserByUID(userId) - .orElseThrow(IllegalStateException::new); - boolean status = telegramService.deleteTelegramUser(userToDelete.getUid()); - logger.info("deleting telegram id of @{} : {}", userToDelete.getName(), status); - } - } else { - logger.warn("error response, isOk: {}, errorCode: {}, description: {}", - response.isOk(), response.errorCode(), response.description()); - } - } else { - if (MessageUtils.isReply(source)) { - messagesService.setLastReadComment(userService.getUserByUID(userId) - .orElseThrow(IllegalStateException::new), source.getMid(), source.getRid()); - User user = userService.getUserByUID(userId).orElseThrow(IllegalStateException::new); - userService.updateLastSeen(user); - applicationEventPublisher.publishEvent( - new MessageReadEvent(this, user, source)); - } - } - } - - public void telegramSignupNotify(Long telegramId, String hash) { - bot.execute(new SendMessage(telegramId, - String.format("You are subscribed to all Juick messages. " + - "[Create or link](http://juick.com/signup?type=durov&hash=%s) " + - "an existing Juick account to get your subscriptions and ability to post messages", hash)) - .parseMode(ParseMode.Markdown), new Callback() { - @Override - public void onResponse(SendMessage request, SendResponse response) { - logger.info("got response: {}", response.message()); - } - - @Override - public void onFailure(SendMessage request, IOException e) { - logger.warn("telegram failure", e); - } - }); - } - - @Override - public void processMessageEvent(MessageEvent messageEvent) { - com.juick.Message jmsg = messageEvent.getMessage(); - List subscribedUsers = messageEvent.getUsers(); - if (jmsg.isService()) { - return; - } - String msgUrl = formatUrl(jmsg); - if (MessageUtils.isPM(jmsg)) { - telegramService.getTelegramIdentifiers(Collections.singletonList(jmsg.getTo())) - .forEach(c -> telegramNotify(c, formatPost(jmsg, true), jmsg)); - } else if (MessageUtils.isReply(jmsg)) { - com.juick.Message op = messagesService.getMessage(jmsg.getMid()); - String fmsg = String.format("[%s](%s) %s", MSG_LINK, msgUrl, formatPost(jmsg, true)); - telegramService.getTelegramIdentifiers( - subscribedUsers - ).forEach(c -> telegramNotify(c, fmsg, jmsg)); - } else { - String msg = String.format("[%s](%s) %s", MSG_LINK, msgUrl, formatPost(jmsg, true)); - - List users = telegramService.getTelegramIdentifiers(subscribedUsers); - List chats = telegramService.getAnonymous(); - // registered subscribed users - - users.forEach(c -> telegramNotify(c, msg, jmsg)); - // anonymous - chats.stream().filter(u -> telegramService.getUser(u) == 0).forEach(c -> telegramNotify(c, msg, jmsg)); - } - } - - @Override - public void processLikeEvent(LikeEvent likeEvent) { - User liker = likeEvent.getUser(); - com.juick.Message message = likeEvent.getMessage(); - List subscribers = likeEvent.getSubscribers(); - logger.info("Like received in tg listener"); - if (!userService.isInBLAny(message.getUser().getUid(), liker.getUid())) { - telegramService.getTelegramIdentifiers(Collections.singletonList(message.getUser())) - .forEach(c -> telegramNotify(c, String.format("%s recommends your [post](%s)", - MessageUtils.getMarkdownUser(liker), formatUrl(message)), new com.juick.Message())); - } - telegramService.getTelegramIdentifiers(subscribers) - .forEach(c -> telegramNotify(c, String.format("%s recommends you someone's [post](%s)", - MessageUtils.getMarkdownUser(liker), formatUrl(message)), new com.juick.Message())); - } - - @Override - public void processPingEvent(PingEvent pingEvent) { - - } - - @Override - public void processMessageReadEvent(MessageReadEvent messageReadEvent) { - - } - - @Override - public void processTopEvent(TopEvent topEvent) { - com.juick.Message message = topEvent.getMessage(); - telegramService.getTelegramIdentifiers(Collections.singletonList(message.getUser())) - .forEach(c -> telegramNotify(c, String.format("Your [post](%s) became popular!", - formatUrl(message)), new com.juick.Message())); - } - - @Override - public void processSubscribeEvent(SubscribeEvent subscribeEvent) { - User subscriber = subscribeEvent.getUser(); - User target = subscribeEvent.getToUser(); - telegramService.getTelegramIdentifiers(Collections.singletonList(target)) - .forEach(c -> telegramNotify(c, String.format("%s subscribed to your blog", - MessageUtils.getMarkdownUser(subscriber)), new com.juick.Message())); - } -} diff --git a/juick-server/src/main/java/com/juick/server/TopManager.java b/juick-server/src/main/java/com/juick/server/TopManager.java deleted file mode 100644 index e5c00242..00000000 --- a/juick-server/src/main/java/com/juick/server/TopManager.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server; - -import com.juick.Message; -import com.juick.Tag; -import com.juick.service.MessagesService; -import com.juick.service.component.TopEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -import javax.inject.Inject; -import java.util.List; -import java.util.stream.Collectors; - -@Component -public class TopManager { - private static Logger logger = LoggerFactory.getLogger(TopManager.class); - @Inject - private MessagesService messagesService; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - - @Scheduled(fixedRate = 3600000) - public void updateTop() { - messagesService.getPopularCandidates().forEach(m -> { - Message jmsg = messagesService.getMessage(m); - logger.info("added {} to popular", m); - messagesService.setMessagePopular(m, 1); - List tags = jmsg.getTags().stream().map(Tag::getName).map(String::toLowerCase).collect(Collectors.toList()); - if (!tags.contains("juick")) { - applicationEventPublisher.publishEvent(new TopEvent(this, jmsg)); - } - }); - } -} diff --git a/juick-server/src/main/java/com/juick/server/TwitterManager.java b/juick-server/src/main/java/com/juick/server/TwitterManager.java deleted file mode 100644 index 613594e6..00000000 --- a/juick-server/src/main/java/com/juick/server/TwitterManager.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.server; - -import com.juick.Message; -import com.juick.User; -import com.juick.service.UserService; -import com.juick.service.component.*; -import com.juick.service.CrosspostService; -import com.juick.util.MessageUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import twitter4j.TwitterFactory; -import twitter4j.conf.ConfigurationBuilder; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; - -/** - * @author Ugnich Anton - */ -@Component -public class TwitterManager implements NotificationListener { - - private static Logger logger = LoggerFactory.getLogger(TwitterManager.class); - - @Inject - private CrosspostService crosspostService; - - @Value("${twitter_consumer_key:12345678}") - private String twitter_consumer_key; - @Value("${twitter_consumer_secret:secret}") - private String twitter_consumer_secret; - @Inject - private UserService userService; - - @Value("${service_user:juick}") - private String serviceUsername; - - private User serviceUser; - - @PostConstruct - public void init() { - serviceUser = userService.getUserByName(serviceUsername); - } - - void twitterPost(final com.juick.Message jmsg) { - crosspostService.getTwitterToken(jmsg.getUser().getUid()).ifPresent(t -> { - String status = MessageUtils.getMessageHashTags(jmsg) + StringUtils.defaultString(jmsg.getText()); - if (status.length() > 253) { - status = status.substring(0, 252) + "…"; - } - status += " http://juick.com/" + jmsg.getMid(); - ConfigurationBuilder configurationBuilder = new ConfigurationBuilder() - .setDebugEnabled(true) - .setOAuthConsumerKey(twitter_consumer_key) - .setOAuthConsumerSecret(twitter_consumer_secret); - TwitterFactory tf = new TwitterFactory(configurationBuilder - .setOAuthAccessToken(t.getToken()) - .setOAuthAccessTokenSecret(t.getSecret()).build()); - try { - tf.getInstance().updateStatus(status); - } catch (Exception e) { - logger.info("Twitter exception", e); - } - }); - } - - @Override - public void processMessageEvent(MessageEvent messageEvent) { - Message msg = messageEvent.getMessage(); - if (MessageUtils.isPM(msg) || MessageUtils.isReply(msg) || msg.isService()) { - return; - } - if (StringUtils.isNotEmpty(crosspostService.getTwitterName(msg.getUser().getUid()))) { - if (msg.getTags().stream().noneMatch(t -> t.getName().equals("notwitter"))) { - twitterPost(msg); - } - } - } - - @Override - public void processSubscribeEvent(SubscribeEvent subscribeEvent) { - - } - - @Override - public void processLikeEvent(LikeEvent likeEvent) { - - } - - @Override - public void processPingEvent(PingEvent pingEvent) { - - } - - @Override - public void processMessageReadEvent(MessageReadEvent messageReadEvent) { - - } - - @Override - public void processTopEvent(TopEvent topEvent) { - Message jmsg = topEvent.getMessage(); - jmsg.setUser(serviceUser); - twitterPost(jmsg); - } -} diff --git a/juick-server/src/main/java/com/juick/server/Utils.java b/juick-server/src/main/java/com/juick/server/Utils.java deleted file mode 100644 index 23768ed2..00000000 --- a/juick-server/src/main/java/com/juick/server/Utils.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.server; - -import javax.servlet.http.HttpServletRequest; -import java.util.Optional; - -/** - * - * @author Ugnich Anton - */ -public class Utils { - - - public static String encodeSphinx(String str) { - return str.replaceAll("@", "\\\\@") - .replaceAll("\\'", "\\\\'") - .replaceAll("=", "\\\\\\\\="); - } - /** - * Returns the viewName to return for coming back to the sender url - * - * @param request Instance of {@link HttpServletRequest} or use an injected instance - * @return Optional with the view name. Recomended to use an alternativa url with - * {@link Optional#orElse(java.lang.Object)} - */ - public static Optional getPreviousPageByRequest(HttpServletRequest request) - { - return Optional.ofNullable(request.getHeader("Referer")); - } -} diff --git a/juick-server/src/main/java/com/juick/server/WebsocketManager.java b/juick-server/src/main/java/com/juick/server/WebsocketManager.java deleted file mode 100644 index 1b62b984..00000000 --- a/juick-server/src/main/java/com/juick/server/WebsocketManager.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server; - -import com.juick.User; -import com.juick.model.AnonymousUser; -import com.juick.model.CommandResult; -import com.juick.server.util.HttpForbiddenException; -import com.juick.server.util.HttpNotFoundException; -import com.juick.service.MessagesService; -import com.juick.service.UserService; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpHeaders; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; -import org.springframework.web.socket.CloseStatus; -import org.springframework.web.socket.PingMessage; -import org.springframework.web.socket.TextMessage; -import org.springframework.web.socket.WebSocketSession; -import org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator; -import org.springframework.web.socket.handler.TextWebSocketHandler; -import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.annotation.Nonnull; -import javax.inject.Inject; -import java.io.IOException; -import java.net.URI; -import java.time.Instant; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * Created by vitalyster on 28.06.2016. - */ -@Component -public class WebsocketManager extends TextWebSocketHandler { - private static final Logger logger = LoggerFactory.getLogger(WebsocketManager.class); - - private final List clients = new CopyOnWriteArrayList<>(); - - @Inject - private UserService userService; - @Inject - private MessagesService messagesService; - @Inject - private CommandsManager commandsManager; - - - @Override - public void afterConnectionEstablished(WebSocketSession session) { - - UserSession userSession = new UserSession(session); - URI hLocation = session.getUri(); - - // Auth - UriComponents uriComponents = UriComponentsBuilder.fromUri(hLocation).build(); - List hash = uriComponents.getQueryParams().get("hash"); - if (hash != null && hash.get(0).length() == 16) { - userSession.visitor = userService.getUserByHash(hash.get(0)); - } else { - logger.debug("wrong hash for {} from {}", userSession.visitor.getUid(), userSession); - } - - if (hLocation.getPath().equals("/ws/")) { - logger.debug("user {} connected", userSession.visitor.getUid()); - } else if (hLocation.getPath().equals("/ws/_all")) { - logger.debug("user {} connected to legacy _all ({})", userSession.visitor.getUid(), hLocation.getPath()); - userSession.legacy = true; - userSession.allMessages = true; - } else if (hLocation.getPath().equals("/ws/_replies")) { - logger.debug("user {} connected to legacy _replies ({})", userSession.visitor.getUid(), hLocation.getPath()); - userSession.legacy = true; - userSession.allReplies = true; - } else if (hLocation.getPath().matches("^/ws/(\\d)+$")) { - int MID = NumberUtils.toInt(hLocation.getPath().substring(4), 0); - if (MID > 0) { - if (messagesService.canViewThread(MID, userSession.visitor.getUid())) { - logger.debug("user {} connected to legacy thread ({}) from {}", userSession.visitor.getUid(), MID, userSession); - userSession.legacy = true; - userSession.MID = MID; - } else { - throw new HttpForbiddenException(); - } - } - } else { - throw new HttpNotFoundException(); - } - clients.add(userSession); - logger.debug("{} clients connected", clients.size()); - } - - @Override - public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { - logger.debug("session closed with status {}: {}", status.getCode(), status.getReason()); - clients.removeIf(c -> c.getDelegate().getId().equals(session.getId())); - logger.debug("{} clients connected", clients.size()); - } - - @Scheduled(fixedRate = 30000) - public void ping() { - clients.forEach(c -> { - try { - if (c.isOpen()) { - c.sendMessage(new PingMessage()); - } - } catch (IOException e) { - logger.error("WebSocket PING exception", e); - } - }); - } - - @Override - protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { - UserSession ws = clients.stream().filter(c -> c.getDelegate().equals(session)) - .findFirst().orElseThrow(IllegalStateException::new); - if (!ws.visitor.isAnonymous()) { - String command = message.getPayload().trim(); - if (StringUtils.isNotEmpty(command)) { - CommandResult result = commandsManager.processCommand(ws.visitor, command, URI.create("")); - ws.sendMessage(new TextMessage(result.getText())); - } - } else { - ws.sendMessage(new TextMessage("Authorization required")); - } - } - - public List getClients() { - return clients; - } - - class UserSession extends ConcurrentWebSocketSessionDecorator { - User visitor; - int MID; - boolean allMessages; - boolean allReplies; - Instant tsConnected; - Instant tsLastData; - boolean legacy; - - UserSession(WebSocketSession session) { - super(session, 60000, 65536); - this.visitor = AnonymousUser.INSTANCE; - tsConnected = tsLastData = Instant.now(); - } - - @Nonnull - @Override - public String toString() { - HttpHeaders headers = getHandshakeHeaders(); - return headers.getOrDefault("X-Real-IP", - Collections.singletonList(getRemoteAddress().toString())).get(0); - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/XMPPConnection.java b/juick-server/src/main/java/com/juick/server/XMPPConnection.java deleted file mode 100644 index 9c0c09e1..00000000 --- a/juick-server/src/main/java/com/juick/server/XMPPConnection.java +++ /dev/null @@ -1,693 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server; - -import com.juick.User; -import com.juick.formatters.PlainTextFormatter; -import com.juick.service.component.*; -import com.juick.model.CommandResult; -import com.juick.model.UserInfo; -import com.juick.server.xmpp.iq.MessageQuery; -import com.juick.server.xmpp.s2s.BasicXmppSession; -import com.juick.server.xmpp.s2s.StanzaListener; -import com.juick.service.*; -import com.juick.util.MessageUtils; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationEventPublisher; -import rocks.xmpp.addr.Jid; -import rocks.xmpp.core.XmppException; -import rocks.xmpp.core.session.XmppSession; -import rocks.xmpp.core.stanza.AbstractIQHandler; -import rocks.xmpp.core.stanza.model.*; -import rocks.xmpp.core.stanza.model.client.ClientMessage; -import rocks.xmpp.core.stanza.model.client.ClientPresence; -import rocks.xmpp.core.stanza.model.errors.Condition; -import rocks.xmpp.extensions.caps.EntityCapabilitiesManager; -import rocks.xmpp.extensions.component.accept.ExternalComponent; -import rocks.xmpp.extensions.disco.ServiceDiscoveryManager; -import rocks.xmpp.extensions.disco.model.info.Identity; -import rocks.xmpp.extensions.filetransfer.FileTransfer; -import rocks.xmpp.extensions.filetransfer.FileTransferManager; -import rocks.xmpp.extensions.nick.model.Nickname; -import rocks.xmpp.extensions.oob.model.x.OobX; -import rocks.xmpp.extensions.ping.PingManager; -import rocks.xmpp.extensions.receipts.MessageDeliveryReceiptsManager; -import rocks.xmpp.extensions.vcard.temp.model.VCard; -import rocks.xmpp.extensions.version.SoftwareVersionManager; -import rocks.xmpp.extensions.version.model.SoftwareVersion; -import rocks.xmpp.util.XmppUtils; - -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.xml.bind.JAXBException; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; -import java.io.IOException; -import java.io.StringWriter; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; - -/** - * @author ugnich - */ -public class XMPPConnection implements StanzaListener, NotificationListener { - - private static final Logger logger = LoggerFactory.getLogger("com.juick.server.xmpp"); - - private ExternalComponent router; - @Inject - private XMPPServer xmpp; - @Inject - private CommandsManager commandsManager; - @Value("${xmppbot_jid:juick@localhost}") - private Jid jid; - @Value("${componentname:localhost}") - private String componentName; - @Value("${component_port:5347}") - private int componentPort; - @Value("${xmpp_password:secret}") - private String password; - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - @Inject - private SubscriptionService subscriptionService; - @Inject - private PMQueriesService pmQueriesService; - @Inject - private BasicXmppSession session; - @Inject - private ExecutorService service; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - @Value("${service_user:juick}") - private String serviceUsername; - - private User serviceUser; - - @PostConstruct - public void init() { - logger.info("stream router start connecting to {}", componentPort); - xmpp.addStanzaListener(this); - router = ExternalComponent.create(componentName, password, session.getConfiguration(), "localhost", componentPort); - ServiceDiscoveryManager serviceDiscoveryManager = router.getManager(ServiceDiscoveryManager.class); - serviceDiscoveryManager.addIdentity(Identity.clientBot().withName("Juick")); - EntityCapabilitiesManager entityCapabilitiesManager = router.getManager(EntityCapabilitiesManager.class); - entityCapabilitiesManager.setNode("https://juick.com/caps"); - MessageDeliveryReceiptsManager messageDeliveryReceiptsManager = router.getManager(MessageDeliveryReceiptsManager.class); - messageDeliveryReceiptsManager.setEnabled(true); - PingManager pingManager = router.getManager(PingManager.class); - pingManager.setEnabled(true); - SoftwareVersionManager softwareVersionManager = router.getManager(SoftwareVersionManager.class); - softwareVersionManager.setSoftwareVersion(new SoftwareVersion("Juick", "2.x", - System.getProperty("os.name", "generic"))); - VCard vCard = new VCard(); - vCard.setFormattedName("Juick"); - vCard.setBirthday(LocalDate.of(2008, 10, 22)); - try { - vCard.setUrl(new URL("http://juick.com/")); - vCard.setPhoto(new VCard.Image("image/png", IOUtils.toByteArray( - getClass().getClassLoader().getResource("juick.png")))); - } catch (MalformedURLException e) { - logger.error("invalid url", e); - } catch (IOException e) { - logger.warn("invalid resource", e); - } - router.addIQHandler(MessageQuery.class, iq -> { - Message warningMessage = new Message(iq.getFrom(), Message.Type.CHAT); - warningMessage.setFrom(jid); - warningMessage.setBody("Your XMPP client constantly polls us with XMPP query which is unsupported for years, please find http://juick.com/query#messages in your client code and remove that"); - router.send(warningMessage); - return iq.createError(new StanzaError(Condition.BAD_REQUEST, "Please stop this spam")); - }); - router.addIQHandler(VCard.class, new AbstractIQHandler(IQ.Type.GET) { - @Override - protected IQ processRequest(IQ iq) { - if (iq.getTo().equals(jid) || iq.getTo().asBareJid().equals(jid.asBareJid()) - || iq.getTo().asBareJid().toEscapedString().equals(jid.getDomain())) { - return iq.createResult(vCard); - } - User user = userService.getUserByName(iq.getTo().getLocal()); - if (!user.isAnonymous()) { - UserInfo info = userService.getUserInfo(user); - VCard userVCard = new VCard(); - userVCard.setFormattedName(info.getFullName()); - userVCard.setNickname(user.getName()); - try { - userVCard.setPhoto(new VCard.Image(new URI("http://i.juick.com/a/" + user.getUid() + ".png"))); - if (info.getUrl() != null) { - userVCard.setUrl(new URL(info.getUrl())); - } - } catch (MalformedURLException | URISyntaxException e) { - logger.warn("url exception", e); - } - return iq.createResult(userVCard); - } - return iq.createError(Condition.BAD_REQUEST); - } - }); - router.addInboundMessageListener(e -> { - ClientMessage result = incomingMessage(e.getMessage()); - if (result != null) { - router.send(result); - } - }); - router.addInboundIQListener(e -> { - IQ iq = e.getIQ(); - Jid jid = iq.getTo(); - if (!jid.getDomain().equals(this.jid.getDomain())) { - router.send(iq); - } - }); - FileTransferManager fileTransferManager = router.getManager(FileTransferManager.class); - fileTransferManager.addFileTransferOfferListener(e -> { - try { - List allowedTypes = new ArrayList() {{ - add("png"); - add("jpg"); - }}; - String attachmentExtension = FilenameUtils.getExtension(e.getName()).toLowerCase(); - String targetFilename = String.format("%s.%s", - DigestUtils.md5Hex(String.format("%s-%s", - e.getInitiator().toString(), e.getSessionId()).getBytes()), attachmentExtension); - if (allowedTypes.contains(attachmentExtension)) { - Path filePath = Paths.get(tmpDir, targetFilename); - FileTransfer ft = e.accept(filePath).get(); - ft.addFileTransferStatusListener(st -> { - logger.debug("{}: received {} of {}", e.getName(), st.getBytesTransferred(), e.getSize()); - if (st.getStatus().equals(FileTransfer.Status.COMPLETED)) { - logger.info("transfer completed"); - try { - Jid initiator = e.getInitiator(); - ClientMessage result = incomingMessageJuick( - userService.getUserByJID(initiator.asBareJid().toEscapedString()), initiator, - e.getDescription(), URI.create(String.format("juick://%s", targetFilename))); - if (result != null) { - router.send(result); - } - } catch (Exception e1) { - logger.error("ft error", e1); - } - - } else if (st.getStatus().equals(FileTransfer.Status.FAILED)) { - logger.info("transfer failed", ft.getException()); - Message msg = new Message(); - msg.setType(Message.Type.CHAT); - msg.setFrom(jid); - msg.setTo(e.getInitiator()); - msg.setBody("File transfer failed, please report to us"); - router.sendMessage(msg); - } else if (st.getStatus().equals(FileTransfer.Status.CANCELED)) { - logger.info("transfer cancelled"); - } - }); - ft.transfer(); - logger.info("transfer started"); - } else { - e.reject(); - logger.info("transfer rejected"); - } - } catch (IOException | InterruptedException | ExecutionException e1) { - logger.error("ft error", e1); - } - }); - router.addConnectionListener(event -> { - if (event.getType().equals(rocks.xmpp.core.session.ConnectionEvent.Type.RECONNECTION_SUCCEEDED)) { - logger.info("component connected"); - } - }); - router.addSessionStatusListener(event -> { - logger.info("event: " + event.getStatus(), event.getThrowable()); - if (event.getStatus().equals(XmppSession.Status.AUTHENTICATED)) { - logger.info("Authenticated, broadcasting..."); - broadcastPresence(null); - } - }); - router.addInboundPresenceListener(event -> { - incomingPresence(event.getPresence()); - }); - service.submit(() -> { - try { - router.connect(); - } catch (XmppException e) { - logger.warn("xmpp exception", e); - } - }); - serviceUser = userService.getUserByName(serviceUsername); - } - - private String stanzaToString(Stanza stanza) throws XMLStreamException, JAXBException { - StringWriter stanzaWriter = new StringWriter(); - XMLStreamWriter xmppStreamWriter = XmppUtils.createXmppStreamWriter( - router.getConfiguration().getXmlOutputFactory().createXMLStreamWriter(stanzaWriter)); - router.createMarshaller().marshal(stanza, xmppStreamWriter); - xmppStreamWriter.flush(); - xmppStreamWriter.close(); - return stanzaWriter.toString(); - } - - private void sendJuickMessage(com.juick.Message jmsg, List users) { - List jids = new ArrayList<>(); - - for (User user : users) { - jids.addAll(userService.getJIDsbyUID(user.getUid())); - } - com.juick.Message fullMsg = messagesService.getMessage(jmsg.getMid()); - String txt = "@" + jmsg.getUser().getName() + ":" + MessageUtils.getTagsString(fullMsg) + "\n"; - String attachmentUrl = MessageUtils.attachmentUrl(fullMsg); - if (StringUtils.isNotEmpty(attachmentUrl)) { - txt += attachmentUrl + "\n"; - } - txt += StringUtils.defaultString(jmsg.getText()) + "\n\n"; - txt += "#" + jmsg.getMid() + " http://juick.com/m/" + jmsg.getMid(); - - Nickname nick = new Nickname("@" + jmsg.getUser().getName()); - - Message msg = new Message(); - msg.setFrom(jid); - msg.setBody(txt); - msg.setType(Message.Type.CHAT); - msg.setThread("juick-" + jmsg.getMid()); - msg.addExtension(jmsg); - msg.addExtension(nick); - if (StringUtils.isNotEmpty(attachmentUrl)) { - try { - OobX oob = new OobX(new URI(attachmentUrl)); - msg.addExtension(oob); - } catch (URISyntaxException e) { - logger.warn("uri exception", e); - } - } - for (String jid : jids) { - msg.setTo(Jid.of(jid)); - router.send(ClientMessage.from(msg)); - } - } - - public void sendJuickComment(com.juick.Message jmsg, List users) { - String replyQuote; - String replyTo; - - com.juick.Message replyMessage = jmsg.getReplyto() > 0 ? messagesService.getReply(jmsg.getMid(), jmsg.getReplyto()) - : messagesService.getMessage(jmsg.getMid()); - replyTo = replyMessage.getUser().getName(); - com.juick.Message fullReply = messagesService.getReply(jmsg.getMid(), jmsg.getRid()); - replyQuote = StringUtils.defaultString(fullReply.getReplyQuote()); - - String txt = "Reply by @" + jmsg.getUser().getName() + ":\n" + replyQuote + "\n@" + replyTo + " "; - String attachmentUrl = MessageUtils.attachmentUrl(fullReply); - if (StringUtils.isNotEmpty(attachmentUrl)) { - txt += attachmentUrl + "\n"; - } - txt += StringUtils.defaultString(jmsg.getText()) + "\n\n" + "#" + jmsg.getMid() + "/" + jmsg.getRid() + " http://juick.com/m/" + jmsg.getMid() + "#" + jmsg.getRid(); - - Message msg = new Message(); - msg.setFrom(jid); - msg.setBody(txt); - msg.setType(Message.Type.CHAT); - msg.addExtension(jmsg); - for (User user : users) { - for (String jid : userService.getJIDsbyUID(user.getUid())) { - msg.setTo(Jid.of(jid)); - router.send(ClientMessage.from(msg)); - } - } - } - - @Override - public void processMessageEvent(MessageEvent event) { - com.juick.Message msg = event.getMessage(); - List subscribers = event.getUsers(); - if (msg.isService()) { - return; - } - if (MessageUtils.isPM(msg)) { - userService.getJIDsbyUID(msg.getTo().getUid()) - .forEach(userJid -> { - Message mm = new Message(); - mm.setTo(Jid.of(userJid)); - mm.setType(Message.Type.CHAT); - boolean inroster = pmQueriesService.havePMinRoster(msg.getUser().getUid(), userJid); - if (inroster) { - mm.setFrom(Jid.of(msg.getUser().getName(), "juick.com", "Juick")); - mm.setBody(msg.getText()); - } else { - mm.setFrom(jid); - mm.setBody("Private message from @" + msg.getUser().getName() + ":\n" + msg.getText()); - } - router.send(ClientMessage.from(mm)); - }); - } else if (MessageUtils.isReply(msg)) { - sendJuickComment(msg, subscribers); - } - else { - sendJuickMessage(msg, subscribers); - } - } - - private ClientMessage makeReply(Jid jidTo, String txt) { - Message reply = new Message(); - reply.setFrom(jid); - reply.setTo(jidTo); - reply.setType(Message.Type.CHAT); - reply.setBody(txt); - return ClientMessage.from(reply); - } - - @Override - public void processSubscribeEvent(SubscribeEvent subscribeEvent) { - - } - - @Override - public void processLikeEvent(LikeEvent likeEvent) { - List users = likeEvent.getSubscribers(); - com.juick.Message jmsg = likeEvent.getMessage(); - User liker = likeEvent.getUser(); - - if (!userService.isInBLAny(jmsg.getUser().getUid(), liker.getUid())) { - userService.getJIDsbyUID(jmsg.getUser().getUid()).forEach(authorJid -> { - Message xmppMessage = new Message(); - xmppMessage.setFrom(jid); - xmppMessage.setTo(Jid.of(authorJid)); - xmppMessage.setType(Message.Type.CHAT); - xmppMessage.addExtension(jmsg); - xmppMessage.setBody(String.format("%s recommended your post #%d. %s", - liker.getName(), jmsg.getMid(), PlainTextFormatter.formatUrl(jmsg))); - router.send(ClientMessage.from(xmppMessage)); - }); - } - - String txt = "Recommended by @" + liker.getName() + ":\n"; - txt += "@" + jmsg.getUser().getName() + ":" + MessageUtils.getTagsString(jmsg) + "\n"; - String attachmentUrl = MessageUtils.attachmentUrl(jmsg); - if (StringUtils.isNotEmpty(attachmentUrl)) { - txt += attachmentUrl + "\n"; - } - txt += StringUtils.defaultString(jmsg.getText()) + "\n\n"; - txt += "#" + jmsg.getMid(); - if (jmsg.getReplies() > 0) { - if (jmsg.getReplies() % 10 == 1 && jmsg.getReplies() % 100 != 11) { - txt += " (" + jmsg.getReplies() + " reply)"; - } else { - txt += " (" + jmsg.getReplies() + " replies)"; - } - } - txt += " http://juick.com/m/" + jmsg.getMid(); - - Nickname nick = new Nickname("@" + jmsg.getUser().getName()); - - Message msg = new Message(); - msg.setFrom(jid); - msg.setBody(txt); - msg.setType(Message.Type.CHAT); - msg.setThread("juick-" + jmsg.getMid()); - msg.addExtension(jmsg); - msg.addExtension(nick); - if (StringUtils.isNotEmpty(attachmentUrl)) { - try { - OobX oob = new OobX(new URI(attachmentUrl)); - msg.addExtension(oob); - } catch (URISyntaxException e) { - logger.warn("uri exception", e); - } - } - - for (User user : users) { - for (String jid : userService.getJIDsbyUID(user.getUid())) { - msg.setTo(Jid.of(jid)); - router.send(ClientMessage.from(msg)); - } - } - } - - @Override - public void processPingEvent(PingEvent pingEvent) { - userService.getJIDsbyUID(pingEvent.getPinger().getUid()) - .forEach(userJid -> { - Presence p = new Presence(Jid.of(userJid)); - p.setFrom(jid); - p.setPriority((byte) 10); - router.send(ClientPresence.from(p)); - }); - } - - @Override - public void processMessageReadEvent(MessageReadEvent messageReadEvent) { - - } - - @Override - public void processTopEvent(TopEvent topEvent) { - com.juick.Message message = topEvent.getMessage(); - try { - commandsManager.processCommand(serviceUser, String.format("! #%d", message.getMid()), URI.create(StringUtils.EMPTY)); - } catch (Exception e) { - logger.warn("XMPP error", e); - } - } - - private void incomingPresence(Presence p) { - final String username = p.getTo().getLocal(); - final boolean toJuick = username.equals(jid.getLocal()); - - if (p.getType() == null) { - Presence reply = new Presence(); - reply.setFrom(p.getTo().asBareJid()); - reply.setTo(p.getFrom().asBareJid()); - reply.setType(Presence.Type.UNSUBSCRIBE); - router.send(ClientPresence.from(reply)); - } else if (p.getType().equals(Presence.Type.PROBE)) { - int uid_to = 0; - if (!toJuick) { - uid_to = userService.getUIDbyName(username); - } else { - User visitor = userService.getUserByJID(p.getFrom().asBareJid().toEscapedString()); - if (visitor != null) { - userService.updateLastSeen(visitor); - } - } - - if (toJuick || uid_to > 0) { - Presence reply = new Presence(); - reply.setFrom(p.getTo().withResource(jid.getResource())); - reply.setTo(p.getFrom()); - reply.setPriority((byte)10); - if (!userService.getActiveJIDs().contains(p.getFrom().asBareJid().toEscapedString())) { - reply.setStatus("Send ON to enable notifications"); - } - router.send(ClientPresence.from(reply)); - } else { - Presence reply = new Presence(); - reply.setFrom(p.getTo()); - reply.setTo(p.getFrom()); - reply.setType(Presence.Type.ERROR); - reply.setId(p.getId()); - reply.setError(new StanzaError(StanzaError.Type.CANCEL, Condition.ITEM_NOT_FOUND)); - router.send(ClientPresence.from(reply)); - } - } else if (p.getType().equals(Presence.Type.SUBSCRIBE)) { - boolean canSubscribe = false; - if (toJuick) { - canSubscribe = true; - } else { - int uid_to = userService.getUIDbyName(username); - if (uid_to > 0) { - pmQueriesService.addPMinRoster(uid_to, p.getFrom().asBareJid().toEscapedString()); - canSubscribe = true; - } - } - if (canSubscribe) { - Presence reply = new Presence(); - reply.setFrom(p.getTo()); - reply.setTo(p.getFrom()); - reply.setType(Presence.Type.SUBSCRIBED); - router.send(ClientPresence.from(reply)); - - reply.setFrom(reply.getFrom().withResource(jid.getResource())); - reply.setPriority((byte) 10); - reply.setType(null); - router.send(ClientPresence.from(reply)); - } else { - Presence reply = new Presence(); - reply.setFrom(p.getTo()); - reply.setTo(p.getFrom()); - reply.setType(Presence.Type.ERROR); - reply.setId(p.getId()); - reply.setError(new StanzaError(StanzaError.Type.CANCEL, Condition.ITEM_NOT_FOUND)); - router.send(ClientPresence.from(reply)); - } - } else if (p.getType().equals(Presence.Type.UNSUBSCRIBE)) { - if (!toJuick) { - int uid_to = userService.getUIDbyName(username); - if (uid_to > 0) { - pmQueriesService.removePMinRoster(uid_to, p.getFrom().asBareJid().toEscapedString()); - } - } - - Presence reply = new Presence(); - reply.setFrom(p.getTo()); - reply.setTo(p.getFrom()); - reply.setType(Presence.Type.UNSUBSCRIBED); - router.send(ClientPresence.from(reply)); - } - } - - public ClientMessage incomingMessage(Message msg) { - ClientMessage result = null; - if (msg.getType() != null && msg.getType().equals(Message.Type.ERROR)) { - StanzaError error = msg.getError(); - if (error != null && error.getCondition().equals(Condition.RESOURCE_CONSTRAINT)) { - // offline query is full, deactivating this jid - if (userService.setActiveStatusForJID(msg.getFrom().toEscapedString(), UserService.ActiveStatus.Inactive)) { - logger.info("{} is inactive now", msg.getFrom()); - return null; - } - } - return null; - } - Jid to = msg.getTo(); - if (to.getDomain().equals(router.getDomain().toEscapedString()) || to.equals(this.jid)) { - User user_from = userService.getUserByJID(msg.getFrom().asBareJid().toEscapedString()); - if ((user_from == null || user_from.isAnonymous()) && !msg.getFrom().equals(jid)) { - String signuphash = userService.getSignUpHashByJID(msg.getFrom().asBareJid().toEscapedString()); - return makeReply(msg.getFrom(), "Для того, чтобы начать пользоваться сервисом, пожалуйста пройдите быструю регистрацию: http://juick.com/signup?type=xmpp&hash=" + signuphash + "\nЕсли у вас уже есть учетная запись на Juick, вы сможете присоединить этот JabberID к ней.\n\nTo start using Juick, please sign up: http://juick.com/signup?type=xmpp&hash=" + signuphash + "\nIf you already have an account on Juick, you will be proposed to attach this JabberID to your existing account."); - } - - com.juick.Message jmsg = msg.getExtension(com.juick.Message.class); - if (jmsg != null) { - if (MessageUtils.isReply(jmsg)) { - // to get quote and attachment - com.juick.Message original = messagesService.getMessage(jmsg.getMid()); - com.juick.Message reply = messagesService.getReply(jmsg.getMid(), jmsg.getRid()); - applicationEventPublisher.publishEvent(new MessageEvent(this, reply, - subscriptionService.getUsersSubscribedToComments(original, reply))); - } else if (!MessageUtils.isPM(jmsg)) { - applicationEventPublisher.publishEvent(new MessageEvent(this, - messagesService.getMessage(jmsg.getMid()), subscriptionService.getSubscribedUsers(jmsg.getUser().getUid(), jmsg))); - } - } else { - URI attachment = URI.create(StringUtils.EMPTY); - OobX oobX = msg.getExtension(OobX.class); - if (oobX != null) { - attachment = oobX.getUri(); - } - try { - if (msg.getTo().asBareJid().equals(jid.asBareJid())) { - return incomingMessageJuick(user_from, msg.getFrom(), StringUtils.defaultString(msg.getBody()), attachment); - } else { - // PM - result = incomingMessageJuick(user_from, msg.getFrom(), - String.format("@%s %s", msg.getTo().getLocal(), StringUtils.defaultString(msg.getBody())), attachment); - } - } catch (Exception e1) { - logger.warn("message exception", e1); - } - } - } else if (to.getDomain().endsWith(jid.getDomain()) && (to.getDomain().equals(jid.getDomain()) - || to.getDomain().endsWith("." + jid.getDomain()))) { - if (logger.isInfoEnabled()) { - try { - logger.info("unhandled message: {}", stanzaToString(msg)); - } catch (JAXBException | XMLStreamException ex) { - logger.error("JAXB exception", ex); - } - } - } else { - return ClientMessage.from(msg); - } - return result; - } - private ClientMessage incomingMessageJuick(User user_from, Jid from, String command, @Nonnull URI attachment) { - if (StringUtils.isBlank(command) && attachment.toString().isEmpty()) { - return null; - } - - int commandlen = command.length(); - - // COMPATIBILITY - if (commandlen > 7 && command.substring(0, 3).equalsIgnoreCase("PM ")) { - command = command.substring(3); - } - - try { - CommandResult result = commandsManager.processCommand(user_from, command.trim(), attachment); - if (StringUtils.isNotBlank(result.getText())) { - return makeReply(from, result.getText()); - } - } catch (Exception e) { - logger.warn("xmpp command exception", e); - return makeReply(from, "Error processing command"); - } - return null; - } - - @Override - public void stanzaReceived(Stanza xmlValue) { - router.send(xmlValue); - } - - private void broadcastPresence(Presence.Type type) { - Presence presence = new Presence(); - presence.setFrom(jid); - if (type != null) { - presence.setType(type); - } - userService.getActiveJIDs().forEach(j -> { - try { - presence.setTo(Jid.of(j)); - router.send(ClientPresence.from(presence)); - } catch (IllegalArgumentException ex) { - logger.warn("Invalid jid: {}", j, ex); - } - }); - } - - @PreDestroy - public void close() throws Exception { - broadcastPresence(Presence.Type.UNAVAILABLE); - if (router != null) { - router.close(); - } - } - - public ExternalComponent getRouter() { - return router; - } -} diff --git a/juick-server/src/main/java/com/juick/server/XMPPServer.java b/juick-server/src/main/java/com/juick/server/XMPPServer.java deleted file mode 100644 index 86ab6a78..00000000 --- a/juick-server/src/main/java/com/juick/server/XMPPServer.java +++ /dev/null @@ -1,429 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server; - -import com.juick.server.xmpp.router.StreamError; -import com.juick.server.xmpp.s2s.*; -import com.juick.service.UserService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.scheduling.annotation.Scheduled; -import org.xmlpull.v1.XmlPullParserException; -import rocks.xmpp.addr.Jid; -import rocks.xmpp.core.stanza.model.Stanza; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.net.ssl.*; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import java.io.IOException; -import java.io.StringReader; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketException; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.SecureRandom; -import java.security.cert.*; -import java.time.Duration; -import java.time.Instant; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * @author ugnich - */ -public class XMPPServer implements ConnectionListener { - private static final Logger logger = LoggerFactory.getLogger("com.juick.server.xmpp"); - - private static final int TIMEOUT_MINUTES = 15; - - @Inject - public ExecutorService service; - @Value("${hostname:localhost}") - private Jid jid; - @Value("${s2s_port:5269}") - private int s2sPort; - @Value("${broken_ssl_hosts:}") - public String[] brokenSSLhosts; - @Value("${banned_hosts:}") - public String[] bannedHosts; - - private final List inConnections = new CopyOnWriteArrayList<>(); - private final Map> outConnections = new ConcurrentHashMap<>(); - private final List outCache = new CopyOnWriteArrayList<>(); - private final List stanzaListeners = new CopyOnWriteArrayList<>(); - private final AtomicBoolean closeFlag = new AtomicBoolean(false); - - SSLContext sc; - CertificateFactory cf; - CertPathValidator cpv; - PKIXParameters params; - private TrustManager[] trustAllCerts = new TrustManager[]{ - new X509TrustManager() { - public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) { - } - - public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) { - } - - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - } - }; - private boolean tlsConfigured = false; - - - private ServerSocket listener; - - @Inject - private BasicXmppSession session; - @Inject - private UserService userService; - @Inject - private KeystoreManager keystoreManager; - - @PostConstruct - public void init() throws KeyStoreException { - closeFlag.set(false); - try { - sc = SSLContext.getInstance("TLSv1.2"); - sc.init(keystoreManager.getKeymanagerFactory().getKeyManagers(), trustAllCerts, new SecureRandom()); - TrustManagerFactory trustManagerFactory = - TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - Set ca = new HashSet<>(); - trustManagerFactory.init((KeyStore)null); - Arrays.stream(trustManagerFactory.getTrustManagers()).forEach(t -> Arrays.stream(((X509TrustManager)t).getAcceptedIssuers()).forEach(cert -> ca.add(new TrustAnchor(cert, null)))); - params = new PKIXParameters(ca); - params.setRevocationEnabled(false); - cpv = CertPathValidator.getInstance("PKIX"); - cf = CertificateFactory.getInstance( "X.509" ); - tlsConfigured = true; - } catch (Exception e) { - logger.warn("tls unavailable"); - } - service.submit(() -> { - try { - listener = new ServerSocket(s2sPort); - logger.info("s2s listener ready"); - while (!listener.isClosed()) { - if (Thread.currentThread().isInterrupted()) break; - Socket socket = listener.accept(); - ConnectionIn client = new ConnectionIn(this, socket); - addConnectionIn(client); - service.submit(client); - } - } catch (SocketException e) { - // shutdown - } catch (IOException | XmlPullParserException e) { - logger.warn("xmpp exception", e); - } - }); - } - - public void addConnectionIn(ConnectionIn c) { - c.setListener(this); - inConnections.add(c); - } - - public void addConnectionOut(ConnectionOut c, Optional socket) { - c.setListener(this); - outConnections.put(c, socket); - } - - public void removeConnectionIn(ConnectionIn c) { - inConnections.remove(c); - } - - public void removeConnectionOut(ConnectionOut c) { - outConnections.remove(c); - } - - public String getFromCache(Jid to) { - final String[] cache = new String[1]; - outCache.stream().filter(c -> c.hostname != null && c.hostname.equals(to)).findFirst().ifPresent(c -> { - cache[0] = c.xml; - outCache.remove(c); - }); - return cache[0]; - } - - public Optional getConnectionOut(Jid hostname, boolean needReady) { - return outConnections.keySet().stream().filter(c -> c.to != null && - c.to.equals(hostname) && (!needReady || c.streamReady)).findFirst(); - } - - public Optional getConnectionIn(String streamID) { - return inConnections.stream().filter(c -> c.streamID != null && c.streamID.equals(streamID)).findFirst(); - } - - public void sendOut(Jid hostname, String xml) { - boolean haveAnyConn = false; - - ConnectionOut connOut = null; - for (ConnectionOut c : outConnections.keySet()) { - if (c.to != null && c.to.equals(hostname)) { - if (c.streamReady) { - connOut = c; - break; - } else { - haveAnyConn = true; - break; - } - } - } - if (connOut != null) { - connOut.send(xml); - return; - } - - boolean haveCache = false; - for (CacheEntry c : outCache) { - if (c.hostname != null && c.hostname.equals(hostname)) { - c.xml += xml; - c.updated = Instant.now(); - haveCache = true; - break; - } - } - if (!haveCache) { - outCache.add(new CacheEntry(hostname, xml)); - } - - if (!haveAnyConn && !closeFlag.get()) { - try { - createDialbackConnection(hostname.toEscapedString(), null, null); - } catch (Exception e) { - logger.warn("dialback error", e); - } - } - } - - void createDialbackConnection(String to, String checkSID, String dbKey) throws Exception { - ConnectionOut connectionOut = new ConnectionOut(getJid(), Jid.of(to), null, null, checkSID, dbKey); - addConnectionOut(connectionOut, Optional.empty()); - service.submit(() -> { - try { - Socket socket = new Socket(); - socket.connect(DNSQueries.getServerAddress(to)); - connectionOut.setInputStream(socket.getInputStream()); - connectionOut.setOutputStream(socket.getOutputStream()); - addConnectionOut(connectionOut, Optional.of(socket)); - connectionOut.connect(); - } catch (IOException e) { - logger.info("dialback to " + to + " exception", e); - } - }); - } - - public void startDialback(Jid from, String streamId, String dbKey) throws Exception { - Optional c = getConnectionOut(from, false); - if (c.isPresent()) { - c.get().sendDialbackVerify(streamId, dbKey); - } else { - createDialbackConnection(from.toEscapedString(), streamId, dbKey); - } - } - - public void addStanzaListener(StanzaListener listener) { - stanzaListeners.add(listener); - } - - public void onStanzaReceived(String xmlValue) { - logger.info("S2S: {}", xmlValue); - Stanza stanza = parse(xmlValue); - stanzaListeners.forEach(l -> l.stanzaReceived(stanza)); - } - - public BasicXmppSession getSession() { - return session; - } - - public List getInConnections() { - return inConnections; - } - - public Map> getOutConnections() { - return outConnections; - } - - @Override - public boolean isTlsAvailable() { - return tlsConfigured; - } - - @Override - public void starttls(ConnectionIn connection) { - logger.debug("stream {} securing", connection.streamID); - connection.sendStanza(""); - try { - connection.setSocket(sc.getSocketFactory().createSocket(connection.getSocket(), connection.getSocket().getInetAddress().getHostAddress(), - connection.getSocket().getPort(), false)); - SSLSocket sslSocket = (SSLSocket) connection.getSocket(); - sslSocket.addHandshakeCompletedListener(handshakeCompletedEvent -> { - try { - CertPath certPath = cf.generateCertPath(Arrays.asList(handshakeCompletedEvent.getPeerCertificates())); - cpv.validate(certPath, params); - connection.setTrusted(true); - logger.info("connection from {} is trusted", connection.from); - } catch (SSLPeerUnverifiedException | CertificateException | CertPathValidatorException | InvalidAlgorithmParameterException e) { - logger.info("connection from {} is NOT trusted, falling back to dialback", connection.from); - } - }); - sslSocket.setUseClientMode(false); - sslSocket.setNeedClientAuth(true); - sslSocket.startHandshake(); - connection.setSecured(true); - logger.debug("stream from {} secured", connection.streamID); - connection.restartParser(); - } catch (XmlPullParserException | IOException sex) { - logger.warn("stream {} ssl error {}", connection.streamID, sex); - connection.sendStanza(""); - removeConnectionIn(connection); - connection.closeConnection(); - } - } - - @Override - public void proceed(ConnectionOut connection) { - try { - Socket socket = outConnections.get(connection).get(); - socket = sc.getSocketFactory().createSocket(socket, socket.getInetAddress().getHostAddress(), - socket.getPort(), false); - SSLSocket sslSocket = (SSLSocket) socket; - sslSocket.addHandshakeCompletedListener(handshakeCompletedEvent -> { - try { - CertPath certPath = cf.generateCertPath(Arrays.asList(handshakeCompletedEvent.getPeerCertificates())); - cpv.validate(certPath, params); - connection.setTrusted(true); - logger.info("connection to {} is trusted", connection.to); - } catch (SSLPeerUnverifiedException | CertificateException | CertPathValidatorException | InvalidAlgorithmParameterException e) { - logger.info("connection to {} is NOT trusted, falling back to dialback", connection.to); - } - }); - sslSocket.setNeedClientAuth(true); - sslSocket.startHandshake(); - connection.setSecured(true); - logger.debug("stream to {} secured", connection.getStreamID()); - connection.setInputStream(socket.getInputStream()); - connection.setOutputStream(socket.getOutputStream()); - connection.restartStream(); - connection.sendOpenStream(); - } catch (NoSuchElementException | XmlPullParserException | IOException sex) { - logger.error("s2s ssl error: {} {}, error {}", connection.to, connection.getStreamID(), sex); - connection.send(""); - removeConnectionOut(connection); - connection.logoff(); - } - } - - @Override - public void verify(ConnectionOut connection, String from, String type, String sid) { - if (from != null && from.equals(connection.to.toEscapedString()) && sid != null && !sid.isEmpty() && type != null) { - getConnectionIn(sid).ifPresent(c -> c.sendDialbackResult(Jid.of(from), type)); - } - } - - @Override - public void dialbackError(ConnectionOut connection, StreamError error) { - logger.warn("Stream error from {}: {}", connection.getStreamID(), error.getCondition()); - removeConnectionOut(connection); - connection.logoff(); - } - - @Override - public void finished(ConnectionOut connection, boolean dirty) { - logger.warn("stream to {} {} finished, dirty={}", connection.to, connection.getStreamID(), dirty); - removeConnectionOut(connection); - connection.logoff(); - } - - @Override - public void exception(ConnectionOut connection, Exception ex) { - logger.error("s2s out exception: {} {}, exception {}", connection.to, connection.getStreamID(), ex); - removeConnectionOut(connection); - connection.logoff(); - } - - @Override - public void ready(ConnectionOut connection) { - logger.debug("stream to {} {} ready", connection.to, connection.getStreamID()); - String cache = getFromCache(connection.to); - if (cache != null) { - logger.debug("stream to {} {} sending cache", connection.to, connection.getStreamID()); - connection.send(cache); - } - } - - @Override - public boolean securing(ConnectionOut connection) { - return tlsConfigured && !Arrays.asList(brokenSSLhosts).contains(connection.to.toEscapedString()); - } - - public Stanza parse(String xml) { - try { - Unmarshaller unmarshaller = session.createUnmarshaller(); - return (Stanza)unmarshaller.unmarshal(new StringReader(xml)); - } catch (JAXBException e) { - logger.error("JAXB exception", e); - } - return null; - } - - public Jid getJid() { - return jid; - } - @Scheduled(fixedDelay = 10000) - public void cleanUp() { - Instant now = Instant.now(); - outConnections.keySet().stream().filter(c -> Duration.between(c.getUpdated(), now).toMinutes() > TIMEOUT_MINUTES) - .forEach(c -> { - logger.info("closing idle outgoing connection to {}", c.to); - c.logoff(); - outConnections.remove(c); - }); - - inConnections.stream().filter(c -> Duration.between(c.updated, now).toMinutes() > TIMEOUT_MINUTES) - .forEach(c -> { - logger.info("closing idle incoming connection from {}", c.from); - c.closeConnection(); - inConnections.remove(c); - }); - } - @PreDestroy - public void preDestroy() throws IOException { - closeFlag.set(true); - if (listener != null && !listener.isClosed()) { - listener.close(); - } - service.shutdown(); - logger.info("XMPP server destroyed"); - } - - public int getServerPort() { - return s2sPort; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/ApiSocialLogin.java b/juick-server/src/main/java/com/juick/server/api/ApiSocialLogin.java deleted file mode 100644 index 8d9f9402..00000000 --- a/juick-server/src/main/java/com/juick/server/api/ApiSocialLogin.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.server.api; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.scribejava.apis.FacebookApi; -import com.github.scribejava.apis.VkontakteApi; -import com.github.scribejava.core.builder.ServiceBuilder; -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthRequest; -import com.github.scribejava.core.model.Verb; -import com.github.scribejava.core.oauth.OAuth20Service; -import com.juick.model.facebook.User; -import com.juick.server.util.HttpBadRequestException; -import com.juick.service.CrosspostService; -import com.juick.service.EmailService; -import com.juick.service.TelegramService; -import com.juick.service.UserService; -import com.juick.model.vk.UsersResponse; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import java.io.IOException; -import java.util.UUID; -import java.util.concurrent.ExecutionException; - -/** - * - * @author Ugnich Anton - */ -@Controller -public class ApiSocialLogin { - - private static final Logger logger = LoggerFactory.getLogger(ApiSocialLogin.class); - - @Value("${facebook_appid:appid}") - private String FACEBOOK_APPID; - @Value("${facebook_secret:secret}") - private String FACEBOOK_SECRET; - private static final String FACEBOOK_REDIRECT = "https://api.juick.com/_fblogin"; - private static final String VK_REDIRECT = "https://api.juick.com/_vklogin"; - private static final String TWITTER_VERIFY_URL = "https://api.twitter.com/1.1/account/verify_credentials.json"; - @Inject - private ObjectMapper jsonMapper; - private ServiceBuilder facebookBuilder, twitterBuilder, vkBuilder; - - @Value("${twitter_consumer_key:appid}") - private String twitterConsumerKey; - @Value("${twitter_consumer_secret:secret}") - private String twitterConsumerSecret; - @Value("${vk_appid:appid}") - private String VK_APPID; - @Value("${vk_secret:secret}") - private String VK_SECRET; - @Value("${telegram_token:secret}") - private String telegramToken; - - @Inject - private CrosspostService crosspostService; - @Inject - private UserService userService; - @Inject - private EmailService emailService; - @Inject - private TelegramService telegramService; - - @PostConstruct - public void init() { - facebookBuilder = new ServiceBuilder(FACEBOOK_APPID); - twitterBuilder = new ServiceBuilder(twitterConsumerKey); - vkBuilder = new ServiceBuilder(VK_APPID); - } - - @GetMapping("/api/_fblogin") - protected String doFacebookLogin(@RequestParam(required = false) String code, - @RequestParam(required = false) String state) throws IOException, ExecutionException, InterruptedException { - if (StringUtils.isBlank(code)) { - String fbstate = UUID.randomUUID().toString(); - crosspostService.addFacebookState(fbstate, state); - OAuth20Service facebookAuthService = facebookBuilder - .apiSecret(FACEBOOK_SECRET) - .callback(FACEBOOK_REDIRECT) - .scope("email") - .state(fbstate) - .build(FacebookApi.instance()); - return "redirect:" + facebookAuthService.getAuthorizationUrl(); - } - - String redirectUrl = crosspostService.verifyFacebookState(state); - - if (StringUtils.isEmpty(redirectUrl)) { - logger.error("state is missing"); - throw new HttpBadRequestException(); - } - OAuth20Service facebookService = facebookBuilder - .apiKey(FACEBOOK_APPID) - .apiSecret(FACEBOOK_SECRET) - .callback(FACEBOOK_REDIRECT) - .scope("email") - .state(state) - .build(FacebookApi.instance()); - OAuth2AccessToken token = facebookService.getAccessToken(code); - final OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://graph.facebook.com/v2.10/me?fields=id,name,link,verified,email"); - facebookService.signRequest(token, meRequest); - String graph = facebookService.execute(meRequest).getBody(); - if (StringUtils.isBlank(graph)) { - logger.error("FACEBOOK GRAPH ERROR"); - throw new HttpBadRequestException(); - } - User fb = jsonMapper.readValue(graph, User.class); - long fbID = NumberUtils.toLong(fb.getId(), 0); - if (fbID == 0 || StringUtils.isBlank(fb.getName()) || StringUtils.isBlank(fb.getLink())) { - logger.error("Missing required fields, id: {}, name: {}, link: {}", fbID, fb.getName(), fb.getLink()); - throw new HttpBadRequestException(); - } - - int uid = crosspostService.getUIDbyFBID(fbID); - if (uid > 0) { - if (!crosspostService.updateFacebookUser(fbID, token.getAccessToken(), fb.getName(), fb.getLink())) { - logger.error("error updating facebook user, id: {}, token: {}", fbID, token.getAccessToken()); - throw new HttpBadRequestException(); - } - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(redirectUrl); - uriComponentsBuilder.queryParam("hash", userService.getHashByUID(uid)); - return "redirect:" + uriComponentsBuilder.build().toUriString(); - } else if (fb.getVerified()) { - if (!crosspostService.createFacebookUser(fbID, state, token.getAccessToken(), fb.getName(), fb.getLink())) { - if (StringUtils.isNotEmpty(fb.getEmail())) { - logger.info("found {} for facebook user {}", fb.getEmail(), fb.getLink()); - Integer userId = crosspostService.getUIDbyFBID(fbID); - if (!emailService.getEmails(userId, false).contains(fb.getEmail())) { - emailService.addEmail(userId, fb.getEmail()); - } - } - logger.info("email not found for facebook user {}", fb.getLink()); - throw new HttpBadRequestException(); - } - return "redirect:/signup?type=fb&hash=" + state; - } else { - logger.error("Facebook account is not verified, id: {}", fbID); - throw new HttpBadRequestException(); - } - }/* - @GetMapping("/_twitter") - protected void doTwitterLogin(HttpServletRequest request, HttpServletResponse response) - throws IOException, ExecutionException, InterruptedException { - String hash = StringUtils.EMPTY, request_token = StringUtils.EMPTY, request_token_secret = StringUtils.EMPTY; - String verifier = request.getParameter("oauth_verifier"); - Cookie[] cookies = request.getCookies(); - for (Cookie cookie : cookies) { - if (cookie.getName().equals("hash")) { - hash = cookie.getValue(); - } - if (cookie.getName().equals("request_token")) { - request_token = cookie.getValue(); - } - if (cookie.getName().equals("request_token_secret")) { - request_token_secret = cookie.getValue(); - } - } - com.juick.User user = UserUtils.getCurrentUser(); - OAuth10aService oAuthService = twitterBuilder - .apiSecret(twitterConsumerSecret) - .callback("http://juick.com/_twitter") - .build(TwitterApi.instance()); - - if (request_token.isEmpty() && request_token_secret.isEmpty() - && (verifier == null || verifier.isEmpty())) { - OAuth1RequestToken requestToken = oAuthService.getRequestToken(); - String authUrl = oAuthService.getAuthorizationUrl(requestToken); - response.addCookie(new Cookie("request_token", requestToken.getToken())); - response.addCookie(new Cookie("request_token_secret", requestToken.getTokenSecret())); - response.setStatus(HttpServletResponse.SC_FOUND); - response.setHeader("Location", authUrl); - } else { - if (verifier != null && verifier.length() > 0) { - OAuth1RequestToken requestToken = new OAuth1RequestToken(request_token, request_token_secret); - OAuth1AccessToken accessToken = oAuthService.getAccessToken(requestToken, verifier); - OAuthRequest oAuthRequest = new OAuthRequest(Verb.GET, TWITTER_VERIFY_URL); - oAuthService.signRequest(accessToken, oAuthRequest); - com.juick.twitter.User twitterUser = jsonMapper.readValue(oAuthService.execute(oAuthRequest).getBody(), - com.juick.twitter.User.class); - if (userService.linkTwitterAccount(user, accessToken.getToken(), accessToken.getTokenSecret(), - twitterUser.getScreenName())) { - response.setStatus(HttpServletResponse.SC_FOUND); - response.setHeader("Location", "http://juick.com/settings"); - } else { - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - } - } - } - }*/ - @GetMapping("/api/_vklogin") - protected String doVKLogin(@RequestParam(required = false) String code, - @RequestParam String state) throws IOException, ExecutionException, InterruptedException { - if (StringUtils.isBlank(code)) { - String vkstate = UUID.randomUUID().toString(); - crosspostService.addVKState(vkstate, state); - OAuth20Service vkAuthService = vkBuilder - .apiSecret(VK_SECRET) - .scope("friends,wall,offline") - .state(vkstate) - .callback(VK_REDIRECT) - .build(VkontakteApi.instance()); - return "redirect:" + vkAuthService.getAuthorizationUrl(); - } - - String redirectUrl = crosspostService.verifyVKState(state); - if (StringUtils.isBlank(redirectUrl)) { - logger.error("state is missing"); - throw new HttpBadRequestException(); - } - - OAuth20Service vkService = vkBuilder - .apiKey(VK_APPID) - .apiSecret(VK_SECRET) - .build(VkontakteApi.instance()); - OAuth2AccessToken token = vkService.getAccessToken(code); - - OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://api.vk.com/method/users.get?fields=screen_name&v=5.73"); - vkService.signRequest(token, meRequest); - String graph = vkService.execute(meRequest).getBody(); - - com.juick.model.vk.User jsonUser = jsonMapper.readValue(graph, UsersResponse.class).getUsers().get(0); - String vkName = jsonUser.getFirstName() + " " + jsonUser.getLastName(); - String vkLink = jsonUser.getScreenName(); - - if (vkName.length() == 1 || StringUtils.isBlank(vkLink)) { - logger.error("vk user error"); - throw new HttpBadRequestException(); - } - - Long vkID = NumberUtils.toLong(jsonUser.getId(), 0); - int uid = crosspostService.getUIDbyVKID(vkID); - if (uid > 0) { - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(redirectUrl); - uriComponentsBuilder.queryParam("hash", userService.getHashByUID(uid)); - return "redirect:" + uriComponentsBuilder.build().toUriString(); - } else { - String loginhash = UUID.randomUUID().toString(); - if (!crosspostService.createVKUser(vkID, loginhash, token.getAccessToken(), vkName, vkLink)) { - logger.error("create vk user error"); - throw new HttpBadRequestException(); - } - return "redirect:/signup?type=vk&hash=" + loginhash; - } - } - /* - @GetMapping("/_tglogin") - public String doDurovLogin(HttpServletRequest request, - @RequestParam Map params, - HttpServletResponse response) { - String dataCheckString = params.entrySet().stream() - .filter(p -> !p.getKey().equals("hash")) - .sorted(Map.Entry.comparingByKey()) - .map(p -> p.getKey() + "=" + p.getValue()) - .collect(Collectors.joining("\n")); - String hash = params.get("hash"); - byte[] secretKey = DigestUtils.sha256(telegramToken); - String resultString = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, secretKey).hmacHex(dataCheckString); - if (hash.equals(resultString)) { - Long tgUser = Long.valueOf(params.get("id")); - int uid = telegramService.getUser(tgUser); - if (uid > 0) { - Cookie c = new Cookie("hash", userService.getHashByUID(uid)); - c.setMaxAge(50 * 24 * 60 * 60); - response.addCookie(c); - return Utils.getPreviousPageByRequest(request).orElse("redirect:/"); - } else { - String username = StringUtils.defaultString(params.get("username"), params.get("first_name")); - telegramService.createTelegramUser(tgUser, username); - return "redirect:/signup?type=durov&hash=" + userService.getSignUpHashByTelegramID(tgUser, username); - } - } else { - logger.warn("invalid tg hash {} for {}", resultString, hash); - } - throw new HttpBadRequestException(); - }*/ -} diff --git a/juick-server/src/main/java/com/juick/server/api/Index.java b/juick-server/src/main/java/com/juick/server/api/Index.java deleted file mode 100644 index 56f01370..00000000 --- a/juick-server/src/main/java/com/juick/server/api/Index.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.api; - -import com.juick.Status; -import com.juick.server.WebsocketManager; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import springfox.documentation.annotations.ApiIgnore; - -import javax.inject.Inject; -import java.net.URI; - -/** - * Created by vitalyster on 25.07.2016. - */ -@RestController -public class Index { - @Inject - private WebsocketManager wsHandler; - - @ApiIgnore - @RequestMapping(value = { "/api/", "/ws/" }, method = RequestMethod.GET, headers = "Connection!=Upgrade") - public ResponseEntity description() { - URI redirectUri = ServletUriComponentsBuilder.fromCurrentRequestUri().path("/swagger-ui.html").build().toUri(); - return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY).location(redirectUri).build(); - } - @ApiIgnore - @RequestMapping(value = "/api/status", method = RequestMethod.GET, - produces = MediaType.APPLICATION_JSON_UTF8_VALUE, headers = "Connection!=Upgrade") - public Status status() { - return Status.getStatus(String.valueOf(wsHandler.getClients().size())); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/Messages.java b/juick-server/src/main/java/com/juick/server/api/Messages.java deleted file mode 100644 index 4f0009dd..00000000 --- a/juick-server/src/main/java/com/juick/server/api/Messages.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.api; - -import com.juick.Message; -import com.juick.Tag; -import com.juick.User; -import com.juick.server.Utils; -import com.juick.service.component.MessageReadEvent; -import com.juick.model.CommandResult; -import com.juick.server.util.HttpBadRequestException; -import com.juick.server.util.HttpNotFoundException; -import com.juick.server.util.UserUtils; -import com.juick.service.MessagesService; -import com.juick.service.TagService; -import com.juick.service.UserService; -import org.apache.commons.io.IOUtils; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.*; - -import javax.inject.Inject; -import java.io.IOException; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; - -/** - * @author ugnich - */ -@RestController -@RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE) -public class Messages { - - private static final ResponseEntity> NOT_FOUND = ResponseEntity - .status(HttpStatus.NOT_FOUND) - .body(Collections.emptyList()); - - private static final ResponseEntity> FORBIDDEN = ResponseEntity - .status(HttpStatus.FORBIDDEN) - .body(Collections.emptyList()); - - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - @Inject - private TagService tagService; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - - // TODO: serialize image urls - - @GetMapping("/api/home") - public ResponseEntity> getHome( - @RequestParam(defaultValue = "0") int before_mid) { - User visitor = UserUtils.getCurrentUser(); - if (!visitor.isAnonymous()) { - int vuid = visitor.getUid(); - List mids = messagesService.getMyFeed(vuid, before_mid, true); - return ResponseEntity.ok(messagesService.getMessages(visitor, mids)); - } - return FORBIDDEN; - } - - @GetMapping("/api/messages") - public ResponseEntity> getMessages( - @RequestParam(required = false) String uname, - @RequestParam(name = "before_mid", defaultValue = "0") Integer before, - @RequestParam(required = false, defaultValue = "0") Integer daysback, - @RequestParam(required = false) String withrecommended, - @RequestParam(required = false) String popular, - @RequestParam(required = false) String search, - @RequestParam(required = false, defaultValue = "0") Integer page, - @RequestParam(required = false) String media, - @RequestParam(required = false) String tag) { - - User visitor = UserUtils.getCurrentUser(); - - List mids; - if (!StringUtils.isEmpty(uname)) { - User user = userService.getUserByName(uname); - if (!user.isAnonymous()) { - if (!StringUtils.isEmpty(media)) { - mids = messagesService.getUserPhotos(user.getUid(), 0, before); - } else if (!StringUtils.isEmpty(tag)) { - Tag tagObject = tagService.getTag(tag, false); - if (tagObject != null) { - mids = messagesService.getUserTag(user.getUid(), tagObject.TID, 0, before); - } else { - return NOT_FOUND; - } - } else if (!StringUtils.isEmpty(withrecommended)) { - mids = messagesService.getUserBlogWithRecommendations(user.getUid(), 0, before); - } else if (daysback > 0) { - mids = messagesService.getUserBlogAtDay(user.getUid(), 0, daysback); - } else if (!StringUtils.isEmpty(search)) { - mids = messagesService.getUserSearch(visitor, user.getUid(), Utils.encodeSphinx(search), 0, page); - } else { - mids = messagesService.getUserBlog(user.getUid(), 0, before); - } - } else { - return NOT_FOUND; - } - } else { - if (!StringUtils.isEmpty(popular)) { - mids = messagesService.getPopular(visitor.getUid(), before); - } else if (!StringUtils.isEmpty(media)) { - mids = messagesService.getPhotos(visitor.getUid(), before); - } else if (!StringUtils.isEmpty(tag)) { - Tag tagObject = tagService.getTag(tag, false); - if (tagObject != null) { - mids = messagesService.getTag(tagObject.TID, visitor.getUid(), before, 20); - } else { - return NOT_FOUND; - } - } else if (!StringUtils.isEmpty(search)) { - mids = messagesService.getSearch(visitor, Utils.encodeSphinx(search), page); - } else { - mids = messagesService.getAll(visitor.getUid(), before); - } - } - return ResponseEntity.ok(messagesService.getMessages(visitor, mids)); - } - @DeleteMapping("/api/messages") - public CommandResult deleteMessage(@RequestParam int mid, @RequestParam(required = false, defaultValue = "0") int rid) { - User visitor = UserUtils.getCurrentUser(); - if (rid > 0) { - if (messagesService.deleteReply(visitor.getUid(), mid, rid)) { - return CommandResult.fromString("Reply deleted"); - } - } - if (messagesService.deleteMessage(visitor.getUid(), mid)) { - return CommandResult.fromString("Message deleted"); - } - throw new HttpBadRequestException(); - } - @GetMapping("/api/messages/discussions") - public List getDiscussions( - @RequestParam(required = false, defaultValue = "0") Long to) { - return messagesService.getMessages(UserUtils.getCurrentUser(), messagesService.getDiscussions(UserUtils.getCurrentUser().getUid(), to)); - } - @GetMapping("/api/thread") - public ResponseEntity> getThread( - @RequestParam(defaultValue = "0") int mid) { - User visitor = UserUtils.getCurrentUser(); - com.juick.Message msg = messagesService.getMessage(mid); - if (msg != null) { - if (!messagesService.canViewThread(mid, visitor.getUid())) { - return FORBIDDEN; - } else { - if (userService.getUserByName(msg.getUser().getName()).isBanned()) { - throw new HttpNotFoundException(); - } - msg.setRecommendations(new HashSet<>(messagesService.getMessageRecommendations(msg.getMid()))); - List replies = messagesService.getReplies(visitor, mid); - if (!visitor.isAnonymous()) { - userService.updateLastSeen(visitor); - applicationEventPublisher.publishEvent( - new MessageReadEvent(this, visitor, msg)); - } - replies.add(0, msg); - return ResponseEntity.ok(replies); - } - } - return NOT_FOUND; - } - @GetMapping(value = "/api/thread/mark_read/{mid}-{rid}.gif", produces = MediaType.IMAGE_GIF_VALUE) - public byte[] markThreadRead(@PathVariable int mid, @PathVariable int rid) throws IOException { - User visitor = UserUtils.getCurrentUser(); - if (!visitor.isAnonymous()) { - messagesService.setLastReadComment(visitor, mid, rid); - Message msg = messagesService.getMessage(mid); - userService.updateLastSeen(visitor); - applicationEventPublisher.publishEvent( - new MessageReadEvent(this, visitor, msg)); - return IOUtils.toByteArray( - Objects.requireNonNull(getClass().getClassLoader().getResource("Transparent.gif"))); - } - throw new HttpBadRequestException(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/Notifications.java b/juick-server/src/main/java/com/juick/server/api/Notifications.java deleted file mode 100644 index 62275f5a..00000000 --- a/juick-server/src/main/java/com/juick/server/api/Notifications.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.api; - -import com.juick.Message; -import com.juick.Status; -import com.juick.ExternalToken; -import com.juick.User; -import com.juick.model.AnonymousUser; -import com.juick.server.util.HttpBadRequestException; -import com.juick.server.util.HttpForbiddenException; -import com.juick.service.MessagesService; -import com.juick.service.PushQueriesService; -import com.juick.service.SubscriptionService; -import com.juick.server.util.UserUtils; -import com.juick.service.UserService; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import springfox.documentation.annotations.ApiIgnore; - -import javax.inject.Inject; -import java.io.IOException; -import java.security.Principal; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Created by vitalyster on 24.10.2016. - */ -@RestController -public class Notifications { - - @Inject - private PushQueriesService pushQueriesService; - @Inject - private MessagesService messagesService; - @Inject - private SubscriptionService subscriptionService; - @Inject - private UserService userService; - - - private User collectTokens(Integer uid) { - User user = userService.getUserByUID(uid).orElse(AnonymousUser.INSTANCE); - user.setUnreadCount(messagesService.getUnread(user).size()); - pushQueriesService.getGCMRegID(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "gcm", t, null))); - pushQueriesService.getAPNSToken(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "apns", t, null))); - pushQueriesService.getMPNSURL(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "mpns", t, null))); - return user; - } - - @ApiIgnore - @RequestMapping(value = "/api/notifications", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public ResponseEntity> doGet( - @RequestParam(required = false, defaultValue = "0") int uid, - @RequestParam(required = false, defaultValue = "0") int mid, - @RequestParam(required = false, defaultValue = "0") int rid) { - User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous() || !(visitor.getName().equals("juick"))) { - throw new HttpForbiddenException(); - } - if (uid > 0 && mid == 0) { - // PM - return ResponseEntity.ok(Collections.singletonList(collectTokens(uid))); - } else { - if (mid > 0) { - // reply - Message msg = messagesService.getMessage(mid); - if (msg != null) { - List users; - if (rid > 0) { - Message op = messagesService.getMessage(mid); - Message reply = messagesService.getReply(mid, rid); - users = subscriptionService.getUsersSubscribedToComments(op, reply); - } else { - users = subscriptionService.getSubscribedUsers(msg.getUser().getUid(), msg); - } - - return ResponseEntity.ok(users.stream().map(User::getUid) - .map(this::collectTokens).collect(Collectors.toList())); - } - } else { - // read - return ResponseEntity.ok(Collections.singletonList(collectTokens(uid))); - } - } - throw new HttpBadRequestException(); - } - - @ApiIgnore - @RequestMapping(value = "/api/notifications", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Status doDelete( - @RequestBody List list) { - User visitor = UserUtils.getCurrentUser(); - if ((visitor.isAnonymous()) || !(visitor.getName().equals("juick"))) { - throw new HttpForbiddenException(); - } - list.forEach(t -> { - switch (t.getType()) { - case "gcm": - pushQueriesService.deleteGCMToken(t.getToken()); - break; - case "apns": - pushQueriesService.deleteAPNSToken(t.getToken()); - break; - case "mpns": - pushQueriesService.deleteMPNSToken(t.getToken()); - break; - default: - throw new HttpBadRequestException(); - } - }); - - return Status.OK; - } - @ApiIgnore - @RequestMapping(value = "/api/notifications/delete", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Status doDeleteTokens( - @RequestBody List list) { - User visitor = UserUtils.getCurrentUser(); - if ((visitor.isAnonymous()) || !(visitor.getName().equals("juick"))) { - throw new HttpForbiddenException(); - } - list.forEach(t -> { - switch (t.getType()) { - case "gcm": - pushQueriesService.deleteGCMToken(t.getToken()); - break; - case "apns": - pushQueriesService.deleteAPNSToken(t.getToken()); - break; - case "mpns": - pushQueriesService.deleteMPNSToken(t.getToken()); - break; - default: - throw new HttpBadRequestException(); - } - }); - - return Status.OK; - } - - @ApiIgnore - @RequestMapping(value = "/api/notifications", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Status doPut( - @RequestBody List list) throws IOException { - User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - list.forEach(t -> { - switch (t.getType()) { - case "gcm": - pushQueriesService.addGCMToken(visitor.getUid(), t.getToken()); - break; - case "apns": - pushQueriesService.addAPNSToken(visitor.getUid(), t.getToken()); - break; - case "mpns": - pushQueriesService.addMPNSToken(visitor.getUid(), t.getToken()); - break; - default: - throw new HttpBadRequestException(); - } - }); - return Status.OK; - } - - @Deprecated - @RequestMapping(value = "/api/android/register", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Status doAndroidRegister( - @RequestParam(name = "regid") String regId) { - User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - pushQueriesService.addGCMToken(visitor.getUid(), regId); - return Status.OK; - } - - @Deprecated - @RequestMapping(value = "/api/android/unregister", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Status doAndroidUnRegister(@RequestParam(name = "regid") String regId) { - pushQueriesService.deleteGCMToken(regId); - return Status.OK; - } - - @Deprecated - @RequestMapping(value = "/api/winphone/register", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Status doWinphoneRegister( - Principal principal, - @RequestParam(name = "url") String regId) { - User visitor = UserUtils.getCurrentUser(); - pushQueriesService.addMPNSToken(visitor.getUid(), regId); - return Status.OK; - } - - @Deprecated - @RequestMapping(value = "/api/winphone/unregister", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Status doWinphoneUnRegister(@RequestParam(name = "url") String regId) { - pushQueriesService.deleteMPNSToken(regId); - return Status.OK; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/PM.java b/juick-server/src/main/java/com/juick/server/api/PM.java deleted file mode 100644 index 0c36fe00..00000000 --- a/juick-server/src/main/java/com/juick/server/api/PM.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.api; - -import com.juick.Chat; -import com.juick.User; -import com.juick.service.component.MessageEvent; -import com.juick.model.AnonymousUser; -import com.juick.model.PrivateChats; -import com.juick.server.util.*; -import com.juick.service.PMQueriesService; -import com.juick.service.UserService; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import javax.inject.Inject; -import java.util.Collections; -import java.util.List; - -/** - * @author ugnich - */ -@RestController -public class PM { - @Inject - private UserService userService; - @Inject - private PMQueriesService pmQueriesService; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - - @RequestMapping(value = "/api/pm", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public List doGetPM( - @RequestParam(required = false) String uname) { - User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - int uid = 0; - if (uname != null && uname.matches("^[a-zA-Z0-9\\-]{2,16}$")) { - uid = userService.getUIDbyName(uname); - } - - if (uid == 0) { - throw new HttpBadRequestException(); - } - - return pmQueriesService.getPMMessages(visitor.getUid(), uid); - } - - @RequestMapping(value = "/api/pm", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public com.juick.Message doPostPM( - @RequestParam String uname, - @RequestParam String body) { - User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - User userTo = AnonymousUser.INSTANCE; - if (WebUtils.isUserName(uname)) { - userTo = userService.getUserByName(uname); - } - - if (userTo.getUid() == 0 || body == null || body.length() < 1 || body.length() > 10240) { - throw new HttpBadRequestException(); - } - - if (userService.isInBLAny(userTo.getUid(), visitor.getUid())) { - throw new HttpForbiddenException(); - } - - if (pmQueriesService.createPM(visitor.getUid(), userTo.getUid(), body)) { - com.juick.Message jmsg = new com.juick.Message(); - jmsg.setUser(visitor); - jmsg.setText(body); - jmsg.setTo(userTo); - applicationEventPublisher.publishEvent(new MessageEvent(this, jmsg, Collections.singletonList(jmsg.getTo()))); - return jmsg; - - } - throw new HttpBadRequestException(); - } - @RequestMapping(value = "/api/groups_pms", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public PrivateChats doGetGroupsPMs( - @RequestParam(defaultValue = "5") int cnt) { - User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - // TODO: ignore cnt param for now but make sure paging param will not be cnt - - List lastconv = pmQueriesService.getLastChats(visitor); - PrivateChats pms = new PrivateChats(); - pms.setUsers(lastconv); - return pms; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/Post.java b/juick-server/src/main/java/com/juick/server/api/Post.java deleted file mode 100644 index 303ff109..00000000 --- a/juick-server/src/main/java/com/juick/server/api/Post.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.api; - -import com.juick.Message; -import com.juick.Reaction; -import com.juick.Status; -import com.juick.User; -import com.juick.server.CommandsManager; -import com.juick.model.CommandResult; -import com.juick.server.util.*; -import com.juick.service.MessagesService; -import com.juick.service.SubscriptionService; -import com.juick.service.UserService; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - -import javax.inject.Inject; -import javax.validation.constraints.NotNull; -import java.net.URI; -import java.net.URL; -import java.util.List; - -/** - * Created by vt on 24/11/2016. - */ -@RestController -public class Post { - private static Logger logger = LoggerFactory.getLogger(Post.class); - - @Inject - private UserService userService; - @Inject - private MessagesService messagesService; - @Inject - private SubscriptionService subscriptionService; - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String imgDir; - @Inject - CommandsManager commandsManager; - - @RequestMapping(value = "/api/post", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - @ResponseStatus(value = HttpStatus.OK) - public CommandResult doPostMessage( - @RequestParam(required = false, defaultValue = StringUtils.EMPTY) String body, - @RequestParam(required = false) String img, - @RequestParam(required = false) MultipartFile attach) throws Exception { - User visitor = UserUtils.getCurrentUser(); - - if (visitor.isAnonymous()) - throw new HttpForbiddenException(); - - if (body.length() > 4096) { - throw new HttpBadRequestException(); - } - body = body.replace("\r", StringUtils.EMPTY); - - URI attachmentFName = HttpUtils.receiveMultiPartFile(attach, tmpDir); - - if (StringUtils.isBlank(attachmentFName.toString()) && img != null && img.length() > 10) { - URI juickUri = URI.create(img); - if (juickUri.getScheme().equals("juick")) { - attachmentFName = juickUri; - } else { - try { - URL imgUrl = new URL(img); - attachmentFName = HttpUtils.downloadImage(imgUrl, tmpDir); - } catch (Exception e) { - logger.error("DOWNLOAD ERROR", e); - throw new HttpBadRequestException(); - } - } - } - if (StringUtils.isBlank(body) && StringUtils.isBlank(attachmentFName.toString())) { - // Should be there for compatibility - throw new HttpBadRequestException(); - } - return commandsManager.processCommand(visitor, body, attachmentFName); - } - - @RequestMapping(value = "/api/comment", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public CommandResult doPostComment( - @RequestParam(defaultValue = "0") int mid, - @RequestParam(defaultValue = "0") int rid, - @RequestParam(required = false, defaultValue = StringUtils.EMPTY) String body, - @RequestParam(required = false) String img, - @RequestParam(required = false) MultipartFile attach) - throws Exception { - User visitor = UserUtils.getCurrentUser(); - int vuid = visitor.getUid(); - if (vuid == 0) { - throw new HttpForbiddenException(); - } - if (mid == 0) { - throw new HttpBadRequestException(); - } - com.juick.Message msg = messagesService.getMessage(mid); - if (msg == null) { - throw new HttpNotFoundException(); - } - - com.juick.Message reply = null; - if (rid > 0) { - reply = messagesService.getReply(mid, rid); - if (reply == null) { - throw new HttpNotFoundException(); - } - } - - if (body.length() > 4096) { - throw new HttpBadRequestException(); - } - body = body.replace("\r", StringUtils.EMPTY); - - if ((msg.ReadOnly && msg.getUser().getUid() != vuid) || userService.isInBLAny(msg.getUser().getUid(), vuid) - || (reply != null && userService.isInBLAny(reply.getUser().getUid(), vuid))) { - throw new HttpForbiddenException(); - } - - URI attachmentFName = HttpUtils.receiveMultiPartFile(attach, tmpDir); - - if (StringUtils.isBlank(attachmentFName.toString()) && img != null && img.length() > 10) { - try { - attachmentFName = HttpUtils.downloadImage(new URL(img), tmpDir); - } catch (Exception e) { - logger.error("DOWNLOAD ERROR", e); - throw new HttpBadRequestException(); - } - } - if (StringUtils.isBlank(body) && StringUtils.isBlank(attachmentFName.toString())) { - // Should be there for compatibility - throw new HttpBadRequestException(); - } - return commandsManager.processCommand(visitor, String.format("#%d/%d %s", mid, rid, body), - attachmentFName); - } - - @PostMapping("/api/like") - @ResponseStatus(value = HttpStatus.OK) - public Status doPostRecomm(@RequestParam Integer mid) throws Exception { - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - com.juick.Message msg = messagesService.getMessage(mid); - if (msg == null) { - throw new HttpNotFoundException(); - } - if (msg.getUser().getUid() == visitor.getUid()) { - throw new HttpForbiddenException(); - } - CommandResult status = commandsManager.processCommand(visitor, String.format("! #%d", mid), - URI.create(StringUtils.EMPTY)); - return Status.getStatus(status.getText()); - } - - @PostMapping("/api/subscribe") - @ResponseStatus(value = HttpStatus.OK) - public Status doPostSubscribe(@RequestParam Integer mid) throws Exception { - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - com.juick.Message msg = messagesService.getMessage(mid); - if (msg == null) { - throw new HttpNotFoundException(); - } - if (msg.getUser().getUid() == visitor.getUid()) { - throw new HttpForbiddenException(); - } - CommandResult status = commandsManager.processCommand(visitor, String.format("S #%d", mid), - URI.create(StringUtils.EMPTY)); - return Status.getStatus(status.getText()); - } - - @GetMapping("/api/reactions") - @ResponseStatus(value = HttpStatus.OK) - public List reactionsList() { - return messagesService.listReactions(); - } - - @PostMapping("/api/react") - @ResponseStatus(value = HttpStatus.OK) - public Status doPostReact(@RequestParam Integer mid,@RequestParam @NotNull int reactionId, - @RequestParam (required = false, defaultValue = "1") int count) { - - logger.info("got reaction with type: {}", reactionId); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - com.juick.Message msg = messagesService.getMessage(mid); - if (msg == null) { - throw new HttpNotFoundException(); - } - if (msg.getUser().getUid() == visitor.getUid()) { - throw new HttpForbiddenException(); - } - MessagesService.RecommendStatus recommendStatus = MessagesService.RecommendStatus.Error; - for (int i = 0; i < count; i++) - recommendStatus = messagesService.likeMessage(mid, visitor.getUid(), - reactionId); - - return recommendStatus == MessagesService.RecommendStatus.Error ? Status.ERROR :Status.OK; - } - - @PostMapping("/api/update") - public CommandResult updateMessage(@RequestParam Integer mid, - @RequestParam(required = false, defaultValue = "0") Integer rid, - @RequestParam String body) { - User visitor = UserUtils.getCurrentUser(); - User author = rid == 0 ? messagesService.getMessageAuthor(mid) : messagesService.getReply(mid, rid).getUser(); - if (visitor.equals(author)) { - if (messagesService.updateMessage(mid, rid, body)) { - Message result = rid == 0 ? messagesService.getMessage(mid) : messagesService.getReply(mid, rid); - return CommandResult.build(result, "Message updated", StringUtils.EMPTY); - } - throw new HttpBadRequestException(); - } - throw new HttpForbiddenException(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/Service.java b/juick-server/src/main/java/com/juick/server/api/Service.java deleted file mode 100644 index ed62886f..00000000 --- a/juick-server/src/main/java/com/juick/server/api/Service.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.juick.server.api; - -import com.juick.Message; -import com.juick.User; -import com.juick.server.CommandsManager; -import com.juick.server.EmailManager; -import com.juick.server.ServerManager; -import com.juick.server.util.HttpForbiddenException; -import com.juick.server.util.UserUtils; -import com.juick.service.EmailService; -import com.juick.service.UserService; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.mail.util.MimeMessageParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.context.request.async.AsyncRequestTimeoutException; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import springfox.documentation.annotations.ApiIgnore; - -import javax.inject.Inject; -import javax.mail.Session; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.nio.file.Paths; -import java.util.*; - -@Controller -public class Service { - private static Logger logger = LoggerFactory.getLogger(Service.class); - @Inject - private UserService userService; - @Inject - private EmailService emailService; - @Inject - private CommandsManager commandsManager; - @Inject - private EmailManager emailManager; - @Value("${api_user:juick}") - private String serviceUser; - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - @Value("${banned_emails:}") - private String[] ignoredEmails; - @Inject - private ServerManager serverManager; - - private Session session = Session.getDefaultInstance(new Properties()); - - @ApiIgnore - @PostMapping("/api/mail") - @ResponseStatus(value = HttpStatus.OK) - public void processMail(InputStream data) throws Exception { - if (UserUtils.getCurrentUser().getName().equals(serviceUser)) { - MimeMessage msg = new MimeMessage(session, data); - String[] returnPaths = msg.getHeader("Return-Path"); - if (returnPaths != null) { - logger.info("got msg with return path {}", returnPaths[0]); - if (returnPaths[0].equals("<>")) { - return; - } - } - String from = msg.getFrom() == null || msg.getFrom().length > 1 ? ((InternetAddress) msg.getSender()).getAddress() - : ((InternetAddress) msg.getFrom()[0]).getAddress(); - - User visitor = userService.getUserByEmail(from); - if (!visitor.isAnonymous()) { - MimeMessageParser parser = new MimeMessageParser(msg); - parser.parse(); - final String[] body = {parser.getPlainContent()}; - if (body[0] == null) { - parser.getAttachmentList().stream() - .filter(a -> a.getContentType().equals("text/plain")).findFirst() - .ifPresent(a -> { - try { - body[0] = IOUtils.toString(a.getInputStream(), StandardCharsets.UTF_8); - logger.info("got text: {}", body[0]); - } catch (IOException e) { - logger.info("attachment error: {}", e); - } - }); - } - final String[] attachmentFName = new String[1]; - parser.getAttachmentList().stream().filter(a -> - a.getContentType().equals("image/jpeg") || a.getContentType().equals("image/png")) - .findFirst().ifPresent(a -> { - logger.info("got attachment: {}", a.getContentType()); - String attachmentType; - if (a.getContentType().equals("image/jpeg")) { - attachmentType = "jpg"; - } else { - attachmentType = "png"; - } - attachmentFName[0] = DigestUtils.md5Hex(UUID.randomUUID().toString()) + "." + attachmentType; - try { - logger.info("got inputstream: {}", a.getInputStream()); - FileOutputStream fos = new FileOutputStream(Paths.get(tmpDir, attachmentFName[0]).toString()); - IOUtils.copy(a.getInputStream(), fos); - fos.close(); - } catch (IOException e) { - logger.info("attachment error: {}", e); - } - }); - String[] inReplyToHeaders = msg.getHeader("In-Reply-To"); - if (inReplyToHeaders != null && inReplyToHeaders.length > 0) { - Scanner inReplyToScanner = new Scanner(inReplyToHeaders[0].trim()).useDelimiter(EmailManager.MSGID_PATTERN); - int mid = Integer.parseInt(inReplyToScanner.next()); - int rid = Integer.parseInt(inReplyToScanner.next()); - logger.info("Message is reply to #{}/{}", mid, rid); - body[0] = rid > 0 ? String.format("#%d/%d %s", mid, rid, body[0]) - : String.format("#%d %s", mid, body[0]); - } - URI attachmentUri = StringUtils.isNotEmpty(attachmentFName[0]) ? URI.create(String.format("juick://%s", attachmentFName[0])) - : URI.create(StringUtils.EMPTY); - commandsManager.processCommand(visitor, body[0], attachmentUri); - } else { - if (!Arrays.asList(ignoredEmails).contains(from)) { - String verificationCode = RandomStringUtils.randomAlphanumeric(8).toUpperCase(); - emailService.addVerificationCode(null, from, verificationCode); - String signupUrl = String.format("Follow this link to create Juick account: https://juick.com/signup?type=email&hash=%s", verificationCode); - emailManager.sendEmail(from, "Juick registration", signupUrl, StringUtils.EMPTY, Collections.emptyMap()); - } - } - } else { - throw new HttpForbiddenException(); - } - } - private void endSession(SseEmitter emitter) { - serverManager.getSessions().stream() - .filter(s -> s.getEmitter().equals(emitter)) - .forEach(session -> serverManager.getSessions().remove(session)); - } - @GetMapping("/api/events") - public SseEmitter handle() throws IOException { - User visitor = UserUtils.getCurrentUser(); - logger.info("{} connected", visitor.getName()); - if (!visitor.isAnonymous()) { - userService.updateLastSeen(visitor); - } - SseEmitter emitter = new SseEmitter(600000L); - serverManager.getSessions().add(new ServerManager.EventSession(visitor, emitter)); - - emitter.onCompletion(() -> endSession(emitter)); - emitter.onTimeout(() -> endSession(emitter)); - - return emitter; - } - @ExceptionHandler(AsyncRequestTimeoutException.class) - public void eventErrorHandler(Exception ex) { - logger.debug("SSE timeout", ex); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/Tags.java b/juick-server/src/main/java/com/juick/server/api/Tags.java deleted file mode 100644 index 7a8e572a..00000000 --- a/juick-server/src/main/java/com/juick/server/api/Tags.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.api; - -import com.juick.User; -import com.juick.model.TagStats; -import com.juick.server.util.UserUtils; -import com.juick.service.TagService; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import javax.inject.Inject; -import java.util.List; - -/** - * Created by vitalyster on 29.11.2016. - */ -@RestController -public class Tags { - @Inject - private TagService tagService; - - @RequestMapping(value = "/api/tags", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public List tags( - @RequestParam(required = false, defaultValue = "0") int user_id - ) { - User visitor = UserUtils.getCurrentUser(); - if (user_id == 0) { - user_id = visitor.getUid(); - } - if (user_id > 0) { - return tagService.getUserTagStats(user_id); - } - return tagService.getTagStats(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/Users.java b/juick-server/src/main/java/com/juick/server/api/Users.java deleted file mode 100644 index 7686d722..00000000 --- a/juick-server/src/main/java/com/juick/server/api/Users.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.api; - -import com.juick.User; -import com.juick.model.ApplicationStatus; -import com.juick.model.UserInfo; -import com.juick.server.util.HttpForbiddenException; -import com.juick.server.util.HttpNotFoundException; -import com.juick.service.CrosspostService; -import com.juick.service.EmailService; -import com.juick.service.MessagesService; -import com.juick.service.UserService; -import com.juick.server.util.UserUtils; -import com.juick.server.util.WebUtils; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.*; - -import javax.inject.Inject; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * @author ugnich - */ -@RestController -public class Users { - @Inject - private UserService userService; - @Inject - private MessagesService messagesService; - @Inject - private CrosspostService crosspostService; - @Inject - private EmailService emailService; - - @RequestMapping(value = "/api/auth", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public String getAuthToken() { - return userService.getHashByUID(UserUtils.getCurrentUser().getUid()); - } - - @RequestMapping(value = "/api/users", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public List doGetUsers( - @RequestParam(value = "uname", required = false) List unames) { - List users = new ArrayList<>(); - - if (unames != null) { - unames.removeIf(WebUtils::isNotUserName); - - if (!unames.isEmpty() && unames.size() < 20) - users.addAll(userService.getUsersByName(unames)); - } - - if (!users.isEmpty()) - return users; - if (!UserUtils.getCurrentUser().isAnonymous()) { - return Collections.singletonList(UserUtils.getCurrentUser()); - } - - throw new HttpNotFoundException(); - } - - @GetMapping("/api/me") - public SecureUser getMe() { - User visitor = UserUtils.getCurrentUser(); - SecureUser me = new SecureUser(); - me.setUid(visitor.getUid()); - me.setName(visitor.getName()); - me.setAuthHash(getAuthToken()); - List unread = messagesService.getUnread(visitor); - me.setUnread(unread); - me.setUnreadCount(unread.size()); - me.setRead(userService.getUserFriends(visitor.getUid())); - me.setReaders(userService.getUserReaders(visitor.getUid())); - return me; - } - - @RequestMapping(value = "/api/users/read", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public List doGetUserRead( - @RequestParam String uname) { - User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - int uid = 0; - if (uname == null) { - uid = visitor.getUid(); - } else { - if (WebUtils.isUserName(uname)) { - com.juick.User u = userService.getUserByName(uname); - if (!u.isAnonymous()) { - uid = u.getUid(); - } - } - } - - if (uid > 0) { - return userService.getUserFriends(uid); - } - throw new HttpNotFoundException(); - } - - @RequestMapping(value = "/api/users/readers", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public List doGetUserReaders( - @RequestParam String uname) { - User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - int uid = 0; - if (uname == null) { - uid = visitor.getUid(); - } else { - if (WebUtils.isUserName(uname)) { - com.juick.User u = userService.getUserByName(uname); - if (!u.isAnonymous()) { - uid = u.getUid(); - } - } - } - - if (uid > 0) { - return userService.getUserReaders(uid); - } - throw new HttpNotFoundException(); - } - - @GetMapping("/api/info/{uname}") - public UserInfo getUserInfo(@PathVariable String uname) { - User user = userService.getUserByName(uname); - if (!user.isBanned()) { - return userService.getUserInfo(user); - } - throw new HttpNotFoundException(); - } - - class SecureUser extends User { - public String getHash() { - return getAuthHash(); - } - public UserInfo getUserInfo() { - return userService.getUserInfo(this); - } - public List getJIDs() { - return userService.getAllJIDs(this); - } - public List getEmails() { - return userService.getEmails(this); - } - public String getActiveEmail() { - return emailService.getNotificationsEmail(this.getUid()); - } - public String getTwitterName() { - return crosspostService.getTwitterName(this.getUid()); - } - public String getTelegramName() { - return crosspostService.getTelegramName(this.getUid()); - } - public ApplicationStatus getFacebookStatus() { - return crosspostService.getFbCrossPostStatus(this.getUid()); - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/Profile.java b/juick-server/src/main/java/com/juick/server/api/activity/Profile.java deleted file mode 100644 index 10390ea1..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/Profile.java +++ /dev/null @@ -1,379 +0,0 @@ -package com.juick.server.api.activity; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.juick.Message; -import com.juick.User; -import com.juick.model.CommandResult; -import com.juick.server.ActivityPubManager; -import com.juick.server.CommandsManager; -import com.juick.server.KeystoreManager; -import com.juick.server.SignatureManager; -import com.juick.server.api.activity.model.Activity; -import com.juick.server.api.activity.model.Context; -import com.juick.server.api.activity.model.activities.Announce; -import com.juick.server.api.activity.model.activities.Create; -import com.juick.server.api.activity.model.activities.Delete; -import com.juick.server.api.activity.model.activities.Follow; -import com.juick.server.api.activity.model.activities.Undo; -import com.juick.server.api.activity.model.objects.Image; -import com.juick.server.api.activity.model.objects.Key; -import com.juick.server.api.activity.model.objects.Note; -import com.juick.server.api.activity.model.objects.OrderedCollection; -import com.juick.server.api.activity.model.objects.OrderedCollectionPage; -import com.juick.server.api.activity.model.objects.Person; -import com.juick.server.util.HttpBadRequestException; -import com.juick.server.util.HttpNotFoundException; -import com.juick.server.util.UserUtils; -import com.juick.service.MessagesService; -import com.juick.service.UserService; -import com.juick.service.activities.DeleteUserEvent; -import com.juick.service.activities.FollowEvent; -import com.juick.service.activities.UndoFollowEvent; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.inject.Inject; -import java.net.URI; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -@RestController -public class Profile { - private static final Logger logger = LoggerFactory.getLogger(Profile.class); - @Inject - private UserService userService; - @Inject - private MessagesService messagesService; - @Inject - private KeystoreManager keystoreManager; - @Inject - private SignatureManager signatureManager; - @Inject - private ActivityPubManager activityPubManager; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - @Inject - private CommandsManager commandsManager; - @Value("${web_domain:localhost}") - private String domain; - @Value("${ap_base_uri:http://localhost:8080/}") - private String baseUri; - @Value("${img_url:http://localhost:8080/i/}") - private String baseImagesUri; - @Inject - private ObjectMapper jsonMapper; - - @GetMapping(value = "/u/{userName}", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public Person getUser(@PathVariable String userName) { - User user = userService.getUserByName(userName); - if (!user.isAnonymous()) { - Person person = new Person(); - person.setId(activityPubManager.personUri(user)); - person.setUrl(activityPubManager.personWebUri(user)); - person.setName(userName); - person.setPreferredUsername(userName); - Key publicKey = new Key(); - publicKey.setId(person.getId() + "#main-key"); - publicKey.setOwner(person.getId()); - publicKey.setPublicKeyPem(keystoreManager.getPublicKeyPem()); - person.setPublicKey(publicKey); - person.setInbox(activityPubManager.inboxUri()); - person.setOutbox(activityPubManager.outboxUri(user)); - person.setFollowers(activityPubManager.followersUri(user)); - person.setFollowing(activityPubManager.followingUri(user)); - UriComponentsBuilder image = UriComponentsBuilder.fromUriString(baseImagesUri); - image.path(String.format("/a/%d.png", user.getUid())); - Image avatar = new Image(); - avatar.setUrl(image.toUriString()); - avatar.setMediaType("image/png"); - person.setIcon(avatar); - return (Person) Context.build(person); - } - throw new HttpNotFoundException(); - } - - @GetMapping(value = "/u/{userName}/blog/toc", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public OrderedCollection getOutbox(@PathVariable String userName) { - User user = userService.getUserByName(userName); - if (!user.isAnonymous()) { - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri); - OrderedCollection blog = new OrderedCollection(); - blog.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString()); - blog.setTotalItems(userService.getStatsMessages(user.getUid())); - blog.setFirst(uriComponentsBuilder.path(String.format("/u/%s/blog", userName)).toUriString()); - return (OrderedCollection) Context.build(blog); - } - throw new HttpNotFoundException(); - } - - @GetMapping(value = "/u/{userName}/blog", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public OrderedCollectionPage getOutboxPage(@PathVariable String userName, - @RequestParam(required = false, defaultValue = "0") int before) { - User visitor = UserUtils.getCurrentUser(); - User user = userService.getUserByName(userName); - if (!user.isAnonymous()) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - String personUri = uri.path(String.format("/u/%s", userName)).toUriString(); - List mids = messagesService.getUserBlog(user.getUid(), 0, before); - List notes = messagesService.getMessages(visitor, mids).stream().map(activityPubManager::makeNote).collect(Collectors.toList()); - OrderedCollectionPage page = new OrderedCollectionPage(); - page.setPartOf(uri.replacePath(String.format("/u/%s/blog/toc", userName)).toUriString()); - page.setFirst(uri.replacePath(String.format("/u/%s/blog", userName)).toUriString()); - page.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString()); - page.setOrderedItems(notes.stream().map(a -> { - Create create = new Create(); - create.setId(a.getId() + "#Create"); - create.setTo(a.getTo()); - create.setActor(personUri); - create.setObject(a); - create.setPublished(a.getPublished()); - return create; - }).collect(Collectors.toList())); - int beforeNext = mids.stream().reduce((fst, second) -> second).orElse(0); - if (beforeNext > 0) { - page.setNext(uri.queryParam("before", beforeNext).toUriString()); - } - page.setLast(uri.replaceQueryParam("before", "1").toUriString()); - return (OrderedCollectionPage) Context.build(page); - } - throw new HttpNotFoundException(); - } - - @GetMapping(value = "/u/{userName}/followers/toc", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public OrderedCollection getFollowers(@PathVariable String userName) { - User user = userService.getUserByName(userName); - if (!user.isAnonymous()) { - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri); - OrderedCollection followers = new OrderedCollection(); - followers.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString()); - followers.setTotalItems(userService.getStatsMyReaders(user.getUid())); - followers.setFirst(uriComponentsBuilder.path(String.format("/u/%s/followers", userName)).toUriString()); - return (OrderedCollection) Context.build(followers); - } - throw new HttpNotFoundException(); - } - - @GetMapping(value = "/u/{userName}/followers", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public OrderedCollectionPage getFollowersPage(@PathVariable String userName, - @RequestParam(required = false, defaultValue = "0") int page) { - User user = userService.getUserByName(userName); - if (!user.isAnonymous()) { - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri); - uriComponentsBuilder.path(String.format("/u/%s/followers", userName)); - List followers = userService.getUserReaders(user.getUid()); - Stream followersPage = followers.stream().skip(20 * page).limit(20); - - OrderedCollectionPage result = new OrderedCollectionPage(); - result.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString()); - result.setOrderedItems(followersPage.map(a -> { - Person follower = new Person(); - follower.setName(a.getName()); - follower.setPreferredUsername(a.getName()); - follower.setUrl(activityPubManager.personWebUri(a)); - return follower; - }).collect(Collectors.toList())); - boolean hasNext = followers.size() <= 20 * page; - if (hasNext) { - result.setNext(uriComponentsBuilder.queryParam("page", page + 1).toUriString()); - } - return (OrderedCollectionPage) Context.build(result); - } - throw new HttpNotFoundException(); - } - - @GetMapping(value = "/u/{userName}/following/toc", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public OrderedCollection getFollowing(@PathVariable String userName) { - User user = userService.getUserByName(userName); - if (!user.isAnonymous()) { - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri); - OrderedCollection following = new OrderedCollection(); - following.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString()); - following.setTotalItems(userService.getUserFriends(user.getUid()).size()); - following.setFirst(uriComponentsBuilder.path(String.format("/u/%s/followers", userName)).toUriString()); - return (OrderedCollection) Context.build(following); - } - throw new HttpNotFoundException(); - } - - @GetMapping(value = "/u/{userName}/following", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public OrderedCollectionPage getFollowingPage(@PathVariable String userName, - @RequestParam(required = false, defaultValue = "0") int page) { - User user = userService.getUserByName(userName); - if (!user.isAnonymous()) { - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri); - uriComponentsBuilder.path(String.format("/u/%s/following", userName)); - List following = userService.getUserFriends(user.getUid()); - Stream followingPage = following.stream().skip(20 * page).limit(20); - - OrderedCollectionPage result = new OrderedCollectionPage(); - result.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString()); - result.setOrderedItems(followingPage.map(a -> { - Person follower = new Person(); - follower.setName(a.getName()); - follower.setPreferredUsername(a.getName()); - follower.setUrl(activityPubManager.personWebUri(a)); - return follower; - }).collect(Collectors.toList())); - boolean hasNext = following.size() <= 20 * page; - if (hasNext) { - result.setNext(uriComponentsBuilder.queryParam("page", page + 1).toUriString()); - } - return (OrderedCollectionPage) Context.build(result); - } - throw new HttpNotFoundException(); - } - - @GetMapping(value = "/n/{mid}-{rid}", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public Context showNote(@PathVariable int mid, @PathVariable int rid) { - if (rid > 0) { - // reply - return Context.build(activityPubManager.makeNote( - messagesService.getReply(mid, rid))); - } - return Context.build(activityPubManager.makeNote( - messagesService.getMessage(mid))); - } - - @PostMapping(value = "/api/inbox", consumes = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public ResponseEntity processInbox(@RequestBody Activity activity, - @RequestHeader(name = "Host") String host, - @RequestHeader(name = "Date") String date, - @RequestHeader(name = "Digest", required = false) String digest, - @RequestHeader(name = "Content-Type") String contentType, - @RequestHeader(name = "User-Agent", required = false) String userAgent, - @RequestHeader(name = "Accept-Encoding", required = false) String acceptEncoding, - @RequestHeader(name = "Signature", required = false) String signature) throws Exception { - UriComponents componentsBuilder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); - Map headers = new HashMap<>(); - headers.put("host", host.split(":", 2)[0]); - headers.put("date", date); - headers.put("digest", digest); - headers.put("content-type", contentType); - headers.put("user-agent", userAgent); - headers.put("accept-encoding", acceptEncoding); - boolean valid = signatureManager.verifySignature(signature, URI.create(activity.getActor()), "POST", - componentsBuilder.getPath(), headers); - if (valid) { - if (activity instanceof Follow) { - Follow followRequest = (Follow) activity; - String actor = followRequest.getActor(); - Person follower = (Person) signatureManager.getContext(URI.create(actor)).orElseThrow(HttpBadRequestException::new); - applicationEventPublisher.publishEvent( - new FollowEvent(this, followRequest)); - return new ResponseEntity<>(HttpStatus.ACCEPTED); - - } - if (activity instanceof Undo) { - String follower = (String) ((Map) activity.getObject()).get("object"); - applicationEventPublisher.publishEvent(new UndoFollowEvent(this, activity.getActor(), follower)); - return new ResponseEntity<>(HttpStatus.OK); - } - if (activity instanceof Delete) { - if (activity.getObject() instanceof String) { - // Delete user - applicationEventPublisher.publishEvent(new DeleteUserEvent(this, (String)activity.getObject())); - return new ResponseEntity<>(HttpStatus.OK); - } - } - if (activity instanceof Create) { - if (activity.getObject() instanceof Map) { - Map note = (Map) activity.getObject(); - if (note.get("type").equals("Note")) { - URI noteId = URI.create((String) note.get("id")); - if (messagesService.replyExists(noteId)) { - return new ResponseEntity<>(HttpStatus.OK); - } else { - String inReplyTo = (String) note.get("inReplyTo"); - if (StringUtils.isNotBlank(inReplyTo)) { - if (inReplyTo.startsWith(baseUri)) { - UriComponents uri = UriComponentsBuilder.fromUriString(inReplyTo).build(); - String postId = uri.getPath().substring(uri.getPath().lastIndexOf('/') + 1).replace("-", "/"); - User user = new User(); - user.setUri(URI.create(activity.getActor())); - String attachment = StringUtils.EMPTY; - if (note.get("attachment") != null && ((List) note.get("attachment")).size() > 0) { - Map attachmentObj = (Map) ((List) note.get("attachment")).get(0); - attachment = (String) attachmentObj.get("url"); - } - CommandResult result = commandsManager.processCommand(user, String.format("#%s %s", postId, note.get("content")), URI.create(attachment)); - logger.info(jsonMapper.writeValueAsString(result)); - if (result.getNewMessage().isPresent()) { - messagesService.updateReplyUri(result.getNewMessage().get(), noteId); - return new ResponseEntity<>(HttpStatus.OK); - } else { - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); - } - } else { - Message reply = messagesService.getReplyByUri(inReplyTo); - if (reply != null) { - User user = new User(); - user.setUri(URI.create(activity.getActor())); - String attachment = StringUtils.EMPTY; - if (note.get("attachment") != null && ((List) note.get("attachment")).size() > 0) { - Map attachmentObj = (Map) ((List) note.get("attachment")).get(0); - attachment = (String) attachmentObj.get("url"); - } - CommandResult result = commandsManager.processCommand(user, String.format("#%d/%d %s", reply.getMid(), reply.getRid(), note.get("content")), URI.create(attachment)); - logger.info(jsonMapper.writeValueAsString(result)); - if (result.getNewMessage().isPresent()) { - messagesService.updateReplyUri(result.getNewMessage().get(), noteId); - return new ResponseEntity<>(HttpStatus.OK); - } else { - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); - } - } - } - } - } - } - } - } - if (activity instanceof Delete) { - Map tombstone = (Map) activity.getObject(); - if (tombstone.get("type").equals("Tombstone")) { - URI actor = URI.create(activity.getActor()); - URI reply = URI.create((String)tombstone.get("id")); - messagesService.deleteReply(actor, reply); - return new ResponseEntity<>(HttpStatus.OK); - } - } - if (activity instanceof Announce) { - logger.info("Announce: {}", jsonMapper.writeValueAsString(activity)); - return new ResponseEntity<>(HttpStatus.OK); - } - logger.warn("Unknown activity: {}", jsonMapper.writeValueAsString(activity)); - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); - } - return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); - } - @PostMapping(value = "/u/", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public User fetchUser(@RequestParam URI uri) { - Person person = (Person) signatureManager.getContext(uri).orElseThrow(HttpBadRequestException::new); - User user = new User(); - user.setUri(URI.create(person.getUrl())); - user.setName(person.getPreferredUsername()); - if (person.getIcon() != null) { - user.setAvatar(person.getIcon().getUrl()); - } - return user; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/Activity.java b/juick-server/src/main/java/com/juick/server/api/activity/model/Activity.java deleted file mode 100644 index ec126b88..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/Activity.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.juick.server.api.activity.model; - -public abstract class Activity extends Context { - - private String actor; - private Object object; - - public String getActor() { - return actor; - } - - public void setActor(String actor) { - this.actor = actor; - } - - public Object getObject() { - return object; - } - - public void setObject(Object object) { - this.object = object; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/Context.java b/juick-server/src/main/java/com/juick/server/api/activity/model/Context.java deleted file mode 100644 index 0df8f8c7..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/Context.java +++ /dev/null @@ -1,123 +0,0 @@ -package com.juick.server.api.activity.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.juick.server.api.activity.model.activities.*; -import com.juick.server.api.activity.model.objects.*; - -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property="type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = Create.class, name = "Create"), - @JsonSubTypes.Type(value = Delete.class, name = "Delete"), - @JsonSubTypes.Type(value = Follow.class, name = "Follow"), - @JsonSubTypes.Type(value = Accept.class, name = "Accept"), - @JsonSubTypes.Type(value = Undo.class, name = "Undo"), - @JsonSubTypes.Type(value = Like.class, name = "Like"), - @JsonSubTypes.Type(value = Block.class, name = "Block"), - @JsonSubTypes.Type(value = Announce.class, name = "Announce"), - @JsonSubTypes.Type(value = Activity.class, name = "Activity"), - @JsonSubTypes.Type(value = Image.class, name = "Image"), - @JsonSubTypes.Type(value = Key.class, name = "Key"), - @JsonSubTypes.Type(value = Link.class, name = "Link"), - @JsonSubTypes.Type(value = Hashtag.class, name = "Hashtag"), - @JsonSubTypes.Type(value = Mention.class, name = "Mention"), - @JsonSubTypes.Type(value = Note.class, name = "Note"), - @JsonSubTypes.Type(value = OrderedCollection.class, name = "OrderedCollection"), - @JsonSubTypes.Type(value = OrderedCollectionPage.class, name = "OrderedCollectionPage"), - @JsonSubTypes.Type(value = Person.class, name = "Person") -}) -public abstract class Context { - - private List context; - - private String id; - - private String name; - - private Instant published; - - private String url; - - private List to; - - private List tags; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getType() { - return getClass().getSimpleName(); - } - - @JsonProperty("@context") - public List getContext() { - return context; - } - - public final static String ACTIVITY_STREAMS_URI = "https://www.w3.org/ns/activitystreams"; - public final static String SECURITY_URI = "https://w3id.org/security/v1"; - public final static String LD_JSON_MEDIA_TYPE = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""; - public final static String ACTIVITY_MEDIA_TYPE = "application/activity+json"; - public final static String ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE = ACTIVITY_MEDIA_TYPE + "; profile=\"https://www.w3.org/ns/activitystreams\""; - - public Instant getPublished() { - return published; - } - - public void setPublished(Instant published) { - this.published = published; - } - - public List getTo() { - return to; - } - - public void setTo(List to) { - this.to = to; - } - - public static Context build(Context response) { - response.context = new ArrayList(Arrays.asList(ACTIVITY_STREAMS_URI, SECURITY_URI)); - response.context.add(Collections.singletonMap("Hashtag", "as:Hashtag")); - return response; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - @JsonProperty("tag") - public List getTags() { - return tags; - } - - public void setTags(List tags) { - this.tags = tags; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Accept.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Accept.java deleted file mode 100644 index 1e0a9968..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Accept.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.activities; - -import com.juick.server.api.activity.model.Activity; - -public class Accept extends Activity { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Announce.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Announce.java deleted file mode 100644 index f2859404..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Announce.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.activities; - -import com.juick.server.api.activity.model.Activity; - -public class Announce extends Activity { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Block.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Block.java deleted file mode 100644 index 0e5a02d4..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Block.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.activities; - -import com.juick.server.api.activity.model.Activity; - -public class Block extends Activity { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Create.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Create.java deleted file mode 100644 index 52507373..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Create.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.activities; - -import com.juick.server.api.activity.model.Activity; - -public class Create extends Activity { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Delete.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Delete.java deleted file mode 100644 index f4392020..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Delete.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.activities; - -import com.juick.server.api.activity.model.Activity; - -public class Delete extends Activity { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Follow.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Follow.java deleted file mode 100644 index 573ecc6e..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Follow.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.activities; - -import com.juick.server.api.activity.model.Activity; - -public class Follow extends Activity { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Like.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Like.java deleted file mode 100644 index 3670293d..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Like.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.activities; - -import com.juick.server.api.activity.model.Activity; - -public class Like extends Activity { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Undo.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Undo.java deleted file mode 100644 index 4e87e9d0..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Undo.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.activities; - -import com.juick.server.api.activity.model.Activity; - -public class Undo extends Activity { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Hashtag.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Hashtag.java deleted file mode 100644 index 34e73be6..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Hashtag.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.juick.server.api.activity.model.Context; - -public class Hashtag extends Context { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Image.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Image.java deleted file mode 100644 index e067f729..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Image.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.juick.server.api.activity.model.Context; - -public class Image extends Context { - private String mediaType; - - public String getMediaType() { - return mediaType; - } - - public void setMediaType(String mediaType) { - this.mediaType = mediaType; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Key.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Key.java deleted file mode 100644 index 075c51dd..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Key.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.juick.server.api.activity.model.Context; - -public class Key extends Context { - private String owner; - private String publicKeyPem; - - public String getOwner() { - return owner; - } - - public void setOwner(String owner) { - this.owner = owner; - } - - public String getPublicKeyPem() { - return publicKeyPem; - } - - public void setPublicKeyPem(String publicKeyPem) { - this.publicKeyPem = publicKeyPem; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Link.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Link.java deleted file mode 100644 index 0c4f26dc..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Link.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.juick.server.api.activity.model.Context; - -public class Link extends Context { - private String href; - - public String getHref() { - return href; - } - - public void setHref(String href) { - this.href = href; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Mention.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Mention.java deleted file mode 100644 index bcb52d37..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Mention.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class Mention extends Link { - @JsonCreator - public Mention(@JsonProperty("href") String href, @JsonProperty("name") String name) { - this.setHref(href); - this.setName(name); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Note.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Note.java deleted file mode 100644 index baad2d3b..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Note.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.fasterxml.jackson.annotation.JsonFormat; -import com.juick.server.api.activity.model.Context; - -import java.util.List; - -public class Note extends Context { - private String content; - private String attributedTo; - private String inReplyTo; - private List attachment; - private List to; - private List cc; - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getAttributedTo() { - return attributedTo; - } - - public void setAttributedTo(String attributedTo) { - this.attributedTo = attributedTo; - } - - @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) - public List getAttachment() { - return attachment; - } - - public void setAttachment(List attachment) { - this.attachment = attachment; - } - - public List getTo() { - return to; - } - - public void setTo(List to) { - this.to = to; - } - - public List getCc() { - return cc; - } - - public void setCc(List cc) { - this.cc = cc; - } - - public String getInReplyTo() { - return inReplyTo; - } - - public void setInReplyTo(String inReplyTo) { - this.inReplyTo = inReplyTo; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/OrderedCollection.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/OrderedCollection.java deleted file mode 100644 index 426cf331..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/OrderedCollection.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.juick.server.api.activity.model.Context; - -public class OrderedCollection extends Context { - - private int totalItems; - - public int getTotalItems() { - return totalItems; - } - - public void setTotalItems(int totalItems) { - this.totalItems = totalItems; - } - private String first; - - public String getFirst() { - return first; - } - - public void setFirst(String first) { - this.first = first; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/OrderedCollectionPage.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/OrderedCollectionPage.java deleted file mode 100644 index 601919ba..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/OrderedCollectionPage.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.juick.server.api.activity.model.Context; - -import java.util.List; - -public class OrderedCollectionPage extends Context { - - private String partOf; - - private String first; - - private String next; - - private String last; - - private List orderedItems; - - public String getNext() { - return next; - } - - public void setNext(String next) { - this.next = next; - } - - public List getOrderedItems() { - return orderedItems; - } - - public void setOrderedItems(List orderedItems) { - this.orderedItems = orderedItems; - } - - public String getPartOf() { - return partOf; - } - - public void setPartOf(String partOf) { - this.partOf = partOf; - } - - public String getFirst() { - return first; - } - - public void setFirst(String first) { - this.first = first; - } - - public String getLast() { - return last; - } - - public void setLast(String last) { - this.last = last; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Person.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Person.java deleted file mode 100644 index 2d3a45d7..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Person.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.juick.server.api.activity.model.Context; - -public class Person extends Context { - - private String name; - private String preferredUsername; - private Image icon; - private String inbox; - private String outbox; - private String following; - private String followers; - private Key publicKey; - - @Override - public String getType() { - return "Person"; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) - public Image getIcon() { - return icon; - } - - public void setIcon(Image icon) { - this.icon = icon; - } - - public String getOutbox() { - return outbox; - } - - public void setOutbox(String outbox) { - this.outbox = outbox; - } - - public String getInbox() { - return inbox; - } - - public void setInbox(String inbox) { - this.inbox = inbox; - } - - public String getFollowing() { - return following; - } - - public void setFollowing(String following) { - this.following = following; - } - - public String getFollowers() { - return followers; - } - - public void setFollowers(String followers) { - this.followers = followers; - } - - @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) - public Key getPublicKey() { - return publicKey; - } - - public void setPublicKey(Key publicKey) { - this.publicKey = publicKey; - } - - public String getPreferredUsername() { - return preferredUsername; - } - - public void setPreferredUsername(String preferredUsername) { - this.preferredUsername = preferredUsername; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/apple/AppSiteAssociation.java b/juick-server/src/main/java/com/juick/server/api/apple/AppSiteAssociation.java deleted file mode 100644 index 81ab6960..00000000 --- a/juick-server/src/main/java/com/juick/server/api/apple/AppSiteAssociation.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.juick.server.api.apple; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; - -import java.util.Collections; -import java.util.List; - -@RestController -public class AppSiteAssociation { - @Value("${ios_app_id:}") - private String appId; - - @GetMapping("/.well-known/apple-app-site-association") - @ResponseBody - public SiteAssociations appSiteAssociations() { - WebCredentials webCredentials = new WebCredentials(); - webCredentials.setApps(Collections.singletonList(appId)); - SiteAssociations siteAssociations = new SiteAssociations(); - siteAssociations.setWebcredentials(webCredentials); - return siteAssociations; - } - - private class SiteAssociations { - private WebCredentials webcredentials; - - public WebCredentials getWebcredentials() { - return webcredentials; - } - - public void setWebcredentials(WebCredentials webcredentials) { - this.webcredentials = webcredentials; - } - } - - private class WebCredentials { - private List apps; - - public List getApps() { - return apps; - } - - public void setApps(List apps) { - this.apps = apps; - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/hostmeta/HostMeta.java b/juick-server/src/main/java/com/juick/server/api/hostmeta/HostMeta.java deleted file mode 100644 index fa4d2a3f..00000000 --- a/juick-server/src/main/java/com/juick/server/api/hostmeta/HostMeta.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.juick.server.api.hostmeta; - -import com.cliqset.xrd.Link; -import com.cliqset.xrd.XRD; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.Collections; - -import static com.cliqset.xrd.XRDConstants.XRD_MEDIA_TYPE; - -@RestController -public class HostMeta { - @Value("${ap_base_uri:http://localhost:8080/}") - private String baseUri; - @GetMapping(value = "/.well-known/host-meta", produces = XRD_MEDIA_TYPE) - public XRD hostMetaResponse() { - Link webfinger = new Link(); - webfinger.setTemplate(String.format("%swebfinger?resource={uri}", baseUri)); - XRD xrd = new XRD(); - xrd.setLinks(Collections.singletonList(webfinger)); - return xrd; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/rss/Feeds.java b/juick-server/src/main/java/com/juick/server/api/rss/Feeds.java deleted file mode 100644 index c72f3a5e..00000000 --- a/juick-server/src/main/java/com/juick/server/api/rss/Feeds.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.api.rss; - -import com.juick.User; -import com.juick.server.util.HttpBadRequestException; -import com.juick.server.util.UserUtils; -import com.juick.service.MessagesService; -import com.juick.service.UserService; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.servlet.ModelAndView; - -import javax.inject.Inject; -import java.util.List; - -/** - * Created by vitalyster on 13.12.2016. - */ -@Controller -public class Feeds { - - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - - @RequestMapping(value = "/rss/{userName}/blog", method = RequestMethod.GET, produces = "text/xml; charset=utf-8") - public ModelAndView getBlog(@PathVariable String userName) { - User user = userService.getUserByName(userName); - if (!user.isAnonymous()) { - List mids = messagesService.getUserBlog(user.getUid(), 0, 0); - ModelAndView modelAndView = new ModelAndView(); - modelAndView.setViewName("messagesView"); - modelAndView.addObject("user", user); - modelAndView.addObject("messages", messagesService.getMessages(UserUtils.getCurrentUser(), mids)); - return modelAndView; - } - throw new HttpBadRequestException(); - } - - @RequestMapping(value = "/rss/", method = RequestMethod.GET, produces = "text/xml; charset=utf-8") - public ModelAndView getLast(@RequestParam(value = "hours", required = false, defaultValue = "0") Integer hours) { - List mids = messagesService.getLastMessages(hours); - ModelAndView modelAndView = new ModelAndView(); - modelAndView.setViewName("messagesView"); - modelAndView.addObject("messages", messagesService.getMessages(UserUtils.getCurrentUser(),mids)); - return modelAndView; - } - @RequestMapping(value = "/rss/comments", method = RequestMethod.GET, produces = "text/xml; charset=utf-8") - public ModelAndView getLastReplies(@RequestParam(value = "hours", required = false, defaultValue = "0") Integer hours) { - ModelAndView modelAndView = new ModelAndView(); - modelAndView.setViewName("repliesView"); - modelAndView.addObject("messages", messagesService.getLastReplies(hours)); - return modelAndView; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/rss/MessagesView.java b/juick-server/src/main/java/com/juick/server/api/rss/MessagesView.java deleted file mode 100644 index c0ae4a97..00000000 --- a/juick-server/src/main/java/com/juick/server/api/rss/MessagesView.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.api.rss; - -import com.juick.Message; -import com.juick.User; -import com.juick.server.api.rss.extension.JuickModule; -import com.juick.server.api.rss.extension.JuickModuleImpl; -import com.juick.util.MessageUtils; -import com.rometools.modules.atom.modules.AtomLinkModule; -import com.rometools.modules.atom.modules.AtomLinkModuleImpl; -import com.rometools.modules.mediarss.MediaEntryModuleImpl; -import com.rometools.modules.mediarss.MediaModule; -import com.rometools.modules.mediarss.MediaModuleImpl; -import com.rometools.modules.mediarss.types.MediaContent; -import com.rometools.modules.mediarss.types.Metadata; -import com.rometools.modules.mediarss.types.Thumbnail; -import com.rometools.modules.mediarss.types.UrlReference; -import com.rometools.rome.feed.atom.Link; -import com.rometools.rome.feed.rss.*; -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.Collections; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * Created by vitalyster on 13.12.2016. - */ -public class MessagesView extends AbstractRssFeedView { - - private static final Logger logger = LoggerFactory.getLogger(MessagesView.class); - - @PostConstruct - public void init() { - setContentType("application/rss+xml;charset=UTF-8"); - } - - @SuppressWarnings("unchecked") - @Override - protected List buildFeedItems(@Nonnull Map model, - @Nonnull HttpServletRequest request, - @Nonnull HttpServletResponse response) { - List msgs = (List)model.get("messages"); - return msgs.stream().map(this::createRssItem).collect(Collectors.toList()); - } - - @Override - protected void buildFeedMetadata(Map model, Channel feed, HttpServletRequest request) { - Object userObj = model.get("user"); - if (userObj != null) { - User user = (User) userObj; - feed.setDescription(String.format("The latest messages by @%s at Juick", user.getName())); - String title = String.format("%s - Juick", user.getName()); - feed.setTitle(title); - String link = String.format("http://juick.com/%s/", user.getName()); - feed.setLink(link); - Image rssImage = new Image(); - rssImage.setUrl(String.format("http://juick.com/a/%d.png", user.getUid())); - rssImage.setTitle(title); - rssImage.setLink(link); - feed.setImage(rssImage); - String href = String.format("http://rss.juick.com/%s/blog", user.getName()); - AtomLinkModule atomLinkModule = new AtomLinkModuleImpl(); - Link atomLink = new Link(); - atomLink.setHref(href); - atomLink.setType("application/rss+xml"); - atomLink.setRel("self"); - atomLinkModule.setLinks(Collections.singletonList(atomLink)); - - feed.getModules().add(atomLinkModule); - } else { - feed.setDescription("The latest messages at Juick"); - feed.setLink("http://juick.com/"); - feed.setTitle("Juick"); - } - - MediaModule mediaModule = new MediaModuleImpl(); - feed.getModules().add(mediaModule); - - - } - - private Item createRssItem(Message msg) { - Item item = new Item(); - String messageUrl = String.format("http://juick.com/%s/%d", msg.getUser().getName(), msg.getMid()); - String messageTitle = String.format("@%s: %s", msg.getUser().getName(), MessageUtils.getTagsString(msg)); - boolean isCode = msg.getTags().stream().anyMatch(t -> t.getName().equals("code")); - String messageDescription = isCode ? MessageUtils.formatMessageCode(StringUtils.defaultString(msg.getText())) - : MessageUtils.formatMessage(StringUtils.defaultString(msg.getText())); - item.setLink(messageUrl); - //item.setGuid(messageUrl); - item.setTitle(messageTitle); - Description description = new Description(); - description.setType("text/html"); - description.setValue(messageDescription); - item.setDescription(description); - item.setPubDate(Date.from(msg.getTimestamp())); - item.setComments(messageUrl); - msg.getTags().stream().map(t -> { - Category category = new Category(); - category.setValue(t.getName()); - return category; - }).forEach(c -> item.getCategories().add(c)); - JuickModule juickModule = new JuickModuleImpl(); - juickModule.setUid(msg.getUser().getUid()); - item.getModules().add(juickModule); - if (StringUtils.isNotEmpty(msg.getAttachmentType())) { - String type = msg.getAttachmentType().equals("jpg") ? "image/jpeg" : "image/png"; - MediaEntryModuleImpl module = new MediaEntryModuleImpl(); - try { - UrlReference reference = new UrlReference(MessageUtils.attachmentUrl(msg)); - MediaContent mediaContent = new MediaContent(reference); - mediaContent.setType(type); - Metadata metadata = new Metadata(); - metadata.setThumbnail(new Thumbnail[]{new Thumbnail(new URI(msg.getPhoto().getThumbnail()))}); - module.setMetadata(metadata); - module.setMediaContents(new MediaContent[]{mediaContent}); - item.getModules().add(module); - } catch (URISyntaxException e) { - logger.error("Invalid URI", e); - } - - } - return item; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/rss/RepliesView.java b/juick-server/src/main/java/com/juick/server/api/rss/RepliesView.java deleted file mode 100644 index a0ab801e..00000000 --- a/juick-server/src/main/java/com/juick/server/api/rss/RepliesView.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.api.rss; - -import com.juick.model.ResponseReply; -import com.juick.util.MessageUtils; -import com.rometools.modules.mediarss.MediaEntryModuleImpl; -import com.rometools.modules.mediarss.MediaModule; -import com.rometools.modules.mediarss.MediaModuleImpl; -import com.rometools.modules.mediarss.types.MediaContent; -import com.rometools.modules.mediarss.types.Metadata; -import com.rometools.modules.mediarss.types.Thumbnail; -import com.rometools.modules.mediarss.types.UrlReference; -import com.rometools.rome.feed.rss.Channel; -import com.rometools.rome.feed.rss.Description; -import com.rometools.rome.feed.rss.Item; -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; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * Created by vitalyster on 13.12.2016. - */ -public class RepliesView extends AbstractRssFeedView { - - private static final Logger logger = LoggerFactory.getLogger(RepliesView.class); - - @PostConstruct - public void init() { - setContentType("application/rss+xml;charset=UTF-8"); - } - - @SuppressWarnings("unchecked") - @Override - protected @Nonnull List buildFeedItems(@Nonnull Map model, - @Nonnull HttpServletRequest request, - @Nonnull HttpServletResponse response) { - List msgs = (List)model.get("messages"); - return msgs.stream().map(this::createRssItem).collect(Collectors.toList()); - } - - @Override - protected void buildFeedMetadata(Map model, Channel feed, HttpServletRequest request) { - feed.setTitle("Juick"); - feed.setLink("http://juick.com/"); - feed.setDescription("The latest comments at Juick"); - MediaModule mediaModule = new MediaModuleImpl(); - feed.getModules().add(mediaModule); - } - - private Item createRssItem(ResponseReply msg) { - Item item = new Item(); - String messageUrl = String.format("http://juick.com/m/%d#%d", msg.getMid(), msg.getRid()); - String messageTitle = String.format("@%s:", msg.getUname()); - String messageDescription = msg.isHtml() ? msg.getDescription() : MessageUtils.formatMessage(msg.getDescription()); - item.setLink(messageUrl); - //item.setGuid(messageUrl); - item.setTitle(messageTitle); - Description description = new Description(); - description.setType("text/html"); - description.setValue(messageDescription); - item.setDescription(description); - item.setPubDate(msg.getPubDate()); - if (StringUtils.isNotEmpty(msg.getAttachmentType())) { - String type = msg.getAttachmentType().equals("jpg") ? "image/jpeg" : "image/png"; - MediaEntryModuleImpl module = new MediaEntryModuleImpl(); - try { - UrlReference reference = new UrlReference( - String.format("http://i.juick.com/photos-1024/%d-%d.%s", msg.getMid(), msg.getRid(), type)); - MediaContent mediaContent = new MediaContent(reference); - mediaContent.setType(type); - Metadata metadata = new Metadata(); - metadata.setThumbnail(new Thumbnail[]{new Thumbnail( - new URI(String.format("http://i.juick.com/ps/%d-%d.%s", msg.getMid(), msg.getRid(), type)))}); - module.setMetadata(metadata); - module.setMediaContents(new MediaContent[]{mediaContent}); - item.getModules().add(module); - } catch (URISyntaxException e) { - logger.error("Invalid URI", e); - } - - } - return item; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModule.java b/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModule.java deleted file mode 100644 index a4198518..00000000 --- a/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModule.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.api.rss.extension; - -import com.rometools.rome.feed.module.Module; - -/** - * Created by vitalyster on 13.12.2016. - */ -public interface JuickModule extends Module { - - String URI = "http://juick.com/"; - - Integer getUid(); - - void setUid(Integer uid); - -} diff --git a/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleGenerator.java b/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleGenerator.java deleted file mode 100644 index 90dec35f..00000000 --- a/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleGenerator.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.api.rss.extension; - -import com.rometools.rome.feed.module.Module; -import com.rometools.rome.io.ModuleGenerator; -import org.jdom2.Element; -import org.jdom2.Namespace; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -/** - * Created by vt on 13/12/2016. - */ -public class JuickModuleGenerator implements ModuleGenerator { - - private static final Namespace JUICK_NS = Namespace.getNamespace("juick", JuickModule.URI); - - @Override - public String getNamespaceUri() { - return JuickModule.URI; - } - - private static final Set NAMESPACES; - - static { - Set nss = new HashSet<>(); - nss.add(JUICK_NS); - NAMESPACES = Collections.unmodifiableSet(nss); - } - - @Override - public Set getNamespaces() { - return NAMESPACES; - } - - @Override - public void generate(Module module, Element element) { - // this is not necessary, it is done to avoid the namespace definition in every item. - Element root = element; - while (root.getParent()!=null && root.getParent() instanceof Element) { - root = element.getParentElement(); - } - root.addNamespaceDeclaration(JUICK_NS); - - JuickModule juickModule = (JuickModule) module; - if (juickModule.getUid() > 0) { - Element user = new Element("user", JUICK_NS); - user.setAttribute("uid", String.valueOf(juickModule.getUid()), JUICK_NS); - element.addContent(user); - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleImpl.java b/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleImpl.java deleted file mode 100644 index dbdd8c85..00000000 --- a/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleImpl.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.api.rss.extension; - -import com.rometools.rome.feed.CopyFrom; -import com.rometools.rome.feed.module.ModuleImpl; - -/** - * Created by vitalyster on 13.12.2016. - */ -public class JuickModuleImpl extends ModuleImpl implements JuickModule { - - private Integer uid; - - public JuickModuleImpl() { - super(JuickModule.class, JuickModule.URI); - } - - @Override - public Integer getUid() { - return uid; - } - - @Override - public void setUid(Integer uid) { - this.uid = uid; - } - - @Override - public Class getInterface() { - return JuickModule.class; - } - - @Override - public void copyFrom(CopyFrom obj) { - JuickModule juickModule = (JuickModule) obj; - setUid(juickModule.getUid()); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleParser.java b/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleParser.java deleted file mode 100644 index a3d0e175..00000000 --- a/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleParser.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.api.rss.extension; - -import com.rometools.rome.feed.module.Module; -import com.rometools.rome.io.ModuleParser; -import org.apache.commons.lang3.math.NumberUtils; -import org.jdom2.Element; - -import java.util.Locale; - -/** - * Created by vitalyster on 13.12.2016. - */ -public class JuickModuleParser implements ModuleParser { - @Override - public String getNamespaceUri() { - return JuickModule.URI; - } - - @Override - public Module parse(Element element, Locale locale) { - JuickModuleImpl juickModule = new JuickModuleImpl(); - juickModule.setUid(NumberUtils.toInt(element.getAttributeValue("uid", JuickModule.URI), 0)); - return juickModule; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/webfinger/Resource.java b/juick-server/src/main/java/com/juick/server/api/webfinger/Resource.java deleted file mode 100644 index 71a0ca31..00000000 --- a/juick-server/src/main/java/com/juick/server/api/webfinger/Resource.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.juick.server.api.webfinger; - -import com.juick.User; -import com.juick.server.api.webfinger.model.Account; -import com.juick.server.api.webfinger.model.Link; -import com.juick.server.util.HttpNotFoundException; -import com.juick.service.UserService; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.util.UriComponentsBuilder; -import rocks.xmpp.addr.Jid; - -import javax.inject.Inject; -import java.util.Collections; - -import static com.juick.server.api.activity.model.Context.ACTIVITY_MEDIA_TYPE; - -@RestController -public class Resource { - @Inject - private UserService userService; - @Value("${web_domain:localhost}") - private String domain; - @Value("${ap_base_uri:http://localhost:8080/}") - private String baseUri; - - @GetMapping("/.well-known/webfinger") - public Account getWebResource(@RequestParam String resource) { - if (resource.startsWith("acct:")) { - Jid account = Jid.of(resource.substring(5)); - if (account.getDomain().equals(domain)) { - User user = userService.getUserByName(account.getLocal()); - if (!user.isAnonymous()) { - UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(baseUri); - builder.path(String.format("/u/%s", user.getName())); - Link blog = new Link(); - blog.setRel("self"); - blog.setType(ACTIVITY_MEDIA_TYPE); - blog.setHref(builder.toUriString()); - Account result = new Account(); - result.setSubject(resource); - result.setLinks(Collections.singletonList(blog)); - return result; - } - } - } - throw new HttpNotFoundException(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/webfinger/model/Account.java b/juick-server/src/main/java/com/juick/server/api/webfinger/model/Account.java deleted file mode 100644 index 892fa303..00000000 --- a/juick-server/src/main/java/com/juick/server/api/webfinger/model/Account.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.juick.server.api.webfinger.model; - -import java.util.List; - -public class Account { - private String subject; - private List links; - - public String getSubject() { - return subject; - } - - public void setSubject(String subject) { - this.subject = subject; - } - - public List getLinks() { - return links; - } - - public void setLinks(List links) { - this.links = links; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/webfinger/model/Link.java b/juick-server/src/main/java/com/juick/server/api/webfinger/model/Link.java deleted file mode 100644 index 48e7ab67..00000000 --- a/juick-server/src/main/java/com/juick/server/api/webfinger/model/Link.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.juick.server.api.webfinger.model; - -public class Link { - private String rel; - private String type; - private String href; - - public String getRel() { - return rel; - } - - public void setRel(String rel) { - this.rel = rel; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getHref() { - return href; - } - - public void setHref(String href) { - this.href = href; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/webhooks/TelegramWebhook.java b/juick-server/src/main/java/com/juick/server/api/webhooks/TelegramWebhook.java deleted file mode 100644 index 7a5cebda..00000000 --- a/juick-server/src/main/java/com/juick/server/api/webhooks/TelegramWebhook.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.api.webhooks; - -import com.juick.server.TelegramBotManager; -import com.pengrad.telegrambot.BotUtils; -import com.pengrad.telegrambot.model.Update; -import org.apache.commons.io.IOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; -import springfox.documentation.annotations.ApiIgnore; - -import javax.inject.Inject; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; - -/** - * Created by vt on 24/11/2016. - */ -@ApiIgnore -@RestController -@ConditionalOnProperty({"telegram_token"}) -public class TelegramWebhook { - private static final Logger logger = LoggerFactory.getLogger(TelegramWebhook.class); - @Inject - private TelegramBotManager telegramBotManager; - - @RequestMapping(value = "/api/tlgmbtwbhk", method = RequestMethod.POST) - @ResponseStatus(value = HttpStatus.OK) - public void processUpdate(InputStream body) throws Exception { - String data = IOUtils.toString(body, StandardCharsets.UTF_8); - logger.info("Telegram update: {}", data); - Update update = BotUtils.parseUpdate(data); - telegramBotManager.processUpdate(update); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/Info.java b/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/Info.java deleted file mode 100644 index c12df55f..00000000 --- a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/Info.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.juick.server.api.xnodeinfo2; - -import com.juick.server.api.xnodeinfo2.model.*; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.inject.Inject; -import java.time.Instant; -import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; -import java.util.Arrays; - -@RestController -public class Info { - @Value("${ap_base_uri:http://localhost:8080/}") - private String baseUri; - @Inject - private JdbcTemplate jdbcTemplate; - - @GetMapping("/.well-known/x-nodeinfo2") - public NodeInfo showNodeInfo() { - NodeInfo nodeInfo = new NodeInfo(); - Server server = new Server(); - server.setBaseUrl(baseUri); - server.setName("Juick"); - server.setSoftware("Juick"); - server.setVersion("2.x"); - nodeInfo.setServer(server); - nodeInfo.setProtocols(Arrays.asList("xmpp", "activitypub", "smtp")); - ServiceInfo serviceInfo = new ServiceInfo(); - serviceInfo.setInbound(Arrays.asList("jabber", "mastodon", "email", "telegram")); - serviceInfo.setOutbound(Arrays.asList("jabber", "mastodon", "telegram", "twitter", "email", "rss")); - nodeInfo.setServices(serviceInfo); - UserStats userStats = new UserStats(); - userStats.setTotal(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM users WHERE banned=0", Integer.class)); - userStats.setActiveMonth(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM users WHERE banned=0 AND last_seen > ?", - Integer.class, ZonedDateTime.now().minus(1, ChronoUnit.MONTHS).toInstant())); - userStats.setActiveHalfyear(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM users WHERE banned=0 AND last_seen > ?", - Integer.class, ZonedDateTime.now().minus(6, ChronoUnit.MONTHS).toInstant())); - Usage usage = new Usage(); - usage.setUsers(userStats); - usage.setLocalPosts(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM messages", - Integer.class)); - usage.setLocalComments(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM replies", - Integer.class)); - nodeInfo.setUsage(usage); - return nodeInfo; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/NodeInfo.java b/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/NodeInfo.java deleted file mode 100644 index 06fe354f..00000000 --- a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/NodeInfo.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.juick.server.api.xnodeinfo2.model; - -import java.util.List; - -public class NodeInfo { - - private Server server; - - private List protocols; - - private ServiceInfo services; - - public String getVersion() { - return "1.0"; - } - - public Server getServer() { - return server; - } - - public void setServer(Server server) { - this.server = server; - } - - public List getProtocols() { - return protocols; - } - - public void setProtocols(List protocols) { - this.protocols = protocols; - } - - public ServiceInfo getServices() { - return services; - } - - public void setServices(ServiceInfo services) { - this.services = services; - } - - public boolean getOpenRegistrations() { - return true; - } - - private Usage usage; - - public Usage getUsage() { - return usage; - } - - public void setUsage(Usage usage) { - this.usage = usage; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/Server.java b/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/Server.java deleted file mode 100644 index a772d268..00000000 --- a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/Server.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.juick.server.api.xnodeinfo2.model; - -public class Server { - private String baseUrl; - private String name; - private String software; - private String version; - - public String getBaseUrl() { - return baseUrl; - } - - public void setBaseUrl(String baseUrl) { - this.baseUrl = baseUrl; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getSoftware() { - return software; - } - - public void setSoftware(String software) { - this.software = software; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/ServiceInfo.java b/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/ServiceInfo.java deleted file mode 100644 index 5b6d2baa..00000000 --- a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/ServiceInfo.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.juick.server.api.xnodeinfo2.model; - -import java.util.List; - -public class ServiceInfo { - private List inbound; - private List outbound; - - public List getInbound() { - return inbound; - } - - public void setInbound(List inbound) { - this.inbound = inbound; - } - - public List getOutbound() { - return outbound; - } - - public void setOutbound(List outbound) { - this.outbound = outbound; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/Usage.java b/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/Usage.java deleted file mode 100644 index e04ea48b..00000000 --- a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/Usage.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.juick.server.api.xnodeinfo2.model; - -public class Usage { - private UserStats users; - private int localPosts; - private int localComments; - - public UserStats getUsers() { - return users; - } - - public void setUsers(UserStats users) { - this.users = users; - } - - public int getLocalPosts() { - return localPosts; - } - - public void setLocalPosts(int localPosts) { - this.localPosts = localPosts; - } - - public int getLocalComments() { - return localComments; - } - - public void setLocalComments(int localComments) { - this.localComments = localComments; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/UserStats.java b/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/UserStats.java deleted file mode 100644 index 515661e3..00000000 --- a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/UserStats.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.juick.server.api.xnodeinfo2.model; - -public class UserStats { - private int total; - private int activeHalfyear; - private int activeMonth; - - public int getTotal() { - return total; - } - - public void setTotal(int total) { - this.total = total; - } - - public int getActiveHalfyear() { - return activeHalfyear; - } - - public void setActiveHalfyear(int activeHalfyear) { - this.activeHalfyear = activeHalfyear; - } - - public int getActiveMonth() { - return activeMonth; - } - - public void setActiveMonth(int activeMonth) { - this.activeMonth = activeMonth; - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/ActivityPubClientConfig.java b/juick-server/src/main/java/com/juick/server/configuration/ActivityPubClientConfig.java deleted file mode 100644 index 9bc1b656..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/ActivityPubClientConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.juick.server.configuration; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.client.SimpleClientHttpRequestFactory; -import org.springframework.web.client.RestTemplate; - -import javax.inject.Inject; - -@Configuration -public class ActivityPubClientConfig { - @Inject - ActivityPubClientErrorHandler activityPubClientErrorHandler; - @Bean - public RestTemplate apClient() { - SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); - requestFactory.setOutputStreaming(false); - RestTemplate restTemplate = new RestTemplate(requestFactory); - restTemplate.setErrorHandler(activityPubClientErrorHandler); - return restTemplate; - } -} \ No newline at end of file diff --git a/juick-server/src/main/java/com/juick/server/configuration/ActivityPubClientErrorHandler.java b/juick-server/src/main/java/com/juick/server/configuration/ActivityPubClientErrorHandler.java deleted file mode 100644 index e535b3e5..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/ActivityPubClientErrorHandler.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.juick.server.configuration; - -import com.juick.service.activities.DeleteUserEvent; -import org.apache.commons.io.IOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -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; -import java.nio.charset.StandardCharsets; - -@Component -public class ActivityPubClientErrorHandler extends DefaultResponseErrorHandler { - private static final Logger logger = LoggerFactory.getLogger(ActivityPubClientErrorHandler.class); - - @Inject - private ApplicationEventPublisher applicationEventPublisher; - @Override - public void handleError(URI contextUri, HttpMethod method, @Nonnull 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)) { - logger.warn("Server report {} is gone, deleting", contextUri.toASCIIString()); - applicationEventPublisher.publishEvent(new DeleteUserEvent(this, contextUri.toASCIIString())); - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java b/juick-server/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java deleted file mode 100644 index 5a5d2c7b..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.configuration; - -import com.juick.server.WebsocketManager; -import com.juick.server.api.rss.MessagesView; -import com.juick.server.api.rss.RepliesView; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.Ordered; -import org.springframework.scheduling.annotation.EnableAsync; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.springframework.web.servlet.view.BeanNameViewResolver; -import org.springframework.web.servlet.view.feed.AbstractRssFeedView; -import org.springframework.web.socket.config.annotation.EnableWebSocket; -import org.springframework.web.socket.config.annotation.ServletWebSocketHandlerRegistry; -import org.springframework.web.socket.config.annotation.WebSocketConfigurer; -import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; -import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean; - -import javax.annotation.Nonnull; -import javax.inject.Inject; - -/** - * Created by aalexeev on 11/12/16. - */ -@Configuration -@EnableAsync(proxyTargetClass = true) -@EnableScheduling -@EnableWebSocket -public class ApiAppConfiguration implements WebMvcConfigurer, WebSocketConfigurer { - @Inject - private WebsocketManager websocketManager; - - @Override - public void registerWebSocketHandlers(@Nonnull WebSocketHandlerRegistry registry) { - ((ServletWebSocketHandlerRegistry) registry).setOrder(Ordered.HIGHEST_PRECEDENCE); - registry.addHandler(websocketManager, "/ws/**").setAllowedOrigins("*"); - } - - @Bean - public ServletServerContainerFactoryBean createWebSocketContainer() { - ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); - container.setMaxTextMessageBufferSize(8192); - container.setMaxBinaryMessageBufferSize(8192); - return container; - } - @Bean - public BeanNameViewResolver beanNameViewResolver() { - return new BeanNameViewResolver(); - } - @Bean - AbstractRssFeedView messagesView() { - return new MessagesView(); - } - @Bean - AbstractRssFeedView repliesView() { - return new RepliesView(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/BaseWebConfiguration.java b/juick-server/src/main/java/com/juick/server/configuration/BaseWebConfiguration.java deleted file mode 100644 index 23a35384..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/BaseWebConfiguration.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.configuration; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.annotation.SchedulingConfigurer; -import org.springframework.scheduling.config.ScheduledTaskRegistrar; -import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter; - -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * Created by vitalyster on 28.06.2016. - */ -@Configuration -public class BaseWebConfiguration implements WebMvcConfigurer, SchedulingConfigurer { - - - @Override - public void configurePathMatch(PathMatchConfigurer configurer) { - configurer.setUseSuffixPatternMatch(false); - } - - @Bean - public ResourceUrlEncodingFilter resourceUrlEncodingFilter() { - return new ResourceUrlEncodingFilter(); - } - - @Override - public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { - taskRegistrar.setScheduler(taskExecutor()); - } - - @Bean(destroyMethod="shutdown") - public Executor taskExecutor() { - return Executors.newScheduledThreadPool(100); - } - - @Bean - public ExecutorService executorService() { - return Executors.newCachedThreadPool(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/SapeConfiguration.java b/juick-server/src/main/java/com/juick/server/configuration/SapeConfiguration.java deleted file mode 100644 index 9727fbb1..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/SapeConfiguration.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.configuration; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import ru.sape.Sape; - -/** - * Created by vitalyster on 29.03.2017. - */ -@Configuration -@ConditionalOnProperty("sape_user") -public class SapeConfiguration { - @Value("${sape_user:}") - private String token; - - @Bean - public Sape sape() { - return new Sape(token, "juick.com", 2000, 3600); - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/SecurityConfig.java b/juick-server/src/main/java/com/juick/server/configuration/SecurityConfig.java deleted file mode 100644 index f02083d5..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/SecurityConfig.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.configuration; - -import com.juick.service.UserService; -import com.juick.service.security.HashParamAuthenticationFilter; -import com.juick.service.security.JuickUserDetailsService; -import com.juick.service.security.deprecated.RequestParamHashRememberMeServices; -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.http.HttpStatus; -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.HttpStatusEntryPoint; -import org.springframework.security.web.authentication.RememberMeServices; -import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices; -import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; -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; - -/** - * Created by aalexeev on 11/21/16. - */ -@EnableWebSecurity -public class SecurityConfig { - @Resource - private UserService userService; - @Value("${auth_remember_me_key:secret}") - private String rememberMeKey; - @Value("${web_domain:localhost}") - private String webDomain; - - private static final String COOKIE_NAME = "juick-remember-me"; - - @Bean - public UserDetailsService userDetailsService() { - return new JuickUserDetailsService(userService); - } - - @Configuration - @Order(1) - public static class ApiConfig extends WebSecurityConfigurerAdapter { - @Value("${auth_remember_me_key:secret}") - private String rememberMeKey; - @Value("${web_domain:localhost}") - private String webDomain; - @Resource - private UserService userService; - ApiConfig() { - super(true); - } - @Bean - RememberMeServices apiTokenServices(){ - return new RequestParamHashRememberMeServices(rememberMeKey, userService); - } - @Bean - public HashParamAuthenticationFilter apiAuthenticationFilter() { - return new HashParamAuthenticationFilter(userService, apiTokenServices()); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - http.antMatcher("/api/**") - .addFilterBefore(apiAuthenticationFilter(), BasicAuthenticationFilter.class) - .authorizeRequests() - .antMatchers(HttpMethod.OPTIONS).permitAll() - .antMatchers("/api/", "/api/messages", "/api/messages/discussions", "/api/users", "/api/thread", "/api/tags", "/api/tlgmbtwbhk", "/api/fbwbhk", - "/api/skypebotendpoint", "/api/_fblogin", "/api/_vklogin", "/api/_tglogin", "/api/inbox", "/api/u/**", "/.well-known/webfinger", "/.well-known/x-nodeinfo2", "/rss/**", "/api/events").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() { - return new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED); - } - - @Bean - public CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration configuration = new CorsConfiguration(); - - configuration.setAllowedOrigins(Collections.singletonList("*")); - configuration.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "OPTIONS", "DELETE")); - configuration.setAllowedHeaders(Collections.singletonList("*")); - - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/api/**", configuration); - - return source; - } - @Override - public void configure(WebSecurity web) { - web.debug(false); - web.ignoring().antMatchers("/api/v2/api-docs", "/api/configuration/ui", "/api/swagger-resources/**", - "/api/configuration/**", "/swagger-ui.html", "/webjars/**", "/h2-console/**"); - } - } - - @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() - .anyRequest().permitAll() - .and() - .anonymous().principal(JuickUser.ANONYMOUS_USER).authorities(JuickUser.ANONYMOUS_AUTHORITY) - .and() - .sessionManagement().invalidSessionUrl("/") - .and() - .logout() - .invalidateHttpSession(true) - .logoutUrl("/logout") - .logoutSuccessUrl("/login?logout") - .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(); - } - @Override - public void configure(WebSecurity web) { - web.debug(false); - web.ignoring().antMatchers("/style*.css", "/scripts*.js", "/h2-console/**", "/.well-known/**", "/ws/**"); - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/StorageConfiguration.java b/juick-server/src/main/java/com/juick/server/configuration/StorageConfiguration.java deleted file mode 100644 index 4101f37d..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/StorageConfiguration.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.juick.server.configuration; - -import com.juick.service.ImagesService; -import com.juick.service.ImagesServiceImpl; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class StorageConfiguration { - - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String imgDir; - @Bean - public ImagesService imagesService() { - return new ImagesServiceImpl(imgDir, tmpDir); - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/TelegramConfig.java b/juick-server/src/main/java/com/juick/server/configuration/TelegramConfig.java deleted file mode 100644 index ebd1fd15..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/TelegramConfig.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.juick.server.configuration; - -import com.juick.server.TelegramBotManager; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -@ConditionalOnProperty(name = "telegram_token") -public class TelegramConfig { - @Bean - public TelegramBotManager telegramBotManager() { - return new TelegramBotManager(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/WwwAppConfiguration.java b/juick-server/src/main/java/com/juick/server/configuration/WwwAppConfiguration.java deleted file mode 100644 index 72889f96..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/WwwAppConfiguration.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.configuration; - -import com.juick.server.www.HelpService; -import com.juick.service.TagService; -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.PebbleViewResolver; -import com.mitchellbosecke.pebble.spring.extension.SpringExtension; -import org.apache.commons.codec.CharEncoding; -import org.commonmark.ext.autolink.AutolinkExtension; -import org.commonmark.node.Link; -import org.commonmark.parser.Parser; -import org.commonmark.renderer.html.HtmlRenderer; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.cache.caffeine.CaffeineCacheManager; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.ViewResolver; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -import javax.inject.Inject; -import java.util.Collections; - -/** - * Created by aalexeev on 11/22/16. - */ -@Configuration -@EnableCaching -public class WwwAppConfiguration implements WebMvcConfigurer { - @Inject - private UserService userService; - @Inject - private TagService tagService; - @Bean - public CaffeineCacheManager cacheManager() { - return new CaffeineCacheManager("help"); - } - - @Bean - public HelpService helpService() { - return new HelpService("help"); - } - - @Bean - public Parser cmParser() { - return Parser.builder().extensions(Collections.singletonList(AutolinkExtension.create())).build(); - } - @Bean - public HtmlRenderer helpRenderer() { - return HtmlRenderer.builder() - .attributeProviderFactory(context -> (node, tagName, attributes) -> { - if (node instanceof Link) { - Link link = (Link) node; - if (link.getDestination().startsWith("/")) { - String destination = "/" + helpService().getHelpPath() + link.getDestination(); - link.setDestination(destination); - attributes.put("href", destination); - } - } - }) - .build(); - } - @Bean - public Loader templateLoader() { - return new ClasspathLoader(); - } - - @Bean - public SpringExtension springExtension() { - return new SpringExtension(); - } - - @Bean - public PebbleEngine pebbleEngine() { - boolean devToolsArePresent = false; - try { - Class.forName("org.springframework.boot.devtools.livereload.Connection"); - devToolsArePresent = true; - } catch (ClassNotFoundException e) { - // release mode - } - return new PebbleEngine.Builder() - .loader(this.templateLoader()) - .cacheActive(!devToolsArePresent) - .extension(springExtension()) - .extension(new FormatterExtension()) - .strictVariables(true) - .build(); - } - - @Bean - public ViewResolver viewResolver() { - PebbleViewResolver viewResolver = new PebbleViewResolver(); - viewResolver.setPrefix("templates"); - viewResolver.setSuffix(".html"); - viewResolver.setPebbleEngine(pebbleEngine()); - viewResolver.setCharacterEncoding(CharEncoding.UTF_8); - return viewResolver; - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/XMPPConfig.java b/juick-server/src/main/java/com/juick/server/configuration/XMPPConfig.java deleted file mode 100644 index 2feef286..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/XMPPConfig.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.juick.server.configuration; - -import com.juick.server.XMPPConnection; -import com.juick.server.XMPPServer; -import com.juick.server.xmpp.JidConverter; -import com.juick.server.xmpp.iq.MessageQuery; -import com.juick.server.xmpp.router.XMPPRouter; -import com.juick.server.xmpp.s2s.BasicXmppSession; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; -import org.springframework.core.convert.ConversionService; -import org.springframework.format.support.DefaultFormattingConversionService; -import rocks.xmpp.core.session.Extension; -import rocks.xmpp.core.session.XmppSessionConfiguration; -import rocks.xmpp.core.session.debug.LogbackDebugger; - -import java.time.Duration; - -@Configuration -@ConditionalOnProperty("xmppbot_jid") -public class XMPPConfig { - @Value("${hostname:localhost}") - private String hostname; - @Bean - public BasicXmppSession session() { - XmppSessionConfiguration configuration = XmppSessionConfiguration.builder() - .extensions(Extension.of(com.juick.Message.class), Extension.of(MessageQuery.class)) - .debugger(LogbackDebugger.class) - .defaultResponseTimeout(Duration.ofMillis(120000)) - .build(); - return BasicXmppSession.create(hostname, configuration); - } - @Bean - public static ConversionService conversionService() { - DefaultFormattingConversionService cs = new DefaultFormattingConversionService(); - cs.addConverter(new JidConverter()); - return cs; - } - @Bean - public XMPPServer xmppServer() { - return new XMPPServer(); - } - @Bean - public XMPPRouter xmppRouter() { - return new XMPPRouter(); - } - @Bean - @DependsOn("xmppRouter") - public XMPPConnection xmppConnection() { - return new XMPPConnection(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/helpers/annotation/UserCommand.java b/juick-server/src/main/java/com/juick/server/helpers/annotation/UserCommand.java deleted file mode 100644 index 4f07001c..00000000 --- a/juick-server/src/main/java/com/juick/server/helpers/annotation/UserCommand.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.helpers.annotation; - -import org.apache.commons.lang3.StringUtils; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Created by oxpa on 22.03.16. - */ -@Target({ElementType.TYPE, ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface UserCommand { - /** - * - * @return a command pattern - */ - String pattern() default StringUtils.EMPTY; - - /** - * - * @return pattern flags - */ - int patternFlags() default 0; - - /** - * - * @return a string used in HELP command output. Basically, only 1 string - */ - String help() default StringUtils.EMPTY; -} diff --git a/juick-server/src/main/java/com/juick/server/util/HttpBadRequestException.java b/juick-server/src/main/java/com/juick/server/util/HttpBadRequestException.java deleted file mode 100644 index 242f2b09..00000000 --- a/juick-server/src/main/java/com/juick/server/util/HttpBadRequestException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.util; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -/** - * Created by vt on 24/11/2016. - */ -@ResponseStatus(value = HttpStatus.BAD_REQUEST) -public class HttpBadRequestException extends RuntimeException { - public HttpBadRequestException() { - super("the request was bad", null, false, false); - } -} diff --git a/juick-server/src/main/java/com/juick/server/util/HttpForbiddenException.java b/juick-server/src/main/java/com/juick/server/util/HttpForbiddenException.java deleted file mode 100644 index 3251ca38..00000000 --- a/juick-server/src/main/java/com/juick/server/util/HttpForbiddenException.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.util; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -/** - * Created by vt on 24/11/2016. - */ -@ResponseStatus(value = HttpStatus.FORBIDDEN) -public class HttpForbiddenException extends RuntimeException { - public HttpForbiddenException() { - super(StringUtils.EMPTY, null, false, false); - } - -} diff --git a/juick-server/src/main/java/com/juick/server/util/HttpNotFoundException.java b/juick-server/src/main/java/com/juick/server/util/HttpNotFoundException.java deleted file mode 100644 index f66ece8b..00000000 --- a/juick-server/src/main/java/com/juick/server/util/HttpNotFoundException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.util; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -/** - * Created by vt on 24/11/2016. - */ -@ResponseStatus(value = HttpStatus.NOT_FOUND) -public class HttpNotFoundException extends RuntimeException { - public HttpNotFoundException() { - super(StringUtils.EMPTY, null, false, false); - } -} diff --git a/juick-server/src/main/java/com/juick/server/util/HttpUtils.java b/juick-server/src/main/java/com/juick/server/util/HttpUtils.java deleted file mode 100644 index b70eb3ad..00000000 --- a/juick-server/src/main/java/com/juick/server/util/HttpUtils.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.server.util; - -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.MediaType; -import org.springframework.web.multipart.MultipartFile; - -import javax.imageio.ImageIO; -import javax.imageio.ImageReader; -import javax.imageio.stream.ImageInputStream; -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URL; -import java.net.URLConnection; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.Iterator; -import java.util.UUID; - -/** - * - * @author Ugnich Anton - */ -public class HttpUtils { - private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class); - - public static URI receiveMultiPartFile(MultipartFile attach, String tmpDir) throws IOException { - if (attach != null && !attach.isEmpty()) { - ImageInputStream iis = ImageIO.createImageInputStream(attach.getInputStream()); - Iterator readers = ImageIO.getImageReaders(iis); - - String format = StringUtils.EMPTY; - while (readers.hasNext()) { - ImageReader read = readers.next(); - format = read.getFormatName(); - } - String attachmentType = attachmentTypeFromFormat(format); - if (attachmentType.equals("jpg") || attachmentType.equals("png")) { - String attachmentFName = DigestUtils.md5Hex(UUID.randomUUID().toString()) + "." + attachmentType; - try { - Files.write(Paths.get(tmpDir, attachmentFName), - attach.getBytes()); - return URI.create(String.format("juick://%s", attachmentFName)); - } catch (IOException e) { - logger.warn("file receive error", e); - } - } - logger.warn("file type is unknown: {}", attach.getOriginalFilename()); - } - return URI.create(StringUtils.EMPTY); - } - - private static String attachmentTypeFromFormat(String format) throws IOException { - if (format != null && format.equals("JPEG")) { - return "jpg"; - } else if (format != null && format.equals("png")) { - return "png"; - } else { - throw new IOException("Wrong file type: " + format); - } - } - - public static String mediaType(String attachmentType) { - return attachmentType.equals("jpg") ? MediaType.IMAGE_JPEG_VALUE : MediaType.IMAGE_PNG_VALUE; - } - - public static URI downloadImage(URL url, String tmpDir) throws IOException { - ImageInputStream iis = ImageIO.createImageInputStream(url.openStream()); - Iterator readers = ImageIO.getImageReaders(iis); - - String format = StringUtils.EMPTY; - while (readers.hasNext()) { - ImageReader read = readers.next(); - format = read.getFormatName(); - } - URLConnection urlConn; - try { - urlConn = url.openConnection(); - } catch (IOException e) { - logger.error(String.format("Failed open url: %s", url.toString())); - throw e; - } - - try (InputStream is = new BufferedInputStream(urlConn.getInputStream())) { - String attachmentType = attachmentTypeFromFormat(format); - - String attachmentFName = DigestUtils.md5Hex(UUID.randomUUID().toString()) + "." + attachmentType; - Files.copy(is, Paths.get(tmpDir, attachmentFName)); - return URI.create(String.format("juick://%s", attachmentFName)); - } catch (IOException e) { - logger.error(String.format("Failed download image by url: %s", url.toString()), e); - throw e; - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/util/ImageUtils.java b/juick-server/src/main/java/com/juick/server/util/ImageUtils.java deleted file mode 100644 index d16faf8f..00000000 --- a/juick-server/src/main/java/com/juick/server/util/ImageUtils.java +++ /dev/null @@ -1,175 +0,0 @@ - -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.util; - -import com.juick.Attachment; -import org.apache.commons.imaging.ImageReadException; -import org.apache.commons.imaging.Imaging; -import org.apache.commons.imaging.common.ImageMetadata; -import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata; -import org.apache.commons.imaging.formats.tiff.TiffField; -import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.imgscalr.Scalr; -import org.imgscalr.Scalr.Rotation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.imageio.ImageIO; -import javax.imageio.ImageReader; -import javax.imageio.stream.FileImageInputStream; -import javax.imageio.stream.ImageInputStream; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.Iterator; - -public class ImageUtils { - private static final Logger logger = LoggerFactory.getLogger(ImageUtils.class); - - private String imgDir; - private String tmpDir; - - public ImageUtils(String imgDir, String tmpDir) { - this.imgDir = imgDir; - this.tmpDir = tmpDir; - } -/** - * Returns BufferedImage, same as ImageIO.read() does. - * - *

JPEG images with EXIF metadata are rotated according to Orientation tag. - * - * @param imageFile a File to read from. - */ - private static BufferedImage readImageWithOrientation(File imageFile) - throws IOException { - - BufferedImage image = ImageIO.read(imageFile); - if (!FilenameUtils.getExtension(imageFile.getName()).equals("jpg")) { - return image; - } - - try { - ImageMetadata metadata = Imaging.getMetadata(imageFile); - - if (metadata instanceof JpegImageMetadata) { - JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata; - TiffField orientationField = jpegMetadata.findEXIFValue(TiffTagConstants.TIFF_TAG_ORIENTATION); - - if (orientationField != null) { - int orientation = orientationField.getIntValue(); - switch (orientation) { - case TiffTagConstants.ORIENTATION_VALUE_ROTATE_90_CW: - image = Scalr.rotate(image, Rotation.CW_90); - break; - case TiffTagConstants.ORIENTATION_VALUE_ROTATE_180: - image = Scalr.rotate(image, Rotation.CW_180); - break; - case TiffTagConstants.ORIENTATION_VALUE_ROTATE_270_CW: - image = Scalr.rotate(image, Rotation.CW_270); - break; - case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL: - image = Scalr.rotate(image, Rotation.FLIP_HORZ); - break; - case TiffTagConstants.ORIENTATION_VALUE_MIRROR_VERTICAL: - image = Scalr.rotate(image, Rotation.FLIP_VERT); - break; - case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL_AND_ROTATE_90_CW: - image = Scalr.rotate(Scalr.rotate(image, Rotation.FLIP_HORZ), Rotation.CW_90); - break; - case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL_AND_ROTATE_270_CW: - image = Scalr.rotate(Scalr.rotate(image, Rotation.FLIP_HORZ), Rotation.CW_270); - break; - case TiffTagConstants.ORIENTATION_VALUE_HORIZONTAL_NORMAL: - default: - // do nothing - break; - } - } - } - } catch (ImageReadException e) { - // failed to read metadata. - // nothing to do here, return image as is. - } - - return image; - } - - public void saveImageWithPreviews(String tempFilename, String outputFilename) - throws IOException { - String ext = FilenameUtils.getExtension(outputFilename); - - Path outputImagePath = Paths.get(imgDir, "p", outputFilename); - // this throws strange exceptions - // Files.move(Paths.get(tmpDir, tempFilename), outputImagePath); - FileUtils.moveFile(Paths.get(tmpDir, tempFilename).toFile(), outputImagePath.toFile()); - BufferedImage originalImage = readImageWithOrientation(outputImagePath.toFile()); - - int width = originalImage.getWidth(); - int height = originalImage.getHeight(); - int maxDimension = (width > height) ? width : height; - BufferedImage image1024 = (maxDimension > 1024) ? Scalr.resize(originalImage, 1024) : originalImage; - BufferedImage image0512 = (maxDimension > 512) ? Scalr.resize(originalImage, 512) : originalImage; - BufferedImage image0160 = (maxDimension > 160) ? Scalr.resize(originalImage, 160) : originalImage; - ImageIO.write(image1024, ext, Paths.get(imgDir, "photos-1024", outputFilename).toFile()); - ImageIO.write(image0512, ext, Paths.get(imgDir, "photos-512", outputFilename).toFile()); - ImageIO.write(image0160, ext, Paths.get(imgDir, "ps", outputFilename).toFile()); - } - - public void saveAvatar(String tempFilename, int uid) - throws IOException { - String ext = FilenameUtils.getExtension(tempFilename); - String originalName = String.format("%s.%s", uid, ext); - Path originalPath = Paths.get(imgDir, "ao", originalName); - Files.move(Paths.get(tmpDir, tempFilename), originalPath, StandardCopyOption.REPLACE_EXISTING); - BufferedImage originalImage = ImageIO.read(originalPath.toFile()); - - String targetExt = "png"; - String targetName = String.format("%s.%s", uid, targetExt); - ImageIO.write(Scalr.resize(originalImage, 96), targetExt, Paths.get(imgDir, "a", targetName).toFile()); - ImageIO.write(Scalr.resize(originalImage, 32), targetExt, Paths.get(imgDir, "as", targetName).toFile()); - } - public Attachment getAttachment(File imgFile) throws IOException { - Attachment attachment = new Attachment(); - try (ImageInputStream stream = ImageIO.createImageInputStream(imgFile)) { - Iterator iter = ImageIO.getImageReaders(stream); - while (iter.hasNext()) { - ImageReader reader = iter.next(); - try { - reader.setInput(stream); - attachment.setWidth(reader.getWidth(reader.getMinIndex())); - attachment.setHeight(reader.getHeight(reader.getMinIndex())); - return attachment; - } catch (Exception e) { - logger.debug("Error reading {}, trying next reader", imgFile.getAbsolutePath()); - } finally { - reader.dispose(); - } - } - } - - logger.warn("Not a known image file {}", imgFile.getAbsolutePath()); - return attachment; - } -} \ No newline at end of file diff --git a/juick-server/src/main/java/com/juick/server/util/TagUtils.java b/juick-server/src/main/java/com/juick/server/util/TagUtils.java deleted file mode 100644 index cb828933..00000000 --- a/juick-server/src/main/java/com/juick/server/util/TagUtils.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.util; - -import com.juick.Tag; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; - -import java.util.List; -import java.util.stream.Collectors; - -/** - * Created by aalexeev on 11/13/16. - */ -public class TagUtils { - private TagUtils() { - throw new IllegalStateException(); - } - - public static String toString(final List tags) { - if (CollectionUtils.isEmpty(tags)) - return StringUtils.EMPTY; - - return tags.stream().map(t -> "*" + t.getName()) - .collect(Collectors.joining(" ")); - } -} diff --git a/juick-server/src/main/java/com/juick/server/util/UserUtils.java b/juick-server/src/main/java/com/juick/server/util/UserUtils.java deleted file mode 100644 index 1adc85ab..00000000 --- a/juick-server/src/main/java/com/juick/server/util/UserUtils.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.util; - -import com.juick.User; -import com.juick.model.AnonymousUser; -import com.juick.service.security.entities.JuickUser; -import javax.annotation.Nonnull; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; - -/** - * Created by aalexeev on 11/14/16. - */ -public class UserUtils { - private UserUtils() { - throw new IllegalStateException(); - } - - public static Authentication getAuthentication() { - return SecurityContextHolder.getContext().getAuthentication(); - } - - public static Object getPrincipal(final Authentication authentication) { - return authentication == null ? null : authentication.getPrincipal(); - } - - @Nonnull - public static User getCurrentUser() { - Object principal = getPrincipal(getAuthentication()); - - if (principal instanceof JuickUser) - return ((JuickUser) principal).getUser(); - - if (principal instanceof User) - return (User) principal; - - return AnonymousUser.INSTANCE; - } -} diff --git a/juick-server/src/main/java/com/juick/server/util/WebUtils.java b/juick-server/src/main/java/com/juick/server/util/WebUtils.java deleted file mode 100644 index 9dd628ee..00000000 --- a/juick-server/src/main/java/com/juick/server/util/WebUtils.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.util; - -import java.util.regex.Pattern; - -/** - * Created by aalexeev on 11/28/16. - */ -public class WebUtils { - private WebUtils() { - throw new IllegalStateException(); - } - - private static final Pattern USER_NAME_PATTERN = Pattern.compile("[a-zA-Z-_\\d]{2,16}"); - - private static final Pattern POST_NUMBER_PATTERN = Pattern.compile("-?\\d+"); - - private static final Pattern JID_PATTERN = Pattern.compile("^[a-zA-Z0-9\\\\-\\\\_\\\\@\\\\.]{6,64}$"); - - - public static boolean isPostNumber(final String aString) { - return aString != null && POST_NUMBER_PATTERN.matcher(aString).matches(); - } - - public static boolean isNotPostNumber(final String aString) { - return !isPostNumber(aString); - } - - public static boolean isUserName(final String aString) { - return aString != null && USER_NAME_PATTERN.matcher(aString).matches(); - } - - public static boolean isNotUserName(final String aString) { - return !isUserName(aString); - } - - public static boolean isJid(final String aString) { - return aString != null && JID_PATTERN.matcher(aString).matches(); - } - - public static boolean isNotJid(final String aString) { - return !isJid(aString); - } - - -} diff --git a/juick-server/src/main/java/com/juick/server/www/HelpService.java b/juick-server/src/main/java/com/juick/server/www/HelpService.java deleted file mode 100644 index 25727962..00000000 --- a/juick-server/src/main/java/com/juick/server/www/HelpService.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.www; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; -import org.springframework.cache.annotation.Cacheable; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.regex.Pattern; - -/** - * Created by aalexeev on 12/11/16. - */ -public class HelpService { - private static final Pattern LANG_PATTERN = Pattern.compile("[a-z]{2}"); - - private static final Pattern PAGE_PATTERN = Pattern.compile("[a-zA-Z0-9\\-_]+"); - - private final String helpPath; - - - public HelpService(String helpPath) { - this.helpPath = helpPath; - } - - @Cacheable("help") - public String getHelp(final String page, final String lang) { - if (canBePage(page) && canBeLang(lang)) { - String path = StringUtils.joinWith("/", helpPath, lang, page + ".md"); - - try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path)) { - if (is != null) - return IOUtils.toString(is, StandardCharsets.UTF_8); - } catch (IOException e) { - } - } - return null; - } - - public boolean canBePage(final String anything) { - return anything != null && PAGE_PATTERN.matcher(anything).matches(); - } - - public boolean canBeLang(final String anything) { - return anything != null && LANG_PATTERN.matcher(anything).matches(); - } - - public String getHelpPath() { - return helpPath; - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/WebApp.java b/juick-server/src/main/java/com/juick/server/www/WebApp.java deleted file mode 100644 index 98327a5d..00000000 --- a/juick-server/src/main/java/com/juick/server/www/WebApp.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.server.www; - -import com.juick.Tag; -import com.juick.service.TagService; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.resource.ResourceUrlProvider; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Stream; - -/** - * - * @author Ugnich Anton - */ -@Component -public class WebApp { - @Inject - private TagService tagService; - @Inject - private ResourceUrlProvider resourceUrlProvider; - - public List parseTags(String tagsStr) { - List tags = new ArrayList<>(); - if (tagsStr != null && !tagsStr.isEmpty()) { - Stream tagsList = Arrays.stream(tagsStr.split("[ \\,]")) - .distinct().map( t -> { - if (t.startsWith("*")) { - t = t.substring(1); - } - if (t.length() > 64) { - t = t.substring(0, 64); - } - return t; - }); - tags = tagService.getTags(tagsList, true); - while (tags.size() > 5) { - tags.remove(5); - } - } - return tags; - } - - public String getStyleUrl() { - return resourceUrlProvider.getForLookupPath("/style.css"); - } - - public String getScriptsUrl() { - return resourceUrlProvider.getForLookupPath("/scripts.js"); - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/controllers/AnythingFilter.java b/juick-server/src/main/java/com/juick/server/www/controllers/AnythingFilter.java deleted file mode 100644 index cdbeafc0..00000000 --- a/juick-server/src/main/java/com/juick/server/www/controllers/AnythingFilter.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.juick.server.www.controllers; - -import com.juick.server.util.WebUtils; -import com.juick.service.MessagesService; -import com.juick.service.UserService; -import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.stereotype.Component; -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 -public class AnythingFilter extends OncePerRequestFilter { - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - - @Override - public void doFilterInternal(@Nonnull HttpServletRequest servletRequest, - @Nonnull HttpServletResponse servletResponse, - @Nonnull FilterChain filterChain) throws IOException, ServletException { - String upgrade = servletRequest.getHeader("Connection"); - if (upgrade != null && upgrade.equals("Upgrade")) { - filterChain.doFilter(servletRequest, servletResponse); - return; - } - UriComponents components = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); - String anything = components.getPath().substring(1); - int before = NumberUtils.toInt(components.getQueryParams().getFirst("before"), 0); - if (before == 0) { - boolean isPostNumber = WebUtils.isPostNumber(anything); - int messageId = isPostNumber ? - NumberUtils.toInt(anything) : 0; - - if (isPostNumber && anything.equals(Integer.toString(messageId))) { - if (messageId > 0) { - com.juick.User author = messagesService.getMessageAuthor(messageId); - - if (author != null) { - servletResponse.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); - servletResponse.setHeader("Location", "/" + author.getName() + "/" + anything); - return; - } - } - } - com.juick.User user = userService.getUserByName(anything); - if (user.getUid() > 0) { - ((HttpServletResponse)servletResponse).sendRedirect("/" + user.getName() + "/"); - } else { - filterChain.doFilter(servletRequest, servletResponse); - } - } else { - com.juick.User user = userService.getUserByName(anything); - if (!user.isAnonymous()) { - ((HttpServletResponse) servletResponse).sendRedirect("/" + user.getName() + "/?before=" + before); - } else { - filterChain.doFilter(servletRequest, servletResponse); - } - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/controllers/Help.java b/juick-server/src/main/java/com/juick/server/www/controllers/Help.java deleted file mode 100644 index 61b58a9d..00000000 --- a/juick-server/src/main/java/com/juick/server/www/controllers/Help.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.www.controllers; - -import com.juick.server.util.HttpNotFoundException; -import com.juick.server.util.UserUtils; -import com.juick.service.MessagesService; -import com.juick.server.www.HelpService; -import org.commonmark.parser.Parser; -import org.commonmark.renderer.html.HtmlRenderer; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; - -import javax.inject.Inject; -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.Locale; -import java.util.Objects; - -/** - * Created by aalexeev on 11/21/16. - */ -@Controller -public class Help { - @Inject - private HelpService helpService; - @Inject - private MessagesService messagesService; - @Inject - private Parser cmParser; - @Inject - private HtmlRenderer helpRenderer; - - @GetMapping({"/help/", "/help", "/help/{langOrPage}", "/help/{lang}/{page}"}) - public String showHelp( - Locale locale, - @PathVariable(required = false, name = "lang") String lang, - @PathVariable(required = false, name = "page") String page, - @PathVariable(required = false, name = "langOrPage") String langOrPage, - Model model) throws IOException, URISyntaxException { - com.juick.User visitor = UserUtils.getCurrentUser(); - - String navigation = null; - - if (langOrPage != null) { - if (helpService.canBeLang(langOrPage)) { - navigation = helpService.getHelp("navigation", langOrPage); - if (navigation != null) - lang = langOrPage; - } - - if (navigation == null && helpService.canBePage(langOrPage)) - page = langOrPage; - } - - if (lang == null) { - lang = locale.getLanguage(); - } - - String content = helpService.getHelp(page, lang); - if (content == null && !Objects.equals("tos", page)) - content = helpService.getHelp("tos", lang); - - if (navigation == null) - navigation = helpService.getHelp("navigation", lang); - - if (content == null || navigation == null) - throw new HttpNotFoundException(); - - model.addAttribute("navigation", helpRenderer.render(cmParser.parse(navigation))); - model.addAttribute("content", helpRenderer.render(cmParser.parse(content))); - model.addAttribute("visitor", visitor); - - return "views/help"; - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/controllers/Login.java b/juick-server/src/main/java/com/juick/server/www/controllers/Login.java deleted file mode 100644 index d933934e..00000000 --- a/juick-server/src/main/java/com/juick/server/www/controllers/Login.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.server.www.controllers; - -import com.juick.server.util.UserUtils; -import com.juick.service.UserService; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; - -import javax.inject.Inject; - -/** - * @author Ugnich Anton - */ -@Controller -public class Login { - @Inject - private UserService userService; - - @GetMapping("/login") - public String getloginForm(@RequestParam(required = false, defaultValue = "true") boolean redirect) { - com.juick.User visitor = UserUtils.getCurrentUser(); - - if (!visitor.isAnonymous()) { - return redirect ? "redirect:/" : "redirect:/login/success"; - } - return "views/login"; - } - @GetMapping("/login/success") - public String getSuccessLogin(ModelMap model) { - model.addAttribute("hash", userService.getHashByUID(UserUtils.getCurrentUser().getUid())); - return "views/login_success"; - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/controllers/MessagesWWW.java b/juick-server/src/main/java/com/juick/server/www/controllers/MessagesWWW.java deleted file mode 100644 index 0708e27f..00000000 --- a/juick-server/src/main/java/com/juick/server/www/controllers/MessagesWWW.java +++ /dev/null @@ -1,593 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.server.www.controllers; - -import com.juick.Message; -import com.juick.Tag; -import com.juick.formatters.PlainTextFormatter; -import com.juick.server.Utils; -import com.juick.server.util.HttpForbiddenException; -import com.juick.server.util.HttpNotFoundException; -import com.juick.server.util.UserUtils; -import com.juick.server.util.WebUtils; -import com.juick.service.*; -import com.juick.util.MessageUtils; -import org.apache.commons.codec.CharEncoding; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.text.StringEscapeUtils; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import org.springframework.web.util.UriComponents; -import ru.sape.Sape; - -import javax.inject.Inject; -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -/** - * - * @author Ugnich Anton - */ -@Controller -public class MessagesWWW { - @Inject - private UserService userService; - @Inject - private TagService tagService; - @Inject - private MessagesService messagesService; - @Inject - private Optional sape; - @Inject - private PMQueriesService pmQueriesService; - @Inject - private CrosspostService crosspostService; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - - void fillUserModel(ModelMap model, com.juick.User user, com.juick.User visitor) { - model.addAttribute("user", user); - model.addAttribute("isSubscribed", userService.isSubscribed(visitor.getUid(), user.getUid())); - model.addAttribute("isInBL", userService.isInBL(visitor.getUid(), user.getUid())); - model.addAttribute("isInBLAny", userService.isInBLAny(user.getUid(), visitor.getUid())); - model.addAttribute("statsIRead", userService.getUserFriends(user.getUid()).size()); - model.addAttribute("statsMyReaders", userService.getStatsMyReaders(user.getUid())); - model.addAttribute("statsMyBL", userService.getUserBLUsers(user.getUid()).size()); - model.addAttribute("statsMessages", userService.getStatsMessages(user.getUid())); - model.addAttribute("statsReplies", userService.getStatsReplies(user.getUid())); - model.addAttribute("iread", userService.getUserReadLeastPopular(user.getUid(), 8)); - model.addAttribute("tagStats", tagService.getUserTagStats(user.getUid()) - .stream().sorted((e1, e2) -> Integer.compare(e2.getUsageCount(), e1.getUsageCount())).limit(20).map(t -> t.getTag().getName()).collect(Collectors.toList())); - } - - @GetMapping("/") - protected String doGet( - @RequestParam(required = false) String tag, - @RequestParam(name = "show", required = false) String paramShow, - @RequestParam(name = "search", required = false) String paramSearch, - @RequestParam(name = "before", required = false, defaultValue = "0") Integer paramBefore, - @RequestParam(name = "to", required = false, defaultValue = "0") Long paramTo, - @RequestParam(name = "page", required = false, defaultValue = "0") Integer page, - @CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie, - ModelMap model) throws IOException { - if (tag != null) { - return "redirect:/tag/" + URLEncoder.encode(tag, CharEncoding.UTF_8); - } - com.juick.User visitor = UserUtils.getCurrentUser(); - - if (paramSearch != null && paramSearch.length() > 64) { - paramSearch = null; - } - - model.addAttribute("discover", false); - - String title; - List mids; - - if (paramSearch != null) { - title = "Поиск: " + StringEscapeUtils.escapeHtml4(paramSearch); - mids = messagesService.getSearch(visitor, Utils.encodeSphinx(paramSearch), page); - } else if (paramShow == null) { - title = "Обсуждения"; - mids = messagesService.getDiscussions(visitor.getUid(), paramTo); - } else if (paramShow.equals("top")) { - title = "Популярные"; - mids = messagesService.getPopular(visitor.getUid(), paramBefore); - model.addAttribute("discover", true); - } else if (paramShow.equals("my") && !visitor.isAnonymous()) { - title = "Моя лента"; - mids = messagesService.getMyFeed(visitor.getUid(), paramBefore, true); - } else if (paramShow.equals("private") && !visitor.isAnonymous()) { - title = "Приватные"; - mids = messagesService.getPrivate(visitor.getUid(), paramBefore); - } else if (paramShow.equals("discuss")) { - return "redirect:/"; - } else if (paramShow.equals("recommended") && !visitor.isAnonymous()) { - title = "Рекомендации"; - mids = messagesService.getRecommended(visitor.getUid(), paramBefore); - } else if (paramShow.equals("photos")) { - title = "Фотографии"; - mids = messagesService.getPhotos(visitor.getUid(), paramBefore); - model.addAttribute("discover", true); - } else if (paramShow.equals("all")) { - title = "Все сообщения"; - mids = messagesService.getAll(visitor.getUid(), paramBefore); - model.addAttribute("discover", true); - } else { - throw new HttpNotFoundException(); - } - - String head = "\n";; - - if (paramBefore > 0 || paramShow != null) { - head = ""; - } - model.addAttribute("title", title); - model.addAttribute("headers", head); - model.addAttribute("visitor", visitor); - model.addAttribute("noindex", !(paramShow == null && paramBefore == 0)); - List msgs = messagesService.getMessages(visitor, mids); - - if (!visitor.isAnonymous()) { - fillUserModel(model, visitor, visitor); - List unread = messagesService.getUnread(visitor); - visitor.setUnreadCount(unread.size()); - List blUIDs = userService.checkBL(visitor.getUid(), - msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList())); - msgs.forEach(m -> { - m.ReadOnly |= blUIDs.contains(m.getUser().getUid()); - m.setUnread(unread.contains(m.getMid())); - }); - } - model.addAttribute("msgs", msgs); - model.addAttribute("tags", tagService.getPopularTags()); - model.addAttribute("headers", head); - model.addAttribute("showAdv", - paramShow == null && paramBefore == 0 && paramSearch == null && visitor.isAnonymous()); - if (mids.size() >= 20) { - String nextpage = (paramShow == null) ? "?to=" + msgs.get(msgs.size() - 1).getUpdated().toEpochMilli() : paramSearch != null ? String.format("?page=%d", page + 1) : "?before=" + mids.get(mids.size() - 1); - if (paramShow != null) { - nextpage += "&show=" + paramShow; - } - if (paramSearch != null) { - nextpage += "&search=" + URLEncoder.encode(paramSearch, CharEncoding.UTF_8); - } - model.addAttribute("nextpage", nextpage); - } - UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); - String queryString = builder.getQuery(); - String requestURI = builder.toUri().getPath(); - if (sape.isPresent() && visitor.isAnonymous() && queryString == null) { - String links = sape.get().getPageLinks(requestURI, sapeCookie).render(); - model.addAttribute("links", links); - } - return "views/index"; - } - - @GetMapping(path = "/{uname}/", headers = "Connection!=Upgrade") - protected String doGetBlog( - @RequestParam(required = false, name = "show") String paramShow, - @RequestParam(required = false, name = "tag") String paramTagStr, - @RequestParam(required = false, name = "search") String paramSearch, - @RequestParam(required = false, name = "page", defaultValue = "0") Integer page, - @PathVariable String uname, - @RequestParam(required = false, defaultValue = "0") Integer before, - @CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie, - ModelMap model) throws IOException { - com.juick.User user = userService.getUserByName(uname); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (user.isBanned() || user.isAnonymous()) { - throw new HttpNotFoundException(); - } - - List mids; - - com.juick.Tag paramTag = null; - if (paramTagStr != null) { - if (paramTagStr.length() < 64) { - paramTag = tagService.getTag(paramTagStr, false); - } - if (paramTag == null) { - throw new HttpNotFoundException(); - } else if (!paramTag.getName().equals(paramTagStr)) { - String url = user.getName() + "/?tag=" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8); - return "redirect:/" + url; - } - } - if (paramSearch != null && paramSearch.length() > 64) { - paramSearch = null; - } - - int privacy = 0; - if (!visitor.isAnonymous()) { - if (user.getUid() == visitor.getUid() || visitor.getUid() == 1) { - privacy = -3; - } else if (userService.isInWL(user.getUid(), visitor.getUid())) { - privacy = -2; - } - } - - String title; - if (paramShow == null) { - if (paramTag != null) { - title = "Блог " + user.getName() + ": *" + StringEscapeUtils.escapeHtml4(paramTag.getName()); - mids = messagesService.getUserTag(user.getUid(), paramTag.TID, privacy, before); - } else if (paramSearch != null) { - title = "Блог " + user.getName() + ": " + StringEscapeUtils.escapeHtml4(paramSearch); - mids = messagesService.getUserSearch(visitor, user.getUid(), Utils.encodeSphinx(paramSearch), privacy, page); - } else { - title = "Блог " + user.getName(); - mids = messagesService.getUserBlog(user.getUid(), privacy, before); - } - } else if (paramShow.equals("recomm")) { - title = "Рекомендации " + user.getName(); - mids = messagesService.getUserRecommendations(user.getUid(), before); - } else if (paramShow.equals("photos")) { - title = "Фотографии " + user.getName(); - mids = messagesService.getUserPhotos(user.getUid(), privacy, before); - } else { - throw new HttpNotFoundException(); - } - - String head = ""; - head += "\n"; - if (paramTag != null && tagService.getTagNoIndex(paramTag.TID)) { - head += ""; - } else if (before > 0 || paramShow != null) { - head += ""; - } - model.addAttribute("pageUrl", "http://juick.com/" + user.getName()); - model.addAttribute("title", title); - model.addAttribute("headers", head); - model.addAttribute("visitor", visitor); - model.addAttribute("noindex", paramShow == null && before == 0); - fillUserModel(model, user, visitor); - model.addAttribute("paramTag", paramTag); - List msgs = messagesService.getMessages(visitor, mids); - - if (!visitor.isAnonymous()) { - List unread = messagesService.getUnread(visitor); - visitor.setUnreadCount(unread.size()); - List blUIDs = userService.checkBL(visitor.getUid(), - msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList())); - msgs.forEach(m -> { - m.ReadOnly |= blUIDs.contains(m.getUser().getUid()); - m.setUnread(unread.contains(m.getMid())); - }); - } - model.addAttribute("msgs", msgs); - model.addAttribute("headers", head); - model.addAttribute("showAdv", - paramShow == null && before == 0 && paramSearch == null && visitor.getUid() == 0); - if (mids.size() >= 20) { - String nextpage = paramSearch != null ? String.format("?page=%d", page + 1) : "?before=" + mids.get(mids.size() - 1); - if (paramShow != null) { - nextpage += "&show=" + paramShow; - } - if (paramSearch != null) { - nextpage += "&search=" + URLEncoder.encode(paramSearch, CharEncoding.UTF_8); - } - if (paramTag != null) { - nextpage += "&tag=" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8); - } - model.addAttribute("nextpage", nextpage); - } - UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); - String queryString = builder.getQuery(); - String requestURI = builder.toUri().getPath(); - if (sape.isPresent() && visitor.isAnonymous() && queryString == null) { - String links = sape.get().getPageLinks(requestURI, sapeCookie).render(); - model.addAttribute("links", links); - } - return "views/blog"; - } - - @GetMapping("/{uname}/tags") - protected String doGetTags(@PathVariable String uname, ModelMap model) throws IOException { - com.juick.User user = userService.getUserByName(uname); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isBanned()) { - throw new HttpNotFoundException(); - } - - model.addAttribute("title", "Теги " + user.getName()); - model.addAttribute("headers", ""); - model.addAttribute("visitor", visitor); - fillUserModel(model, user, visitor); - model.addAttribute("tags", tagService.getUserTagStats(user.getUid()).stream() - .sorted((e1, e2) -> Integer.compare(e2.getUsageCount(), e1.getUsageCount())).map(t -> t.getTag().getName()).collect(Collectors.toList())); - - return "views/blog_tags"; - } - - @GetMapping("/{uname}/friends") - protected String doGetFriends(@PathVariable String uname, ModelMap model) throws IOException { - com.juick.User user = userService.getUserByName(uname); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isBanned()) { - throw new HttpNotFoundException(); - } - model.addAttribute("title", "Подписки " + user.getName()); - model.addAttribute("headers", ""); - model.addAttribute("visitor", visitor); - fillUserModel(model, user, visitor); - model.addAttribute("users", userService.getUserFriends(user.getUid())); - - return "views/users"; - } - - @GetMapping("/{uname}/readers") - protected String doGetReaders(@PathVariable String uname, ModelMap model) throws IOException { - com.juick.User user = userService.getUserByName(uname); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isBanned()) { - throw new HttpForbiddenException(); - } - model.addAttribute("title", "Читатели " + user.getName()); - model.addAttribute("headers", ""); - model.addAttribute("visitor", visitor); - fillUserModel(model, user, visitor); - model.addAttribute("users", userService.getUserReaders(user.getUid())); - - return "views/users"; - } - - @GetMapping("/{uname}/bl") - protected String doGetBL(@PathVariable String uname, ModelMap model) throws IOException { - com.juick.User user = userService.getUserByName(uname); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isBanned() || visitor.getUid() != user.getUid()) { - throw new HttpForbiddenException(); - } - model.addAttribute("title", "Черный список " + user.getName()); - model.addAttribute("headers", ""); - model.addAttribute("visitor", visitor); - fillUserModel(model, user, visitor); - model.addAttribute("users", userService.getUserBLUsers(user.getUid())); - - return "views/users"; - } - @GetMapping("/tag/{tagName}") - protected String tagAction(HttpServletRequest request, - @PathVariable String tagName, - @CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie, - @RequestParam(required = false, defaultValue = "0") int before, - ModelMap model) throws IOException { - com.juick.User visitor = UserUtils.getCurrentUser(); - - String paramTagStr = StringEscapeUtils.unescapeHtml4(tagName); - com.juick.Tag paramTag = tagService.getTag(paramTagStr, false); - if (paramTag == null) { - throw new HttpNotFoundException(); - } else if (paramTag.SynonymID > 0 && paramTag.TID != paramTag.SynonymID) { - com.juick.Tag synTag = tagService.getTag(paramTag.SynonymID); - String url = "/tag/" + URLEncoder.encode(StringEscapeUtils.escapeHtml4(synTag.getName()), CharEncoding.UTF_8); - if (request.getQueryString() != null) { - url += "?" + request.getQueryString(); - } - return "redirect:" + url; - } else if (!paramTag.getName().equals(paramTagStr)) { - String url = "/tag/" + URLEncoder.encode(StringEscapeUtils.escapeHtml4(paramTag.getName()), CharEncoding.UTF_8); - if (request.getQueryString() != null) { - url += "?" + request.getQueryString(); - } - return "redirect:" + url; - } - - String title = "*" + StringEscapeUtils.escapeHtml4(paramTag.getName()); - model.addAttribute("title", title); - List mids = messagesService.getTag(paramTag.TID, visitor.getUid(), before, (visitor.isAnonymous()) ? 40 : 20); - List msgs = messagesService.getMessages(visitor, mids); - if (!visitor.isAnonymous()) { - List unread = messagesService.getUnread(visitor); - visitor.setUnreadCount(unread.size()); - List blUIDs = userService.checkBL( - visitor.getUid(), - msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList()) - ); - msgs.forEach(m -> { - m.ReadOnly |= blUIDs.contains(m.getUser().getUid()); - m.setUnread(unread.contains(m.getMid())); - }); - fillUserModel(model, visitor, visitor); - } - - String head = StringUtils.EMPTY; - if (tagService.getTagNoIndex(paramTag.TID)) { - head = ""; - } else if (before > 0 || mids.size() < 5) { - head = ""; - } - model.addAttribute("headers", head); - model.addAttribute("visitor", visitor); - model.addAttribute("tag", paramTag); - model.addAttribute("title", title); - model.addAttribute("msgs", msgs); - model.addAttribute("tags", tagService.getPopularTags()); - model.addAttribute("noindex", before > 0); - model.addAttribute("showAdv", before == 0 && visitor.isAnonymous()); - model.addAttribute("isSubscribed", tagService.isSubscribed(visitor, paramTag)); - model.addAttribute("isInBL", tagService.isInBL(visitor, paramTag)); - if (mids.size() >= 20) { - String nextpage = "/tag/" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8) + "?before=" + mids.get(mids.size() - 1); - model.addAttribute("nextpage", nextpage); - } - UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); - String queryString = builder.getQuery(); - String requestURI = builder.toUri().getPath(); - if (sape.isPresent() && visitor.isAnonymous() && queryString == null) { - String links = sape.get().getPageLinks(requestURI, sapeCookie).render(); - model.addAttribute("links", links); - } - return "views/index"; - } - @GetMapping("/pm/inbox") - protected String doGetInbox(ModelMap model) { - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - return "redirect:/login"; - } - String title = "PM: Inbox"; - List msgs = pmQueriesService.getLastPMInbox(visitor.getUid()); - fillUserModel(model, visitor, visitor); - model.addAttribute("title", title); - model.addAttribute("visitor", visitor); - model.addAttribute("msgs", msgs); - model.addAttribute("tags", tagService.getPopularTags()); - return "views/pm_inbox"; - } - - @GetMapping("/pm/sent") - protected String doGetSent(@RequestParam(required = false) String uname, - ModelMap model) { - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - return "redirect:/login"; - } - String title = "PM: Sent"; - List msgs = pmQueriesService.getLastPMSent(visitor.getUid()); - - if (WebUtils.isNotUserName(uname)) { - uname = StringUtils.EMPTY; - } - fillUserModel(model, visitor, visitor); - model.addAttribute("title", title); - model.addAttribute("visitor", visitor); - model.addAttribute("msgs", msgs); - model.addAttribute("tags", tagService.getPopularTags()); - model.addAttribute("uname", uname); - return "views/pm_sent"; - } - @GetMapping(value = "/{uname}/{mid}", produces = MediaType.TEXT_HTML_VALUE) - protected String threadAction(ModelMap model, - @PathVariable String uname, - @PathVariable int mid, - @CookieValue(name = "sape_cookie", - required = false, defaultValue = StringUtils.EMPTY) String sapeCookie) { - com.juick.User visitor = UserUtils.getCurrentUser(); - - if (!messagesService.canViewThread(mid, visitor.getUid())) { - throw new HttpForbiddenException(); - } - - com.juick.Message msg = messagesService.getMessage(mid); - - if (msg == null || msg.getUser().isBanned()) { - throw new HttpNotFoundException(); - } - - com.juick.User user = userService.getUserByName(uname); - if (user.isAnonymous() || !msg.getUser().equals(user)) { - return String.format("redirect:/%s/%d", msg.getUser().getName(), mid); - } - msg.VisitorCanComment = !visitor.isAnonymous(); - List replies = messagesService.getReplies(visitor, msg.getMid()); - // this should be after getReplies to mark thread as read - fillUserModel(model, user, visitor); - if (!visitor.isAnonymous()) { - List unread = messagesService.getUnread(visitor); - visitor.setUnreadCount(unread.size()); - boolean isMsgAuthor = visitor.getUid() == msg.getUser().getUid(); - boolean isInBL = userService.isInBLAny(msg.getUser().getUid(), visitor.getUid()); - msg.VisitorCanComment = isMsgAuthor || !(msg.ReadOnly || isInBL); - } - model.addAttribute("msg", msg); - - String title = msg.getUser().getName() + ": " + MessageUtils.getTagsString(msg); - - model.addAttribute("title", title); - model.addAttribute("visitor", visitor); - String headers = ""; - String pageUrl = "https://juick.com/" + msg.getUser().getName() + "/" + msg.getMid(); - if (msg.Hidden) { - headers += ""; - } - String cardType = StringUtils.isNotEmpty(msg.getAttachmentType()) ? "summary_large_image" : "summary"; - if (StringUtils.isNotEmpty(msg.getAttachmentType())) { - // additional check in case of broken images - if (msg.getAttachment() != null) { - String msgImage = msg.getAttachment().getMedium().getUrl(); - headers += ""; - } - } else { - String msgImage ="https://i.juick.com/a/" + msg.getUser().getUid() + ".png"; - headers += ""; - } - model.addAttribute("ogtype", "article"); - String cardDescription = StringEscapeUtils.escapeHtml4(PlainTextFormatter.formatTwitterCard(msg)); - headers += "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n"; - String twitterName = crosspostService.getTwitterName(msg.getUser().getUid()); - if (StringUtils.isNotEmpty(twitterName)) { - headers += "\n"; - } - if (msg.getTags().size() > 0) { - headers += "\n"; - } - model.addAttribute("headers", headers); - model.addAttribute("visitorSubscribed", messagesService.isSubscribed(visitor.getUid(), msg.getMid())); - model.addAttribute("visitorInBL", userService.isInBL(msg.getUser().getUid(), visitor.getUid())); - model.addAttribute("recomm", messagesService.getMessageRecommendations(msg.getMid())); - List blUIDs = new ArrayList<>(); - for (Message reply : replies) { - if (reply.getUser().getUid() != msg.getUser().getUid() - && !blUIDs.contains(reply.getUser().getUid())) { - blUIDs.add(reply.getUser().getUid()); - } - reply.VisitorCanComment = !visitor.isAnonymous(); - if (!visitor.isAnonymous()) { - boolean isMsgAuthor = visitor.getUid() == msg.getUser().getUid(); - boolean isReplyAuthor = visitor.getUid() == reply.getUser().getUid(); - reply.VisitorCanComment = isMsgAuthor || (!msg.ReadOnly - && msg.VisitorCanComment && (isReplyAuthor || !userService.isInBLAny(visitor.getUid(), reply.getUser().getUid()))); - } - } - model.addAttribute("replies", replies); - model.addAttribute("showAdv", visitor.isAnonymous()); - UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); - String queryString = builder.getQuery(); - String requestURI = builder.toUri().getPath(); - if (sape.isPresent() && visitor.isAnonymous() && queryString == null) { - String links = sape.get().getPageLinks(requestURI, sapeCookie).render(); - model.addAttribute("links", links); - } - return "views/thread"; - } - - // when message id is not fit to int - @ExceptionHandler(NumberFormatException.class) - public ResponseEntity notFoundAction() { - return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/controllers/NewMessage.java b/juick-server/src/main/java/com/juick/server/www/controllers/NewMessage.java deleted file mode 100644 index 6b5938a5..00000000 --- a/juick-server/src/main/java/com/juick/server/www/controllers/NewMessage.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.server.www.controllers; - -import com.juick.server.util.UserUtils; -import com.juick.service.TagService; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.text.StringEscapeUtils; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; - -import javax.inject.Inject; -import java.util.stream.Collectors; - -/** - * @author Ugnich Anton - */ -@Controller -public class NewMessage { - - @Inject - private TagService tagService; - @GetMapping("/post") - protected String postAction(@RequestParam(required = false) String body, ModelMap model) { - com.juick.User visitor = UserUtils.getCurrentUser(); - model.addAttribute("title", "Написать"); - model.addAttribute("headers", ""); - model.addAttribute("visitor", visitor); - if (body == null) { - body = StringUtils.EMPTY; - } else { - if (body.length() > 4096) { - body = body.substring(0, 4096); - } - body = StringEscapeUtils.escapeHtml4(body); - } - model.addAttribute("body", body); - model.addAttribute("visitor", visitor); - model.addAttribute("tags", tagService.getUserTagStats(visitor.getUid()).stream() - .sorted((e1, e2) -> Integer.compare(e2.getUsageCount(), e1.getUsageCount())).map(t -> t.getTag().getName()).collect(Collectors.toList())); - return "views/post"; - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/controllers/Settings.java b/juick-server/src/main/java/com/juick/server/www/controllers/Settings.java deleted file mode 100644 index cc8f43eb..00000000 --- a/juick-server/src/main/java/com/juick/server/www/controllers/Settings.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.server.www.controllers; - -import com.juick.User; -import com.juick.model.NotifyOpts; -import com.juick.model.UserInfo; -import com.juick.server.util.HttpBadRequestException; -import com.juick.server.util.HttpUtils; -import com.juick.server.util.UserUtils; -import com.juick.service.*; -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.multipart.MultipartFile; - -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 java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -/** - * - * @author Ugnich Anton - */ -@Controller -public class Settings { - private static final Logger logger = LoggerFactory.getLogger(Settings.class); - - @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String imgDir; - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - @Inject - private TagService tagService; - @Inject - private UserService userService; - @Inject - private CrosspostService crosspostService; - @Inject - private SubscriptionService subscriptionService; - @Inject - private EmailService emailService; - @Inject - private TelegramService telegramService; - @Inject - private ImagesService imagesService; - - @GetMapping("/settings") - protected String doGet(HttpServletRequest request, HttpServletResponse response, ModelMap model) throws IOException { - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - response.sendRedirect("/login"); - } - List pages = Arrays.asList("main", "password", "about", "auth-email", "privacy"); - String page = request.getParameter("page"); - if (StringUtils.isEmpty(page) || !pages.contains(page)) { - page = "main"; - } - - model.addAttribute("title", "Настройки"); - model.addAttribute("visitor", visitor); - model.addAttribute("tags", tagService.getPopularTags()); - model.addAttribute("auths", userService.getAuthCodes(visitor)); - model.addAttribute("email_active", emailService.getNotificationsEmail(visitor.getUid())); - model.addAttribute("ehash", userService.getEmailHash(visitor)); - model.addAttribute("emails", userService.getEmails(visitor)); - model.addAttribute("jids", userService.getAllJIDs(visitor)); - List hours = IntStream.rangeClosed(0, 23).boxed() - .map(i -> StringUtils.leftPad(String.format("%d", i), 2, "0")).collect(Collectors.toList()); - model.addAttribute("hours", hours); - model.addAttribute("fbstatus", crosspostService.getFbCrossPostStatus(visitor.getUid())); - model.addAttribute("twitter_name", crosspostService.getTwitterName(visitor.getUid())); - model.addAttribute("telegram_name", crosspostService.getTelegramName(visitor.getUid())); - model.addAttribute("notify_options", subscriptionService.getNotifyOptions(visitor)); - model.addAttribute("userinfo", userService.getUserInfo(visitor)); - if (page.equals("auth-email")) { - if (emailService.verifyAddressByCode(visitor.getUid(), request.getParameter("code"))) { - ; - model.addAttribute("result", "OK!"); - } else { - model.addAttribute("result", "Sorry, code unknown."); - } - } - return String.format("views/settings_%s", page); - } - - @PostMapping("/settings") - protected String doPost(HttpServletRequest request, HttpServletResponse response, - @RequestParam(required = false) MultipartFile avatar, - ModelMap model) - throws IOException { - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpBadRequestException(); - } - List pages = Arrays.asList("main", "password", "about", "email", "email-add", "email-del", - "email-subscr", "auth-email", "privacy", "jid-del", "twitter-del", "telegram-del", "facebook-disable", - "facebook-enable", "vk-del"); - String page = request.getParameter("page"); - if (StringUtils.isEmpty(page) || !pages.contains(page)) { - throw new HttpBadRequestException(); - } - String result = StringUtils.EMPTY; - switch (page) { - case "password": - if (userService.updatePassword(visitor, request.getParameter("password"))) { - result = "

Password has been changed.

"; - String hash = userService.getHashByUID(visitor.getUid()); - Cookie c = new Cookie("hash", hash); - c.setMaxAge(365 * 24 * 60 * 60); - response.addCookie(c); - } - break; - case "main": - NotifyOpts opts = new NotifyOpts(); - opts.setRepliesEnabled(StringUtils.isNotEmpty(request.getParameter("jnotify"))); - opts.setSubscriptionsEnabled(StringUtils.isNotEmpty(request.getParameter("subscr_notify"))); - opts.setRecommendationsEnabled(StringUtils.isNotEmpty(request.getParameter("recomm"))); - if (subscriptionService.setNotifyOptions(visitor, opts)) { - result = "

Notification options has been updated

"; - } - break; - case "about": - UserInfo info = new UserInfo(); - info.setFullName(request.getParameter("fullname")); - info.setCountry(request.getParameter("country")); - info.setUrl(request.getParameter("url")); - info.setDescription(request.getParameter("descr")); - String avatarTmpPath = HttpUtils.receiveMultiPartFile(avatar, tmpDir).getHost(); - if (StringUtils.isNotEmpty(avatarTmpPath)) { - imagesService.saveAvatar(avatarTmpPath, visitor.getUid()); - } - if (userService.updateUserInfo(visitor, info)) { - result = String.format("

Your info is updated.

Back to blog.

", visitor.getName()); - } - break; - case "jid-del": - // FIXME: stop using ugnich-csv in parameters - String[] params = request.getParameter("delete").split(";", 2); - boolean res = false; - if (params[0].equals("xmpp")) { - res = userService.deleteJID(visitor.getUid(), params[1]); - } else if (params[0].equals("xmpp-unauth")) { - res = userService.unauthJID(visitor.getUid(), params[1]); - } - if (res) { - result = "

Deleted. Back.

"; - } else { - result = "

Error

"; - } - break; - case "email-add": - if (!emailService.verifyAddressByCode(visitor.getUid(), request.getParameter("account"))) { - String authCode = RandomStringUtils.randomAlphanumeric(8).toUpperCase(); - if (emailService.addVerificationCode(visitor.getUid(), request.getParameter("account"), authCode)) { - Session session = Session.getDefaultInstance(System.getProperties()); - try { - MimeMessage message = new MimeMessage(session); - message.setFrom(new InternetAddress("noreply@mail.juick.com")); - message.addRecipient(Message.RecipientType.TO, new InternetAddress(request.getParameter("account"))); - message.setSubject("Juick authorization link"); - message.setText(String.format("Follow link to attach this email to Juick account:\n" + - "http://juick.com/settings?page=auth-email&code=%s\n\n" + - "If you don't know, what this mean - just ignore this mail.\n", authCode)); - Transport.send(message); - result = "

Authorization link has been sent to your email. Follow it to proceed.

" + - "

Back

"; - - } catch (MessagingException ex) { - logger.error("mail exception", ex); - throw new HttpBadRequestException(); - } - } - } - break; - case "email-del": - if (emailService.deleteEmail(visitor.getUid(), request.getParameter("account"))) { - result = "

Deleted. Back.

"; - } else { - result = "

An error occured while deleting.

"; - } - break; - case "email-subscr": - if (emailService.setNotificationsEmail(visitor.getUid(), request.getParameter("account"))) { - result = String.format("

Saved! Will send notifications to %s." + - "

Back

", request.getParameter("account")); - } else { - result = "

Disabled.

Back

"; - } - break; - case "twitter-del": - crosspostService.deleteTwitterToken(visitor.getUid()); - for (Cookie cookie : request.getCookies()) { - if (cookie.getName().equals("request_token")) { - cookie.setMaxAge(0); - response.addCookie(cookie); - } - if (cookie.getName().equals("request_token_secret")) { - cookie.setMaxAge(0); - response.addCookie(cookie); - } - } - result = "

Back

"; - break; - case "telegram-del": - telegramService.deleteTelegramUser(visitor.getUid()); - result = "

Back

"; - break; - case "facebook-disable": - crosspostService.disableFBCrosspost(visitor.getUid()); - result = "

Back

"; - break; - case "facebook-enable": - crosspostService.enableFBCrosspost(visitor.getUid()); - result = "

Back

"; - break; - case "vk-del": - crosspostService.deleteVKUser(visitor.getUid()); - result = "

Back

"; - break; - default: - throw new HttpBadRequestException(); - } - - model.addAttribute("title", "Настройки"); - model.addAttribute("visitor", visitor); - model.addAttribute("result", result); - return "views/settings_result"; - } - @PostMapping("/settings/unsubscribe") - public String unsubscribeOneClick(@RequestParam(name = "List-Unsubscribe") String unsubscribe, - ModelMap model) { - User user = UserUtils.getCurrentUser(); - if (!user.isAnonymous()) { - if (unsubscribe.equals("One-Click")) { - emailService.setNotificationsEmail(user.getUid(), StringUtils.EMPTY); - model.addAttribute("title", "Настройки"); - model.addAttribute("visitor", user); - model.addAttribute("result", "Unsubscribed"); - return "views/settings_result"; - } - } - throw new HttpBadRequestException(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/controllers/SignUp.java b/juick-server/src/main/java/com/juick/server/www/controllers/SignUp.java deleted file mode 100644 index 6a4fe063..00000000 --- a/juick-server/src/main/java/com/juick/server/www/controllers/SignUp.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.server.www.controllers; - -import com.juick.server.util.HttpBadRequestException; -import com.juick.server.util.HttpForbiddenException; -import com.juick.server.util.UserUtils; -import com.juick.service.CrosspostService; -import com.juick.service.EmailService; -import com.juick.service.MessengerService; -import com.juick.service.UserService; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; - -import javax.inject.Inject; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletResponse; - -/** - * - * @author Ugnich Anton - */ -@Controller -public class SignUp { - - @Inject - private UserService userService; - @Inject - private CrosspostService crosspostService; - @Inject - private MessengerService messengerService; - @Inject - private EmailService emailService; - - - @GetMapping("/signup") - protected String doGet(@RequestParam String type, @RequestParam String hash, ModelMap model) { - com.juick.User visitor = UserUtils.getCurrentUser(); - - if (hash.length() > 36 || !type.matches("^[a-zA-Z0-9\\-]+$") - || !hash.matches("^[a-zA-Z0-9\\-]+$")) { - throw new HttpBadRequestException(); - } - - String account = null; - switch (type) { - case "fb": - account = crosspostService.getFacebookNameByHash(hash); - break; - case "vk": - account = crosspostService.getVKNameByHash(hash); - break; - case "xmpp": - account = crosspostService.getJIDByHash(hash); - break; - case "durov": - account = crosspostService.getTelegramNameByHash(hash); - break; - case "messenger": - account = messengerService.getDisplayName(hash); - break; - case "email": - account = emailService.getEmailByAuthCode(hash); - } - if (account == null) { - throw new HttpBadRequestException(); - } - - model.addAttribute("title", "Новый пользователь"); - model.addAttribute("visitor", visitor); - model.addAttribute("account", account); - model.addAttribute("type", type); - model.addAttribute("hash", hash); - return "views/signup"; - } - - @PostMapping("/signup") - protected String doPost( - HttpServletResponse response, - @RequestParam String type, - @RequestParam String hash, - @RequestParam String action, - @RequestParam(required = false) String username, - @RequestParam(required = false) String password) { - com.juick.User visitor = UserUtils.getCurrentUser(); - int uid = 0; - - if (hash.length() > 36 || !type.matches("^[a-zA-Z0-9\\-]+$") || !hash.matches("^[a-zA-Z0-9\\-]+$")) { - throw new HttpBadRequestException(); - } - - if (action.charAt(0) == 'l') { - - if (visitor.isAnonymous()) { - if (username.length() > 32) { - throw new HttpBadRequestException(); - } - uid = userService.checkPassword(username, password); - } else { - uid = visitor.getUid(); - } - - if (uid <= 0) { - throw new HttpForbiddenException(); - } - - if (!(type.charAt(0) == 'f' && crosspostService.setFacebookUser(hash, uid)) - && !(type.charAt(0) == 'v' && crosspostService.setVKUser(hash, uid)) - && !(type.charAt(0) == 'd' && crosspostService.setTelegramUser(hash, uid)) - && !(type.charAt(0) == 'x' && userService.getAllJIDs(visitor).size() > 0 && crosspostService.setJIDUser(hash, uid)) - && !(type.charAt(0) == 'm' && messengerService.linkMessengerUser(hash, uid))) { - if (type.equals("email")) { - String email = emailService.getEmailByAuthCode(hash); - emailService.addEmail(uid, email); - emailService.deleteAuthCode(hash); - } else { - throw new HttpBadRequestException(); - } - } - - } else { // Create new account - if (username.length() < 2 || username.length() > 16 || !username.matches("^[a-zA-Z0-9\\-]+$") || password.length() < 6 || password.length() > 32) { - throw new HttpBadRequestException(); - } - - // CHECK USERNAME - - uid = userService.createUser(username, password); - if (uid <= 0) { - throw new HttpBadRequestException(); - } - - if (!(type.charAt(0) == 'f' && crosspostService.setFacebookUser(hash, uid)) - && !(type.charAt(0) == 'v' && crosspostService.setVKUser(hash, uid)) - && !(type.charAt(0) == 'd' && crosspostService.setTelegramUser(hash, uid)) - && !(type.charAt(0) == 'm' && messengerService.linkMessengerUser(hash, uid))) { - if (type.equals("email")) { - String email = emailService.getEmailByAuthCode(hash); - emailService.addEmail(uid, email); - emailService.deleteAuthCode(hash); - } else { - throw new HttpBadRequestException(); - } - } - } - - if (visitor.isAnonymous()) { - hash = userService.getHashByUID(uid); - Cookie c = new Cookie("hash", hash); - c.setMaxAge(365 * 24 * 60 * 60); - response.addCookie(c); - } - return "redirect:/"; - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/controllers/SocialLogin.java b/juick-server/src/main/java/com/juick/server/www/controllers/SocialLogin.java deleted file mode 100644 index bc631a1a..00000000 --- a/juick-server/src/main/java/com/juick/server/www/controllers/SocialLogin.java +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.server.www.controllers; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.scribejava.apis.FacebookApi; -import com.github.scribejava.apis.TwitterApi; -import com.github.scribejava.apis.VkontakteApi; -import com.github.scribejava.core.builder.ServiceBuilder; -import com.github.scribejava.core.model.*; -import com.github.scribejava.core.oauth.OAuth10aService; -import com.github.scribejava.core.oauth.OAuth20Service; -import com.juick.model.facebook.User; -import com.juick.server.Utils; -import com.juick.server.util.HttpBadRequestException; -import com.juick.server.util.UserUtils; -import com.juick.service.CrosspostService; -import com.juick.service.EmailService; -import com.juick.service.TelegramService; -import com.juick.service.UserService; -import com.juick.model.vk.UsersResponse; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.codec.digest.HmacAlgorithms; -import org.apache.commons.codec.digest.HmacUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.CookieValue; -import org.springframework.web.bind.annotation.GetMapping; -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 java.io.IOException; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; - -/** - * - * @author Ugnich Anton - */ -@Controller -public class SocialLogin { - - private static final Logger logger = LoggerFactory.getLogger(SocialLogin.class); - - @Value("${facebook_appid:appid}") - private String FACEBOOK_APPID; - @Value("${facebook_secret:secret}") - private String FACEBOOK_SECRET; - @Value("${ap_base_uri:http://localhost:8080/}") - private String baseUri; - private String facebookRedirectUri; - private static final String VK_REDIRECT = "http://juick.com/_vklogin"; - private static final String TWITTER_VERIFY_URL = "https://api.twitter.com/1.1/account/verify_credentials.json"; - @Inject - private ObjectMapper jsonMapper; - private ServiceBuilder facebookBuilder, twitterBuilder, vkBuilder; - - @Value("${twitter_consumer_key:appid}") - private String twitterConsumerKey; - @Value("${twitter_consumer_secret:secret}") - private String twitterConsumerSecret; - @Value("${vk_appid:appid}") - private String VK_APPID; - @Value("${vk_secret:secret}") - private String VK_SECRET; - @Value("${telegram_token:secret}") - private String telegramToken; - - @Inject - private CrosspostService crosspostService; - @Inject - private UserService userService; - @Inject - private EmailService emailService; - @Inject - private TelegramService telegramService; - - @PostConstruct - public void init() { - facebookBuilder = new ServiceBuilder(FACEBOOK_APPID); - twitterBuilder = new ServiceBuilder(twitterConsumerKey); - vkBuilder = new ServiceBuilder(VK_APPID); - UriComponentsBuilder facebookRedirectBuilder = UriComponentsBuilder.fromUriString(baseUri); - facebookRedirectUri = facebookRedirectBuilder.replacePath("/_fblogin").build().toUriString(); - } - - @GetMapping("/_fblogin") - protected String doFacebookLogin(HttpServletRequest request, - @RequestParam(required = false) String code, - @RequestParam(required = false) String state, - HttpServletResponse response) throws IOException, ExecutionException, InterruptedException { - if (StringUtils.isBlank(code)) { - String fbstate = UUID.randomUUID().toString(); - if (StringUtils.isBlank(state)) { - state = Utils.getPreviousPageByRequest(request).orElse("https://juick.com/"); - } - crosspostService.addFacebookState(fbstate, state); - OAuth20Service facebookAuthService = facebookBuilder - .apiSecret(FACEBOOK_SECRET) - .callback(facebookRedirectUri) - .scope("email") - .state(fbstate) - .build(FacebookApi.instance()); - return "redirect:" + facebookAuthService.getAuthorizationUrl(); - } - - String redirectUrl = crosspostService.verifyFacebookState(state); - if (StringUtils.isEmpty(redirectUrl)) { - logger.error("state is missing"); - throw new HttpBadRequestException(); - } - OAuth20Service facebookService = facebookBuilder - .apiKey(FACEBOOK_APPID) - .apiSecret(FACEBOOK_SECRET) - .callback(facebookRedirectUri) - .scope("email") - .state(state) - .build(FacebookApi.instance()); - OAuth2AccessToken token = facebookService.getAccessToken(code); - final OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://graph.facebook.com/v2.10/me?fields=id,name,link,verified,email"); - facebookService.signRequest(token, meRequest); - String graph = facebookService.execute(meRequest).getBody(); - if (StringUtils.isBlank(graph)) { - logger.error("FACEBOOK GRAPH ERROR"); - throw new HttpBadRequestException(); - } - User fb = jsonMapper.readValue(graph, User.class); - long fbID = NumberUtils.toLong(fb.getId(), 0); - if (fbID == 0 || StringUtils.isBlank(fb.getName()) || StringUtils.isBlank(fb.getLink())) { - logger.error("Missing required fields, id: {}, name: {}, link: {}", fbID, fb.getName(), fb.getLink()); - throw new HttpBadRequestException(); - } - - int uid = crosspostService.getUIDbyFBID(fbID); - if (uid > 0) { - if (!crosspostService.updateFacebookUser(fbID, token.getAccessToken(), fb.getName(), fb.getLink())) { - logger.error("error updating facebook user, id: {}, token: {}", fbID, token.getAccessToken()); - throw new HttpBadRequestException(); - } - Cookie c = new Cookie("hash", userService.getHashByUID(uid)); - c.setMaxAge(50 * 24 * 60 * 60); - response.addCookie(c); - return "redirect:" + redirectUrl; - } else if (fb.getVerified()) { - if (!crosspostService.createFacebookUser(fbID, state, token.getAccessToken(), fb.getName(), fb.getLink())) { - if (StringUtils.isNotEmpty(fb.getEmail())) { - logger.info("found {} for facebook user {}", fb.getEmail(), fb.getLink()); - Integer userId = crosspostService.getUIDbyFBID(fbID); - if (!emailService.getEmails(userId, false).contains(fb.getEmail())) { - emailService.addEmail(userId, fb.getEmail()); - } - } - logger.info("email not found for facebook user {}", fb.getLink()); - throw new HttpBadRequestException(); - } - return "redirect:/signup?type=fb&hash=" + state; - } else { - logger.error("Facebook account is not verified, id: {}", fbID); - throw new HttpBadRequestException(); - } - } - @GetMapping("/_twitter") - protected void doTwitterLogin(HttpServletRequest request, HttpServletResponse response) - throws IOException, ExecutionException, InterruptedException { - String hash = StringUtils.EMPTY, request_token = StringUtils.EMPTY, request_token_secret = StringUtils.EMPTY; - String verifier = request.getParameter("oauth_verifier"); - Cookie[] cookies = request.getCookies(); - for (Cookie cookie : cookies) { - if (cookie.getName().equals("hash")) { - hash = cookie.getValue(); - } - if (cookie.getName().equals("request_token")) { - request_token = cookie.getValue(); - } - if (cookie.getName().equals("request_token_secret")) { - request_token_secret = cookie.getValue(); - } - } - com.juick.User user = UserUtils.getCurrentUser(); - OAuth10aService oAuthService = twitterBuilder - .apiSecret(twitterConsumerSecret) - .callback("https://juick.com/_twitter") - .build(TwitterApi.instance()); - - if (request_token.isEmpty() && request_token_secret.isEmpty() - && (verifier == null || verifier.isEmpty())) { - OAuth1RequestToken requestToken = oAuthService.getRequestToken(); - String authUrl = oAuthService.getAuthorizationUrl(requestToken); - response.addCookie(new Cookie("request_token", requestToken.getToken())); - response.addCookie(new Cookie("request_token_secret", requestToken.getTokenSecret())); - response.setStatus(HttpServletResponse.SC_FOUND); - response.setHeader("Location", authUrl); - } else { - if (verifier != null && verifier.length() > 0) { - OAuth1RequestToken requestToken = new OAuth1RequestToken(request_token, request_token_secret); - OAuth1AccessToken accessToken = oAuthService.getAccessToken(requestToken, verifier); - OAuthRequest oAuthRequest = new OAuthRequest(Verb.GET, TWITTER_VERIFY_URL); - oAuthService.signRequest(accessToken, oAuthRequest); - com.juick.model.twitter.User twitterUser = jsonMapper.readValue(oAuthService.execute(oAuthRequest).getBody(), - com.juick.model.twitter.User.class); - if (userService.linkTwitterAccount(user, accessToken.getToken(), accessToken.getTokenSecret(), - twitterUser.getScreenName())) { - response.setStatus(HttpServletResponse.SC_FOUND); - response.setHeader("Location", "http://juick.com/settings"); - } else { - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - } - } - } - } - @GetMapping("/_vklogin") - protected String doVKLogin(HttpServletRequest request, - @RequestParam(required = false) String code, - @RequestParam(required = false) String state, - @CookieValue(required = false) String vkstate, - HttpServletResponse response) throws IOException, ExecutionException, InterruptedException { - if (StringUtils.isBlank(code)) { - vkstate = UUID.randomUUID().toString(); - Cookie c = new Cookie("vkstate", vkstate); - response.addCookie(c); - OAuth20Service vkAuthService = vkBuilder - .apiSecret(VK_SECRET) - .scope("friends,wall,offline") - .state(vkstate) - .callback(VK_REDIRECT) - .build(VkontakteApi.instance()); - return "redirect:" + vkAuthService.getAuthorizationUrl(); - } - - if (StringUtils.isBlank(vkstate) || !vkstate.equals(state)) { - throw new HttpBadRequestException(); - } else { - Cookie c = new Cookie("vkstate", "-"); - c.setMaxAge(0); - response.addCookie(c); - } - - OAuth20Service vkService = vkBuilder - .apiKey(VK_APPID) - .apiSecret(VK_SECRET) - .build(VkontakteApi.instance()); - OAuth2AccessToken token = vkService.getAccessToken(code); - - OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://api.vk.com/method/users.get?fields=screen_name&v=5.73"); - vkService.signRequest(token, meRequest); - String graph = vkService.execute(meRequest).getBody(); - - com.juick.model.vk.User jsonUser = jsonMapper.readValue(graph, UsersResponse.class).getUsers().get(0); - String vkName = jsonUser.getFirstName() + " " + jsonUser.getLastName(); - String vkLink = jsonUser.getScreenName(); - - if (vkName.length() == 1 || StringUtils.isBlank(vkLink)) { - logger.error("vk user error"); - throw new HttpBadRequestException(); - } - - Long vkID = NumberUtils.toLong(jsonUser.getId(), 0); - int uid = crosspostService.getUIDbyVKID(vkID); - if (uid > 0) { - Cookie c = new Cookie("hash", userService.getHashByUID(uid)); - c.setMaxAge(50 * 24 * 60 * 60); - response.addCookie(c); - return "redirect:/" + Utils.getPreviousPageByRequest(request).orElse(StringUtils.EMPTY); - } else { - String loginhash = UUID.randomUUID().toString(); - if (!crosspostService.createVKUser(vkID, loginhash, token.getAccessToken(), vkName, vkLink)) { - logger.error("create vk user error"); - throw new HttpBadRequestException(); - } - return "redirect:/signup?type=vk&hash=" + loginhash; - } - } - - @GetMapping("/_tglogin") - public String doDurovLogin(HttpServletRequest request, - @RequestParam Map params, - HttpServletResponse response) { - String dataCheckString = params.entrySet().stream() - .filter(p -> !p.getKey().equals("hash")) - .sorted(Map.Entry.comparingByKey()) - .map(p -> p.getKey() + "=" + p.getValue()) - .collect(Collectors.joining("\n")); - String hash = params.get("hash"); - byte[] secretKey = DigestUtils.sha256(telegramToken); - String resultString = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, secretKey).hmacHex(dataCheckString); - if (hash.equals(resultString)) { - Long tgUser = Long.valueOf(params.get("id")); - int uid = telegramService.getUser(tgUser); - if (uid > 0) { - Cookie c = new Cookie("hash", userService.getHashByUID(uid)); - c.setMaxAge(50 * 24 * 60 * 60); - response.addCookie(c); - return "redirect:/" + Utils.getPreviousPageByRequest(request).orElse(StringUtils.EMPTY); - } else { - String username = StringUtils.defaultString(params.get("username"), params.get("first_name")); - telegramService.createTelegramUser(tgUser, username); - return "redirect:/signup?type=durov&hash=" + userService.getSignUpHashByTelegramID(tgUser, username); - } - } else { - logger.warn("invalid tg hash {} for {}", resultString, hash); - } - throw new HttpBadRequestException(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/JidConverter.java b/juick-server/src/main/java/com/juick/server/xmpp/JidConverter.java deleted file mode 100644 index e9a9707e..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/JidConverter.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.juick.server.xmpp; - -import org.springframework.core.convert.converter.Converter; -import org.springframework.lang.Nullable; -import rocks.xmpp.addr.Jid; - -public class JidConverter implements Converter { - @Nullable - @Override - public Jid convert(String jidStr) { - return Jid.of(jidStr); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/XMPPStatusPage.java b/juick-server/src/main/java/com/juick/server/xmpp/XMPPStatusPage.java deleted file mode 100644 index 231696ec..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/XMPPStatusPage.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.juick.server.xmpp; - -import com.juick.server.XMPPServer; -import com.juick.server.xmpp.helpers.XMPPStatus; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; -import rocks.xmpp.addr.Jid; -import springfox.documentation.annotations.ApiIgnore; - -import javax.inject.Inject; -import java.util.stream.Collectors; - -@RestController -@ConditionalOnProperty("xmppbot_jid") -public class XMPPStatusPage { - @Inject - private XMPPServer xmpp; - @ApiIgnore - @RequestMapping(method = RequestMethod.GET, value = "/api/xmpp-status", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public XMPPStatus xmppStatus() { - XMPPStatus status = new XMPPStatus(); - if (xmpp != null) { - status.setInbound(xmpp.getInConnections().stream().map(c -> c.from).flatMap(j -> j.stream().map(Jid::getDomain)).collect(Collectors.toList())); - status.setOutbound(xmpp.getOutConnections().keySet().stream() - .map(c -> c.to).map(Jid::getDomain).collect(Collectors.toList())); - } - return status; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/helpers/XMPPStatus.java b/juick-server/src/main/java/com/juick/server/xmpp/helpers/XMPPStatus.java deleted file mode 100644 index 99d89866..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/helpers/XMPPStatus.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.xmpp.helpers; - -import com.juick.server.xmpp.s2s.ConnectionIn; -import com.juick.server.xmpp.s2s.ConnectionOut; - -import java.util.List; -import java.util.Set; - -/** - * Created by vitalyster on 16.02.2017. - */ -public class XMPPStatus { - private List inbound; - private List outbound; - - public List getInbound() { - return inbound; - } - - public void setInbound(List inbound) { - this.inbound = inbound; - } - - public List getOutbound() { - return outbound; - } - - public void setOutbound(List outbound) { - this.outbound = outbound; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/iq/MessageQuery.java b/juick-server/src/main/java/com/juick/server/xmpp/iq/MessageQuery.java deleted file mode 100644 index 7500cbf8..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/iq/MessageQuery.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.juick.server.xmpp.iq; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement(name = "query") -public class MessageQuery { - private MessageQuery() { - - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/iq/package-info.java b/juick-server/src/main/java/com/juick/server/xmpp/iq/package-info.java deleted file mode 100644 index dada8289..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/iq/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlSchema(namespace = "http://juick.com/query#messages", elementFormDefault = XmlNsForm.QUALIFIED) -package com.juick.server.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 diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/Handshake.java b/juick-server/src/main/java/com/juick/server/xmpp/router/Handshake.java deleted file mode 100644 index 0bc501dd..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/Handshake.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.juick.server.xmpp.router; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; - -/** - * Created by vitalyster on 30.01.2017. - */ -public class Handshake { - private String value; - - public static Handshake parse(XmlPullParser parser) throws IOException, XmlPullParserException { - parser.next(); - Handshake handshake = new Handshake(); - handshake.setValue(XmlUtils.getTagText(parser)); - return handshake; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - @Override - public String toString() { - StringBuilder str = new StringBuilder("").append(getValue()).append(""); - } else { - str.append("/>"); - } - return str.toString(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/Stream.java b/juick-server/src/main/java/com/juick/server/xmpp/router/Stream.java deleted file mode 100644 index 2154edf6..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/Stream.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Juick - * Copyright (C) 2008-2011, Ugnich Anton - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.server.xmpp.router; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlPullParserFactory; -import rocks.xmpp.addr.Jid; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.UUID; - -/** - * - * @author Ugnich Anton - */ -public abstract class Stream { - - public boolean isLoggedIn() { - return loggedIn; - } - - public void setLoggedIn(boolean loggedIn) { - this.loggedIn = loggedIn; - } - - public Jid from; - public Jid to; - private InputStream is; - private OutputStream os; - private XmlPullParserFactory factory; - protected XmlPullParser parser; - private OutputStreamWriter writer; - StreamHandler streamHandler; - private boolean loggedIn; - private Instant created; - private Instant updated; - String streamId; - private boolean secured; - - public Stream(final Jid from, final Jid to, final InputStream is, final OutputStream os) throws XmlPullParserException { - this.from = from; - this.to = to; - this.is = is; - this.os = os; - factory = XmlPullParserFactory.newInstance(); - created = updated = Instant.now(); - streamId = UUID.randomUUID().toString(); - } - - public void restartStream() throws XmlPullParserException { - parser = factory.newPullParser(); - parser.setInput(new InputStreamReader(is, StandardCharsets.UTF_8)); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - writer = new OutputStreamWriter(os, StandardCharsets.UTF_8); - } - - public void connect() { - try { - restartStream(); - handshake(); - parse(); - } catch (XmlPullParserException e) { - StreamError invalidXmlError = new StreamError("invalid-xml"); - send(invalidXmlError.toString()); - connectionFailed(new Exception(invalidXmlError.getCondition())); - } catch (IOException e) { - connectionFailed(e); - } - } - - public void setHandler(final StreamHandler streamHandler) { - this.streamHandler = streamHandler; - } - - public abstract void handshake() throws XmlPullParserException, IOException; - - public void logoff() { - setLoggedIn(false); - try { - writer.flush(); - writer.close(); - //TODO close parser - } catch (final Exception e) { - connectionFailed(e); - } - } - - public void send(final String str) { - try { - updated = Instant.now(); - writer.write(str); - writer.flush(); - } catch (final Exception e) { - connectionFailed(e); - } - } - - private void parse() throws IOException, XmlPullParserException { - while (parser.next() != XmlPullParser.END_DOCUMENT) { - if (parser.getEventType() == XmlPullParser.IGNORABLE_WHITESPACE) { - setUpdated(); - } - if (parser.getEventType() != XmlPullParser.START_TAG) { - continue; - } - setUpdated(); - final String tag = parser.getName(); - switch (tag) { - case "message": - case "presence": - case "iq": - streamHandler.stanzaReceived(XmlUtils.parseToString(parser, false)); - break; - case "error": - StreamError error = StreamError.parse(parser); - connectionFailed(new Exception(error.getCondition())); - return; - default: - XmlUtils.skip(parser); - break; - } - } - } - - /** - * This method is used to be called on a parser or a connection error. - * It tries to close the XML-Reader and XML-Writer one last time. - */ - private void connectionFailed(final Exception ex) { - if (isLoggedIn()) { - try { - writer.close(); - //TODO close parser - } catch (Exception e) { - } - } - if (streamHandler != null) { - streamHandler.fail(ex); - } - } - - public Instant getCreated() { - return created; - } - - public Instant getUpdated() { - return updated; - } - public String getStreamId() { - return streamId; - } - - public boolean isSecured() { - return secured; - } - - public void setSecured(boolean secured) { - this.secured = secured; - } - - public void setUpdated() { - this.updated = Instant.now(); - } - - public InputStream getInputStream() { - return is; - } - - public void setInputStream(InputStream is) { - this.is = is; - } - - public OutputStream getOutputStream() { - return os; - } - - public void setOutputStream(OutputStream os) { - this.os = os; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamComponentServer.java b/juick-server/src/main/java/com/juick/server/xmpp/router/StreamComponentServer.java deleted file mode 100644 index a58adfc5..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamComponentServer.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.juick.server.xmpp.router; - -import org.apache.commons.codec.digest.DigestUtils; -import org.xmlpull.v1.XmlPullParserException; -import rocks.xmpp.addr.Jid; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.UUID; - -/** - * Created by vitalyster on 30.01.2017. - */ -public class StreamComponentServer extends Stream { - - private String streamId, secret; - - public String getStreamId() { - return streamId; - } - - - public StreamComponentServer(InputStream is, OutputStream os, String password) throws XmlPullParserException { - super(null, null, is, os); - secret = password; - streamId = UUID.randomUUID().toString(); - } - @Override - public void handshake() throws XmlPullParserException, IOException { - parser.next(); - if (!parser.getName().equals("stream") - || !parser.getNamespace(null).equals(StreamNamespaces.NS_COMPONENT_ACCEPT) - || !parser.getNamespace("stream").equals(StreamNamespaces.NS_STREAM)) { - throw new IOException("invalid stream"); - } - Jid domain = Jid.of(parser.getAttributeValue(null, "to")); - if (streamHandler.filter(null, domain)) { - send(new XMPPError(XMPPError.Type.cancel, "forbidden").toString()); - throw new IOException("invalid domain"); - } - from = domain; - to = domain; - send(String.format("", StreamNamespaces.NS_STREAM, StreamNamespaces.NS_COMPONENT_ACCEPT, from.asBareJid().toEscapedString(), streamId)); - Handshake handshake = Handshake.parse(parser); - boolean authenticated = handshake.getValue().equals(DigestUtils.sha1Hex(streamId + secret)); - setLoggedIn(authenticated); - if (!authenticated) { - send(new XMPPError(XMPPError.Type.cancel, "not-authorized").toString()); - streamHandler.fail(new IOException("stream:stream, failed authentication")); - return; - } - send(new Handshake().toString()); - streamHandler.ready(this); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamError.java b/juick-server/src/main/java/com/juick/server/xmpp/router/StreamError.java deleted file mode 100644 index f731f039..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamError.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.juick.server.xmpp.router; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; - - -/** - * Created by vitalyster on 03.02.2017. - */ -public class StreamError { - - private String condition; - private String text; - - public StreamError() {} - - public StreamError(String condition) { - this.condition = condition; - } - - public static StreamError parse(XmlPullParser parser) throws IOException, XmlPullParserException { - StreamError streamError = new StreamError(); - final int initial = parser.getDepth(); - while (true) { - int eventType = parser.next(); - if (eventType == XmlPullParser.START_TAG && parser.getDepth() == initial + 1) { - final String tag = parser.getName(); - final String xmlns = parser.getNamespace(); - if (tag.equals("text") && xmlns.equals(StreamNamespaces.NS_XMPP_STREAMS)) { - streamError.text = XmlUtils.getTagText(parser); - } else if (xmlns.equals(StreamNamespaces.NS_XMPP_STREAMS)) { - streamError.condition = tag; - } else { - XmlUtils.skip(parser); - } - } else if (eventType == XmlPullParser.END_TAG && parser.getDepth() == initial) { - break; - } - } - return streamError; - } - - public String getCondition() { - return condition; - } - - @Override - public String toString() { - return String.format("<%s xmlns='%s'/>", condition, StreamNamespaces.NS_XMPP_STREAMS); - } - - public String getText() { - return text; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamFeatures.java b/juick-server/src/main/java/com/juick/server/xmpp/router/StreamFeatures.java deleted file mode 100644 index e8fc324f..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamFeatures.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Juick - * Copyright (C) 2008-2013, Ugnich Anton - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.server.xmpp.router; - -import java.io.IOException; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -/** - * - * @author Ugnich Anton - */ -public class StreamFeatures { - - public static final int NOTAVAILABLE = -1; - public static final int AVAILABLE = 0; - public static final int REQUIRED = 1; - public int STARTTLS = NOTAVAILABLE; - public int ZLIB = NOTAVAILABLE; - public int PLAIN = NOTAVAILABLE; - public int DIGEST_MD5 = NOTAVAILABLE; - public int REGISTER = NOTAVAILABLE; - public int EXTERNAL = NOTAVAILABLE; - - public static StreamFeatures parse(final XmlPullParser parser) throws XmlPullParserException, IOException { - StreamFeatures features = new StreamFeatures(); - final int initial = parser.getDepth(); - while (true) { - int eventType = parser.next(); - if (eventType == XmlPullParser.START_TAG && parser.getDepth() == initial + 1) { - final String tag = parser.getName(); - final String xmlns = parser.getNamespace(); - if (tag.equals("starttls") && xmlns != null && xmlns.equals("urn:ietf:params:xml:ns:xmpp-tls")) { - features.STARTTLS = AVAILABLE; - while (parser.next() == XmlPullParser.START_TAG) { - if (parser.getName().equals("required")) { - features.STARTTLS = REQUIRED; - } else { - XmlUtils.skip(parser); - } - } - } else if (tag.equals("compression") && xmlns != null && xmlns.equals("http://jabber.org/features/compress")) { - while (parser.next() == XmlPullParser.START_TAG) { - if (parser.getName().equals("method")) { - final String method = XmlUtils.getTagText(parser).toUpperCase(); - if (method.equals("ZLIB")) { - features.ZLIB = AVAILABLE; - } - } else { - XmlUtils.skip(parser); - } - } - } else if (tag.equals("mechanisms") && xmlns != null && xmlns.equals("urn:ietf:params:xml:ns:xmpp-sasl")) { - while (parser.next() == XmlPullParser.START_TAG) { - if (parser.getName().equals("mechanism")) { - final String mechanism = XmlUtils.getTagText(parser).toUpperCase(); - if (mechanism.equals("PLAIN")) { - features.PLAIN = AVAILABLE; - } else if (mechanism.equals("DIGEST-MD5")) { - features.DIGEST_MD5 = AVAILABLE; - } else if (mechanism.equals("EXTERNAL")) { - features.EXTERNAL = AVAILABLE; - } - } else { - XmlUtils.skip(parser); - } - } - } else if (tag.equals("register") && xmlns != null && xmlns.equals("http://jabber.org/features/iq-register")) { - features.REGISTER = AVAILABLE; - XmlUtils.skip(parser); - } else { - XmlUtils.skip(parser); - } - } else if (eventType == XmlPullParser.END_TAG && parser.getDepth() == initial) { - break; - } - } - return features; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamHandler.java b/juick-server/src/main/java/com/juick/server/xmpp/router/StreamHandler.java deleted file mode 100644 index 048c61ec..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamHandler.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.juick.server.xmpp.router; - -import rocks.xmpp.addr.Jid; - -/** - * Created by vitalyster on 01.02.2017. - */ -public interface StreamHandler { - void ready(StreamComponentServer componentServer); - void fail(final Exception ex); - boolean filter(Jid from, Jid to); - void stanzaReceived(String stanza); -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamNamespaces.java b/juick-server/src/main/java/com/juick/server/xmpp/router/StreamNamespaces.java deleted file mode 100644 index 1b9b1965..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamNamespaces.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.juick.server.xmpp.router; - -public class StreamNamespaces { - public static final String NS_STREAM = "http://etherx.jabber.org/streams"; - public static final String NS_TLS = "urn:ietf:params:xml:ns:xmpp-tls"; - public static final String NS_DB = "jabber:server:dialback"; - public static final String NS_SERVER = "jabber:server"; - public static final String NS_COMPONENT_ACCEPT = "jabber:component:accept"; - public static final String NS_XMPP_STREAMS = "urn:ietf:params:xml:ns:xmpp-streams"; -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPError.java b/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPError.java deleted file mode 100644 index 0cf9a3bc..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPError.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Juick - * Copyright (C) 2008-2013, ugnich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.server.xmpp.router; - -import org.apache.commons.text.StringEscapeUtils; - -/** - * - * @author ugnich - */ -public class XMPPError { - - public static final class Type { - - public static final String auth = "auth"; - public static final String cancel = "cancel"; - public static final String continue_ = "continue"; - public static final String modify = "modify"; - public static final String wait = "wait"; - } - private final static String TagName = "error"; - public String by = null; - private String type; - private String condition; - private String text = null; - - public XMPPError(String type, String condition) { - this.type = type; - this.condition = condition; - } - - @Override - public String toString() { - StringBuilder str = new StringBuilder("<").append(TagName).append(""); - if (by != null) { - str.append(" by=\"").append(StringEscapeUtils.escapeXml10(by)).append("\""); - } - if (type != null) { - str.append(" type=\"").append(StringEscapeUtils.escapeXml10(type)).append("\""); - } - - if (condition != null) { - str.append(">"); - str.append("<").append(StringEscapeUtils.escapeXml10(condition)).append(" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\""); - if (text != null) { - str.append(">").append(StringEscapeUtils.escapeXml10(text)).append(""); - } else { - str.append("/>"); - } - str.append(""); - } else { - str.append("/>"); - } - - return str.toString(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPRouter.java b/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPRouter.java deleted file mode 100644 index 6d67fa9c..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPRouter.java +++ /dev/null @@ -1,220 +0,0 @@ -package com.juick.server.xmpp.router; - -import com.juick.server.XMPPServer; -import com.juick.server.xmpp.s2s.BasicXmppSession; -import com.juick.server.xmpp.s2s.CacheEntry; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.xmlpull.v1.XmlPullParserException; -import rocks.xmpp.addr.Jid; -import rocks.xmpp.core.stanza.model.IQ; -import rocks.xmpp.core.stanza.model.Message; -import rocks.xmpp.core.stanza.model.Presence; -import rocks.xmpp.core.stanza.model.Stanza; -import rocks.xmpp.core.stanza.model.server.ServerIQ; -import rocks.xmpp.core.stanza.model.server.ServerMessage; -import rocks.xmpp.core.stanza.model.server.ServerPresence; -import rocks.xmpp.util.XmppUtils; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; -import java.io.IOException; -import java.io.StringReader; -import java.io.StringWriter; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketException; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ExecutorService; - -public class XMPPRouter implements StreamHandler { - private static final Logger logger = LoggerFactory.getLogger("com.juick.server.xmpp"); - - @Inject - private ExecutorService service; - - private final List connections = Collections.synchronizedList(new ArrayList<>()); - private final List outCache = new CopyOnWriteArrayList<>(); - - private ServerSocket listener; - - @Inject - private BasicXmppSession session; - - @Value("${router_port:5347}") - private int routerPort; - - @Inject - private XMPPServer xmppServer; - - @PostConstruct - public void init() { - logger.info("component router initialized"); - service.submit(() -> { - try { - listener = new ServerSocket(routerPort); - logger.info("component router listening on {}", routerPort); - while (!listener.isClosed()) { - if (Thread.currentThread().isInterrupted()) break; - Socket socket = listener.accept(); - service.submit(() -> { - try { - StreamComponentServer client = new StreamComponentServer(socket.getInputStream(), socket.getOutputStream(), "secret"); - addConnectionIn(client); - client.setHandler(this); - client.connect(); - } catch (IOException e) { - logger.error("component error", e); - } catch (XmlPullParserException e) { - e.printStackTrace(); - } - }); - } - } catch (SocketException e) { - // shutdown - } catch (IOException e) { - logger.warn("io exception", e); - } - }); - } - - @PreDestroy - public void close() throws Exception { - if (!listener.isClosed()) { - listener.close(); - } - synchronized (getConnections()) { - for (Iterator i = getConnections().iterator(); i.hasNext(); ) { - StreamComponentServer c = i.next(); - c.logoff(); - i.remove(); - } - } - service.shutdown(); - logger.info("XMPP router destroyed"); - } - - private void addConnectionIn(StreamComponentServer c) { - synchronized (getConnections()) { - getConnections().add(c); - } - } - - private void sendOut(Stanza s) { - try { - StringWriter stanzaWriter = new StringWriter(); - XMLStreamWriter xmppStreamWriter = XmppUtils.createXmppStreamWriter( - session.getConfiguration().getXmlOutputFactory().createXMLStreamWriter(stanzaWriter)); - session.createMarshaller().marshal(s, xmppStreamWriter); - xmppStreamWriter.flush(); - xmppStreamWriter.close(); - String xml = stanzaWriter.toString(); - logger.info("XMPPRouter (out): {}", xml); - sendOut(s.getTo().getDomain(), xml); - } catch (XMLStreamException | JAXBException e1) { - logger.info("jaxb exception", e1); - } - } - - private void sendOut(String hostname, String xml) { - boolean haveAnyConn = false; - - StreamComponentServer connOut = null; - synchronized (getConnections()) { - for (StreamComponentServer c : getConnections()) { - if (c.to != null && c.to.getDomain().equals(hostname)) { - if (c.isLoggedIn()) { - connOut = c; - break; - } else { - logger.info("bouncing stanza to {} component until it will be ready", hostname); - boolean haveCache = false; - for (CacheEntry entry : outCache) { - if (entry.hostname != null && entry.hostname.equals(hostname)) { - entry.xml += xml; - entry.updated = Instant.now(); - haveCache = true; - break; - } - } - if (!haveCache) { - outCache.add(new CacheEntry(Jid.of(hostname), xml)); - } - } - } - } - } - if (connOut != null) { - connOut.send(xml); - return; - } - xmppServer.sendOut(Jid.of(hostname), xml); - - } - - public List getConnections() { - return connections; - } - - private Stanza parse(String xml) { - try { - Unmarshaller unmarshaller = session.createUnmarshaller(); - return (Stanza)unmarshaller.unmarshal(new StringReader(xml)); - } catch (JAXBException e) { - logger.error("JAXB exception", e); - } - return null; - } - @Override - public void stanzaReceived(String stanza) { - Stanza input = parse(stanza); - if (input instanceof Message) { - sendOut(ServerMessage.from((Message)input)); - } else if (input instanceof IQ) { - sendOut(ServerIQ.from((IQ)input)); - } else { - sendOut(ServerPresence.from((Presence) input)); - } - } - - public String getFromCache(Jid to) { - final String[] cache = new String[1]; - outCache.stream().filter(c -> c.hostname != null && c.hostname.equals(to)).findFirst().ifPresent(c -> { - cache[0] = c.xml; - outCache.remove(c); - }); - return cache[0]; - } - - @Override - public void ready(StreamComponentServer componentServer) { - logger.info("component {} ready", componentServer.to); - String cache = getFromCache(componentServer.to); - if (cache != null) { - logger.debug("sending cache to {}", componentServer.to); - componentServer.send(cache); - } - } - - @Override - public void fail(Exception e) { - - } - - @Override - public boolean filter(Jid jid, Jid jid1) { - return false; - } -} \ No newline at end of file diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/XmlUtils.java b/juick-server/src/main/java/com/juick/server/xmpp/router/XmlUtils.java deleted file mode 100644 index 7579489f..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/XmlUtils.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Juick - * Copyright (C) 2008-2011, Ugnich Anton - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.server.xmpp.router; - -import java.io.IOException; - -import org.apache.commons.text.StringEscapeUtils; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -/** - * - * @author Ugnich Anton - */ -public class XmlUtils { - - public static void skip(XmlPullParser parser) throws XmlPullParserException, IOException { - String tag = parser.getName(); - while (parser.getName() != null && !(parser.next() == XmlPullParser.END_TAG && parser.getName().equals(tag))) { - } - } - - public static String getTagText(XmlPullParser parser) throws XmlPullParserException, IOException { - String ret = ""; - String tag = parser.getName(); - - if (parser.next() == XmlPullParser.TEXT) { - ret = parser.getText(); - } - - while (!(parser.getEventType() == XmlPullParser.END_TAG && parser.getName().equals(tag))) { - parser.next(); - } - - return ret; - } - - public static String parseToString(XmlPullParser parser, boolean skipXMLNS) throws XmlPullParserException, IOException { - String tag = parser.getName(); - StringBuilder ret = new StringBuilder("<").append(tag); - - // skipXMLNS for xmlns="jabber:client" - - String ns = parser.getNamespace(); - if (!skipXMLNS && ns != null && !ns.isEmpty()) { - ret.append(" xmlns=\"").append(ns).append("\""); - } - - for (int i = 0; i < parser.getAttributeCount(); i++) { - String attr = parser.getAttributeName(i); - if ((!skipXMLNS || !attr.equals("xmlns")) && !attr.contains(":")) { - ret.append(" ").append(attr).append("=\"").append(StringEscapeUtils.escapeXml10(parser.getAttributeValue(i))).append("\""); - } - } - ret.append(">"); - - while (!(parser.next() == XmlPullParser.END_TAG && parser.getName().equals(tag))) { - int event = parser.getEventType(); - if (event == XmlPullParser.START_TAG) { - if (!parser.getName().contains(":")) { - ret.append(parseToString(parser, false)); - } else { - skip(parser); - } - } else if (event == XmlPullParser.TEXT) { - ret.append(StringEscapeUtils.escapeXml10(parser.getText())); - } - } - - ret.append(""); - return ret.toString(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/BasicXmppSession.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/BasicXmppSession.java deleted file mode 100644 index ae28f827..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/BasicXmppSession.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.xmpp.s2s; - -import rocks.xmpp.addr.Jid; -import rocks.xmpp.core.XmppException; -import rocks.xmpp.core.session.XmppSession; -import rocks.xmpp.core.session.XmppSessionConfiguration; -import rocks.xmpp.core.stanza.model.IQ; -import rocks.xmpp.core.stanza.model.Message; -import rocks.xmpp.core.stanza.model.Presence; -import rocks.xmpp.core.stanza.model.server.ServerIQ; -import rocks.xmpp.core.stanza.model.server.ServerMessage; -import rocks.xmpp.core.stanza.model.server.ServerPresence; -import rocks.xmpp.core.stream.model.StreamElement; - -/** - * Created by vitalyster on 06.02.2017. - */ -public class BasicXmppSession extends XmppSession { - protected BasicXmppSession(String xmppServiceDomain, XmppSessionConfiguration configuration) { - super(xmppServiceDomain, configuration); - } - - public static BasicXmppSession create(String xmppServiceDomain, XmppSessionConfiguration configuration) { - BasicXmppSession session = new BasicXmppSession(xmppServiceDomain, configuration); - notifyCreationListeners(session); - return session; - } - - @Override - public void connect(Jid from) throws XmppException { - - } - - @Override - public Jid getConnectedResource() { - return null; - } - - @Override - protected StreamElement prepareElement(StreamElement element) { - if (element instanceof Message) { - element = ServerMessage.from((Message) element); - } else if (element instanceof Presence) { - element = ServerPresence.from((Presence) element); - } else if (element instanceof IQ) { - element = ServerIQ.from((IQ) element); - } - - return element; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/CacheEntry.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/CacheEntry.java deleted file mode 100644 index 33e875bd..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/CacheEntry.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.xmpp.s2s; - -import rocks.xmpp.addr.Jid; - -import java.time.Instant; - -/** - * - * @author ugnich - */ -public class CacheEntry { - - public Jid hostname; - public Instant created; - public Instant updated; - public String xml; - - public CacheEntry(Jid hostname, String xml) { - this.hostname = hostname; - this.created = this.updated =Instant.now(); - this.xml = xml; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/Connection.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/Connection.java deleted file mode 100644 index 4fa8e741..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/Connection.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.xmpp.s2s; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.juick.server.XMPPServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlPullParserFactory; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.net.Socket; -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.UUID; - -/** - * - * @author ugnich - */ -public class Connection { - - protected static final Logger logger = LoggerFactory.getLogger(Connection.class); - - public String streamID; - public Instant created; - public Instant updated; - public long bytesLocal = 0; - public long packetsLocal = 0; - XMPPServer xmpp; - private Socket socket; - public static final String NS_DB = "jabber:server:dialback"; - public static final String NS_TLS = "urn:ietf:params:xml:ns:xmpp-tls"; - public static final String NS_SASL = "urn:ietf:params:xml:ns:xmpp-sasl"; - public static final String NS_STREAM = "http://etherx.jabber.org/streams"; - XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); - XmlPullParser parser = factory.newPullParser(); - OutputStreamWriter writer; - private boolean secured = false; - private boolean authenticated = false; - private boolean trusted = false; - - - - public Connection(XMPPServer xmpp) throws XmlPullParserException { - this.xmpp = xmpp; - created = updated = Instant.now(); - } - - public void logParser() { - if (streamID == null) { - return; - } - String tag = "IN: <" + parser.getName(); - for (int i = 0; i < parser.getAttributeCount(); i++) { - tag += " " + parser.getAttributeName(i) + "=\"" + parser.getAttributeValue(i) + "\""; - } - tag += ">...\n"; - logger.trace(tag); - } - - public void sendStanza(String xml) { - if (streamID != null) { - logger.trace("OUT: {}\n", xml); - } - try { - writer.write(xml); - writer.flush(); - } catch (IOException e) { - logger.error("send stanza failed", e); - } - - updated = Instant.now(); - bytesLocal += xml.length(); - packetsLocal++; - } - - public void closeConnection() { - if (streamID != null) { - logger.debug("closing stream {}", streamID); - } - - try { - writer.write(""); - } catch (Exception e) { - } - - try { - writer.close(); - } catch (Exception e) { - } - - try { - socket.close(); - } catch (Exception e) { - } - } - - public boolean isSecured() { - return secured; - } - - public void setSecured(boolean secured) { - this.secured = secured; - } - - public void restartParser() throws XmlPullParserException, IOException { - streamID = UUID.randomUUID().toString(); - parser = factory.newPullParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new InputStreamReader(socket.getInputStream())); - writer = new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8); - } - - @JsonIgnore - public Socket getSocket() { - return socket; - } - - public void setSocket(Socket socket) { - this.socket = socket; - } - - public boolean isAuthenticated() { - return authenticated; - } - - public void setAuthenticated(boolean authenticated) { - this.authenticated = authenticated; - } - - public boolean isTrusted() { - return trusted; - } - - public void setTrusted(boolean trusted) { - this.trusted = trusted; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionIn.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionIn.java deleted file mode 100644 index 72c3ba8d..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionIn.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.xmpp.s2s; - -import com.juick.server.XMPPServer; -import com.juick.server.xmpp.router.StreamError; -import com.juick.server.xmpp.router.XmlUtils; -import org.apache.commons.lang3.StringUtils; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import rocks.xmpp.addr.Jid; - -import java.io.EOFException; -import java.io.IOException; -import java.net.Socket; -import java.net.SocketException; -import java.time.Instant; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.stream.Collectors; - -/** - * @author ugnich - */ -public class ConnectionIn extends Connection implements Runnable { - - final public List from = new CopyOnWriteArrayList<>(); - public Instant received; - public long packetsRemote = 0; - ConnectionListener listener; - - public ConnectionIn(XMPPServer xmpp, Socket socket) throws XmlPullParserException, IOException { - super(xmpp); - this.setSocket(socket); - restartParser(); - } - - @Override - public void run() { - try { - parser.next(); // stream:stream - updateTsRemoteData(); - if (!parser.getName().equals("stream") - || !parser.getNamespace("stream").equals(NS_STREAM)) { -// || !parser.getAttributeValue(null, "version").equals("1.0") -// || !parser.getAttributeValue(null, "to").equals(Main.HOSTNAME)) { - throw new Exception(String.format("stream from %s invalid", getSocket().getRemoteSocketAddress())); - } - streamID = parser.getAttributeValue(null, "id"); - if (streamID == null) { - streamID = UUID.randomUUID().toString(); - } - boolean xmppversionnew = parser.getAttributeValue(null, "version") != null; - String from = parser.getAttributeValue(null, "from"); - - if (Arrays.asList(xmpp.bannedHosts).contains(from)) { - closeConnection(); - return; - } - sendOpenStream(from, xmppversionnew); - - while (parser.next() != XmlPullParser.END_DOCUMENT) { - updateTsRemoteData(); - if (parser.getEventType() != XmlPullParser.START_TAG) { - continue; - } - logParser(); - - packetsRemote++; - - String tag = parser.getName(); - if (tag.equals("result") && parser.getNamespace().equals(NS_DB)) { - String dfrom = parser.getAttributeValue(null, "from"); - String to = parser.getAttributeValue(null, "to"); - logger.debug("stream from {} to {} {} asking for dialback", dfrom, to, streamID); - if (dfrom.endsWith(xmpp.getJid().toEscapedString()) && (dfrom.equals(xmpp.getJid().toEscapedString()) - || dfrom.endsWith("." + xmpp.getJid()))) { - logger.warn("stream from {} is invalid", dfrom); - break; - } - if (to != null && to.equals(xmpp.getJid().toEscapedString())) { - String dbKey = XmlUtils.getTagText(parser); - updateTsRemoteData(); - xmpp.startDialback(Jid.of(dfrom), streamID, dbKey); - } else { - logger.warn("stream from " + dfrom + " " + streamID + " invalid to " + to); - break; - } - } else if (tag.equals("verify") && parser.getNamespace().equals(NS_DB)) { - String vfrom = parser.getAttributeValue(null, "from"); - String vto = parser.getAttributeValue(null, "to"); - String vid = parser.getAttributeValue(null, "id"); - String vkey = XmlUtils.getTagText(parser); - updateTsRemoteData(); - final boolean[] valid = {false}; - if (vfrom != null && vto != null && vid != null && vkey != null) { - xmpp.getConnectionOut(Jid.of(vfrom), false).ifPresent(c -> { - String dialbackKey = c.dbKey; - valid[0] = vkey.equals(dialbackKey); - }); - } - if (valid[0]) { - sendStanza(""); - logger.debug("stream from {} {} dialback verify valid", vfrom, streamID); - setAuthenticated(true); - } else { - sendStanza(""); - logger.warn("stream from {} {} dialback verify invalid", vfrom, streamID); - } - } else if (tag.equals("presence") && checkFromTo(parser) && isAuthenticated()) { - String xml = XmlUtils.parseToString(parser, false); - logger.debug("stream {} presence: {}", streamID, xml); - xmpp.onStanzaReceived(xml); - } else if (tag.equals("message") && checkFromTo(parser)) { - updateTsRemoteData(); - String xml = XmlUtils.parseToString(parser, false); - logger.debug("stream {} message: {}", streamID, xml); - xmpp.onStanzaReceived(xml); - - } else if (tag.equals("iq") && checkFromTo(parser) && isAuthenticated()) { - updateTsRemoteData(); - String type = parser.getAttributeValue(null, "type"); - String xml = XmlUtils.parseToString(parser, false); - if (type == null || !type.equals("error")) { - logger.debug("stream {} iq: {}", streamID, xml); - xmpp.onStanzaReceived(xml); - } - } else if (!isSecured() && tag.equals("starttls") && !isAuthenticated()) { - listener.starttls(this); - } else if (isSecured() && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) { - sendOpenStream(null, true); - } else if (isSecured() && tag.equals("auth") && parser.getNamespace().equals(NS_SASL) - && parser.getAttributeValue(null, "mechanism").equals("EXTERNAL") - && !isAuthenticated() && isTrusted()) { - sendStanza(""); - logger.info("stream {} authenticated externally", streamID); - this.from.add(Jid.of(from)); - setAuthenticated(true); - restartParser(); - } else if (tag.equals("error")) { - StreamError streamError = StreamError.parse(parser); - logger.debug("Stream error {} from {}: {}", streamError.getCondition(), streamID, streamError.getText()); - xmpp.removeConnectionIn(this); - closeConnection(); - } else { - String unhandledStanza = XmlUtils.parseToString(parser, true); - logger.warn("Unhandled stanza from {}: {}", streamID, unhandledStanza); - } - } - logger.warn("stream {} finished", streamID); - xmpp.removeConnectionIn(this); - closeConnection(); - } catch (EOFException | SocketException ex) { - logger.debug("stream {} closed (dirty)", streamID); - xmpp.removeConnectionIn(this); - closeConnection(); - } catch (Exception e) { - logger.debug("stream {} error {}", streamID, e); - xmpp.removeConnectionIn(this); - closeConnection(); - } - } - - void updateTsRemoteData() { - received = Instant.now(); - } - - void sendOpenStream(String from, boolean xmppversionnew) throws IOException { - String openStream = ""; - if (xmppversionnew) { - openStream += ""; - if (listener != null && listener.isTlsAvailable() && !Arrays.asList(xmpp.brokenSSLhosts).contains(from)) { - if (!isSecured()) { - openStream += ""; - } else if (!isAuthenticated() && isTrusted()) { - openStream += "" + - "EXTERNAL" + - ""; - } - } - openStream += ""; - } - sendStanza(openStream); - } - - public void sendDialbackResult(Jid sfrom, String type) { - sendStanza(""); - if (type.equals("valid")) { - from.add(sfrom); - logger.debug("stream from {} {} ready", sfrom, streamID); - setAuthenticated(true); - } - } - - boolean checkFromTo(XmlPullParser parser) throws Exception { - String cfrom = parser.getAttributeValue(null, "from"); - String cto = parser.getAttributeValue(null, "to"); - if (StringUtils.isNotEmpty(cfrom) && StringUtils.isNotEmpty(cto)) { - Jid jidfrom = Jid.of(cfrom); - for (Jid aFrom : from) { - if (aFrom.equals(Jid.of(jidfrom.getDomain()))) { - return true; - } - } - } - logger.warn("rejected from {}, to {}, stream {}", cfrom, cto, from.stream().collect(Collectors.joining(","))); - return false; - } - public void setListener(ConnectionListener listener) { - this.listener = listener; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionListener.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionListener.java deleted file mode 100644 index 4c32b9ae..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionListener.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.juick.server.xmpp.s2s; - - -import com.juick.server.xmpp.router.StreamError; - -public interface ConnectionListener { - boolean isTlsAvailable(); - void starttls(ConnectionIn connection); - void proceed(ConnectionOut connection); - void verify(ConnectionOut connection, String from, String type, String sid); - void dialbackError(ConnectionOut connection, StreamError error); - void finished(ConnectionOut connection, boolean dirty); - void exception(ConnectionOut connection, Exception ex); - void ready(ConnectionOut connection); - boolean securing(ConnectionOut connection); -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionOut.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionOut.java deleted file mode 100644 index be485ab1..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionOut.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.xmpp.s2s; - -import com.juick.server.xmpp.router.Stream; -import com.juick.server.xmpp.router.StreamError; -import com.juick.server.xmpp.router.StreamFeatures; -import com.juick.server.xmpp.router.XmlUtils; -import com.juick.server.xmpp.s2s.util.DialbackUtils; -import org.apache.commons.codec.Charsets; -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.text.RandomStringGenerator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.xmlpull.v1.XmlPullParser; -import rocks.xmpp.addr.Jid; - -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.SocketException; -import java.util.UUID; - -import static com.juick.server.xmpp.router.StreamNamespaces.NS_STREAM; -import static com.juick.server.xmpp.s2s.Connection.NS_SASL; - -/** - * @author ugnich - */ -public class ConnectionOut extends Stream { - protected static final Logger logger = LoggerFactory.getLogger(ConnectionOut.class); - public static final String NS_TLS = "urn:ietf:params:xml:ns:xmpp-tls"; - public static final String NS_DB = "jabber:server:dialback"; - private boolean secured = false; - private boolean trusted = false; - public boolean streamReady = false; - String checkSID = null; - String dbKey = null; - private String streamID; - ConnectionListener listener; - RandomStringGenerator generator = new RandomStringGenerator.Builder().withinRange('a', 'z').build(); - - public ConnectionOut(Jid from, Jid to, InputStream is, OutputStream os, String checkSID, String dbKey) throws Exception { - super(from, to, is, os); - this.to = to; - this.checkSID = checkSID; - this.dbKey = dbKey; - if (dbKey == null) { - this.dbKey = DialbackUtils.generateDialbackKey(generator.generate(15), to, from, streamID); - } - streamID = UUID.randomUUID().toString(); - } - - public void sendOpenStream() throws IOException { - send(""); - } - - void processDialback() throws Exception { - if (checkSID != null) { - sendDialbackVerify(checkSID, dbKey); - } - send("" + - dbKey + ""); - } - - @Override - public void handshake() { - try { - restartStream(); - - sendOpenStream(); - - parser.next(); // stream:stream - streamID = parser.getAttributeValue(null, "id"); - if (streamID == null || streamID.isEmpty()) { - throw new Exception("stream to " + to + " invalid first packet"); - } - - logger.debug("stream to {} {} open", to, streamID); - boolean xmppversionnew = parser.getAttributeValue(null, "version") != null; - if (!xmppversionnew) { - processDialback(); - } - - while (parser.next() != XmlPullParser.END_DOCUMENT) { - if (parser.getEventType() != XmlPullParser.START_TAG) { - continue; - } - - String tag = parser.getName(); - if (tag.equals("result") && parser.getNamespace().equals(NS_DB)) { - String type = parser.getAttributeValue(null, "type"); - if (type != null && type.equals("valid")) { - streamReady = true; - listener.ready(this); - } else { - logger.warn("stream to {} {} dialback fail", to, streamID); - } - XmlUtils.skip(parser); - } else if (tag.equals("verify") && parser.getNamespace().equals(NS_DB)) { - String from = parser.getAttributeValue(null, "from"); - String type = parser.getAttributeValue(null, "type"); - String sid = parser.getAttributeValue(null, "id"); - listener.verify(this, from, type, sid); - XmlUtils.skip(parser); - } else if (tag.equals("features") && parser.getNamespace().equals(NS_STREAM)) { - StreamFeatures features = StreamFeatures.parse(parser); - if (listener != null && !secured && features.STARTTLS >= 0 - && listener.securing(this)) { - logger.debug("stream to {} {} securing", to.toEscapedString(), streamID); - send(""); - } else if (secured && features.EXTERNAL >=0) { - String authid = Base64.encodeBase64String(from.toEscapedString().getBytes(Charsets.UTF_8)); - send(String.format("%s", authid)); - } else if (secured && streamReady) { - listener.ready(this); - } else { - processDialback(); - } - } else if (tag.equals("proceed") && parser.getNamespace().equals(NS_TLS)) { - listener.proceed(this); - } else if (tag.equals("success") && parser.getNamespace().equals(NS_SASL)) { - streamReady = true; - restartStream(); - sendOpenStream(); - } else if (secured && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) { - streamID = parser.getAttributeValue(null, "id"); - } else if (tag.equals("error")) { - StreamError streamError = StreamError.parse(parser); - listener.dialbackError(this, streamError); - } else { - String unhandledStanza = XmlUtils.parseToString(parser, false); - logger.warn("Unhandled stanza from {} {} : {}", to, streamID, unhandledStanza); - } - } - listener.finished(this, false); - } catch (EOFException | SocketException eofex) { - listener.finished(this, true); - } catch (Exception e) { - listener.exception(this, e); - } - } - - public void sendDialbackVerify(String sid, String key) { - send("" + - key + ""); - } - public void setListener(ConnectionListener listener) { - this.listener = listener; - } - - public String getStreamID() { - return streamID; - } - - public boolean isSecured() { - return secured; - } - - public void setSecured(boolean secured) { - this.secured = secured; - } - - public boolean isTrusted() { - return trusted; - } - - public void setTrusted(boolean trusted) { - this.trusted = trusted; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/DNSQueries.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/DNSQueries.java deleted file mode 100644 index 1367d333..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/DNSQueries.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.xmpp.s2s; - -import org.apache.commons.lang3.math.NumberUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.InetSocketAddress; -import java.util.Hashtable; -import java.util.Random; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.DirContext; -import javax.naming.directory.InitialDirContext; - -/** - * - * @author ugnich - */ -public class DNSQueries { - - private static final Logger logger = LoggerFactory.getLogger(DNSQueries.class); - - private static Random rand = new Random(); - - public static InetSocketAddress getServerAddress(String hostname) { - - String host = hostname; - int port = 5269; - - Hashtable env = new Hashtable<>(5); - env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory"); - try { - DirContext ctx = new InitialDirContext(env); - Attribute att = ctx.getAttributes("_xmpp-server._tcp." + hostname, new String[]{"SRV"}).get("SRV"); - - if (att != null && att.size() > 0) { - int i = rand.nextInt(att.size()); - String srv[] = att.get(i).toString().split(" "); - port = NumberUtils.toInt(srv[2], 5269); - host = srv[3]; - } - ctx.close(); - } catch (NamingException e) { - logger.debug("SRV record for {} is not resolved, falling back to A record", hostname); - } - return new InetSocketAddress(host, port); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/StanzaListener.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/StanzaListener.java deleted file mode 100644 index 6932298f..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/StanzaListener.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.xmpp.s2s; - - -import rocks.xmpp.core.stanza.model.Stanza; - -/** - * Created by vitalyster on 07.12.2016. - */ -public interface StanzaListener { - void stanzaReceived(Stanza xmlValue); -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/util/DialbackUtils.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/util/DialbackUtils.java deleted file mode 100644 index d25dbad8..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/util/DialbackUtils.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.server.xmpp.s2s.util; - -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.codec.digest.HmacAlgorithms; -import org.apache.commons.codec.digest.HmacUtils; -import rocks.xmpp.addr.Jid; - -/** - * Created by vitalyster on 05.12.2016. - */ -public class DialbackUtils { - private DialbackUtils() { - throw new IllegalStateException(); - } - - public static String generateDialbackKey(String secret, Jid to, Jid from, String id) { - return new HmacUtils(HmacAlgorithms.HMAC_SHA_256, DigestUtils.sha256(secret)) - .hmacHex(to.toEscapedString() + " " + from.toEscapedString() + " " + id); - } -} diff --git a/juick-server/src/main/java/com/juick/service/ActivityPubService.java b/juick-server/src/main/java/com/juick/service/ActivityPubService.java deleted file mode 100644 index 892022cf..00000000 --- a/juick-server/src/main/java/com/juick/service/ActivityPubService.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.juick.service; - -import com.juick.User; -import com.juick.model.AnonymousUser; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.annotation.Nonnull; -import javax.inject.Inject; -import java.util.List; - -@Repository -public class ActivityPubService extends BaseJdbcService implements SocialService { - @Value("${ap_base_uri:http://localhost:8080/}") - private String baseUri; - @Inject - private UserService userService; - - @Transactional(readOnly = true) - @Override - public @Nonnull User getUserByAccountUri(String acct) { - UriComponents baseUriComponents = UriComponentsBuilder.fromUriString(baseUri).build(); - UriComponents acctComponents = UriComponentsBuilder.fromUriString(acct).build(); - if (acctComponents.getHost().equals(baseUriComponents.getHost())) { - // /u/ugnich -> ugnich - String userName = acctComponents.getPath().substring(3); - return userService.getUserByName(userName); - } - return AnonymousUser.INSTANCE; - } - - @Transactional(readOnly = true) - @Override - public @Nonnull List getFollowers(User user) { - return getJdbcTemplate().queryForList("SELECT acct FROM followers WHERE user_id=?", String.class, user.getUid()); - } - - @Transactional - @Override - public void addFollower(User user, String acct) { - getJdbcTemplate().update("INSERT INTO followers(user_id, acct) " + - "VALUES(?, ?)", user.getUid(), acct); - } - - @Transactional - @Override - public void removeFollower(User user, String acct) { - getJdbcTemplate().update("DELETE FROM followers WHERE user_id=? AND acct=?", user.getUid(), acct); - } - - @Transactional - @Override - public void removeAccount(String acct) { - getJdbcTemplate().update("DELETE FROM followers WHERE acct=?", acct); - } -} diff --git a/juick-server/src/main/java/com/juick/service/BaseJdbcService.java b/juick-server/src/main/java/com/juick/service/BaseJdbcService.java deleted file mode 100644 index 496a04ba..00000000 --- a/juick-server/src/main/java/com/juick/service/BaseJdbcService.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; - -import javax.inject.Inject; - -/** - * Created by aalexeev on 11/13/16. - */ -public class BaseJdbcService { - @Inject - JdbcTemplate jdbcTemplate; - @Inject - NamedParameterJdbcTemplate namedParameterJdbcTemplate; - - public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() { - return namedParameterJdbcTemplate; - } - - public JdbcTemplate getJdbcTemplate() { - return jdbcTemplate; - } -} diff --git a/juick-server/src/main/java/com/juick/service/CrosspostServiceImpl.java b/juick-server/src/main/java/com/juick/service/CrosspostServiceImpl.java deleted file mode 100644 index d190faba..00000000 --- a/juick-server/src/main/java/com/juick/service/CrosspostServiceImpl.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service; - -import com.juick.ExternalToken; -import com.juick.model.ApplicationStatus; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.Optional; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -public class CrosspostServiceImpl extends BaseJdbcService implements CrosspostService { - - @Transactional(readOnly = true) - @Override - public Optional getTwitterToken(final int uid) { - List list = getJdbcTemplate().query( - "SELECT uname, access_token, access_token_secret FROM twitter WHERE user_id = ? AND crosspost = 1", - (rs, num) -> new ExternalToken(rs.getString(1), "twitter", - rs.getString(2), rs.getString(3)), - uid); - - return list.isEmpty() ? - Optional.empty() : Optional.of(list.get(0)); - } - - @Transactional - @Override - public boolean deleteTwitterToken(Integer uid) { - return getJdbcTemplate().update("DELETE FROM twitter WHERE user_id=?", uid) > 0; - } - - @Override - public void addFacebookState(String state, String redirectUri) { - jdbcTemplate.update("INSERT INTO facebook(loginhash, fb_link) VALUES(?, ?)", state, redirectUri); - } - - @Override - public void addVKState(String state, String redirectUri) { - jdbcTemplate.update("INSERT INTO vk(loginhash, vk_link) VALUES(?, ?)", state, redirectUri); - } - - @Override - public String verifyFacebookState(String state) { - try { - return jdbcTemplate.queryForObject("SELECT fb_link FROM facebook WHERE loginhash=?", - String.class, state); - } catch (EmptyResultDataAccessException e) { - return StringUtils.EMPTY; - } - } - - @Override - public String verifyVKState(String state) { - try { - return jdbcTemplate.queryForObject("SELECT vk_link FROM vk WHERE loginhash=?", - String.class, state); - } catch (EmptyResultDataAccessException e) { - return StringUtils.EMPTY; - } - } - - @Transactional(readOnly = true) - @Override - public Optional> getFacebookTokens(final int uid) { - List>> list = getJdbcTemplate().query( - "SELECT fb_id, access_token FROM facebook WHERE user_id = ? AND access_token IS NOT NULL AND crosspost = 1", - (rs, num) -> Optional.of(Pair.of(rs.getString(1), rs.getString(2))), - uid); - return list.isEmpty() ? - Optional.empty() : list.get(0); - } - - @Transactional(readOnly = true) - @Override - public ApplicationStatus getFbCrossPostStatus(final int uid) { - List list = getJdbcTemplate().query( - "SELECT 1, crosspost FROM facebook WHERE user_id = ? LIMIT 1", - (rs, num) -> { - ApplicationStatus status = new ApplicationStatus(); - - status.setConnected(rs.getInt(1) > 0); - status.setCrosspostEnabled(rs.getBoolean(2)); - - return status; - }, - uid); - - return list.isEmpty() ? - new ApplicationStatus() : list.get(0); - } - - @Transactional - @Override - public boolean enableFBCrosspost(Integer uid) { - return getJdbcTemplate().update("UPDATE facebook SET crosspost=1 WHERE user_id=?", uid) > 0; - } - - @Transactional - @Override - public void disableFBCrosspost(Integer uid) { - getJdbcTemplate().update("UPDATE facebook SET crosspost=0 WHERE user_id=?", uid); - } - - @Transactional(readOnly = true) - @Override - public String getTwitterName(final int uid) { - List list = getJdbcTemplate().queryForList( - "SELECT uname FROM twitter WHERE user_id = ?", - String.class, - uid); - - return list.isEmpty() ? - StringUtils.EMPTY : list.get(0); - } - - @Transactional(readOnly = true) - @Override - public String getTelegramName(final int uid) { - List list = getJdbcTemplate().queryForList( - "SELECT tg_name FROM telegram WHERE user_id = ?", - String.class, - uid); - - return list.isEmpty() ? - StringUtils.EMPTY : list.get(0); - } - - @Transactional(readOnly = true) - @Override - public Optional> getVkTokens(final int uid) { - List>> list = getJdbcTemplate().query( - "SELECT vk_id, access_token FROM vk WHERE user_id = ? AND crosspost = 1", - (rs, num) -> Optional.of(Pair.of(rs.getString(1), rs.getString(2))), - uid); - - return list.isEmpty() ? - Optional.empty() : list.get(0); - } - - @Transactional - @Override - public void deleteVKUser(Integer uid) { - getJdbcTemplate().update("DELETE FROM vk WHERE user_id=?", uid); - } - - @Transactional(readOnly = true) - @Override - public int getUIDbyFBID(long fbID) { - try { - return getJdbcTemplate().queryForObject("SELECT user_id FROM facebook WHERE fb_id=? AND user_id IS NOT NULL", - Integer.class, fbID); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - - @Transactional - @Override - public boolean createFacebookUser(long fbID, String loginhash, String token, String fbName, String fbLink) { - return getJdbcTemplate().update("UPDATE facebook SET fb_id=?, access_token=?, fb_name=?, fb_link=? WHERE loginhash=?", - fbID, token, fbName, fbLink, loginhash) > 0; - } - - @Transactional - @Override - public boolean updateFacebookUser(long fbID, String token, String fbName, String fbLink) { - return getJdbcTemplate().update("UPDATE facebook SET access_token=?,fb_name=?,fb_link=? WHERE fb_id=?", - token, fbName, fbLink, fbID) > 0; - } - - @Transactional(readOnly = true) - @Override - public int getUIDbyVKID(long vkID) { - try { - return getJdbcTemplate().queryForObject("SELECT user_id FROM vk WHERE vk_id=? AND user_id IS NOT NULL", Integer.class, vkID); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - - @Transactional - @Override - public boolean createVKUser(long vkID, String loginhash, String token, String vkName, String vkLink) { - return getJdbcTemplate().update("INSERT INTO vk(vk_id,loginhash,access_token,vk_name,vk_link) VALUES (?,?,?,?,?)", - vkID, loginhash, token, vkName, vkLink) > 0; - } - - @Transactional(readOnly = true) - @Override - public String getFacebookNameByHash(String hash) { - try { - List> fb = getJdbcTemplate().query("SELECT fb_name,fb_link FROM facebook WHERE loginhash=?", - (rs, num) -> Pair.of(rs.getString(1), rs.getString(2)), hash); - if (fb.size() > 0) { - return "" + fb.get(0).getLeft() + ""; - } - return null; - } catch (EmptyResultDataAccessException e) { - return null; - } - } - - @Transactional - @Override - public String getTelegramNameByHash(String hash) { - try { - String name = getJdbcTemplate().queryForObject("SELECT tg_name FROM telegram WHERE loginhash=?", String.class, hash); - return "" + name + ""; - } catch (EmptyResultDataAccessException e) { - return null; - } - } - - @Transactional - @Override - public boolean setFacebookUser(String hash, int uid) { - return getJdbcTemplate().update("UPDATE facebook SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; - } - - @Transactional - @Override - public String getVKNameByHash(String hash) { - List> logins = getJdbcTemplate().query("SELECT vk_name,vk_link FROM vk WHERE loginhash=?", - (rs, num) -> Pair.of(rs.getString(1), rs.getString(2)), hash); - if (logins.size() > 0) { - return "" + logins.get(0).getLeft() + ""; - } - return null; - } - - @Transactional - @Override - public boolean setVKUser(String hash, int uid) { - return getJdbcTemplate().update("UPDATE vk SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; - } - - @Transactional - @Override - public boolean setTelegramUser(String hash, int uid) { - return getJdbcTemplate().update("UPDATE telegram SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; - } - - @Transactional(readOnly = true) - @Override - public String getJIDByHash(String hash) { - try { - return getJdbcTemplate().queryForObject("SELECT jid FROM jids WHERE loginhash=?", String.class, hash); - } catch (EmptyResultDataAccessException e) { - return null; - } - } - - @Transactional - @Override - public boolean setJIDUser(String hash, int uid) { - return getJdbcTemplate().update("UPDATE jids SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; - } -} diff --git a/juick-server/src/main/java/com/juick/service/EmailServiceImpl.java b/juick-server/src/main/java/com/juick/service/EmailServiceImpl.java deleted file mode 100644 index 78bdd42a..00000000 --- a/juick-server/src/main/java/com/juick/service/EmailServiceImpl.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import javax.inject.Inject; -import java.util.List; - -/** - * Created by vitalyster on 09.12.2016. - */ -@Repository -@Transactional -public class EmailServiceImpl extends BaseJdbcService implements EmailService { - - @Override - public boolean verifyAddressByCode(Integer userId, String code) { - try { - String address = getJdbcTemplate().queryForObject("SELECT account FROM auth WHERE user_id=? AND protocol='email' AND authcode=?", - String.class, userId, code); - addEmail(userId, address); - getJdbcTemplate().update("DELETE FROM auth WHERE user_id=? AND authcode=?", userId, code); - } catch (EmptyResultDataAccessException e) { - return false; - } - return true; - } - - @Override - public boolean addVerificationCode(Integer userId, String account, String code) { - return getJdbcTemplate().update("INSERT INTO auth(user_id,protocol,account,authcode) VALUES (?,'email',?,?)", - userId, account, code) > 0; - } - - @Override - public boolean addEmail(Integer userId, String email) { - return getJdbcTemplate().update("INSERT INTO emails(user_id,email, subscr_hour) VALUES (?,?, 1)", userId, email) > 0; - } - - @Override - public boolean deleteEmail(Integer userId, String account) { - return getNamedParameterJdbcTemplate().update("DELETE FROM emails " + - "WHERE (SELECT COUNT(*) cnt FROM (select user_id, email FROM emails e) c WHERE user_id=:uid) > 1 " + - "AND user_id=:uid AND email=:email", - new MapSqlParameterSource() - .addValue("uid", userId) - .addValue("email", account)) > 0; - } - - @Transactional(readOnly = true) - @Override - public String getNotificationsEmail(Integer userId) { - List list = getJdbcTemplate().queryForList( - "SELECT email FROM emails WHERE user_id=? AND subscr_hour IS NOT NULL", String.class, userId); - return list.isEmpty() ? StringUtils.EMPTY : list.get(0); - } - - @Override - public boolean setNotificationsEmail(Integer userId, String account) { - getJdbcTemplate().update("UPDATE emails SET subscr_hour=NULL WHERE user_id=?", userId); - return StringUtils.isNotEmpty(account) && getJdbcTemplate().update( - "UPDATE emails SET subscr_hour=1 WHERE user_id=? AND email=?", userId, account) > 0; - } - - @Transactional(readOnly = true) - @Override - public List getEmails(Integer userId, boolean active) { - return getJdbcTemplate().queryForList("SELECT email FROM emails WHERE user_id=? " + - (active ? "AND subscr_hour IS NOT NULL" : ""), String.class, userId); - } - @Transactional(readOnly = true) - @Override - public String getEmailByAuthCode(String code) { - try { - return getJdbcTemplate().queryForObject("SELECT account FROM auth WHERE protocol='email' AND authcode=?", String.class, code); - } catch (EmptyResultDataAccessException e) { - return StringUtils.EMPTY; - } - } - - @Transactional - @Override - public void deleteAuthCode(String code) { - getJdbcTemplate().update("DELETE FROM auth WHERE authcode=?", code); - } - -} diff --git a/juick-server/src/main/java/com/juick/service/ImagesServiceImpl.java b/juick-server/src/main/java/com/juick/service/ImagesServiceImpl.java deleted file mode 100644 index 67c8360e..00000000 --- a/juick-server/src/main/java/com/juick/service/ImagesServiceImpl.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.juick.service; - -import com.juick.Attachment; -import com.juick.Message; -import com.juick.Photo; -import com.juick.server.util.ImageUtils; -import org.springframework.util.StringUtils; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Paths; - -public class ImagesServiceImpl implements ImagesService { - private ImageUtils imageUtils; - private String imgDir; - private String tmpDir; - public ImagesServiceImpl(String imgDir, String tmpDir) { - this.imgDir = imgDir; - this.tmpDir = tmpDir; - imageUtils = new ImageUtils(imgDir, tmpDir); - } - @Override - public void setAttachmentMetadata(String baseUrl, Message msg) throws Exception { - if (!StringUtils.isEmpty(msg.getAttachmentType())) { - Photo photo = new Photo(); - if (msg.getRid()> 0) { - photo.setSmall(String.format("%sphotos-512/%d-%d.%s", baseUrl, msg.getMid(), msg.getRid(), msg.getAttachmentType())); - photo.setMedium(String.format("%sphotos-1024/%d-%d.%s", baseUrl, msg.getMid(), msg.getRid(), msg.getAttachmentType())); - photo.setThumbnail(String.format("%sps/%d-%d.%s", baseUrl, msg.getMid(), msg.getRid(), msg.getAttachmentType())); - } else { - photo.setSmall(String.format("%sphotos-512/%d.%s", baseUrl, msg.getMid(), msg.getAttachmentType())); - photo.setMedium(String.format("%sphotos-1024/%d.%s", baseUrl, msg.getMid(), msg.getAttachmentType())); - photo.setThumbnail(String.format("%sps/%d.%s", baseUrl, msg.getMid(), msg.getAttachmentType())); - } - msg.setPhoto(photo); - String imageName = String.format("%s.%s", msg.getMid(), msg.getAttachmentType()); - if (msg.getRid() > 0) { - imageName = String.format("%s-%s.%s", msg.getMid(), msg.getRid(), msg.getAttachmentType()); - } - File fullImage = Paths.get(imgDir, "p", imageName).toFile(); - File mediumImage = Paths.get(imgDir, "photos-1024", imageName).toFile(); - File smallImage = Paths.get(imgDir, "photos-512", imageName).toFile(); - File thumbnailImage = Paths.get(imgDir, "ps", imageName).toFile(); - StringBuilder builder = new StringBuilder(); - builder.append(baseUrl); - builder.append(msg.getAttachmentType().equals("mp4") ? "video" : "p"); - builder.append("/").append(msg.getMid()); - if (msg.getRid() > 0) { - builder.append("-").append(msg.getRid()); - } - builder.append(".").append(msg.getAttachmentType()); - String originalUrl = builder.toString(); - - Attachment original = imageUtils.getAttachment(fullImage); - original.setUrl(originalUrl); - - Attachment medium = imageUtils.getAttachment(mediumImage); - medium.setUrl(photo.getMedium()); - original.setMedium(medium); - - Attachment small = imageUtils.getAttachment(smallImage); - small.setUrl(photo.getSmall()); - original.setSmall(small); - - Attachment thumb = imageUtils.getAttachment(thumbnailImage); - thumb.setUrl(photo.getMedium()); - original.setThumbnail(thumb); - - msg.setAttachment(original); - } - } - - @Override - public void saveImageWithPreviews(String tempFilename, String outputFilename) throws IOException { - imageUtils.saveImageWithPreviews(tempFilename, outputFilename); - } - - @Override - public void saveAvatar(String tempFilename, int uid) throws IOException { - imageUtils.saveAvatar(tempFilename, uid); - } -} diff --git a/juick-server/src/main/java/com/juick/service/MessagesServiceImpl.java b/juick-server/src/main/java/com/juick/service/MessagesServiceImpl.java deleted file mode 100644 index 0b7faf87..00000000 --- a/juick-server/src/main/java/com/juick/service/MessagesServiceImpl.java +++ /dev/null @@ -1,1143 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service; - -import com.juick.*; -import com.juick.model.AnonymousUser; -import com.juick.model.PrivacyOpts; -import com.juick.model.ResponseReply; -import com.juick.server.util.HttpNotFoundException; -import com.juick.util.MessageUtils; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.jdbc.core.ConnectionCallback; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.SqlParameterSource; -import org.springframework.jdbc.core.simple.SimpleJdbcInsert; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import javax.inject.Inject; -import java.net.URI; -import java.sql.*; -import java.time.Instant; -import java.util.*; -import java.util.Date; -import java.util.stream.Collectors; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -public class MessagesServiceImpl extends BaseJdbcService implements MessagesService { - private static final Logger logger = LoggerFactory.getLogger(MessagesServiceImpl.class); - @Inject - private UserService userService; - @Inject - private TagService tagService; - @Inject - private SearchService searchService; - @Inject - private ImagesService imagesService; - @Value("${img_url:https://i.juick.com/}") - private String baseImagesUrl; - - private class MessageMapper implements RowMapper { - @Override - public Message mapRow(ResultSet rs, int rowNum) throws SQLException { - Message msg = new Message(); - msg.setMid(rs.getInt(1)); - msg.setRid(rs.getInt(2)); - msg.setReplyto(rs.getInt(3)); - User user = new User(); - user.setUid(rs.getInt(4)); - user.setName(Optional.ofNullable(rs.getString(5)).orElse(AnonymousUser.INSTANCE.getName())); - user.setBanned(rs.getBoolean(6)); - user.setUri(URI.create(Optional.ofNullable(rs.getString(22)).orElse(StringUtils.EMPTY))); - msg.setUser(user); - msg.setTimestamp(rs.getTimestamp(7).toInstant()); - msg.ReadOnly = rs.getBoolean(8); - msg.setPrivacy(rs.getInt(9)); - msg.FriendsOnly = msg.getPrivacy() < 0; - msg.setReplies(rs.getInt(10)); - msg.setAttachmentType(rs.getString(11)); - msg.setLikes(rs.getInt(12)); - msg.Hidden = rs.getBoolean(13); - String tagsStr = rs.getString(14); - msg.setTags(MessageUtils.parseTags(tagsStr)); - msg.setRepliesBy(rs.getString(15)); - msg.setText(rs.getString(16)); - msg.setReplyQuote(MessageUtils.formatQuote(rs.getString(17))); - msg.setUpdated(rs.getTimestamp(18).toInstant()); - int quoteUid = rs.getInt(19); - User quoteUser = new User(); - quoteUser.setUid(quoteUid); - quoteUser.setName(rs.getString(20)); - if (quoteUid == 0) { - quoteUser.setName(AnonymousUser.INSTANCE.getName()); - quoteUser.setUri(URI.create(Optional.ofNullable(rs.getString(23)).orElse(StringUtils.EMPTY))); - } - msg.setTo(quoteUser); - msg.setUpdatedAt(rs.getTimestamp(21).toInstant()); - msg.setReplyUri(URI.create(Optional.ofNullable(rs.getString(24)).orElse(StringUtils.EMPTY))); - msg.setHtml(rs.getBoolean(25)); - if (StringUtils.isNotEmpty(msg.getAttachmentType())) { - try { - imagesService.setAttachmentMetadata(baseImagesUrl, msg); - } catch (Exception e) { - logger.warn("exception reading images for mid {} rid {}", msg.getMid(), msg.getRid(), e); - } - } - return msg; - } - } - - - - /** - * @see Java, JDBC and MySQL Types - */ - @Transactional - @Override - public int createMessage(final int uid, final String txt, final String attachment, final Collection tags) { - SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(getJdbcTemplate()).withTableName("messages") - .usingColumns("user_id", "attach", "ts") - .usingGeneratedKeyColumns("message_id"); - Map insertMap = new HashMap<>(); - insertMap.put("user_id", uid); - Instant now = Instant.now(); - insertMap.put("ts", Timestamp.from(now)); - if (StringUtils.isNotEmpty(attachment)) { - insertMap.put("attach", attachment); - } - int mid = simpleJdbcInsert.executeAndReturnKey(insertMap).intValue(); - if (mid > 0) { - String tagsNames = StringUtils.EMPTY; - - if (CollectionUtils.isNotEmpty(tags)) { - StringBuilder tasNamesBuilder = new StringBuilder(); - List params = new ArrayList<>(tags.size()); - - boolean next = false; - - for (Tag tag : tags) { - if (next) { - tasNamesBuilder.append(" "); - } else - next = true; - - tasNamesBuilder.append(tag.getName()); - params.add(new Object[]{mid, tag.TID}); - } - tagsNames = tasNamesBuilder.toString(); - - getJdbcTemplate().batchUpdate( - "INSERT INTO messages_tags(message_id, tag_id) VALUES (?, ?)", - params, new int[]{Types.INTEGER, Types.INTEGER}); - } - - getJdbcTemplate().update( - "INSERT INTO messages_txt(message_id, tags, txt, updated_at) VALUES (?, ?, ?, ?)", - new Object[]{mid, tagsNames, txt, Timestamp.from(now)}, - new int[]{Types.INTEGER, Types.VARCHAR, Types.VARCHAR, Types.TIMESTAMP}); - getJdbcTemplate().update("UPDATE users SET lastmessage=?, last_seen=? where id=?", Timestamp.from(now), Timestamp.from(now), uid); - } - - return mid; - } - - /** - * @param mid - * @param rid - * @param user - * @param txt - * @param attachment - * @return - * @see Java, JDBC and MySQL Types - */ - @Transactional - @Override - public int createReply(final int mid, final int rid, final User user, final String txt, final String attachment) { - int ridnew = getReplyIDIncrement(mid); - Date ts = Date.from(Instant.now()); - getJdbcTemplate().update("INSERT INTO replies(message_id, reply_id, user_id, replyto, attach, txt, ts, updated_at, user_uri) " + - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", - mid, ridnew, user.getUid(), rid, attachment, txt, ts, ts, user.getUri().toASCIIString()); - - if (ridnew > 0) { - getJdbcTemplate().update( - "UPDATE messages SET replies = replies + 1, updated=? WHERE message_id = ?", - ts, mid); - setLastReadComment(user, mid, ridnew); - getJdbcTemplate().update("UPDATE users SET lastmessage=?, last_seen=? where id=?", ts, ts, user.getUid()); - } - return ridnew; - } - - @Override - public int getReplyIDIncrement(final int mid) { - return getJdbcTemplate().execute((ConnectionCallback) conn -> { - conn.setAutoCommit(false); - final int replyNo; - try (PreparedStatement ps = conn.prepareStatement("SELECT maxreplyid+1 FROM messages WHERE message_id=? FOR UPDATE")) { - ps.setInt(1, mid); - try (ResultSet resultSet = ps.executeQuery()) { - if (resultSet.next()) { - replyNo = resultSet.getInt(1); - } else { - throw new IncorrectResultSizeDataAccessException("while getting getReplyIDIncrement, mid=" + mid, 1, 0); - } - } - } - try (PreparedStatement ps = conn.prepareStatement("UPDATE messages SET maxreplyid=? WHERE message_id=?")) { - ps.setInt(1, replyNo); - ps.setInt(2, mid); - if (ps.executeUpdate() != 1) { - throw new IncorrectResultSizeDataAccessException("Cannot find a message to update: " + mid, 1, 0); - } - } - conn.commit(); - return replyNo; - }); - - } - - @Transactional - void updateRepliesBy(int mid) { - List users = getJdbcTemplate().queryForList("SELECT users.nick FROM replies " + - "INNER JOIN users ON replies.user_id=users.id WHERE replies.message_id=? " + - "GROUP BY replies.user_id ORDER BY COUNT(replies.reply_id) DESC LIMIT 5", String.class, mid); - String result = users.stream().map(u -> "@" + u).collect(Collectors.joining(",")); - getJdbcTemplate().update("UPDATE messages_txt SET repliesby=? WHERE message_id=?", result, mid); - } - - @Transactional - @Override - public RecommendStatus recommendMessage(final int mid, final int vuid, final String userUri) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", vuid) - .addValue("uri", userUri) - .addValue("like_id", Reaction.LIKE) - .addValue("mid", mid); - int wasDeleted = getNamedParameterJdbcTemplate() - .update("DELETE FROM favorites WHERE user_id=:uid AND message_id=:mid AND like_id=:like_id AND user_uri=:uri", sqlParameterSource); - if (wasDeleted > 0) { - return RecommendStatus.Deleted; - } else { - boolean wasAdded = getJdbcTemplate() - .update("INSERT INTO favorites(user_id, message_id, ts, like_id, user_uri) VALUES (?, ?, NOW(), ?, ?)", vuid, mid,Reaction.LIKE, userUri) == 1; - if (wasAdded) { - return RecommendStatus.Added; - } - } - return RecommendStatus.Error; - } - - @Override - public RecommendStatus recommendMessage(int mid, int vuid) { - return recommendMessage(mid, vuid, StringUtils.EMPTY); - } - - @Override - public List listReactions() { - return jdbcTemplate.query("SELECT like_id, description FROM reactions", (rs, rowNum) -> { - Reaction reaction = new Reaction(rs.getInt("like_id")); - reaction.setDescription(rs.getString("description")); - return reaction; - }); - } - - @Override - public RecommendStatus likeMessage(int mid, int vuid, int reactionId) { - return likeMessage(mid, vuid, reactionId, StringUtils.EMPTY); - } - - @Transactional - @Override - public RecommendStatus likeMessage(int mid, int vuid, int reactionId, String userUri) throws IllegalArgumentException { - boolean wasAdded = getJdbcTemplate() - .update("INSERT INTO favorites(user_id, message_id, ts, like_id, user_uri) VALUES (?, ?, NOW(), ?, ?)", vuid, mid, reactionId, userUri) == 1; - if (wasAdded) { - return RecommendStatus.Added; - } - - return RecommendStatus.Error; - } - - @Transactional(readOnly = true) - @Override - public boolean canViewThread(final int mid, final int uid) { - List list = getJdbcTemplate().query( - "SELECT user_id, privacy FROM messages WHERE message_id = ?", - (rs, rowNum) -> { - PrivacyOpts res = new PrivacyOpts(); - - res.setUid(rs.getInt(1)); - res.setPrivacy(rs.getInt(2)); - - return res; - }, - mid); - - PrivacyOpts privacyOpts = list.isEmpty() ? null : list.get(0); - - return privacyOpts == null || - privacyOpts.getPrivacy() >= 0 || - uid == privacyOpts.getUid() || - ((privacyOpts.getPrivacy() == -1 || privacyOpts.getPrivacy() == -2) && - uid > 0 && userService.isInWL(privacyOpts.getUid(), uid)); - } - - @Transactional(readOnly = true) - @Override - public boolean isReadOnly(final int mid) { - List list = getJdbcTemplate().queryForList( - "SELECT readonly FROM messages WHERE message_id = ?", - new Object[]{mid}, - Integer.class); - - return !list.isEmpty() && list.get(0) == 1; - } - - @Transactional(readOnly = true) - @Override - public boolean isSubscribed(final int uid, final int mid) { - List list = getJdbcTemplate().queryForList( - "SELECT 1 FROM subscr_messages WHERE suser_id = ? AND message_id = ?", - new Object[]{uid, mid}, - Integer.class); - - return !list.isEmpty() && list.get(0) == 1; - } - - @Transactional(readOnly = true) - @Override - public int getMessagePrivacy(final int mid) { - List list = getJdbcTemplate().queryForList( - "SELECT privacy FROM messages WHERE message_id = ?", - new Object[]{mid}, - Integer.class); - - return list.isEmpty() ? -4 : list.get(0); - } - - @Transactional(readOnly = true) - @Override - public com.juick.Message getMessage(final int mid) { - - List list = getJdbcTemplate().query( - "SELECT messages.message_id as mid, 0 as rid, 0 as replyto, " - + "messages.user_id as uid, users.nick, users.banned as banned, " - + "" - + "messages.ts," - + "messages.readonly, messages.privacy, messages.replies," - + "messages.attach, COUNT(DISTINCT favorites.user_id) as likes, messages.hidden," - + "txt.tags, txt.repliesby, txt.txt, '' as q, messages.updated as updated, 0 as to_uid, " - + "NULL as to_name, txt.updated_at, '' as user_uri, '' as to_uri, '' as reply_uri, 0 as html FROM messages " - + "INNER JOIN users ON messages.user_id = users.id " - + "INNER JOIN messages_txt AS txt " - + "ON messages.message_id = txt.message_id " - + "LEFT JOIN favorites " - + "ON messages.message_id = favorites.message_id AND favorites.like_id=1 " - + "WHERE messages.message_id = ? " - + "GROUP BY mid, rid, replyto, uid, nick, banned, messages.ts, readonly, " - + "privacy, replies, attach, tags, repliesby, q, updated_at, user_uri, to_uri, reply_uri, html", - new MessageMapper(), - mid); - if (!list.isEmpty()) { - final Message message = list.get(0); - Map> reactionStats = updateReactionsFor(Collections.singletonList(mid)); - message.setReactions(reactionStats.get(message.getMid())); - return message; - } - return null; - } - - @Transactional(readOnly = true) - @Override - public com.juick.Message getReply(final int mid, final int rid) { - List list = getJdbcTemplate().query( - "SELECT replies.user_id, users.nick," - + "replies.replyto, replies.ts," - + "replies.attach, replies.txt, IFNULL(q.txt,t.txt) as quote, " - + "COALESCE(q.user_id, m.user_id) AS to_uid, COALESCE(qu.nick, mu.nick) AS to_name, " - + "replies.updated_at, replies.user_uri as uri, " - + "q.user_uri AS to_uri, replies.reply_uri AS reply_uri, replies.html, q.reply_uri " - + "FROM replies LEFT JOIN users ON replies.user_id = users.id " - + "LEFT JOIN replies q ON replies.message_id = q.message_id and replies.replyto = q.reply_id " - + "LEFT JOIN messages_txt t ON replies.message_id = t.message_id " - + "LEFT JOIN messages m ON replies.message_id = m.message_id " - + "LEFT JOIN users qu ON q.user_id=qu.id " - + "LEFT JOIN users mu ON m.user_id=mu.id " - + "WHERE replies.message_id = ? AND replies.reply_id = ?", - (rs, num) -> { - Message msg = new Message(); - - msg.setMid(mid); - msg.setRid(rid); - msg.setUser(new User()); - msg.getUser().setUid(rs.getInt(1)); - msg.getUser().setName(rs.getString(2)); - if (msg.getUser().getUid() == 0) { - msg.getUser().setName(AnonymousUser.INSTANCE.getName()); - msg.getUser().setUri( - URI.create(Optional.ofNullable(rs.getString(11)).orElse(StringUtils.EMPTY))); - } - msg.setReplyto(rs.getInt(3)); - msg.setTimestamp(rs.getTimestamp(4).toInstant()); - msg.setAttachmentType(rs.getString(5)); - msg.setText(rs.getString(6)); - String quote = rs.getString(7); - - if (!StringUtils.isEmpty(quote)) { - msg.setReplyQuote(MessageUtils.formatQuote(quote)); - } - int quoteUid = rs.getInt(8); - User quoteUser = new User(); - quoteUser.setUid(quoteUid); - quoteUser.setName(Optional.ofNullable(rs.getString(9)).orElse(AnonymousUser.INSTANCE.getName())); - quoteUser.setUri(URI.create(Optional.ofNullable(rs.getString(12)).orElse(StringUtils.EMPTY))); - msg.setTo(quoteUser); - msg.setUpdatedAt(rs.getTimestamp(10).toInstant()); - msg.setReplyUri(URI.create(Optional.ofNullable(rs.getString(13)).orElse(StringUtils.EMPTY))); - msg.setHtml(rs.getBoolean(14)); - msg.setReplyToUri(URI.create(Optional.ofNullable(rs.getString(15)).orElse(StringUtils.EMPTY))); - if (StringUtils.isNotEmpty(msg.getAttachmentType())) { - try { - imagesService.setAttachmentMetadata(baseImagesUrl, msg); - } catch (Exception e) { - logger.warn("exception reading images for mid {} rid {}", msg.getMid(), msg.getRid(), e); - } - } - return msg; - }, - mid, rid); - - return list.isEmpty() ? null : list.get(0); - } - - @Override - public Message getReplyByUri(String replyUri) { - List replies = getJdbcTemplate().query("SELECT message_id, reply_id from replies WHERE reply_uri=?", (rs, rowNum) -> getReply(rs.getInt(1), rs.getInt(2)), replyUri); - return replies.isEmpty() ? null : replies.get(0); - } - - @Transactional(readOnly = true) - @Override - public User getMessageAuthor(final int mid) { - List list = getJdbcTemplate().query( - "SELECT messages.user_id, users.nick " - + "FROM messages INNER JOIN users ON messages.user_id = users.id WHERE messages.message_id = ?", - new Object[]{mid}, - (rs, num) -> { - User res = new com.juick.User(); - res.setUid(rs.getInt(1)); - res.setName(rs.getString(2)); - return res; - }); - - return list.isEmpty() ? - null : list.get(0); - } - - @Transactional(readOnly = true) - @Override - public List getMessageRecommendations(final int mid) { - return getJdbcTemplate().queryForList( - "SELECT DISTINCT users.nick FROM favorites " + - "INNER JOIN users ON (favorites.message_id = ? AND favorites.user_id = users.id) " + - "INNER JOIN messages m ON favorites.message_id=m.message_id WHERE favorites.like_id=1 " + - "AND NOT EXISTS (SELECT 1 FROM bl_users WHERE " + - "(user_id = favorites.user_id AND bl_user_id = m.user_id) " + - "OR (user_id = m.user_id AND bl_user_id = favorites.user_id))", - String.class, mid); - } - - @Transactional(readOnly = true) - @Override - public List getAll(final int visitorUid, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("before", before) - .addValue("visitorUid", visitorUid); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT m.message_id FROM messages m WHERE " + - (before > 0 ? - " m.message_id < :before AND " : StringUtils.EMPTY) + - " m.hidden = 0 AND (m.privacy > 0" + - (visitorUid > 1 ? - " OR m.user_id = :visitorUid) AND NOT EXISTS (" + - " SELECT 1 FROM bl_users b WHERE b.user_id = :visitorUid AND b.bl_user_id = m.user_id)" : - ")") + - " AND NOT EXISTS (SELECT 1 FROM bl_tags bt WHERE bt.tag_id IN " + - "(SELECT tag_id FROM messages_tags WHERE message_id = m.message_id) and :visitorUid = bt.user_id)" + - " AND NOT EXISTS (SELECT 1 from users u WHERE u.banned = 1 and u.id = m.user_id and u.id <> :visitorUid) ORDER BY m.message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List getTag(final int tid, final int visitorUid, final int before, final int cnt) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("tid", tid) - .addValue("cnt", cnt) - .addValue("before", before) - .addValue("visitorUid", visitorUid); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT messages.message_id FROM (tags INNER JOIN messages_tags " + - "ON ((tags.synonym_id = :tid OR tags.tag_id = :tid) AND tags.tag_id = messages_tags.tag_id)) " + - "INNER JOIN messages ON messages.message_id = messages_tags.message_id WHERE " + - (before > 0 ? - " messages.message_id < :before AND " : StringUtils.EMPTY) + - "(messages.privacy > 0 OR messages.user_id = :visitorUid) " + - "AND NOT EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :visitorUid and b.bl_user_id = messages.user_id) " + - "ORDER BY messages.message_id DESC LIMIT :cnt", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List getTags(final String tids, final int visitorUid, final int before, final int cnt) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("cnt", cnt) - .addValue("before", before) - .addValue("visitorUid", visitorUid); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT messages.message_id FROM messages_tags " + - "INNER JOIN messages USING(message_id) WHERE messages_tags.tag_id IN (" + tids + ") " + - (before > 0 ? - " AND messages.message_id < :before " : StringUtils.EMPTY) + - " AND (messages.privacy > 0 OR messages.user_id = :visitorUid) " + - "ORDER BY messages.message_id DESC LIMIT :cnt", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List getPlace(final int placeId, final int visitorUid, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("placeId", placeId) - .addValue("before", before) - .addValue("visitorUid", visitorUid); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT message_id FROM messages WHERE place_id = :placeId " + - (before > 0 ? - " AND message_id < :before " : StringUtils.EMPTY) + - " AND (privacy > 0 OR user_id = :visitorUid) ORDER BY message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List getMyFeed(final int uid, final int before, boolean recommended) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("before", before); - - List mids = getNamedParameterJdbcTemplate().queryForList( - "(SELECT message_id FROM messages " + - " INNER JOIN subscr_users ON (subscr_users.suser_id = :uid AND subscr_users.user_id = messages.user_id) " + - " WHERE " + - (before > 0 ? - " message_id < :before AND " : StringUtils.EMPTY) + - " (privacy >= 0 OR (privacy >= -2 AND privacy <= -1" + - " AND EXISTS (SELECT 1 FROM wl_users w WHERE w.wl_user_id = :uid and w.user_id = messages.user_id))) " + - " AND NOT EXISTS (SELECT 1 FROM bl_tags bt WHERE bt.tag_id IN " + - "(SELECT tag_id FROM messages_tags WHERE message_id = messages.message_id) and :uid = bt.user_id))" + - " UNION " + - " (SELECT message_id FROM messages WHERE user_id=:uid " + - (before > 0 ? - " AND message_id < :before " : StringUtils.EMPTY) + - (recommended ? - ") UNION " + - " (SELECT f.message_id as message_id FROM favorites f INNER JOIN messages ON " + - "f.message_id=messages.message_id WHERE " + - "EXISTS (SELECT 1 FROM subscr_users s WHERE s.suser_id = :uid and f.user_id = s.user_id)" + - (before > 0 ? - " AND f.message_id < :before " : StringUtils.EMPTY) : StringUtils.EMPTY) + - " AND NOT EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :uid and b.bl_user_id = messages.user_id)" + - " AND NOT EXISTS (SELECT 1 FROM bl_tags bt WHERE bt.tag_id IN " + - "(SELECT tag_id FROM messages_tags WHERE message_id = messages.message_id) and :uid = bt.user_id)) " + - "ORDER BY message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - - return mids; - } - - @Transactional(readOnly = true) - @Override - public List getPrivate(final int uid, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("before", before); - - return getNamedParameterJdbcTemplate().queryForList - ("SELECT message_id FROM messages WHERE user_id = :uid AND privacy < 0" + - (before > 0 ? - " AND message_id < :before " : StringUtils.EMPTY) + - "ORDER BY message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List getDiscussions(final int uid, final Long to) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("to", new Timestamp(to)); - - if (uid == 0) { - return getNamedParameterJdbcTemplate().query("SELECT message_id FROM messages WHERE " + - (to != 0 ? - " updated < :to AND" : StringUtils.EMPTY) + - " NOT EXISTS (SELECT 1 from users u WHERE u.banned = 1" + - " AND u.id = messages.user_id and u.id <> :uid) " + - " ORDER BY updated DESC, message_id DESC LIMIT 20", - sqlParameterSource, - (rs, rowNum) -> rs.getInt(1)); - } - return getNamedParameterJdbcTemplate().query( - "SELECT messages.message_id, messages.updated FROM subscr_messages " + - "INNER JOIN messages ON messages.message_id=subscr_messages.message_id " + - "WHERE suser_id = :uid " + - (to != 0 ? - "AND updated < :to " : StringUtils.EMPTY) + - " AND NOT EXISTS (SELECT 1 from users u WHERE u.banned = 1" + - " AND u.id = messages.user_id and u.id <> :uid) " + - "ORDER BY updated DESC, message_id DESC LIMIT 20", - sqlParameterSource, - (rs, rowNum) -> rs.getInt(1)); - } - - @Transactional(readOnly = true) - @Override - public List getRecommended(final int uid, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("before", before); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT f.message_id FROM favorites f WHERE " + - "EXISTS (SELECT 1 FROM subscr_users s WHERE s.suser_id = :uid and f.user_id = s.user_id)" + - (before > 0 ? - " AND f.message_id < :before " : StringUtils.EMPTY) + - "ORDER BY f.message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List getPopular(final int visitorUid, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("vid", visitorUid) - .addValue("before", before); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT m.message_id FROM messages m WHERE m.privacy > 0 " + - (before > 0 ? - " AND m.message_id < :before " : StringUtils.EMPTY) + - " AND m.popular > 0 AND NOT EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :vid and b.bl_user_id = m.user_id) " + - " AND NOT EXISTS (SELECT 1 FROM bl_tags bt WHERE bt.tag_id IN " + - "(SELECT tag_id FROM messages_tags WHERE message_id = m.message_id) and :vid = bt.user_id)" + - " ORDER BY m.message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List getPhotos(final int visitorUid, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("vid", visitorUid) - .addValue("before", before); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT m.message_id FROM messages m WHERE (m.privacy > 0 OR m.user_id = :vid) " + - (before > 0 ? - " AND m.message_id < :before " : StringUtils.EMPTY) + - " AND m.attach IS NOT NULL " + - " AND NOT EXISTS (SELECT 1 FROM bl_tags bt WHERE bt.tag_id IN " + - "(SELECT tag_id FROM messages_tags WHERE message_id = m.message_id) and :vid = bt.user_id)" + - " AND NOT EXISTS (SELECT 1 from users u WHERE u.banned = 1 and u.id = m.user_id and u.id <> :vid) " + - " AND NOT EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :vid and b.bl_user_id = m.user_id) " + - " ORDER BY m.message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List getSearch(final User visitor, final String search, final int page) { - return searchService.searchInAllMessages(visitor, search, page); - } - - @Transactional(readOnly = true) - @Override - public List getUserBlog(final int uid, final int privacy, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("privacy", privacy) - .addValue("before", before); - - ; - if (userService.getUserByUID(uid).orElseThrow(IllegalStateException::new).isBanned()) { - throw new HttpNotFoundException(); - } - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT message_id FROM messages WHERE user_id = :uid" + - (before > 0 ? - " AND message_id < :before" : StringUtils.EMPTY) + - " AND privacy >= :privacy ORDER BY message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List getUserTag(final int uid, final int tid, final int privacy, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("tid", tid) - .addValue("privacy", privacy) - .addValue("before", before); - - if (userService.getUserByUID(uid).orElseThrow(IllegalStateException::new).isBanned()) { - throw new HttpNotFoundException(); - } - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT messages.message_id FROM messages_tags INNER JOIN messages " + - " USING (message_id) WHERE messages.user_id = :uid AND messages_tags.tag_id = :tid " + - (before > 0 ? - " AND messages.message_id < :before " : StringUtils.EMPTY) + - " AND messages.privacy >= :privacy ORDER BY messages.message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List getUserBlogAtDay(final int uid, final int privacy, final int daysback) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("privacy", privacy) - .addValue("daysback", daysback); - - if (userService.getUserByUID(uid).orElseThrow(IllegalStateException::new).isBanned()) { - throw new HttpNotFoundException(); - } - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT message_id FROM messages WHERE user_id = :uid" + - (daysback > 0 ? - " AND ts >= date(NOW() - INTERVAL :daysback day)" + - " AND ts < date(NOW() - INTERVAL :daysback day + INTERVAL 1 day)" : StringUtils.EMPTY) + - " AND privacy >= :privacy ORDER BY message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List getUserBlogWithRecommendations(final int uid, final int privacy, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("privacy", privacy) - .addValue("before", before); - - if (userService.getUserByUID(uid).orElseThrow(IllegalStateException::new).isBanned()) { - throw new HttpNotFoundException(); - } - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT message_id FROM " + - "(SELECT message_id FROM favorites " + - " WHERE user_id = :uid " + - (before > 0 ? - " AND message_id < :before " : StringUtils.EMPTY) + - " ORDER BY message_id DESC LIMIT 20) as r" + - " UNION ALL " + - "SELECT message_id FROM " + - "(SELECT message_id FROM messages WHERE user_id = :uid" + - (before > 0 ? - " AND message_id < :before" : StringUtils.EMPTY) + - " AND privacy >= :privacy ORDER BY message_id DESC LIMIT 20) as m " + - "ORDER BY message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List getUserRecommendations(final int uid, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("before", before); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT message_id FROM favorites " + - " WHERE user_id = :uid " + - (before > 0 ? - " AND message_id < :before " : StringUtils.EMPTY) + - " ORDER BY message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List getUserPhotos(final int uid, final int privacy, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("privacy", privacy) - .addValue("before", before); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT message_id FROM messages WHERE user_id = :uid " + - (before > 0 ? - " AND message_id < :before " : StringUtils.EMPTY) + - " AND privacy >= :privacy AND attach IS NOT NULL ORDER BY message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List getUserSearch(final User visitor, final int UID, final String search, final int privacy, final int page) { - return searchService.searchByStringAndUser(visitor, search, UID, page); - } - - @Transactional(readOnly = true) - @Override - public List getMessages(final User visitor, final List mids) { - if (CollectionUtils.isNotEmpty(mids)) { - - List msgs = getNamedParameterJdbcTemplate().query( - "WITH RECURSIVE banned(message_id, reply_id) " - + "AS (SELECT message_id, reply_id FROM replies WHERE replies.message_id IN (:ids) " - + "AND (EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :uid AND b.bl_user_id = replies.user_id) " - + "OR EXISTS (SELECT 1 from users u WHERE u.banned = 1 and u.id = replies.user_id and u.id <> :uid)) " - + "UNION ALL SELECT replies.message_id, replies.reply_id FROM replies INNER JOIN banned " - + "ON banned.reply_id = replies.replyto AND banned.message_id=replies.message_id " - + "WHERE replies.message_id IN (:ids)) " - + "SELECT messages.message_id, 0 as rid, 0 as replyto, " - + "messages.user_id,users.nick, 0 as banned, " - + "messages.ts," - + "messages.readonly,messages.privacy, messages.replies-COUNT(DISTINCT banned.reply_id) as replies," - + "messages.attach,COUNT(DISTINCT favorites.user_id) AS likes,messages.hidden," - + "messages_txt.tags,messages_txt.repliesby, messages_txt.txt, '' as q, " - + "messages.updated, 0 as to_uid, NULL as to_name, messages_txt.updated_at, '' as user_uri, " - + "'' as to_uri, '' as reply_uri, 0 as html " - + "FROM (messages INNER JOIN messages_txt " - + "ON messages.message_id=messages_txt.message_id) " - + "INNER JOIN users ON messages.user_id=users.id " - + "LEFT JOIN favorites " - + "ON messages.message_id = favorites.message_id AND favorites.like_id=1 " - + "LEFT JOIN banned " - + "ON messages.message_id = banned.message_id " - + "WHERE messages.message_id IN (:ids) GROUP BY " - + "messages.message_id, rid, replyto, messages.user_id, users.nick, banned, messages.ts, " - + "messages.readonly, messages.privacy, messages.attach, messages.hidden, messages_txt.tags, " - + "messages_txt.repliesby, messages_txt.txt, q, messages.updated, to_uid, to_name, updated_at, " - + "user_uri, reply_uri, html", - new MapSqlParameterSource("ids", mids) - .addValue("uid", visitor.getUid()), - new MessageMapper()); - - - Map> likes = updateReactionsFor(mids); - - msgs.forEach(i -> i.setReactions(likes.get(i.getMid()))); - - msgs.sort(Comparator.comparing(item -> mids.indexOf(item.getMid()))); - - return msgs; - } - return Collections.emptyList(); - } - - - @Transactional(readOnly = true) - @Override - public Map> updateReactionsFor(final List mids) { - - return getNamedParameterJdbcTemplate().query("select f.message_id as mid, f.like_id as lid," + - " r.description as descr, count(f.like_id) as cnt" + - " from favorites f LEFT JOIN reactions r ON f.like_id = r.like_id " + - " where f.message_id IN (:mids) " + - " group by f.message_id, f.like_id", new MapSqlParameterSource("mids", mids), (ResultSet rs) -> { - Map> results = new HashMap<>(); - - - while (rs.next()) { - int messageId = rs.getInt("mid"); - int likeId = rs.getInt("lid"); - int count = rs.getInt("cnt"); - String description = rs.getString("descr"); - Reaction reaction = new Reaction(likeId); - reaction.setCount(count); - reaction.setDescription(description); - results.computeIfAbsent(messageId, HashSet::new); - results.get(messageId).add(reaction); - } - - return results; - }); - - } - - - @Transactional - @Override - public List getReplies(final User user, final int mid) { - List replies = getNamedParameterJdbcTemplate().query( - "WITH RECURSIVE banned(reply_id, user_id) AS (" + - "SELECT reply_id, user_id FROM replies " + - "WHERE replies.message_id = :mid " + - "AND EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :uid AND b.bl_user_id = replies.user_id) " + - "UNION ALL SELECT replies.reply_id, replies.user_id FROM replies " + - "INNER JOIN banned ON banned.reply_id = replies.replyto " + - "WHERE replies.message_id = :mid) " + - "SELECT replies.message_id as mid, replies.reply_id, replies.replyto, " + - "replies.user_id, users.nick, users.banned, " + - "replies.ts, " + - "0 as readonly, 0 as privacy, 0 as replies, " + - "replies.attach, 0 as likes, 0 as hidden, " + - "NULL as tags, NULL as repliesby, replies.txt, " + - "IFNULL(qw.txt, t.txt) as q, " + - "NOW(), " + - "COALESCE(qw.user_id, m.user_id) as to_uid, COALESCE(qu.nick, mu.nick) as to_name, " + - "replies.updated_at, replies.user_uri as uri, " + - "qw.user_uri as to_uri, replies.reply_uri, replies.html " + - "FROM replies LEFT JOIN users " + - "ON replies.user_id = users.id " + - "LEFT JOIN replies qw ON replies.message_id = qw.message_id and replies.replyto = qw.reply_id " + - "LEFT JOIN messages_txt t on replies.message_id = t.message_id " + - "LEFT JOIN messages m on replies.message_id = m.message_id " + - "LEFT JOIN users qu ON qw.user_id=qu.id " + - "LEFT JOIN users mu ON m.user_id=mu.id " + - "WHERE replies.message_id = :mid " + - "AND NOT EXISTS (SELECT 1 from users u WHERE u.banned = 1 and u.id = replies.user_id and u.id <> :uid)" + - "AND NOT EXISTS (SELECT 1 FROM banned WHERE banned.reply_id = replies.reply_id) " + - "AND NOT EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :uid AND b.bl_user_id = m.user_id) " + - "ORDER BY replies.reply_id ASC", - new MapSqlParameterSource("mid", mid).addValue("uid", user.getUid()), - new MessageMapper()); - if (replies.size() > 0) { - setRead(user, mid); - } - return replies; - } - - @Transactional - @Override - public boolean setMessagePopular(final int mid, final int popular) { - int ret; - MapSqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("mid", mid) - .addValue("popular", popular); - - switch (popular) { - case -2: - ret = getNamedParameterJdbcTemplate().update( - "UPDATE messages SET hidden = 1 WHERE message_id = :mid", - sqlParameterSource); - break; - case -1: - sqlParameterSource.addValue("popular", 0); - default: - ret = getNamedParameterJdbcTemplate().update( - "UPDATE messages SET popular = :popular WHERE message_id = :mid", - sqlParameterSource); - break; - } - - if (popular == -1) - ret = getNamedParameterJdbcTemplate().update( - "INSERT INTO top_ignore_messages VALUES (:mid)", - sqlParameterSource); - - return ret > 0; - } - - @Transactional - @Override - public boolean setMessagePrivacy(final int mid) { - return getJdbcTemplate().update("UPDATE messages SET privacy=1 WHERE message_id=?", mid) > 0; - } - - @Transactional - @Override - public boolean deleteMessage(final int uid, final int mid) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("mid", mid) - .addValue("uid", uid); - - if (getNamedParameterJdbcTemplate().update( - "DELETE FROM messages WHERE message_id = :mid AND user_id = :uid", sqlParameterSource) > 0) { - - getNamedParameterJdbcTemplate().update("DELETE FROM messages_txt WHERE message_id = :mid", sqlParameterSource); - getNamedParameterJdbcTemplate().update("DELETE FROM replies WHERE message_id = :mid", sqlParameterSource); - getNamedParameterJdbcTemplate().update("DELETE FROM subscr_messages WHERE message_id = :mid", sqlParameterSource); - getNamedParameterJdbcTemplate().update("DELETE FROM messages_tags WHERE message_id = :mid", sqlParameterSource); - - return true; - } - return false; - } - @Transactional - @Override - public boolean deleteReply(final int uid, final int mid, final int rid) { - User author = getMessageAuthor(mid); - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("mid", mid) - .addValue("uid", uid) - .addValue("rid", rid); - boolean result; - if (author.getUid() == uid) { - result = getNamedParameterJdbcTemplate() - .update("DELETE FROM replies WHERE message_id=:mid AND reply_id=:rid", sqlParameterSource) > 0; - } else { - result = getNamedParameterJdbcTemplate() - .update("DELETE FROM replies WHERE message_id=:mid AND reply_id=:rid AND user_id=:uid" - , sqlParameterSource) > 0; - } - if (result) { - getNamedParameterJdbcTemplate().update("UPDATE messages SET replies=replies-1 WHERE message_id=:mid", sqlParameterSource); - updateRepliesBy(mid); - return true; - } - return false; - } - - @Transactional(readOnly = true) - @Override - public List getLastMessages(int hours) { - return getJdbcTemplate().queryForList("SELECT message_id FROM messages WHERE messages.ts>TIMESTAMPADD(HOUR,?,NOW())", - Integer.class, -hours); - - } - - @Transactional(readOnly = true) - @Override - public List getLastReplies(int hours) { - return getJdbcTemplate().query("SELECT users2.nick,replies.message_id,replies.reply_id," + - "users.nick,replies.txt," + - "replies.ts,replies.attach,replies.ts+0, replies.html " + - "FROM ((replies INNER JOIN users ON replies.user_id=users.id) " + - "INNER JOIN messages ON replies.message_id=messages.message_id) " + - "INNER JOIN users AS users2 ON messages.user_id=users2.id " + - "WHERE replies.ts>TIMESTAMPADD(HOUR,?,NOW()) AND messages.privacy>0", (rs, rowNum) -> { - ResponseReply reply = new ResponseReply(); - reply.setMuname(rs.getString(1)); - reply.setMid(rs.getInt(2)); - reply.setRid(rs.getInt(3)); - reply.setUname(rs.getString(4)); - reply.setDescription(rs.getString(5)); - reply.setPubDate(rs.getTimestamp(6)); - reply.setAttachmentType(rs.getString(7)); - reply.setHtml(rs.getBoolean(8)); - return reply; - }, -hours); - } - @Transactional(readOnly = true) - @Override - public List getPopularCandidates() { - return getJdbcTemplate().queryForList("SELECT replies.message_id FROM replies " + - "INNER JOIN messages ON replies.message_id = messages.message_id " + - "LEFT JOIN messages_tags ON messages_tags.message_id = messages.message_id " + - "WHERE COALESCE(messages_tags.tag_id, 0) != 2 " + - "AND COALESCE(messages_tags.tag_id, 0) != 805 AND replies.ts > TIMESTAMPADD(HOUR, -2, CURRENT_TIMESTAMP) " + - "AND messages.popular=0 GROUP BY messages.message_id having COUNT(DISTINCT(replies.user_id)) > 5 " + - "UNION ALL SELECT favorites.message_id FROM favorites " + - "INNER JOIN messages ON messages.message_id = favorites.message_id " + - "LEFT JOIN messages_tags ON messages_tags.message_id = messages.message_id " + - "WHERE COALESCE(messages_tags.tag_id, 0) != 2 AND favorites.ts > TIMESTAMPADD(HOUR, -2, CURRENT_TIMESTAMP) " + - "AND messages.popular=0 GROUP BY messages.message_id HAVING COUNT(DISTINCT favorites.user_id) > 1;", Integer.class); - } - @Transactional - @Override - public void setLastReadComment(User user, Integer mid, Integer rid) { - jdbcTemplate.update("UPDATE subscr_messages SET last_read_rid=GREATEST(?, last_read_rid) WHERE message_id=? AND suser_id=?", - rid, mid, user.getUid()); - } - @Transactional - @Override - public void setRead(User user, Integer mid) { - jdbcTemplate.update("UPDATE subscr_messages SET last_read_rid=(select replies from messages " + - "where messages.message_id=subscr_messages.message_id) WHERE message_id=? AND suser_id=?", - mid, user.getUid()); - } - - @Transactional(readOnly = true) - @Override - public List getUnread(User user) { - return jdbcTemplate.queryForList( - "select subscr_messages.message_id " + - "from subscr_messages inner join messages on subscr_messages.message_id=messages.message_id " + - "where subscr_messages.suser_id=? and " + - "messages.replies>subscr_messages.last_read_rid", - Integer.class, user.getUid()); - } - - @Transactional - @Override - public boolean updateMessage(Integer mid, Integer rid, String body) { - Instant now = Instant.now(); - if (rid == 0) { - return jdbcTemplate.update("UPDATE messages_txt SET txt=?, updated_at=? WHERE message_id=?", body, Timestamp.from(now), mid) > 0; - } else { - return jdbcTemplate.update("UPDATE replies SET txt=?, updated_at=? WHERE message_id=? and reply_id=?", - body, Timestamp.from(now), mid, rid) > 0; - } - } - - @Override - public boolean updateReplyUri(Message reply, URI replyUri) { - return jdbcTemplate.update("UPDATE replies SET reply_uri=?, html=1 WHERE message_id=? AND reply_id=?", - replyUri.toASCIIString(), reply.getMid(), reply.getRid()) > 0; - } - - @Override - public boolean replyExists(URI replyUri) { - return jdbcTemplate.queryForList("SELECT reply_id FROM replies WHERE reply_uri=?", - Integer.class, replyUri.toASCIIString()).size() > 0; - } - - @Override - public boolean deleteReply(URI userUri, URI replyUri) { - return jdbcTemplate.update("DELETE FROM replies WHERE user_uri=? AND reply_uri=?", - userUri.toASCIIString(), replyUri.toASCIIString()) > 0; - } -} diff --git a/juick-server/src/main/java/com/juick/service/MessengerServiceImpl.java b/juick-server/src/main/java/com/juick/service/MessengerServiceImpl.java deleted file mode 100644 index 57101ffe..00000000 --- a/juick-server/src/main/java/com/juick/service/MessengerServiceImpl.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.juick.service; - -import com.juick.User; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.Optional; -import java.util.UUID; - -@Repository -public class MessengerServiceImpl extends BaseJdbcService implements MessengerService { - - @Transactional(readOnly = true) - @Override - public Integer getUserId(String senderId) { - List list = getJdbcTemplate().queryForList( - "SELECT id FROM users INNER JOIN messenger " + - "ON messenger.user_id = users.id WHERE messenger.sender_id=?", Integer.class, senderId); - - return list.isEmpty() ? 0 : list.get(0); - } - @Transactional(readOnly = true) - @Override - public Optional getSenderId(User user) { - List list = getJdbcTemplate().queryForList( - "SELECT sender_id FROM messenger " + - "WHERE user_id=?", String.class, user.getUid()); - - return list.isEmpty() ? Optional.empty() : Optional.of(list.get(0)); - } - - @Transactional - @Override - public boolean createMessengerUser(String senderId, String displayName) { - return getJdbcTemplate().update( - "INSERT INTO messenger(sender_id, display_name, loginhash) VALUES(?,?,?)", - senderId, displayName, UUID.randomUUID().toString()) > 0; - } - @Transactional(readOnly = true) - @Override - public String getDisplayName(String hash) { - try { - return getJdbcTemplate().queryForObject("SELECT display_name FROM messenger WHERE loginhash=?", String.class, hash); - } catch (EmptyResultDataAccessException e) { - return null; - } - } - @Transactional - @Override - public String getSignUpHash(final String senderId, final String username) { - List list = getJdbcTemplate().queryForList( - "SELECT loginhash FROM messenger WHERE sender_id = ? AND user_id IS NULL", - String.class, - senderId); - - if (list.isEmpty()) { - String hash = UUID.randomUUID().toString(); - getJdbcTemplate().update( - "INSERT INTO messenger(sender_id, loginhash, display_name) VALUES (?, ?, ?)", senderId, hash, username); - return hash; - } - return list.get(0); - } - @Transactional - @Override - public boolean linkMessengerUser(String hash, int uid) { - return getJdbcTemplate().update("UPDATE messenger SET user_id=?, loginhash=NULL WHERE loginhash=?", uid, hash) > 0; - } -} diff --git a/juick-server/src/main/java/com/juick/service/PMQueriesServiceImpl.java b/juick-server/src/main/java/com/juick/service/PMQueriesServiceImpl.java deleted file mode 100644 index 712e4b0e..00000000 --- a/juick-server/src/main/java/com/juick/service/PMQueriesServiceImpl.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service; - -import com.juick.Chat; -import com.juick.User; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.SqlParameterSource; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -public class PMQueriesServiceImpl extends BaseJdbcService implements PMQueriesService { - - @Transactional - @Override - public boolean createPM(final int uidFrom, final int uid_to, final String body) { - return getJdbcTemplate().update( - "INSERT INTO pm(user_id, user_id_to, txt) VALUES (?, ?, ?)", - uidFrom, uid_to, body) > 0; - } - - @Transactional - @Override - public boolean addPMinRoster(final int uid, final String jid) { - return getJdbcTemplate().update( - "INSERT INTO pm_inroster(user_id, jid) VALUES (?, ?) ON DUPLICATE KEY UPDATE user_id=user_id", uid, jid) > 0; - } - - @Transactional - @Override - public boolean removePMinRoster(final int uid, final String jid) { - return getJdbcTemplate().update( - "DELETE FROM pm_inroster WHERE user_id = ? AND jid = ?", uid, jid) > 0; - } - - @Transactional - @Override - public boolean havePMinRoster(final int uid, final String jid) { - List res = getJdbcTemplate().queryForList( - "SELECT 1 FROM pm_inroster WHERE user_id = ? AND jid = ?", - Integer.class, - uid, jid); - return res.size() > 0; - } - - @Transactional(readOnly = true) - @Override - public List getLastChats(final User user) { - return getJdbcTemplate().query( - "SELECT l.user_id, users.nick, l.last, pm.txt FROM pm " - + "INNER JOIN users ON users.id = pm.user_id " - + "" - + "INNER JOIN (SELECT user_id, MAX(ts) AS last FROM pm " - + "WHERE user_id_to=? GROUP BY user_id) l ON l.last = pm.ts " - + "WHERE pm.user_id_to=? " - + "ORDER BY l.last DESC", - (rs, rowNum) -> { - com.juick.Chat u = new com.juick.Chat(); - u.setUid(rs.getInt(1)); - u.setName(rs.getString(2)); - u.setLastMessageTimestamp(rs.getTimestamp(3).toInstant()); - u.setLastMessageText(rs.getString(4).trim()); - return u; - }, - user.getUid(), user.getUid()); - } - - @Transactional - @Override - public List getPMMessages(final int uid, final int uidTo) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("uidTo", uidTo); - - return getNamedParameterJdbcTemplate().query( - "SELECT pm.user_id, pm.txt, pm.ts, users.nick FROM pm INNER JOIN users ON users.id=pm.user_id WHERE (user_id = :uid AND user_id_to = :uidTo) " - + "OR (user_id_to = :uid AND user_id = :uidTo) ORDER BY ts DESC LIMIT 20", - sqlParameterSource, - (rs, rowNum) -> { - com.juick.Message msg = new com.juick.Message(); - int uuid = rs.getInt(1); - User user = new User(); - user.setUid(uuid); - user.setName(rs.getString(4)); - msg.setUser(user); - msg.setText(rs.getString(2).trim()); - msg.setTimestamp(rs.getTimestamp(3).toInstant()); - return msg; - }); - } - - @Transactional(readOnly = true) - @Override - public List getLastPMInbox(final int uid) { - return getJdbcTemplate().query( - "SELECT pm.user_id, users.nick, pm.txt, pm.ts " + - "FROM pm INNER JOIN users ON pm.user_id=users.id WHERE pm.user_id_to=? ORDER BY pm.ts DESC LIMIT 20", - (rs, num) -> { - com.juick.Message msg = new com.juick.Message(); - msg.setUser(new User()); - msg.getUser().setUid(rs.getInt(1)); - msg.getUser().setName(rs.getString(2)); - msg.setText(rs.getString(3).trim()); - msg.setTimestamp(rs.getTimestamp(4).toInstant()); - return msg; - }, - uid); - } - - @Transactional(readOnly = true) - @Override - public List getLastPMSent(final int uid) { - return getJdbcTemplate().query( - "SELECT pm.user_id_to, users.nick, pm.txt, " + - "pm.ts FROM pm INNER JOIN users ON pm.user_id_to=users.id " + - "WHERE pm.user_id=? ORDER BY pm.ts DESC LIMIT 20", - (rs, num) -> { - com.juick.Message msg = new com.juick.Message(); - msg.setUser(new User()); - msg.getUser().setUid(rs.getInt(1)); - msg.getUser().setName(rs.getString(2)); - msg.setText(rs.getString(3).trim()); - msg.setTimestamp(rs.getTimestamp(4).toInstant()); - return msg; - }, - uid); - } -} diff --git a/juick-server/src/main/java/com/juick/service/PrivacyQueriesServiceImpl.java b/juick-server/src/main/java/com/juick/service/PrivacyQueriesServiceImpl.java deleted file mode 100644 index 9f9cda1d..00000000 --- a/juick-server/src/main/java/com/juick/service/PrivacyQueriesServiceImpl.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service; - -import com.juick.Tag; -import com.juick.User; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -@Transactional -public class PrivacyQueriesServiceImpl extends BaseJdbcService implements PrivacyQueriesService { - - @Override - public PrivacyResult blacklistUser(final User user, final User target) { - int result = getJdbcTemplate().update( - "DELETE FROM bl_users WHERE user_id = ? AND bl_user_id = ?", - user.getUid(), target.getUid()); - - if (result > 0) - return PrivacyResult.Removed; - - getJdbcTemplate().update( - "INSERT INTO bl_users(user_id, bl_user_id) VALUES (?, ?)", - user.getUid(), target.getUid()); - - return PrivacyResult.Added; - } - - @Override - public PrivacyResult blacklistTag(final User user, final Tag tag) { - int result = getJdbcTemplate().update( - "DELETE FROM bl_tags WHERE user_id = ? AND tag_id = ?", - user.getUid(), tag.TID); - - if (result > 0) - return PrivacyResult.Removed; - - getJdbcTemplate().update( - "INSERT INTO bl_tags(user_id, tag_id) VALUES (?, ?)", - user.getUid(), tag.TID); - - return PrivacyResult.Added; - } -} diff --git a/juick-server/src/main/java/com/juick/service/PushQueriesServiceImpl.java b/juick-server/src/main/java/com/juick/service/PushQueriesServiceImpl.java deleted file mode 100644 index 7f97956c..00000000 --- a/juick-server/src/main/java/com/juick/service/PushQueriesServiceImpl.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service; - -import org.apache.commons.collections4.CollectionUtils; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import javax.inject.Inject; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -public class PushQueriesServiceImpl extends BaseJdbcService implements PushQueriesService { - - @Transactional(readOnly = true) - @Override - public List getGCMRegID(final int uid) { - return getJdbcTemplate().queryForList( - "SELECT regid FROM android WHERE user_id=?", - String.class, - uid); - } - - @Transactional(readOnly = true) - @Override - public List getGCMTokens(final Collection uids) { - if (CollectionUtils.isEmpty(uids)) - return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT regid FROM android INNER JOIN users ON (users.id = android.user_id) WHERE users.id IN (:ids)", - new MapSqlParameterSource("ids", uids), - String.class); - } - - @Transactional - @Override - public boolean addGCMToken(Integer uid, String token) { - return getJdbcTemplate().update("INSERT IGNORE INTO android(user_id,regid) VALUES (?, ?)", - uid, token) > 0; - } - - @Transactional - @Override - public boolean deleteGCMToken(String token) { - return getJdbcTemplate().update("DELETE FROM android WHERE regid=?", token) > 0; - } - - @Transactional(readOnly = true) - @Override - public List getMPNSURL(final int uid) { - return getJdbcTemplate().queryForList( - "SELECT url FROM winphone WHERE user_id=?", - String.class, - uid); - } - - @Transactional(readOnly = true) - @Override - public List getMPNSTokens(final Collection uids) { - if (CollectionUtils.isEmpty(uids)) - return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT url FROM winphone INNER JOIN users ON (users.id=winphone.user_id) WHERE users.id IN (:ids)", - new MapSqlParameterSource("ids", uids), - String.class); - } - - @Transactional - @Override - public boolean addMPNSToken(Integer uid, String token) { - return getJdbcTemplate().update("INSERT IGNORE INTO winphone(user_id,url) VALUES (?, ?)", - uid, token) > 0; - } - - @Transactional - @Override - public boolean deleteMPNSToken(String token) { - return getJdbcTemplate().update("DELETE FROM winphone WHERE url=?", token) > 0; - } - - @Transactional(readOnly = true) - @Override - public List getAPNSToken(final int uid) { - return getJdbcTemplate().queryForList( - "SELECT token from ios WHERE user_id=?", - String.class, - uid); - } - - @Transactional - @Override - public boolean deleteAPNSToken(String token) { - return getJdbcTemplate().update("DELETE FROM ios WHERE token=?", token) > 0; - } - - @Transactional(readOnly = true) - @Override - public List getAPNSTokens(final Collection uids) { - if (CollectionUtils.isEmpty(uids)) - return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT token FROM ios INNER JOIN users ON (users.id = ios.user_id) WHERE users.id IN (:ids)", - new MapSqlParameterSource("ids", uids), - String.class); - } - - @Transactional - @Override - public boolean addAPNSToken(Integer uid, String token) { - try { - return getJdbcTemplate().update("INSERT INTO ios(user_id,token) VALUES (?, ?)", - uid, token) > 0; - } catch (DuplicateKeyException e) { - return true; - } - } -} diff --git a/juick-server/src/main/java/com/juick/service/ShowQueriesServiceImpl.java b/juick-server/src/main/java/com/juick/service/ShowQueriesServiceImpl.java deleted file mode 100644 index 0fba35f1..00000000 --- a/juick-server/src/main/java/com/juick/service/ShowQueriesServiceImpl.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service; - -import com.juick.User; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Collections; -import java.util.List; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -@Transactional(readOnly = true) -public class ShowQueriesServiceImpl extends BaseJdbcService implements ShowQueriesService { - - @Override - public List getRecommendedUsers(final User forUser) { - if (forUser == null) - return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT u.nick FROM subscr_users su1 INNER JOIN users u " + - "ON su1.user_id = u.id " + - "WHERE NOT EXISTS (SELECT 1 FROM subscr_users su2 WHERE su2.suser_id = :uid and su1.user_id = su2.user_id) " + - "AND EXISTS (SELECT 1 FROM subscr_users su3 WHERE su3.suser_id = :uid and su3.user_id = su1.suser_id ) " + - "AND NOT EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :uid and su1.user_id = b.bl_user_id) " + - "AND su1.user_id != :uid AND u.lastmessage > UNIX_TIMESTAMP() - 259200 " + - "GROUP BY su1.user_id ORDER BY count(*) DESC LIMIT 10", - new MapSqlParameterSource("uid", forUser.getUid()), - String.class); - } - - @Override - public List getTopUsers() { - return getJdbcTemplate().query( - "SELECT users.nick,COUNT(subscr_users.suser_id) AS cnt " + - "FROM (subscr_users INNER JOIN users ON subscr_users.user_id=users.id) " + - "INNER JOIN useroptions ON users.id=useroptions.user_id " + - "WHERE useroptions.privacy_view>0 AND users.lastmessage > UNIX_TIMESTAMP() - 259200 " + - "AND users.id!=2 GROUP BY subscr_users.user_id ORDER BY cnt DESC LIMIT 10", - (rs, rowNum) -> rs.getString(1)); - } -} diff --git a/juick-server/src/main/java/com/juick/service/SphinxSearchService.java b/juick-server/src/main/java/com/juick/service/SphinxSearchService.java deleted file mode 100644 index de7bd7f2..00000000 --- a/juick-server/src/main/java/com/juick/service/SphinxSearchService.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service; - -import com.juick.User; -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import javax.inject.Inject; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * Created by aalexeev on 11/18/16. - */ - -@Repository -@Transactional(readOnly = true) -public class SphinxSearchService extends BaseJdbcService implements SearchService { - private static final int DEFAULT_MAX_RESULT = 20; - - private int maxResult = DEFAULT_MAX_RESULT; - - @Inject - UserService userService; - - public String sortHint(String searchString) { - boolean isOneWord = searchString.split("[^\\S\\+]+").length == 1; - return isOneWord ? "extended:@id desc" : "extended:@weight desc, @id desc"; - } - - @Override - public List searchInAllMessages(User visitor, final String searchString, final int page) { - if (StringUtils.isBlank(searchString)) - return Collections.emptyList(); - - Map sphinxQuery = new HashMap<>(); - sphinxQuery.put("limit", String.valueOf(maxResult)); - sphinxQuery.put("mode", "any"); - sphinxQuery.put("sort", sortHint(searchString)); - String usersFilter = userService.getUserBLUsers(visitor.getUid()).stream().map(u -> String.valueOf(u.getUid())).collect(Collectors.joining(",")); - sphinxQuery.put("!filter", "user_id," + usersFilter); - if (page > 0) { - sphinxQuery.put("offset", String.valueOf(page * maxResult)); - } - - return getJdbcTemplate().queryForList( - String.format("SELECT id FROM search WHERE query = '%s;%s'", searchString, - sphinxQuery.entrySet().stream().map(Object::toString) - .collect(Collectors.joining(";"))), Integer.class); - } - - @Override - public List searchByStringAndUser(User visitor, final String searchString, final int userId, int page) { - if (StringUtils.isBlank(searchString)) - return Collections.emptyList(); - - Map sphinxQuery = new HashMap<>(); - sphinxQuery.put("limit", String.valueOf(maxResult)); - sphinxQuery.put("mode", "any"); - sphinxQuery.put("sort", sortHint(searchString)); - if (page > 0) { - sphinxQuery.put("offset", String.valueOf(page * maxResult)); - } - return getJdbcTemplate().queryForList( - String.format("SELECT id FROM search WHERE query = '%s;%s;filter=user_id,%d'", searchString, - sphinxQuery.entrySet().stream().map(Object::toString) - .collect(Collectors.joining(";")), userId), Integer.class); - } - - @Override - public void setMaxResult(int maxResult) { - if (maxResult <= 0) - throw new IllegalArgumentException("maxResult value (" + maxResult + ") must be greater then 0"); - - this.maxResult = maxResult; - } -} \ No newline at end of file diff --git a/juick-server/src/main/java/com/juick/service/SubscriptionServiceImpl.java b/juick-server/src/main/java/com/juick/service/SubscriptionServiceImpl.java deleted file mode 100644 index 5ce3593b..00000000 --- a/juick-server/src/main/java/com/juick/service/SubscriptionServiceImpl.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service; - -import com.juick.Message; -import com.juick.Tag; -import com.juick.User; -import com.juick.model.NotifyOpts; -import com.juick.util.MessageUtils; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.collections4.IteratorUtils; -import org.apache.commons.collections4.ListUtils; -import org.apache.commons.lang3.StringUtils; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import javax.annotation.Nonnull; -import javax.inject.Inject; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -public class SubscriptionServiceImpl extends BaseJdbcService implements SubscriptionService { - @Inject - private UserService userService; - @Inject - private MessagesService messagesService; - @Inject - private TagService tagService; - - @Transactional(readOnly = true) - @Override - public List getSubscribedUsers(final int uid, final Message msg) { - int mid = msg.getMid(); - User author = messagesService.getMessageAuthor(mid); - - List subscribers = userService.getUserReaders(uid); - List mentionedUsers = userService.getUsersByName(MessageUtils.getMentions(msg).stream() - .map(u -> u.substring(1)).collect(Collectors.toList())); - List users = ListUtils.union(subscribers, mentionedUsers); - List tags = tagService.getMessageTagsIDs(mid); - List tagsStr = tagService.getMessageTags(mid).stream().map(t -> t.getTag().getName()).collect(Collectors.toList()); - - Set set = new HashSet<>(); - set.addAll( - users.stream() - .map(User::getUid).filter(u -> Collections.disjoint(tagService.getUserBLTags(u), tagsStr)) - .collect(Collectors.toList())); - - - if (!tags.isEmpty()) { - List tagUsers = getNamedParameterJdbcTemplate().queryForList( - "SELECT st.suser_id FROM subscr_tags st " + - "WHERE st.tag_id IN (:ids) AND st.suser_id != :uid " + - " AND NOT EXISTS (SELECT 1 FROM bl_users bu WHERE bu.bl_user_id = :authorUid and st.suser_id = bu.user_id)" + - " AND NOT EXISTS (SELECT 1 FROM bl_tags bt WHERE bt.tag_id IN (:ids) and st.suser_id = bt.user_id)", - new MapSqlParameterSource() - .addValue("ids", tags) - .addValue("uid", uid) - .addValue("authorUid", author.getUid()), - Integer.class); - set.addAll(tagUsers); - } - return userService.getUsersByID(set); - } - @Override - public List getUsersSubscribedToComments(@Nonnull final Message msg, @Nonnull final Message reply) { - return getUsersSubscribedToComments(msg, reply, false); - } - - @Transactional(readOnly = true) - @Override - public List getUsersSubscribedToComments(@Nonnull final Message msg, @Nonnull final Message reply, - boolean blacklisted) { - List subscribers = userService.getUsersByID(getJdbcTemplate().queryForList( - "SELECT suser_id FROM subscr_messages WHERE message_id=? AND suser_id!=?", - Integer.class, - msg.getMid(), reply.getUser().getUid())); - List mentionedUsers = userService.getUsersByName(MessageUtils.getMentions(reply).stream() - .map(u -> u.substring(1)).collect(Collectors.toList())); - List users = IteratorUtils.toList(CollectionUtils.union(subscribers, mentionedUsers).iterator()); - if (!users.isEmpty()) { - return users.stream() - .filter(u -> blacklisted || !userService.isReplyToBL(u, reply)) - .collect(Collectors.toList()); - } - return Collections.emptyList(); - } - - @Override - public List getUsersSubscribedToUserRecommendations(final int uid, final Message msg) { - List msgTags = tagService.getMessageTags(msg.getMid()).stream().map(t -> t.getTag().getName()).collect(Collectors.toList()); - if (msg.getLikes() == 1) { - return userService.getUserReaders(uid).stream() - .filter(u -> !u.equals(msg.getUser())) - .filter(u -> !userService.isInBLAny(u.getUid(), msg.getUser().getUid())) - .filter(u -> Collections.disjoint(tagService.getUserBLTags(u.getUid()), msgTags)) - .collect(Collectors.toList()); - } - return Collections.emptyList(); - } - - @Transactional - @Override - public boolean subscribeMessage(final Message message, final User user) { - try { - boolean result = getJdbcTemplate().update( - "INSERT INTO subscr_messages(suser_id, message_id) VALUES (?, ?)", user.getUid(), message.getMid()) == 1; - messagesService.setLastReadComment(user, message.getMid(), message.getReplies()); - return result; - } catch (DuplicateKeyException e) { - return true; - } - } - - @Transactional - @Override - public boolean unSubscribeMessage(final int mid, final int vuid) { - return getJdbcTemplate().update( - "DELETE FROM subscr_messages WHERE message_id=? AND suser_id=?", mid, vuid) > 0; - } - - @Transactional - @Override - public boolean subscribeUser(final User user, final User toUser) { - try { - return getJdbcTemplate().update( - "INSERT INTO subscr_users(user_id,suser_id) VALUES (?,?)", toUser.getUid(), user.getUid()) == 1; - } catch (DuplicateKeyException e) { - return true; - } - } - - @Transactional - @Override - public boolean unSubscribeUser(final User user, final User fromUser) { - return getJdbcTemplate().update( - "DELETE FROM subscr_users WHERE suser_id=? AND user_id=?", user.getUid(), fromUser.getUid()) > 0; - } - - @Transactional - @Override - public boolean subscribeTag(final User user, final Tag toTag) { - try { - - return getJdbcTemplate().update( - "INSERT INTO subscr_tags(tag_id,suser_id) VALUES (?,?)", toTag.TID, user.getUid()) == 1; - } catch (DuplicateKeyException e) { - return true; - } - } - - @Transactional - @Override - public boolean unSubscribeTag(final User user, final Tag toTag) { - return getJdbcTemplate().update( - "DELETE FROM subscr_tags WHERE tag_id=? AND suser_id=?", toTag.TID, user.getUid()) > 0; - } - - @Transactional(readOnly = true) - @Override - public List getSubscribedTags(User user) { - return getJdbcTemplate().queryForList("SELECT tags.name FROM subscr_tags INNER JOIN tags " + - "ON(tags.tag_id = subscr_tags.tag_id) " + - "WHERE subscr_tags.suser_id=? ORDER BY tags.name", String.class, user.getUid()); - } - - @Transactional(readOnly = true) - @Override - public NotifyOpts getNotifyOptions(final User user) { - List list = getJdbcTemplate().query( - "SELECT jnotify,subscr_notify,recommendations FROM useroptions WHERE user_id=?", - (rs, num) -> { - NotifyOpts options = new NotifyOpts(); - options.setRepliesEnabled(rs.getInt(1) > 0); - options.setSubscriptionsEnabled(rs.getInt(2) > 0); - options.setRecommendationsEnabled(rs.getInt(3) > 0); - return options; - }, - user.getUid()); - - return list.isEmpty() ? - new NotifyOpts() : list.get(0); - } - - @Transactional - @Override - public boolean setNotifyOptions(final User user, final NotifyOpts options) { - int jnotify = getJdbcTemplate().update( - "UPDATE useroptions SET jnotify=? WHERE user_id=?", - options.isRepliesEnabled() ? 1 : 0, - user.getUid()); - - int subscr_notify = getJdbcTemplate().update( - "UPDATE useroptions SET subscr_notify=? WHERE user_id=?", - options.isSubscriptionsEnabled() ? 1 : 0, - user.getUid()); - - int recommendations = getJdbcTemplate().update( - "UPDATE useroptions SET recommendations=? WHERE user_id=?", - options.isRecommendationsEnabled() ? 1 : 0, - user.getUid()); - - return jnotify > 0 && subscr_notify > 0 && recommendations > 0; - } -} diff --git a/juick-server/src/main/java/com/juick/service/TagServiceImpl.java b/juick-server/src/main/java/com/juick/service/TagServiceImpl.java deleted file mode 100644 index 42159d3b..00000000 --- a/juick-server/src/main/java/com/juick/service/TagServiceImpl.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service; - -import com.juick.Tag; -import com.juick.User; -import com.juick.model.TagStats; -import com.juick.util.StreamUtils; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -public class TagServiceImpl extends BaseJdbcService implements TagService { - - @Transactional(readOnly = true) - @Override - public com.juick.Tag getTag(final int tid) { - List list = getJdbcTemplate().query( - "SELECT synonym_id,name FROM tags WHERE tag_id=?", - (rs, num) -> { - Tag ret = new Tag(rs.getString(2)); - ret.TID = tid; - ret.SynonymID = rs.getInt(1); - return ret; - }, - tid); - - return list.isEmpty() ? - null : list.get(0); - } - - @Transactional - @Override - public com.juick.Tag getTag(final String tag, final boolean autoCreate) { - if (StringUtils.isBlank(tag)) - return null; - - List list = getJdbcTemplate().query( - "SELECT tag_id, synonym_id, name FROM tags WHERE name = ?", - (rs, rowNum) -> { - Tag ret1 = new Tag(rs.getString(3)); - ret1.TID = rs.getInt(1); - ret1.SynonymID = rs.getInt(2); - return ret1; - }, - tag); - - Tag ret = list.isEmpty() ? - null : list.get(0); - - if (ret == null && autoCreate) { - ret = new com.juick.Tag(tag); - ret.TID = createTag(tag); - } - - return ret; - } - - @Override - public List getTags(Stream tags, final boolean autoCreate) { - return tags.filter(StringUtils::isNotBlank).map(tag -> getTag(tag, autoCreate)).filter(Objects::nonNull).distinct() - .collect(Collectors.toList()); - } - - @Transactional(readOnly = true) - @Override - public boolean getTagNoIndex(final int tagId) { - List list = getJdbcTemplate().queryForList( - "SELECT noindex FROM tags WHERE tag_id=?", Integer.class, tagId); - - return !list.isEmpty() && list.get(0) == 1; - } - - @Transactional - @Override - public int createTag(final String name) { - KeyHolder holder = new GeneratedKeyHolder(); - getJdbcTemplate().update( - con -> { - PreparedStatement stmt = con.prepareStatement( - "INSERT INTO tags(name) VALUES (?)", - Statement.RETURN_GENERATED_KEYS); - stmt.setString(1, name); - return stmt; - }, - holder); - - return holder.getKey().intValue(); - } - - private class TagStatsMapper implements RowMapper { - - @Override - public TagStats mapRow(ResultSet rs, int rowNum) throws SQLException { - Tag t = new Tag(rs.getString(1)); - TagStats s = new TagStats(); - s.setTag(t); - s.setUsageCount(rs.getInt(2)); - return s; - } - } - - @Transactional(readOnly = true) - @Override - public List getUserTagStats(final int uid) { - return getJdbcTemplate().query( - "SELECT tags.name,COUNT(messages.message_id) " + - "FROM (messages INNER JOIN messages_tags ON (messages.user_id=? " + - "AND messages.message_id=messages_tags.message_id)) " + - "INNER JOIN tags ON messages_tags.tag_id=tags.tag_id GROUP BY tags.tag_id ORDER BY tags.name ASC", - new TagStatsMapper(), - uid); - } - - @Transactional(readOnly = true) - @Override - public List getUserBLTags(final int uid) { - return getJdbcTemplate().queryForList( - "SELECT tags.name FROM tags INNER JOIN bl_tags " + - "ON (bl_tags.user_id = ? AND bl_tags.tag_id = tags.tag_id) ORDER BY tags.name", - String.class, uid); - } - - @Transactional(readOnly = true) - @Override - public List getPopularTags() { - return getJdbcTemplate().queryForList( - "select name from tags where noindex=0 order by stat_messages desc limit 20", String.class); - } - - @Transactional(readOnly = true) - @Override - public List getTagStats() { - return getJdbcTemplate().query( - "SELECT tags.name,COUNT(DISTINCT messages.user_id) AS cnt " + - "FROM (messages INNER JOIN messages_tags ON (messages.ts>TIMESTAMPADD(DAY,-3,NOW()) " + - "AND messages.message_id=messages_tags.message_id)) " + - "INNER JOIN tags ON messages_tags.tag_id=tags.tag_id " + - "WHERE tags.tag_id NOT IN (SELECT tag_id FROM tags_ignore) " + - "GROUP BY tags.tag_id ORDER BY cnt DESC LIMIT 20", new TagStatsMapper()); - } - - @Transactional - @Override - public List updateTags(final int mid, final Collection newTags) { - List currentTags = getMessageTags(mid).stream() - .map(TagStats::getTag).collect(Collectors.toList()); - - if (CollectionUtils.isEmpty(newTags)) - return currentTags; - - List idsForDelete = newTags.stream() - .filter(currentTags::contains) - .map(tag -> tag.TID) - .collect(Collectors.toList()); - if (newTags.size() - idsForDelete.size() >= 5) { - return currentTags; - } - - if (!idsForDelete.isEmpty()) - getNamedParameterJdbcTemplate().update( - "DELETE FROM messages_tags WHERE message_id = :mid AND tag_id in (:ids)", - new MapSqlParameterSource().addValue("ids", idsForDelete).addValue("mid", mid)); - - newTags.stream().filter(t -> !currentTags.contains(t)) - .forEach(t -> getJdbcTemplate().update("INSERT INTO messages_tags(message_id,tag_id) VALUES (?,?)", mid, t.TID)); - - List result = getMessageTags(mid).stream() - .map(TagStats::getTag).collect(Collectors.toList()); - jdbcTemplate.update("UPDATE messages_txt SET tags=? WHERE message_id=?", result.stream() - .map(Tag::getName).collect(Collectors.joining(" ")), mid); - return result; - } - - @Override - public Pair> fromString(final String txt) { - String firstLine = txt.split("\\n", 2)[0]; - Supplier> tagsStream = () -> StreamUtils.takeWhile(Arrays.stream(firstLine.split("\\ ")), - t -> !t.equals("*") && t.startsWith("*")); - int tagsLength = tagsStream.get().collect(Collectors.joining(" ")).length(); - String body = txt.substring(tagsLength); - List tags = tagsStream.get().map(t -> getTag(t.substring(1), true)) - .distinct().collect(Collectors.toList()); - return Pair.of(body, tags); - } - - @Transactional(readOnly = true) - @Override - public List getMessageTags(final int mid) { - return getJdbcTemplate().query( - "SELECT tags.tag_id,synonym_id,name,stat_messages FROM tags " + - "INNER JOIN messages_tags ON (messages_tags.message_id = ? AND messages_tags.tag_id = tags.tag_id)", - (rs, num) -> { - com.juick.Tag t = new com.juick.Tag(rs.getString(3)); - t.TID = rs.getInt(1); - t.SynonymID = rs.getInt(2); - TagStats s = new TagStats(); - s.setTag(t); - s.setUsageCount(rs.getInt(4)); - return s; - }, mid); - } - - @Transactional(readOnly = true) - @Override - public List getMessageTagsIDs(final int mid) { - return getJdbcTemplate().queryForList( - "SELECT tag_id FROM messages_tags WHERE message_id = ?", - Integer.class, mid); - } - - @Override - public boolean blacklistTag(User user, Tag tag) { - int rowcount = getNamedParameterJdbcTemplate().update("DELETE FROM bl_tags WHERE tag_id = :tid AND user_id = :uid", - new MapSqlParameterSource().addValue("tid", tag.TID).addValue("uid", user.getUid())); - return rowcount <= 0 && getNamedParameterJdbcTemplate() - .update("INSERT INTO bl_tags(user_id, tag_id) VALUES(:uid,:tid)", - new MapSqlParameterSource().addValue("tid", tag.TID) - .addValue("uid", user.getUid())) > 0; - } - - @Transactional(readOnly = true) - @Override - public boolean isInBL(User user, Tag tag) { - List list = getJdbcTemplate().queryForList( - "SELECT 1 FROM bl_tags WHERE user_id = ? AND tag_id = ?", - Integer.class, user.getUid(), tag.TID); - return !list.isEmpty() && list.get(0) == 1; - } - - @Transactional(readOnly = true) - @Override - public boolean isSubscribed(User user, Tag tag) { - List list = getJdbcTemplate().queryForList( - "SELECT 1 FROM subscr_tags WHERE suser_id = ? AND tag_id = ?", - Integer.class, user.getUid(), tag.TID); - return !list.isEmpty() && list.get(0) == 1; - } - -} diff --git a/juick-server/src/main/java/com/juick/service/TelegramServiceImpl.java b/juick-server/src/main/java/com/juick/service/TelegramServiceImpl.java deleted file mode 100644 index 99cbabf6..00000000 --- a/juick-server/src/main/java/com/juick/service/TelegramServiceImpl.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service; - -import com.juick.User; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Collections; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; - -/** - * Created by vt on 24/11/2016. - */ -@Repository -public class TelegramServiceImpl extends BaseJdbcService implements TelegramService { - - @Transactional - @Override - public boolean deleteAnonymous(Long id) { - return getJdbcTemplate().update("DELETE FROM telegram WHERE tg_id=? AND user_id IS NULL", id) > 0; - } - - @Transactional(readOnly = true) - @Override - public List getAnonymous() { - return getJdbcTemplate().queryForList("SELECT tg_id FROM telegram WHERE user_id IS NULL", Long.class); - } - - @Transactional(readOnly = true) - @Override - public int getUser(final long tgId) { - List list = getJdbcTemplate().queryForList( - "SELECT id FROM users INNER JOIN telegram " + - "ON telegram.user_id = users.id WHERE telegram.tg_id=?", Integer.class, tgId); - - return list.isEmpty() ? 0 : list.get(0); - } - - @Transactional - @Override - public boolean createTelegramUser(final long tgID, final String tgName) { - return getJdbcTemplate().update( - "INSERT INTO telegram(tg_id, tg_name, loginhash) VALUES(?,?,?)", - tgID, tgName, UUID.randomUUID().toString()) > 0; - } - - @Transactional - @Override - public boolean deleteTelegramUser(Integer uid) { - return getJdbcTemplate().update("DELETE FROM telegram WHERE user_id=?", uid) > 0; - } - - @Transactional(readOnly = true) - @Override - public List getTelegramIdentifiers(List users) { - List uids = users.stream().map(User::getUid).collect(Collectors.toList()); - if (uids.isEmpty()) { - return Collections.emptyList(); - } - return getNamedParameterJdbcTemplate().queryForList("" + - "SELECT tg_id FROM telegram WHERE user_id IN(:uids)", new MapSqlParameterSource() - .addValue("uids", uids), Long.class); - } -} diff --git a/juick-server/src/main/java/com/juick/service/UserServiceImpl.java b/juick-server/src/main/java/com/juick/service/UserServiceImpl.java deleted file mode 100644 index fdc4f28c..00000000 --- a/juick-server/src/main/java/com/juick/service/UserServiceImpl.java +++ /dev/null @@ -1,668 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service; - -import com.juick.Message; -import com.juick.User; -import com.juick.model.AnonymousUser; -import com.juick.model.Auth; -import com.juick.model.UserInfo; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.StringUtils; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import javax.annotation.Nonnull; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.sql.Timestamp; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -public class UserServiceImpl extends BaseJdbcService implements UserService { - - private class UserMapper implements RowMapper { - @Override - public User mapRow(ResultSet rs, int rowNum) throws SQLException { - User user = new User(); - - user.setUid(rs.getInt(1)); - user.setName(rs.getString(2)); - user.setCredentials(rs.getString(3)); - user.setBanned(rs.getBoolean(4)); - Timestamp seen = rs.getTimestamp(5); - if (seen != null) { - user.setSeen(seen.toInstant()); - } - return user; - } - } - - @Transactional - @Override - public String getSignUpHashByJID(final String jid) { - List list = getJdbcTemplate().queryForList( - "SELECT loginhash FROM jids WHERE jid = ? AND user_id IS NULL", String.class, jid); - - if (list.isEmpty()) { - String hash = UUID.randomUUID().toString(); - getJdbcTemplate().update("INSERT INTO jids(jid, loginhash) VALUES (?, ?)", jid, hash); - return hash; - } - return list.get(0); - } - - @Transactional - @Override - public String getSignUpHashByTelegramID(final Long telegramId, final String username) { - List list = getJdbcTemplate().queryForList( - "SELECT loginhash FROM telegram WHERE tg_id = ? AND user_id IS NULL", - String.class, - telegramId); - - if (list.isEmpty()) { - String hash = UUID.randomUUID().toString(); - getJdbcTemplate().update( - "INSERT INTO telegram(tg_id, loginhash, tg_name) VALUES (?, ?, ?)", telegramId, hash, username); - return hash; - } - return list.get(0); - } - - @Transactional - @Override - public int createUser(final String username, final String password) { - KeyHolder holder = new GeneratedKeyHolder(); - try { - getJdbcTemplate().update( - con -> { - PreparedStatement stmt = con.prepareStatement( - "INSERT INTO users(nick,passw) VALUES (?,?)", - Statement.RETURN_GENERATED_KEYS); - stmt.setString(1, username); - stmt.setString(2, password); - return stmt; - }, - holder); - } catch (DuplicateKeyException e) { - return -1; - } - - int uid = holder.getKey().intValue(); - - getJdbcTemplate().update("INSERT INTO useroptions(user_id) VALUES (?)", uid); - getJdbcTemplate().update("INSERT INTO subscr_users(user_id, suser_id) VALUES (2, ?)", uid); - - return uid; - } - - @Transactional(readOnly = true) - @Override - public Optional getUserByUID(final int uid) { - List list = getJdbcTemplate().query( - "SELECT id, nick, passw, banned, last_seen FROM users WHERE id = ?", new UserMapper(), uid); - - return list.isEmpty() ? Optional.empty() : Optional.of(list.get(0)); - } - - @Transactional(readOnly = true) - @Nonnull - @Override - public User getUserByName(final String username) { - if (StringUtils.isNotBlank(username)) { - List list = getJdbcTemplate().query( - "SELECT id, nick, passw, banned, last_seen FROM users WHERE nick = ?", new UserMapper(), username); - - if (!list.isEmpty()) - return list.get(0); - } - return AnonymousUser.INSTANCE; - } - - @Override - @Transactional(readOnly = true) - @Nonnull - public User getUserByEmail(String email) { - if (StringUtils.isNotBlank(email)) { - List list = getJdbcTemplate().query( - "SELECT id, nick, passw, banned, last_seen FROM users WHERE id = (SELECT DISTINCT user_id FROM emails WHERE email = ?)", - new UserMapper(), - email); - - if (!list.isEmpty()) - return list.get(0); - } - return AnonymousUser.INSTANCE; - } - - @Transactional(readOnly = true) - @Override - public User getUserByJID(final String jid) { - User result = null; - - if (StringUtils.isNotBlank(jid)) { - List list = getJdbcTemplate().query( - "SELECT id, nick, passw, banned, last_seen FROM users WHERE id = (SELECT user_id FROM jids WHERE jid = ?)", - new UserMapper(), - jid); - - if (!list.isEmpty()) - result = list.get(0); - } - return result; - } - - @Transactional(readOnly = true) - @Override - public List getUsersByName(final Collection unames) { - if (CollectionUtils.isEmpty(unames)) - return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().query( - "SELECT id, nick, passw, banned, last_seen FROM users WHERE nick IN (:unames)", - new MapSqlParameterSource("unames", unames), - new UserMapper()); - } - - @Transactional(readOnly = true) - @Override - public List getUsersByID(final Collection uids) { - if (CollectionUtils.isEmpty(uids)) - return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().query( - "SELECT id, nick, passw, banned, last_seen FROM users WHERE id IN (:ids)", - new MapSqlParameterSource("ids", uids), - new UserMapper()); - } - - @Transactional(readOnly = true) - @Override - public List getJIDsbyUID(final int uid) { - return getJdbcTemplate().queryForList("SELECT jid FROM jids WHERE user_id = ? AND active = 1", String.class, uid); - } - - @Transactional(readOnly = true) - @Override - public int getUIDbyJID(final String jid) { - if (StringUtils.isNotBlank(jid)) { - List list = getJdbcTemplate().queryForList( - "SELECT user_id FROM jids WHERE jid = ?", Integer.class, jid); - - if (!list.isEmpty()) - return list.get(0); - } - return 0; - } - - @Transactional(readOnly = true) - @Override - public int getUIDbyName(final String uname) { - if (StringUtils.isNotBlank(uname)) { - List list = getJdbcTemplate().queryForList( - "SELECT id FROM users WHERE nick = ?", Integer.class, uname); - - if (!list.isEmpty()) - return list.get(0); - } - return 0; - } - - @Transactional(readOnly = true) - @Override - public int getUIDbyHash(final String hash) { - if (StringUtils.isNotBlank(hash)) { - List list = getJdbcTemplate().queryForList( - "SELECT user_id FROM logins WHERE hash = ?", Integer.class, hash); - - if (!list.isEmpty()) - return list.get(0); - } - return 0; - } - - @Transactional(readOnly = true) - @Override - public com.juick.User getUserByHash(final String hash) { - if (StringUtils.isNotBlank(hash)) { - List list = getJdbcTemplate().query( - "SELECT logins.user_id, users.nick, users.passw, users.banned, last_seen FROM logins " + - "INNER JOIN users ON logins.user_id = users.id WHERE logins.hash = ?", - new UserMapper(), - hash); - - if (!list.isEmpty()) { - User user = list.get(0); - user.setAuthHash(hash); - return user; - } - } - return AnonymousUser.INSTANCE; - } - - @Transactional - @Override - public String getHashByUID(final int uid) { - List list = getJdbcTemplate().queryForList( - "SELECT hash FROM logins WHERE user_id = ?", String.class, uid); - - if (list.isEmpty()) { - String hash = RandomStringUtils.randomAlphanumeric(16).toUpperCase(); - getJdbcTemplate().update("INSERT INTO logins(user_id, hash) VALUES (?, ?)", uid, hash); - return hash; - } - return list.get(0); - } - - @Transactional(readOnly = true) - @Override - public int checkPassword(final String username, final String password) { - if (StringUtils.isNotBlank(username)) { - List list = getJdbcTemplate().query( - "SELECT id, nick, passw, banned, last_seen FROM users WHERE nick = ?", - new UserMapper(), - username); - - if (!list.isEmpty()) { - User user = list.get(0); - if (Objects.equals(password, user.getCredentials())) - return user.getUid(); - } - } - return -1; - } - - @Transactional - @Override - public boolean updatePassword(final User user, final String newPassword) { - return user != null && - user.getUid() > 0 && - getJdbcTemplate().update("UPDATE users SET passw = ? WHERE id = ?", newPassword, user.getUid()) > 0; - } - - @Transactional(readOnly = true) - @Override - public int getUserOptionInt(final int uid, final String option, final int defaultValue) { - if (StringUtils.isBlank(option)) - return defaultValue; - - List list = getJdbcTemplate().queryForList( - "SELECT " + option + " FROM useroptions WHERE user_id = ?", Integer.class, uid); - - return list.isEmpty() ? defaultValue : list.get(0); - } - - @Transactional - @Override - public int setUserOptionInt(final int uid, final String option, final int value) { - if (StringUtils.isBlank(option)) - return 0; - - return getJdbcTemplate().update("UPDATE useroptions SET " + option + "= ? WHERE user_id = ?", value, uid); - } - - @Transactional(readOnly = true) - @Override - public UserInfo getUserInfo(final User user) { - List list = getJdbcTemplate().query( - "SELECT fullname, country, url, descr FROM usersinfo WHERE user_id = ?", - ((rs, rowNum) -> { - UserInfo info = new UserInfo(); - info.setFullName(rs.getString(1)); - info.setCountry(rs.getString(2)); - info.setUrl(rs.getString(3)); - info.setDescription(rs.getString(4)); - return info; - }), - user.getUid()); - - return list.isEmpty() ? new UserInfo() : list.get(0); - } - - @Transactional - @Override - public boolean updateUserInfo(final User user, final UserInfo info) { - return getJdbcTemplate().update( - "INSERT INTO usersinfo(user_id, fullname, country, url, descr) VALUES (?, ?, ?, ?, ?) " + - "ON DUPLICATE KEY UPDATE fullname = ?, country = ?, url = ?, descr = ?", - user.getUid(), - info.getFullName(), - info.getCountry(), - info.getUrl(), - info.getDescription(), - info.getFullName(), - info.getCountry(), - info.getUrl(), - info.getDescription()) > 0; - } - - @Transactional(readOnly = true) - @Override - public boolean getCanMedia(final int uid) { - List list = getJdbcTemplate().queryForList( - "SELECT users.lastphoto - UNIX_TIMESTAMP() FROM users WHERE id = ?", - Integer.class, - uid); - - return !list.isEmpty() && list.get(0) < 3600; - } - - @Transactional(readOnly = true) - @Override - public boolean isInWL(final int uid, final int check) { - List list = getJdbcTemplate().queryForList( - "SELECT 1 FROM wl_users WHERE user_id = ? AND wl_user_id = ?", - Integer.class, uid, check); - - return !list.isEmpty() && list.get(0) == 1; - } - - @Transactional(readOnly = true) - @Override - public boolean isInBL(final int uid, final int check) { - List list = getJdbcTemplate().queryForList( - "SELECT 1 FROM bl_users WHERE user_id = ? AND bl_user_id = ?", Integer.class, uid, check); - - return !list.isEmpty() && list.get(0) == 1; - } - - @Transactional(readOnly = true) - @Override - public boolean isInBLAny(final int uid, final int uid2) { - List list = getJdbcTemplate().queryForList( - "SELECT 1 FROM bl_users WHERE (user_id = ? AND bl_user_id = ?) " - + "OR (user_id = ? AND bl_user_id = ?)", - new Object[]{uid, uid2, uid2, uid}, - Integer.class); - - return !list.isEmpty() && list.get(0) == 1; - } - - @Transactional(readOnly = true) - @Override - public boolean isReplyToBL(final User user, final Message reply) { - return getNamedParameterJdbcTemplate().queryForObject("WITH RECURSIVE banned(reply_id, user_id) AS (" + - "SELECT reply_id, user_id FROM replies " + - "WHERE replies.message_id = :mid " + - "AND EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :uid AND b.bl_user_id = replies.user_id) " + - "UNION ALL SELECT replies.reply_id, replies.user_id FROM replies " + - "INNER JOIN banned ON banned.reply_id = replies.replyto " + - "WHERE replies.message_id = :mid) " + - "SELECT COUNT(reply_id) from replies " + - "INNER JOIN messages m ON m.message_id = replies.message_id " + - "WHERE replies.message_id = :mid " + - "AND replies.reply_id = :rid " + - "AND (EXISTS (SELECT 1 FROM banned WHERE banned.reply_id = replies.reply_id) " + - "OR EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :uid AND b.bl_user_id = m.user_id)" + - "OR EXISTS (SELECT 1 FROM bl_users b WHERE b.bl_user_id = :uid AND b.user_id = m.user_id))", - new MapSqlParameterSource("uid", user.getUid()) - .addValue("mid", reply.getMid()) - .addValue("rid", reply.getRid()), - Integer.class) > 0; - } - - @Transactional(readOnly = true) - @Override - public List checkBL(final int visitor, final Collection uids) { - if (CollectionUtils.isEmpty(uids)) - return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT user_id FROM bl_users WHERE bl_user_id = :visitor and user_id IN (:ids)", - new MapSqlParameterSource() - .addValue("visitor", visitor) - .addValue("ids", uids), - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public boolean isSubscribed(final int uid, final int check) { - List list = getJdbcTemplate().queryForList( - "SELECT 1 FROM subscr_users WHERE suser_id = ? AND user_id = ?", - Integer.class, uid, check); - - return !list.isEmpty() && list.get(0) == 1; - } - - @Transactional(readOnly = true) - @Override - public List getUserReadLeastPopular(final int uid, final int cnt) { - return getJdbcTemplate().query( - "SELECT users.id,users.nick FROM (subscr_users " + - "INNER JOIN users_subscr ON (subscr_users.suser_id=? " + - "AND subscr_users.user_id=users_subscr.user_id)) INNER JOIN users " + - "ON subscr_users.user_id=users.id ORDER BY cnt LIMIT ?", - (rs, num) -> { - com.juick.User u = new com.juick.User(); - u.setUid(rs.getInt(1)); - u.setName(rs.getString(2)); - return u; - }, - uid, - cnt); - } - - @Transactional(readOnly = true) - @Override - public List getUserReaders(final int uid) { - return getJdbcTemplate().query( - "SELECT users.id, users.nick FROM subscr_users " + - "INNER JOIN users ON subscr_users.suser_id=users.id " + - "WHERE subscr_users.user_id=? ORDER BY users.nick", - (rs, num) -> { - com.juick.User u = new com.juick.User(); - u.setUid(rs.getInt(1)); - u.setName(rs.getString(2)); - return u; - }, - uid); - } - - @Transactional(readOnly = true) - @Override - public List getUserFriends(final int uid) { - return getJdbcTemplate().query( - "SELECT users.id,users.nick FROM subscr_users " + - "INNER JOIN users ON subscr_users.user_id=users.id " + - "WHERE subscr_users.suser_id=? AND users.id!=? " + - "ORDER BY users.nick", - (rs, num) -> { - com.juick.User u = new com.juick.User(); - u.setUid(rs.getInt(1)); - u.setName(rs.getString(2)); - return u; - }, - uid, - uid); - } - - @Transactional(readOnly = true) - @Override - public Integer getUserRecommendations(User user) { - try { - return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM favorites WHERE user_id=?", Integer.class, user.getUid()); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - - @Transactional(readOnly = true) - @Override - public List getUserBLUsers(final int uid) { - return getJdbcTemplate().query("SELECT users.id,users.nick FROM users INNER JOIN bl_users " + - "ON(bl_users.bl_user_id=users.id) WHERE bl_users.user_id=? ORDER BY users.nick", - (rs, num) -> { - com.juick.User u = new com.juick.User(); - u.setUid(rs.getInt(1)); - u.setName(rs.getString(2)); - return u; - }, uid); - } - - @Transactional - @Override - public boolean linkTwitterAccount( - final User user, final String accessToken, final String accessTokenSecret, final String screenName) { - return getJdbcTemplate().update("INSERT INTO twitter(user_id,access_token,access_token_secret,uname) " + - "VALUES (?,?,?,?)" + - " ON DUPLICATE KEY UPDATE access_token=?,access_token_secret=?,uname=?", - user.getUid(), accessToken, accessTokenSecret, screenName, accessToken, accessTokenSecret, screenName) > 0; - } - - @Transactional(readOnly = true) - @Override - public int getStatsMyReaders(final int uid) { - List list = getJdbcTemplate().queryForList("SELECT COUNT(*) FROM subscr_users WHERE user_id = ?", Integer.class, uid); - return list.isEmpty() ? 0 : list.get(0); - } - - @Transactional(readOnly = true) - @Override - public int getStatsMessages(final int uid) { - List list = getJdbcTemplate().queryForList("SELECT COUNT(*) FROM messages WHERE user_id = ?", Integer.class, uid); - return list.isEmpty() ? 0 : list.get(0); - } - - @Transactional(readOnly = true) - @Override - public int getStatsReplies(final int uid) { - List list = getJdbcTemplate().queryForList("SELECT COUNT(*) FROM replies WHERE user_id = ?", Integer.class, uid); - return list.isEmpty() ? 0 : list.get(0); - } - - @Transactional - @Override - public boolean setActiveStatusForJID(final String JID, final UserService.ActiveStatus jidStatus) { - User user = getUserByJID(JID); - if (user != null) { - int newStatus = jidStatus == UserService.ActiveStatus.Active ? 1 : 0; - return getJdbcTemplate().update( - "UPDATE jids SET active = ? WHERE user_id = ? AND jid = ?", - newStatus, user.getUid(), JID) >= 0; - } - return false; - } - - @Transactional(readOnly = true) - @Override - public List getAllJIDs(final User user) { - return getJdbcTemplate().queryForList( - "SELECT jid FROM jids WHERE user_id=?", String.class, user.getUid()); - } - - @Transactional(readOnly = true) - @Override - public List getAuthCodes(final User user) { - return getJdbcTemplate().query( - "SELECT account,authcode FROM auth WHERE user_id=? AND protocol='xmpp'", - (rs, num) -> new Auth(rs.getString(1), rs.getString(2)), - user.getUid()); - } - - @Transactional(readOnly = true) - @Override - public List getEmails(final User user) { - return getJdbcTemplate().queryForList("SELECT email FROM emails WHERE user_id=?", String.class, user.getUid()); - } - - @Transactional(readOnly = true) - @Override - public String getEmailHash(final User user) { - List list = getJdbcTemplate().queryForList( - "SELECT hash FROM mail WHERE user_id = ?", - String.class, - user.getUid()); - return list.isEmpty() ? StringUtils.EMPTY : list.get(0) + "@mail.juick.com"; - } - - @Transactional - @Override - public int deleteLoginForUser(final String name) { - if (StringUtils.isBlank(name)) - return 0; - - return getJdbcTemplate().update( - "delete from logins where user_id in (select id from users where nick = ?)", name); - } - - @Transactional - @Override - public int setLoginForUser(final int uid, final String loginHash) { - if (StringUtils.isEmpty(loginHash)) - return 0; - - return getNamedParameterJdbcTemplate().update( - "INSERT INTO logins (user_id, hash) VALUES(:uid, :hash) ON DUPLICATE KEY UPDATE hash = :hash", - new MapSqlParameterSource() - .addValue("hash", loginHash) - .addValue("uid", uid)); - } - - @Transactional - @Override - public void logout(int uid) { - getJdbcTemplate().update("DELETE FROM logins WHERE user_id=?", uid); - } - - @Transactional - @Override - public boolean deleteJID(int uid, String jid) { - return getNamedParameterJdbcTemplate().update("DELETE FROM jids " + - "WHERE (SELECT COUNT(*) cnt FROM (select user_id, jid FROM jids j) c WHERE user_id=:uid) > 1 " + - "AND user_id=:uid AND jid=:jid", - new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("jid", jid)) > 0; - } - - @Transactional - @Override - public boolean unauthJID(int uid, String jid) { - return getJdbcTemplate() - .update("DELETE FROM auth WHERE user_id=? AND protocol='xmpp' AND account=?", uid, jid) > 0; - } - - @Transactional(readOnly = true) - @Override - public List getActiveJIDs() { - return getJdbcTemplate().queryForList("SELECT jid FROM jids WHERE active=1 AND loginhash IS NULL", String.class); - } - - @Override - public void updateLastSeen(User user) { - getJdbcTemplate().update("UPDATE users SET last_seen=now() WHERE id=?", user.getUid()); - } -} diff --git a/juick-server/src/main/java/com/juick/service/activities/ActivityListener.java b/juick-server/src/main/java/com/juick/service/activities/ActivityListener.java deleted file mode 100644 index 863bda04..00000000 --- a/juick-server/src/main/java/com/juick/service/activities/ActivityListener.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.juick.service.activities; - -import org.springframework.context.event.EventListener; -import org.springframework.scheduling.annotation.Async; - -public interface ActivityListener { - @Async - @EventListener - void processFollowEvent(FollowEvent event); - @Async - @EventListener - void undoFollowEvent(UndoFollowEvent event); - @Async - @EventListener - void deleteUserEvent(DeleteUserEvent event); - @Async - @EventListener - void deleteMessageEvent(DeleteMessageEvent event); -} diff --git a/juick-server/src/main/java/com/juick/service/activities/DeleteMessageEvent.java b/juick-server/src/main/java/com/juick/service/activities/DeleteMessageEvent.java deleted file mode 100644 index 67e40f44..00000000 --- a/juick-server/src/main/java/com/juick/service/activities/DeleteMessageEvent.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.juick.service.activities; - -import com.juick.Message; -import org.springframework.context.ApplicationEvent; - -public class DeleteMessageEvent extends ApplicationEvent { - private Message message; - /** - * Create a new ApplicationEvent. - * - * @param source the object on which the event initially occurred (never {@code null}) - */ - public DeleteMessageEvent(Object source, Message message) { - super(source); - this.message = message; - } - - public Message getMessage() { - return message; - } -} diff --git a/juick-server/src/main/java/com/juick/service/activities/DeleteUserEvent.java b/juick-server/src/main/java/com/juick/service/activities/DeleteUserEvent.java deleted file mode 100644 index 8b51da9d..00000000 --- a/juick-server/src/main/java/com/juick/service/activities/DeleteUserEvent.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.juick.service.activities; - -import org.springframework.context.ApplicationEvent; - -public class DeleteUserEvent extends ApplicationEvent { - private String userUri; - /** - * Create a new ApplicationEvent. - * - * @param source the object on which the event initially occurred (never {@code null}) - */ - public DeleteUserEvent(Object source, String userUri) { - super(source); - this.userUri = userUri; - } - - public String getUserUri() { - return userUri; - } -} diff --git a/juick-server/src/main/java/com/juick/service/activities/FollowEvent.java b/juick-server/src/main/java/com/juick/service/activities/FollowEvent.java deleted file mode 100644 index c96613ba..00000000 --- a/juick-server/src/main/java/com/juick/service/activities/FollowEvent.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.juick.service.activities; - -import com.juick.server.api.activity.model.activities.Follow; -import org.springframework.context.ApplicationEvent; - -public class FollowEvent extends ApplicationEvent { - private Follow request; - /** - * Create a new ApplicationEvent. - * - * @param source the object on which the event initially occurred (never {@code null}) - */ - public FollowEvent(Object source, Follow followRequest) { - super(source); - this.request = followRequest; - } - - public Follow getRequest() { - return request; - } -} diff --git a/juick-server/src/main/java/com/juick/service/activities/UndoFollowEvent.java b/juick-server/src/main/java/com/juick/service/activities/UndoFollowEvent.java deleted file mode 100644 index 2b48e6f6..00000000 --- a/juick-server/src/main/java/com/juick/service/activities/UndoFollowEvent.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.juick.service.activities; - -import org.springframework.context.ApplicationEvent; - -public class UndoFollowEvent extends ApplicationEvent { - private String actor; - private String object; - /** - * Create a new ApplicationEvent. - * - * @param source the object on which the event initially occurred (never {@code null}) - */ - public UndoFollowEvent(Object source, String actor, String object) { - super(source); - this.actor = actor; - this.object = object; - } - - public String getActor() { - return actor; - } - - public String getObject() { - return object; - } -} diff --git a/juick-server/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java b/juick-server/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java deleted file mode 100644 index 9215d09a..00000000 --- a/juick-server/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service.security; - -import com.juick.User; -import com.juick.service.security.entities.JuickUser; -import com.juick.service.UserService; -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.authentication.RememberMeAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -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.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.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) { - Assert.notNull(userService, "userService should not be null"); - Assert.notNull(rememberMeServices, "rememberMeServices should not be null"); - - this.userService = userService; - this.rememberMeServices = rememberMeServices; - } - - @Override - protected void doFilterInternal( - 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 = new RememberMeAuthenticationToken( - ((AbstractRememberMeServices)rememberMeServices).getKey(), new JuickUser(userWithPassword), JuickUser.USER_AUTHORITY); - - SecurityContextHolder.getContext().setAuthentication(authentication); - - rememberMeServices.loginSuccess(request, response, authentication); - } - } - - filterChain.doFilter(request, response); - } - - private boolean authenticationIsRequired() { - Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); - - return existingAuth == null || - !existingAuth.isAuthenticated() || - existingAuth instanceof AnonymousAuthenticationToken; - } - - private String getHashFromRequest(HttpServletRequest request) { - String paramHash = request.getParameter(PARAM_NAME); - Cookie cookieHash = WebUtils.getCookie(request, PARAM_NAME); - - if (paramHash == null && cookieHash != null) { - return cookieHash.getValue(); - } - return paramHash; - } -} diff --git a/juick-server/src/main/java/com/juick/service/security/JuickUserDetailsService.java b/juick-server/src/main/java/com/juick/service/security/JuickUserDetailsService.java deleted file mode 100644 index 59425fab..00000000 --- a/juick-server/src/main/java/com/juick/service/security/JuickUserDetailsService.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service.security; - -import com.juick.service.UserService; -import com.juick.service.security.entities.JuickUser; -import org.apache.commons.lang3.StringUtils; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.util.Assert; - -/** - * Created by aalexeev on 11/28/16. - */ -public class JuickUserDetailsService implements UserDetailsService { - private final UserService userService; - - public JuickUserDetailsService(final UserService userService) { - Assert.notNull(userService, "UserService must be initialized"); - this.userService = userService; - } - - @Override - public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException { - if (StringUtils.isBlank(username)) - throw new UsernameNotFoundException("Invalid user name " + username); - - com.juick.User user = userService.getUserByName(username); - - if (!user.isAnonymous()) { - user.setAuthHash(userService.getHashByUID(user.getUid())); - return new JuickUser(user); - } - - throw new UsernameNotFoundException("The username " + username + " is not found"); - } -} diff --git a/juick-server/src/main/java/com/juick/service/security/NullUserDetailsService.java b/juick-server/src/main/java/com/juick/service/security/NullUserDetailsService.java deleted file mode 100644 index 91acefa3..00000000 --- a/juick-server/src/main/java/com/juick/service/security/NullUserDetailsService.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service.security; - -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; - -/** - * Created by aalexeev on 11/28/16. - */ -public class NullUserDetailsService implements UserDetailsService { - @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - throw new UsernameNotFoundException( - "loadUserByUsername called for NullUserDetailsService, user " + username + "can not be found"); - } -} diff --git a/juick-server/src/main/java/com/juick/service/security/deprecated/CookieSimpleHashRememberMeServices.java b/juick-server/src/main/java/com/juick/service/security/deprecated/CookieSimpleHashRememberMeServices.java deleted file mode 100644 index e385d7dd..00000000 --- a/juick-server/src/main/java/com/juick/service/security/deprecated/CookieSimpleHashRememberMeServices.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service.security.deprecated; - -import com.juick.User; -import com.juick.service.security.entities.JuickUser; -import com.juick.service.UserService; -import com.juick.service.security.NullUserDetailsService; -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.env.Environment; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.web.authentication.RememberMeServices; -import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices; -import org.springframework.security.web.authentication.rememberme.InvalidCookieException; -import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationException; -import org.springframework.util.Assert; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.Optional; - -/** - * Created by aalexeev on 11/28/16. - * - * @deprecated not recommended use for secure reasons - */ -@Deprecated -public class CookieSimpleHashRememberMeServices extends AbstractRememberMeServices implements RememberMeServices { - private static final Logger logger = LoggerFactory.getLogger(CookieSimpleHashRememberMeServices.class); - - private static final String COOKIE_PARAM_NAME = "hash"; - - private final UserService userService; - - public CookieSimpleHashRememberMeServices( - final String key, final UserService userService, final Environment environment) { - super(key, new NullUserDetailsService()); - - Assert.notNull(userService); - Assert.notNull(environment); - - this.userService = userService; - - setCookieName(COOKIE_PARAM_NAME); - setCookieDomain(environment.getProperty("web_domain", "localhost")); - setAlwaysRemember(true); - } - - @Override - public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { - super.logout(request, response, authentication); - userService.deleteLoginForUser(authentication.getName()); - } - - @Override - protected void onLoginSuccess( - HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) { - String username = successfulAuthentication.getName(); - - logger.debug("Creating new persistent login for user {}", username); - - try { - int uid = userService.getUIDbyName(username); - - Assert.isTrue(uid > 0); - - String hash = RandomStringUtils.randomAlphanumeric(16).toUpperCase(); - - userService.setLoginForUser(uid, hash); - - setCookie(new String[]{hash}, getTokenValiditySeconds(), request, response); - } catch (Exception e) { - logger.error("Failed to save cookies", e); - } - } - - @Override - protected UserDetails processAutoLoginCookie( - String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) - throws RememberMeAuthenticationException, UsernameNotFoundException { - String hash = cookieTokens[0]; - - if (StringUtils.isBlank(hash)) { - hash = request.getParameter("hash"); - } - if (StringUtils.isBlank(hash)) { - throw new InvalidCookieException("Cookie is invalid and hash parameter not found"); - } - - int uid = userService.getUIDbyHash(hash); - if (uid <= 0) - throw new UsernameNotFoundException("User not found by hash, cookies" + cookieTokens); - - Optional userOptional = userService.getUserByUID(uid); - - Assert.isTrue(userOptional.isPresent()); - - return new JuickUser(userService.getUserByName(userOptional.get().getName())); - } - - @Override - protected String[] decodeCookie(String cookieValue) throws InvalidCookieException { - return new String[]{cookieValue}; - } - - @Override - protected String encodeCookie(String[] cookieTokens) { - return cookieTokens != null && cookieTokens.length > 0 ? cookieTokens[0] : StringUtils.EMPTY; - } -} diff --git a/juick-server/src/main/java/com/juick/service/security/deprecated/RequestParamHashRememberMeServices.java b/juick-server/src/main/java/com/juick/service/security/deprecated/RequestParamHashRememberMeServices.java deleted file mode 100644 index 3631e5a4..00000000 --- a/juick-server/src/main/java/com/juick/service/security/deprecated/RequestParamHashRememberMeServices.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service.security.deprecated; - -import com.juick.User; -import com.juick.service.security.entities.JuickUser; -import com.juick.service.UserService; -import com.juick.service.security.NullUserDetailsService; -import org.apache.commons.lang3.StringUtils; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.web.authentication.RememberMeServices; -import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices; -import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationException; -import org.springframework.util.Assert; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * Created by aalexeev on 11/30/16. - * - * @deprecated for security reasons - */ -@Deprecated -public class RequestParamHashRememberMeServices extends AbstractRememberMeServices implements RememberMeServices { - private static final String PARAM_NAME = "hash"; - - private final UserService userService; - - public RequestParamHashRememberMeServices(String key, UserService userService) { - super(key, new NullUserDetailsService()); - - Assert.notNull(userService); - this.userService = userService; - setAlwaysRemember(false); - } - - @Override - protected void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) { - // do nothing - } - - @Override - protected boolean rememberMeRequested(HttpServletRequest request, String parameter) { - return false; // always false - } - - @Override - protected void cancelCookie(HttpServletRequest request, HttpServletResponse response) { - // do nothing - } - - @Override - protected String extractRememberMeCookie(HttpServletRequest request) { - return PARAM_NAME; // return any not blank value - } - - @Override - protected UserDetails processAutoLoginCookie( - String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) - throws RememberMeAuthenticationException, UsernameNotFoundException { - String hash = request.getParameter(PARAM_NAME); - - if (StringUtils.isNotBlank(hash)) { - User user = userService.getUserByHash(hash); - if (!user.isAnonymous()) - return new JuickUser(userService.getUserByName(user.getName())); - } - throw new UsernameNotFoundException("User not found by hash " + hash); - } -} diff --git a/juick-server/src/main/java/com/juick/service/security/entities/JuickUser.java b/juick-server/src/main/java/com/juick/service/security/entities/JuickUser.java deleted file mode 100644 index c43f112f..00000000 --- a/juick-server/src/main/java/com/juick/service/security/entities/JuickUser.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.service.security.entities; - -import com.juick.User; -import com.juick.model.AnonymousUser; -import org.apache.commons.lang3.StringUtils; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * Created by aalexeev on 11/21/16. - */ -public class JuickUser implements UserDetails { - static final GrantedAuthority ROLE_USER = new SimpleGrantedAuthority("ROLE_USER"); - static final GrantedAuthority ROLE_ANONYMOUS = new SimpleGrantedAuthority("ROLE_ANONYMOUS"); - - public static final List USER_AUTHORITY = Collections.singletonList(ROLE_USER); - public static final List ANONYMOUS_AUTHORITY = Collections.singletonList(ROLE_ANONYMOUS); - - public static final JuickUser ANONYMOUS_USER = new JuickUser(AnonymousUser.INSTANCE, ANONYMOUS_AUTHORITY); - - private final com.juick.User user; - private final Collection authorities; - - public JuickUser(com.juick.User user) { - this(user, USER_AUTHORITY); - } - - public JuickUser(com.juick.User user, Collection authorities) { - this.user = user; - this.authorities = authorities; - } - - @Override - public Collection getAuthorities() { - return authorities; - } - - @Override - public String getPassword() { - return "{noop}" + user.getCredentials(); - } - - @Override - public String getUsername() { - return user.getName(); - } - - @Override - public boolean isAccountNonExpired() { - return true; - } - - @Override - public boolean isAccountNonLocked() { - return StringUtils.isNotBlank(user.getCredentials()); - } - - @Override - public boolean isCredentialsNonExpired() { - return isAccountNonLocked(); - } - - @Override - public boolean isEnabled() { - return !user.isBanned() && isCredentialsNonExpired(); - } - - public User getUser() { - return user; - } -} -- cgit v1.2.3