From e5c8298beee5dde90ca98cc4707faac4bf0e2f0c Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 7 Jul 2016 15:13:47 +0300 Subject: reorganize project --- build.gradle | 94 --- juick-core/build.gradle | 15 + juick-core/src/main/java/com/juick/Group.java | 29 + juick-core/src/main/java/com/juick/Message.java | 244 ++++++++ juick-core/src/main/java/com/juick/PM.java | 16 + juick-core/src/main/java/com/juick/Place.java | 33 + .../src/main/java/com/juick/Recommendation.java | 16 + juick-core/src/main/java/com/juick/Tag.java | 41 ++ juick-core/src/main/java/com/juick/User.java | 96 +++ .../main/java/com/juick/json/JSONSerializer.java | 54 ++ .../java/com/juick/json/MessageSerializer.java | 138 +++++ .../main/java/com/juick/json/PlaceSerializer.java | 73 +++ .../main/java/com/juick/json/UserSerializer.java | 66 ++ .../src/main/java/com/juick/server/AdsQueries.java | 51 ++ .../java/com/juick/server/CrosspostQueries.java | 62 ++ .../java/com/juick/server/MessagesQueries.java | 663 ++++++++++++++++++++ .../src/main/java/com/juick/server/PMQueries.java | 145 +++++ .../main/java/com/juick/server/PushQueries.java | 74 +++ .../src/main/java/com/juick/server/SQLHelpers.java | 168 +++++ .../main/java/com/juick/server/ShowQueries.java | 32 + .../com/juick/server/SubscriptionsQueries.java | 104 ++++ .../src/main/java/com/juick/server/TagQueries.java | 166 +++++ .../main/java/com/juick/server/UserQueries.java | 440 +++++++++++++ .../src/main/java/com/juick/server/Utils.java | 86 +++ .../java/com/juick/server/helpers/PrivacyOpts.java | 29 + .../com/juick/xmpp/extensions/JuickMessage.java | 184 ++++++ .../java/com/juick/xmpp/extensions/JuickUser.java | 75 +++ juick-www/build.gradle | 88 +++ .../com/juick/server/protocol/JuickProtocol.java | 375 +++++++++++ .../com/juick/server/protocol/ProtocolReply.java | 23 + .../server/protocol/annotation/UserCommand.java | 31 + .../java/com/juick/www/CrosspostComponent.java | 323 ++++++++++ .../src/main/java/com/juick/www/Discover.java | 118 ++++ juick-www/src/main/java/com/juick/www/Errors.java | 42 ++ .../src/main/java/com/juick/www/FacebookLogin.java | 152 +++++ juick-www/src/main/java/com/juick/www/Help.java | 90 +++ juick-www/src/main/java/com/juick/www/Home.java | 170 +++++ juick-www/src/main/java/com/juick/www/Login.java | 246 ++++++++ juick-www/src/main/java/com/juick/www/Main.java | 310 ++++++++++ .../src/main/java/com/juick/www/NewMessage.java | 413 +++++++++++++ juick-www/src/main/java/com/juick/www/PM.java | 224 +++++++ .../src/main/java/com/juick/www/PageTemplates.java | 479 +++++++++++++++ .../src/main/java/com/juick/www/PushComponent.java | 311 ++++++++++ juick-www/src/main/java/com/juick/www/RSS.java | 101 +++ .../src/main/java/com/juick/www/Settings.java | 91 +++ juick-www/src/main/java/com/juick/www/SignUp.java | 258 ++++++++ .../src/main/java/com/juick/www/TwitterAuth.java | 87 +++ juick-www/src/main/java/com/juick/www/User.java | 343 +++++++++++ .../src/main/java/com/juick/www/UserThread.java | 364 +++++++++++ juick-www/src/main/java/com/juick/www/Utils.java | 242 ++++++++ .../main/java/com/juick/www/VKontakteLogin.java | 128 ++++ .../main/java/com/juick/xmpp/s2s/CacheEntry.java | 19 + .../main/java/com/juick/xmpp/s2s/CleaningUp.java | 107 ++++ .../main/java/com/juick/xmpp/s2s/Connection.java | 148 +++++ .../main/java/com/juick/xmpp/s2s/ConnectionIn.java | 222 +++++++ .../java/com/juick/xmpp/s2s/ConnectionOut.java | 166 +++++ .../main/java/com/juick/xmpp/s2s/DNSQueries.java | 47 ++ .../src/main/java/com/juick/xmpp/s2s/JuickBot.java | 378 ++++++++++++ .../java/com/juick/xmpp/s2s/XMPPComponent.java | 443 +++++++++++++ juick-www/src/main/java/ru/sape/Sape.java | 25 + .../src/main/java/ru/sape/SapeConnection.java | 107 ++++ juick-www/src/main/java/ru/sape/SapePageLinks.java | 95 +++ .../src/main/java/ru/sape/SerializedPhpParser.java | 221 +++++++ juick-www/src/webapp/WEB-INF/juick.conf.example | 5 + .../src/webapp/WEB-INF/logging.properties.example | 5 + juick-www/src/webapp/WEB-INF/web.xml | 50 ++ juick-www/src/webapp/favicon.png | Bin 0 -> 244 bytes juick-www/src/webapp/logo.png | Bin 0 -> 1184 bytes juick-www/src/webapp/scripts.js | 684 +++++++++++++++++++++ juick-www/src/webapp/style.css | 263 ++++++++ settings.gradle | 2 +- src/main/java/com/juick/CrosspostComponent.java | 323 ---------- src/main/java/com/juick/Group.java | 29 - src/main/java/com/juick/Message.java | 244 -------- src/main/java/com/juick/PM.java | 16 - src/main/java/com/juick/Place.java | 33 - src/main/java/com/juick/PushComponent.java | 311 ---------- src/main/java/com/juick/Recommendation.java | 16 - src/main/java/com/juick/Tag.java | 41 -- src/main/java/com/juick/User.java | 96 --- src/main/java/com/juick/http/www/Discover.java | 118 ---- src/main/java/com/juick/http/www/Errors.java | 42 -- .../java/com/juick/http/www/FacebookLogin.java | 152 ----- src/main/java/com/juick/http/www/Help.java | 90 --- src/main/java/com/juick/http/www/Home.java | 170 ----- src/main/java/com/juick/http/www/Login.java | 246 -------- src/main/java/com/juick/http/www/Main.java | 310 ---------- src/main/java/com/juick/http/www/NewMessage.java | 413 ------------- src/main/java/com/juick/http/www/PM.java | 224 ------- .../java/com/juick/http/www/PageTemplates.java | 479 --------------- src/main/java/com/juick/http/www/RSS.java | 101 --- src/main/java/com/juick/http/www/Settings.java | 91 --- src/main/java/com/juick/http/www/SignUp.java | 258 -------- src/main/java/com/juick/http/www/TwitterAuth.java | 87 --- src/main/java/com/juick/http/www/User.java | 343 ----------- src/main/java/com/juick/http/www/UserThread.java | 364 ----------- src/main/java/com/juick/http/www/Utils.java | 242 -------- .../java/com/juick/http/www/VKontakteLogin.java | 128 ---- src/main/java/com/juick/json/JSONSerializer.java | 54 -- .../java/com/juick/json/MessageSerializer.java | 138 ----- src/main/java/com/juick/json/PlaceSerializer.java | 73 --- src/main/java/com/juick/json/UserSerializer.java | 66 -- src/main/java/com/juick/server/AdsQueries.java | 51 -- .../java/com/juick/server/CrosspostQueries.java | 62 -- .../java/com/juick/server/MessagesQueries.java | 663 -------------------- src/main/java/com/juick/server/PMQueries.java | 145 ----- src/main/java/com/juick/server/PushQueries.java | 74 --- src/main/java/com/juick/server/SQLHelpers.java | 168 ----- src/main/java/com/juick/server/ShowQueries.java | 32 - .../com/juick/server/SubscriptionsQueries.java | 104 ---- src/main/java/com/juick/server/TagQueries.java | 166 ----- src/main/java/com/juick/server/UserQueries.java | 440 ------------- src/main/java/com/juick/server/Utils.java | 86 --- .../java/com/juick/server/helpers/PrivacyOpts.java | 29 - .../com/juick/server/protocol/JuickProtocol.java | 375 ----------- .../com/juick/server/protocol/ProtocolReply.java | 23 - .../server/protocol/annotation/UserCommand.java | 31 - .../com/juick/xmpp/extensions/JuickMessage.java | 184 ------ .../java/com/juick/xmpp/extensions/JuickUser.java | 75 --- src/main/java/com/juick/xmpp/s2s/CacheEntry.java | 19 - src/main/java/com/juick/xmpp/s2s/CleaningUp.java | 107 ---- src/main/java/com/juick/xmpp/s2s/Connection.java | 148 ----- src/main/java/com/juick/xmpp/s2s/ConnectionIn.java | 222 ------- .../java/com/juick/xmpp/s2s/ConnectionOut.java | 166 ----- src/main/java/com/juick/xmpp/s2s/DNSQueries.java | 47 -- src/main/java/com/juick/xmpp/s2s/JuickBot.java | 378 ------------ .../java/com/juick/xmpp/s2s/XMPPComponent.java | 443 ------------- src/main/java/ru/sape/Sape.java | 25 - src/main/java/ru/sape/SapeConnection.java | 107 ---- src/main/java/ru/sape/SapePageLinks.java | 95 --- src/main/java/ru/sape/SerializedPhpParser.java | 221 ------- src/main/webapp/WEB-INF/juick.conf.example | 5 - src/main/webapp/WEB-INF/logging.properties.example | 5 - src/main/webapp/WEB-INF/web.xml | 50 -- src/main/webapp/favicon.png | Bin 244 -> 0 bytes src/main/webapp/logo.png | Bin 1184 -> 0 bytes src/main/webapp/scripts.js | 684 --------------------- src/main/webapp/style.css | 263 -------- 138 files changed, 11095 insertions(+), 11086 deletions(-) delete mode 100644 build.gradle create mode 100644 juick-core/build.gradle create mode 100644 juick-core/src/main/java/com/juick/Group.java create mode 100644 juick-core/src/main/java/com/juick/Message.java create mode 100644 juick-core/src/main/java/com/juick/PM.java create mode 100644 juick-core/src/main/java/com/juick/Place.java create mode 100644 juick-core/src/main/java/com/juick/Recommendation.java create mode 100644 juick-core/src/main/java/com/juick/Tag.java create mode 100644 juick-core/src/main/java/com/juick/User.java create mode 100644 juick-core/src/main/java/com/juick/json/JSONSerializer.java create mode 100644 juick-core/src/main/java/com/juick/json/MessageSerializer.java create mode 100644 juick-core/src/main/java/com/juick/json/PlaceSerializer.java create mode 100644 juick-core/src/main/java/com/juick/json/UserSerializer.java create mode 100644 juick-core/src/main/java/com/juick/server/AdsQueries.java create mode 100644 juick-core/src/main/java/com/juick/server/CrosspostQueries.java create mode 100644 juick-core/src/main/java/com/juick/server/MessagesQueries.java create mode 100644 juick-core/src/main/java/com/juick/server/PMQueries.java create mode 100644 juick-core/src/main/java/com/juick/server/PushQueries.java create mode 100644 juick-core/src/main/java/com/juick/server/SQLHelpers.java create mode 100644 juick-core/src/main/java/com/juick/server/ShowQueries.java create mode 100644 juick-core/src/main/java/com/juick/server/SubscriptionsQueries.java create mode 100644 juick-core/src/main/java/com/juick/server/TagQueries.java create mode 100644 juick-core/src/main/java/com/juick/server/UserQueries.java create mode 100644 juick-core/src/main/java/com/juick/server/Utils.java create mode 100644 juick-core/src/main/java/com/juick/server/helpers/PrivacyOpts.java create mode 100644 juick-core/src/main/java/com/juick/xmpp/extensions/JuickMessage.java create mode 100644 juick-core/src/main/java/com/juick/xmpp/extensions/JuickUser.java create mode 100644 juick-www/build.gradle create mode 100644 juick-www/src/main/java/com/juick/server/protocol/JuickProtocol.java create mode 100644 juick-www/src/main/java/com/juick/server/protocol/ProtocolReply.java create mode 100644 juick-www/src/main/java/com/juick/server/protocol/annotation/UserCommand.java create mode 100644 juick-www/src/main/java/com/juick/www/CrosspostComponent.java create mode 100644 juick-www/src/main/java/com/juick/www/Discover.java create mode 100644 juick-www/src/main/java/com/juick/www/Errors.java create mode 100644 juick-www/src/main/java/com/juick/www/FacebookLogin.java create mode 100644 juick-www/src/main/java/com/juick/www/Help.java create mode 100644 juick-www/src/main/java/com/juick/www/Home.java create mode 100644 juick-www/src/main/java/com/juick/www/Login.java create mode 100644 juick-www/src/main/java/com/juick/www/Main.java create mode 100644 juick-www/src/main/java/com/juick/www/NewMessage.java create mode 100644 juick-www/src/main/java/com/juick/www/PM.java create mode 100644 juick-www/src/main/java/com/juick/www/PageTemplates.java create mode 100644 juick-www/src/main/java/com/juick/www/PushComponent.java create mode 100644 juick-www/src/main/java/com/juick/www/RSS.java create mode 100644 juick-www/src/main/java/com/juick/www/Settings.java create mode 100644 juick-www/src/main/java/com/juick/www/SignUp.java create mode 100644 juick-www/src/main/java/com/juick/www/TwitterAuth.java create mode 100644 juick-www/src/main/java/com/juick/www/User.java create mode 100644 juick-www/src/main/java/com/juick/www/UserThread.java create mode 100644 juick-www/src/main/java/com/juick/www/Utils.java create mode 100644 juick-www/src/main/java/com/juick/www/VKontakteLogin.java create mode 100644 juick-www/src/main/java/com/juick/xmpp/s2s/CacheEntry.java create mode 100644 juick-www/src/main/java/com/juick/xmpp/s2s/CleaningUp.java create mode 100644 juick-www/src/main/java/com/juick/xmpp/s2s/Connection.java create mode 100644 juick-www/src/main/java/com/juick/xmpp/s2s/ConnectionIn.java create mode 100644 juick-www/src/main/java/com/juick/xmpp/s2s/ConnectionOut.java create mode 100644 juick-www/src/main/java/com/juick/xmpp/s2s/DNSQueries.java create mode 100644 juick-www/src/main/java/com/juick/xmpp/s2s/JuickBot.java create mode 100644 juick-www/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java create mode 100644 juick-www/src/main/java/ru/sape/Sape.java create mode 100644 juick-www/src/main/java/ru/sape/SapeConnection.java create mode 100644 juick-www/src/main/java/ru/sape/SapePageLinks.java create mode 100644 juick-www/src/main/java/ru/sape/SerializedPhpParser.java create mode 100644 juick-www/src/webapp/WEB-INF/juick.conf.example create mode 100644 juick-www/src/webapp/WEB-INF/logging.properties.example create mode 100644 juick-www/src/webapp/WEB-INF/web.xml create mode 100644 juick-www/src/webapp/favicon.png create mode 100644 juick-www/src/webapp/logo.png create mode 100644 juick-www/src/webapp/scripts.js create mode 100644 juick-www/src/webapp/style.css delete mode 100644 src/main/java/com/juick/CrosspostComponent.java delete mode 100644 src/main/java/com/juick/Group.java delete mode 100644 src/main/java/com/juick/Message.java delete mode 100644 src/main/java/com/juick/PM.java delete mode 100644 src/main/java/com/juick/Place.java delete mode 100644 src/main/java/com/juick/PushComponent.java delete mode 100644 src/main/java/com/juick/Recommendation.java delete mode 100644 src/main/java/com/juick/Tag.java delete mode 100644 src/main/java/com/juick/User.java delete mode 100644 src/main/java/com/juick/http/www/Discover.java delete mode 100644 src/main/java/com/juick/http/www/Errors.java delete mode 100644 src/main/java/com/juick/http/www/FacebookLogin.java delete mode 100644 src/main/java/com/juick/http/www/Help.java delete mode 100644 src/main/java/com/juick/http/www/Home.java delete mode 100644 src/main/java/com/juick/http/www/Login.java delete mode 100644 src/main/java/com/juick/http/www/Main.java delete mode 100644 src/main/java/com/juick/http/www/NewMessage.java delete mode 100644 src/main/java/com/juick/http/www/PM.java delete mode 100644 src/main/java/com/juick/http/www/PageTemplates.java delete mode 100644 src/main/java/com/juick/http/www/RSS.java delete mode 100644 src/main/java/com/juick/http/www/Settings.java delete mode 100644 src/main/java/com/juick/http/www/SignUp.java delete mode 100644 src/main/java/com/juick/http/www/TwitterAuth.java delete mode 100644 src/main/java/com/juick/http/www/User.java delete mode 100644 src/main/java/com/juick/http/www/UserThread.java delete mode 100644 src/main/java/com/juick/http/www/Utils.java delete mode 100644 src/main/java/com/juick/http/www/VKontakteLogin.java delete mode 100644 src/main/java/com/juick/json/JSONSerializer.java delete mode 100644 src/main/java/com/juick/json/MessageSerializer.java delete mode 100644 src/main/java/com/juick/json/PlaceSerializer.java delete mode 100644 src/main/java/com/juick/json/UserSerializer.java delete mode 100644 src/main/java/com/juick/server/AdsQueries.java delete mode 100644 src/main/java/com/juick/server/CrosspostQueries.java delete mode 100644 src/main/java/com/juick/server/MessagesQueries.java delete mode 100644 src/main/java/com/juick/server/PMQueries.java delete mode 100644 src/main/java/com/juick/server/PushQueries.java delete mode 100644 src/main/java/com/juick/server/SQLHelpers.java delete mode 100644 src/main/java/com/juick/server/ShowQueries.java delete mode 100644 src/main/java/com/juick/server/SubscriptionsQueries.java delete mode 100644 src/main/java/com/juick/server/TagQueries.java delete mode 100644 src/main/java/com/juick/server/UserQueries.java delete mode 100644 src/main/java/com/juick/server/Utils.java delete mode 100644 src/main/java/com/juick/server/helpers/PrivacyOpts.java delete mode 100644 src/main/java/com/juick/server/protocol/JuickProtocol.java delete mode 100644 src/main/java/com/juick/server/protocol/ProtocolReply.java delete mode 100644 src/main/java/com/juick/server/protocol/annotation/UserCommand.java delete mode 100644 src/main/java/com/juick/xmpp/extensions/JuickMessage.java delete mode 100644 src/main/java/com/juick/xmpp/extensions/JuickUser.java delete mode 100644 src/main/java/com/juick/xmpp/s2s/CacheEntry.java delete mode 100644 src/main/java/com/juick/xmpp/s2s/CleaningUp.java delete mode 100644 src/main/java/com/juick/xmpp/s2s/Connection.java delete mode 100644 src/main/java/com/juick/xmpp/s2s/ConnectionIn.java delete mode 100644 src/main/java/com/juick/xmpp/s2s/ConnectionOut.java delete mode 100644 src/main/java/com/juick/xmpp/s2s/DNSQueries.java delete mode 100644 src/main/java/com/juick/xmpp/s2s/JuickBot.java delete mode 100644 src/main/java/com/juick/xmpp/s2s/XMPPComponent.java delete mode 100644 src/main/java/ru/sape/Sape.java delete mode 100644 src/main/java/ru/sape/SapeConnection.java delete mode 100644 src/main/java/ru/sape/SapePageLinks.java delete mode 100644 src/main/java/ru/sape/SerializedPhpParser.java delete mode 100644 src/main/webapp/WEB-INF/juick.conf.example delete mode 100644 src/main/webapp/WEB-INF/logging.properties.example delete mode 100644 src/main/webapp/WEB-INF/web.xml delete mode 100644 src/main/webapp/favicon.png delete mode 100644 src/main/webapp/logo.png delete mode 100644 src/main/webapp/scripts.js delete mode 100644 src/main/webapp/style.css diff --git a/build.gradle b/build.gradle deleted file mode 100644 index d23b241a..00000000 --- a/build.gradle +++ /dev/null @@ -1,94 +0,0 @@ -subprojects { - apply plugin: 'java' - repositories { - mavenCentral() - } -} - -buildscript { - repositories { - mavenCentral() - jcenter() - } - dependencies { - classpath 'com.eriwen:gradle-css-plugin:1.11.1' - classpath 'com.eriwen:gradle-js-plugin:1.12.1' - classpath 'org.akhikhl.gretty:gretty:+' - classpath 'com.tkruse.gradle:gradle-groovysh-plugin:1.0.8' - } -} -apply plugin: 'java' -apply plugin: 'war' -apply plugin: 'css' -apply plugin: 'org.akhikhl.gretty' -apply plugin: 'com.eriwen.gradle.js' -apply plugin: 'com.github.tkruse.groovysh' - -repositories { - mavenCentral() -} - -dependencies { - compile project(':deps:com.juick.xmpp') - compile 'org.slf4j:slf4j-jdk14:1.7.13' - compile 'com.ganyo:gcm-server:1.0.+' - compile 'com.notnoop.apns:apns:1.0.0.Beta6' - compile 'com.github.scribejava:scribejava-apis:2.7.3' - compile 'org.apache.httpcomponents:httpclient:4.5.1' - compile 'org.apache.commons:commons-lang3:3.4' - compile "org.springframework:spring-jdbc:4.3.1.RELEASE" - compile 'org.json:json:20151123' - testCompile 'junit:junit:4.12' - providedCompile 'javax.servlet:javax.servlet-api:3.1.0' - providedRuntime 'mysql:mysql-connector-java:5.1.39' -} - -javascript.source { - dev { - js { - srcDir "src/main/webapp" - include "*.js" - } - } -} - -combineJs { - source = javascript.source.dev.js.files - dest = file("${buildDir}/scripts.all.js") -} - -minifyJs { - source = combineJs - dest = file("${buildDir}/scripts.min.js") - sourceMap = file("${buildDir}/scripts.sourcemap.json") -} - -css.source { - dev { - css { - srcDir "src/main/webapp" - include "*.css" - } - } -} - -combineCss { - source = css.source.dev.css.files - dest = "${buildDir}/style.all.css" -} - -minifyCss { - source = combineCss - dest = "${buildDir}/style.min.css" -} - -assemble.dependsOn 'minifyCss' -assemble.dependsOn 'minifyJs' - -compileJava.options.encoding = 'UTF-8' - -gretty { - httpPort = 8080 - contextPath = '' - servletContainer = 'tomcat8' -} \ No newline at end of file diff --git a/juick-core/build.gradle b/juick-core/build.gradle new file mode 100644 index 00000000..bb10c52b --- /dev/null +++ b/juick-core/build.gradle @@ -0,0 +1,15 @@ +apply plugin: 'java' + +repositories { + mavenCentral() +} + +dependencies { + compile project(':deps:com.juick.xmpp') + compile 'org.apache.httpcomponents:httpclient:4.5.1' + compile 'org.apache.commons:commons-lang3:3.4' + compile "org.springframework:spring-jdbc:4.3.1.RELEASE" + compile 'org.json:json:20151123' +} + +compileJava.options.encoding = 'UTF-8' \ No newline at end of file diff --git a/juick-core/src/main/java/com/juick/Group.java b/juick-core/src/main/java/com/juick/Group.java new file mode 100644 index 00000000..008d3af2 --- /dev/null +++ b/juick-core/src/main/java/com/juick/Group.java @@ -0,0 +1,29 @@ +/* + * 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; + +/** + * + * @author Ugnich Anton + */ +public class Group { + + public String Name = null; + public int GID = 0; + public int UsersCnt = 0; +} diff --git a/juick-core/src/main/java/com/juick/Message.java b/juick-core/src/main/java/com/juick/Message.java new file mode 100644 index 00000000..c128b4f2 --- /dev/null +++ b/juick-core/src/main/java/com/juick/Message.java @@ -0,0 +1,244 @@ +/* + * 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; + +import java.util.*; + +/** + * + * @author Ugnich Anton + */ +public class Message implements Comparable { + + private int MID = 0; + + private int RID = 0; + + public int ReplyTo = 0; + private String Text = null; + private User User = null; + public List Tags = new ArrayList<>(); + private Date date; + public int TimeAgo = 0; + public int Privacy = 1; + public boolean FriendsOnly = false; + public boolean ReadOnly = false; + public boolean Hidden = false; + public boolean VisitorCanComment = true; + public int Replies = 0; + public String RepliesBy = null; + public String AttachmentType = null; + public String Photo = null; + public String Video = null; + public Place Place = null; + public int Likes = 0; + private boolean liked = false; + public List childs = new ArrayList<>(); + private Optional PM = Optional.empty(); + private Optional Recommendation = Optional.empty(); + + public Message() { + } + + public Message(Message msg) { + setMID(msg.getMID()); + setRID(msg.getRID()); + ReplyTo = msg.ReplyTo; + setText(msg.getText()); + setUser(msg.getUser()); + Tags = msg.Tags; + setDate(msg.getDate()); + TimeAgo = msg.TimeAgo; + Privacy = msg.Privacy; + FriendsOnly = msg.FriendsOnly; + ReadOnly = msg.ReadOnly; + Hidden = msg.Hidden; + Replies = msg.Replies; + AttachmentType = msg.AttachmentType; + Photo = msg.Photo; + Video = msg.Video; + Place = msg.Place; + Likes = msg.Likes; + setLiked(msg.isLiked()); + childs = msg.childs; + PM = msg.PM; + Recommendation = msg.Recommendation; + } + + public void parseTags(String strTags) { + if (strTags != null) { + Tags.addAll(Arrays.asList(strTags.split(" "))); + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Message)) { + return false; + } + Message jmsg = (Message) obj; + return (this.getMID() == jmsg.getMID() && this.getRID() == jmsg.getRID()); + } + + @Override + public int compareTo(Object obj) throws ClassCastException { + if (!(obj instanceof Message)) { + throw new ClassCastException(); + } + Message jmsg = (Message) obj; + + if (this.getMID() != jmsg.getMID()) { + if (this.getMID() > jmsg.getMID()) { + return -1; + } else { + return 1; + } + } + + if (this.getRID() != jmsg.getRID()) { + if (this.getRID() < jmsg.getRID()) { + return -1; + } else { + return 1; + } + } + + return 0; + } + + public int getChildsCount() { + int cnt = childs.size(); + for (Message child : childs) { + cnt += child.getChildsCount(); + } + return cnt; + } + + public void cleanupChilds() { + if (!childs.isEmpty()) { + for (Message child : childs) { + child.cleanupChilds(); + } + childs.clear(); + } + } + + public String getAttachmentURL() { + if (AttachmentType != null) { + String url = "http://i.juick.com/"; + url += AttachmentType.equals("mp4") ? "video" : "photos-1024"; + url += "/" + getMID(); + if (getRID() > 0) { + url += "-" + getRID(); + } + url += "." + AttachmentType; + return url; + } else { + return null; + } + } + + public String getTagsString() { + String ret = ""; + if (!Tags.isEmpty()) { + for (String Tag : Tags) { + ret += " *" + Tag; + } + if (FriendsOnly) { + ret += " *friends"; + } + if (Privacy == -2) { + ret += " *private"; + } + if (Privacy == -1) { + ret += " *friends"; + } + if (Privacy == 2) { + ret += " *public"; + } + if (ReadOnly) { + ret += " *readonly"; + } + } + return ret; + } + + public int getMID() { + return MID; + } + + public void setMID(int MID) { + this.MID = MID; + } + + public int getRID() { + return RID; + } + + public void setRID(int RID) { + this.RID = RID; + } + + public com.juick.User getUser() { + return User; + } + + public void setUser(com.juick.User user) { + User = user; + } + + public String getText() { + return Text; + } + + public void setText(String text) { + Text = text; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + + this.date = date; + } + + public Optional getPM() { + return PM; + } + + public void setPM(com.juick.PM PM) { + this.PM = Optional.ofNullable(PM); + } + + public Optional getRecommendation() { + return Recommendation; + } + public void setRecommendation(Recommendation recommendation) { + this.Recommendation = Optional.ofNullable(recommendation); + } + + public boolean isLiked() { + return liked; + } + + public void setLiked(boolean liked) { + this.liked = liked; + } +} diff --git a/juick-core/src/main/java/com/juick/PM.java b/juick-core/src/main/java/com/juick/PM.java new file mode 100644 index 00000000..658cafc9 --- /dev/null +++ b/juick-core/src/main/java/com/juick/PM.java @@ -0,0 +1,16 @@ +package com.juick; + +/** + * Created by vt on 08/02/16. + */ +public class PM { + private User to; + + public User getTo() { + return to; + } + + public PM(User to) { + this.to = to; + } +} diff --git a/juick-core/src/main/java/com/juick/Place.java b/juick-core/src/main/java/com/juick/Place.java new file mode 100644 index 00000000..7174ed6e --- /dev/null +++ b/juick-core/src/main/java/com/juick/Place.java @@ -0,0 +1,33 @@ +/* + * 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; + +/** + * + * @author Ugnich Anton + */ +public class Place { + + public int pid = 0; + public double lat = 0; + public double lon = 0; + public String name = null; + public int users = 0; + public int messages = 0; + public int distance = 0; +} diff --git a/juick-core/src/main/java/com/juick/Recommendation.java b/juick-core/src/main/java/com/juick/Recommendation.java new file mode 100644 index 00000000..4cfa67c2 --- /dev/null +++ b/juick-core/src/main/java/com/juick/Recommendation.java @@ -0,0 +1,16 @@ +package com.juick; + +/** + * Created by vt on 08/02/16. + */ +public class Recommendation { + User from; + + public User getFrom() { + return from; + } + + public Recommendation(User from) { + this.from = from; + } +} diff --git a/juick-core/src/main/java/com/juick/Tag.java b/juick-core/src/main/java/com/juick/Tag.java new file mode 100644 index 00000000..119aad99 --- /dev/null +++ b/juick-core/src/main/java/com/juick/Tag.java @@ -0,0 +1,41 @@ +/* + * 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; + +import java.util.Objects; + +/** + * + * @author Ugnich Anton + */ +public class Tag { + + public String Name = null; + public int TID = 0; + public int SynonymID = 0; + public int UsageCnt = 0; + + @Override + public boolean equals(Object o) { + boolean equal = false; + if (o != null && o instanceof Tag) { + equal = Objects.equals(Name, ((Tag) o).Name); + } + return equal; + } +} diff --git a/juick-core/src/main/java/com/juick/User.java b/juick-core/src/main/java/com/juick/User.java new file mode 100644 index 00000000..b9111d8f --- /dev/null +++ b/juick-core/src/main/java/com/juick/User.java @@ -0,0 +1,96 @@ +/* + * 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; + +/** + * + * @author Ugnich Anton + */ +public class User { + + private int UID = 0; + private String UName = null; + public Object Avatar = null; + private String FullName = null; + private String JID = null; + public int MessagesCount = 0; + private String AuthHash = null; + public boolean Banned = false; + + public User() { + } + + public User(User u) { + setUID(u.getUID()); + setUName(u.getUName()); + Avatar = u.Avatar; + setFullName(u.getFullName()); + setJID(u.getJID()); + MessagesCount = u.MessagesCount; + setAuthHash(u.getAuthHash()); + Banned = u.Banned; + } + + @Override + public boolean equals(Object obj) { + return (obj instanceof User && ((User) obj).getUID() == this.getUID()); + } + + public int getUID() { + return UID; + } + + public void setUID(int UID) { + this.UID = UID; + } + + public String getUName() { + return UName; + } + + public void setUName(String UName) { + this.UName = UName; + } + + public String getFullName() { + return FullName; + } + + public void setFullName(String fullName) { + FullName = fullName; + } + + public String getJID() { + return JID; + } + + public void setJID(String JID) { + this.JID = JID; + } + + public String getAuthHash() { + return AuthHash; + } + + public void setAuthHash(String authHash) { + AuthHash = authHash; + } + public Integer getUnreadCount() { + return MessagesCount; + } +} diff --git a/juick-core/src/main/java/com/juick/json/JSONSerializer.java b/juick-core/src/main/java/com/juick/json/JSONSerializer.java new file mode 100644 index 00000000..142cacf0 --- /dev/null +++ b/juick-core/src/main/java/com/juick/json/JSONSerializer.java @@ -0,0 +1,54 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.juick.json; + +import java.text.ParseException; +import java.util.Iterator; +import java.util.List; +import org.json.JSONObject; + +/** + * + * @author vt + * @param + */ +public abstract class JSONSerializer { + + /** + * + * @param json + * @return + */ + public abstract T deserialize(JSONObject json) throws ParseException; + + /** + * + * @param obj + * @return + */ + public abstract JSONObject serialize(T obj); + + /** + * + * @param objs + * @return + */ + public String serializeList(List objs) { + String json = "["; + + Iterator i = objs.iterator(); + while (i.hasNext()) { + T m = i.next(); + if (json.length() > 1) { + json += ","; + } + json += serialize(m).toString(); + } + + json += "]"; + return json; + } +} diff --git a/juick-core/src/main/java/com/juick/json/MessageSerializer.java b/juick-core/src/main/java/com/juick/json/MessageSerializer.java new file mode 100644 index 00000000..e8402d51 --- /dev/null +++ b/juick-core/src/main/java/com/juick/json/MessageSerializer.java @@ -0,0 +1,138 @@ +/* + * 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.json; + +import com.juick.Message; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.TimeZone; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author Ugnich Anton + */ +public class MessageSerializer extends JSONSerializer { + + private final static Logger LOGGER = Logger.getLogger(MessageSerializer.class.getName()); + + UserSerializer userSerializer = new UserSerializer(); + PlaceSerializer placeSerializer = new PlaceSerializer(); + + final SimpleDateFormat df; + + public MessageSerializer() { + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + @Override + public Message deserialize(JSONObject json) throws JSONException, ParseException { + com.juick.Message jmsg = new com.juick.Message(); + jmsg.setMID(json.getInt("mid")); + if (json.has("rid")) { + jmsg.setRID(json.getInt("rid")); + } + if (json.has("replyto")) { + jmsg.ReplyTo = json.getInt("replyto"); + } + + jmsg.FriendsOnly = json.has("friendsonly"); + jmsg.ReadOnly = json.has("readonly"); + + jmsg.setText(json.getString("body").replace(""", "\"")); + jmsg.setUser(userSerializer.deserialize(json.getJSONObject("user"))); + + jmsg.setDate(df.parse(json.getString("timestamp"))); + + if (json.has("tags")) { + JSONArray tags = json.getJSONArray("tags"); + for (int n = 0; n < tags.length(); n++) { + jmsg.Tags.add(tags.getString(n).replace(""", "\"")); + } + } + + if (json.has("replies")) { + jmsg.Replies = json.getInt("replies"); + } + + if (json.has("photo")) { + jmsg.Photo = json.getJSONObject("photo").getString("small"); + } + + return jmsg; + } + + @Override + public JSONObject serialize(Message msg) { + JSONObject json = new JSONObject(); + + try { + if (msg.getMID() > 0) { + json.put("mid", msg.getMID()); + } + if (msg.getRID() > 0) { + json.put("rid", msg.getRID()); + } + if (msg.ReplyTo > 0) { + json.put("replyto", msg.ReplyTo); + } + if (msg.FriendsOnly) { + json.put("friendsonly", 1); + } + if (msg.ReadOnly) { + json.put("readonly", 1); + } + if (msg.getText() != null) { + json.put("body", msg.getText()); + } + if (msg.getDate() != null) { + json.put("timestamp", df.format(msg.getDate())); + } + if (msg.getUser() != null) { + json.put("user", userSerializer.serialize(msg.getUser())); + } + if (msg.Tags != null && msg.Tags.size() > 0) { + json.put("tags", new JSONArray(msg.Tags)); + } + if (msg.Replies > 0) { + json.put("replies", msg.Replies); + } + if (msg.Place != null) { + json.put("place", placeSerializer.serialize(msg.Place)); + } + if (msg.AttachmentType != null) { + String fname = msg.getMID() + (msg.getRID() > 0 ? "-" + msg.getRID() : "") + "." + msg.AttachmentType; + JSONObject photo = new JSONObject(); + photo.put("thumbnail", "http://i.juick.com/ps/" + fname); + photo.put("small", "http://i.juick.com/photos-512/" + fname); + photo.put("medium", "http://i.juick.com/photos-1024/" + fname); + json.put("photo", photo); + } + } catch (JSONException e) { + LOGGER.log(Level.SEVERE, "JSON Exception", e); + } + + return json; + } +} diff --git a/juick-core/src/main/java/com/juick/json/PlaceSerializer.java b/juick-core/src/main/java/com/juick/json/PlaceSerializer.java new file mode 100644 index 00000000..f433f7f0 --- /dev/null +++ b/juick-core/src/main/java/com/juick/json/PlaceSerializer.java @@ -0,0 +1,73 @@ +/* + * 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.json; + +import com.juick.Place; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * + * @author Ugnich Anton + */ +public class PlaceSerializer extends JSONSerializer { + + @Override + public Place deserialize(JSONObject json) throws JSONException { + Place jplace = new Place(); + + jplace.pid = json.getInt("pid"); + jplace.lat = json.getDouble("lat"); + jplace.lon = json.getDouble("lon"); + jplace.name = json.getString("name").replace(""", "\""); + if (json.has("users")) { + jplace.users = json.getInt("users"); + } + if (json.has("messages")) { + jplace.messages = json.getInt("messages"); + } + if (json.has("distance")) { + jplace.distance = json.getInt("distance"); + } + + return jplace; + } + + @Override + public JSONObject serialize(Place place) { + JSONObject json = new JSONObject(); + + try { + if (place.pid > 0) { + json.put("pid", place.pid); + } + if (place.lat >= -90 && place.lat <= 90) { + json.put("lat", place.lat); + } + if (place.lon >= -180 && place.lon <= 180) { + json.put("lon", place.lon); + } + if (place.name != null) { + json.put("name", place.name); + } + } catch (JSONException e) { + } + + return json; + } +} diff --git a/juick-core/src/main/java/com/juick/json/UserSerializer.java b/juick-core/src/main/java/com/juick/json/UserSerializer.java new file mode 100644 index 00000000..390f4bf6 --- /dev/null +++ b/juick-core/src/main/java/com/juick/json/UserSerializer.java @@ -0,0 +1,66 @@ +/* + * 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.json; + +import com.juick.User; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * + * @author Ugnich Anton + */ +public class UserSerializer extends JSONSerializer { + + @Override + public User deserialize(JSONObject json) throws JSONException { + User juser = new User(); + juser.setUID(json.getInt("uid")); + juser.setUName(json.getString("uname")); + if (json.has("fullname")) { + juser.setFullName(json.getString("fullname")); + } + return juser; + } + + @Override + public JSONObject serialize(User user) { + JSONObject json = new JSONObject(); + + try { + if (user.getUID() > 0) { + json.put("uid", user.getUID()); + } + if (user.getUName() != null) { + json.put("uname", user.getUName()); + } + if (user.getFullName() != null) { + json.put("fullname", user.getFullName()); + } + if (user.getJID() != null) { + json.put("jid", user.getJID()); + } + if (user.getUnreadCount() > 0) { + json.put("unreadCount", user.getUnreadCount()); + } + } catch (JSONException e) { + } + + return json; + } +} diff --git a/juick-core/src/main/java/com/juick/server/AdsQueries.java b/juick-core/src/main/java/com/juick/server/AdsQueries.java new file mode 100644 index 00000000..06590817 --- /dev/null +++ b/juick-core/src/main/java/com/juick/server/AdsQueries.java @@ -0,0 +1,51 @@ +/* + * Juick + * Copyright (C) 2008-2011, 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; + +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; + +/** + * + * @author ugnich + */ +public class AdsQueries { + + public static int getAdMID(JdbcTemplate sql, int uid) { + if (uid > 0) { + try { + return sql.queryForObject("SELECT message_id FROM ads_messages " + + "WHERE message_id NOT IN (SELECT message_id FROM ads_messages_log WHERE user_id=? " + + "AND ts>UNIX_TIMESTAMP()-60*60*24 GROUP BY message_id HAVING COUNT(*)>2) ORDER BY RAND() LIMIT 1", + Integer.class, uid); + } catch (EmptyResultDataAccessException e) { + return 0; + } + } else { + try { + return sql.queryForObject("SELECT message_id FROM ads_messages ORDER BY RAND() LIMIT 1", Integer.class); + } catch (EmptyResultDataAccessException e) { + return 0; + } + } + } + + public static void logAdMID(JdbcTemplate sql, int uid, int mid) { + sql.update("INSERT INTO ads_messages_log(user_id,message_id,ts) VALUES (?,?,UNIX_TIMESTAMP())", uid, mid); + } +} diff --git a/juick-core/src/main/java/com/juick/server/CrosspostQueries.java b/juick-core/src/main/java/com/juick/server/CrosspostQueries.java new file mode 100644 index 00000000..04898d0a --- /dev/null +++ b/juick-core/src/main/java/com/juick/server/CrosspostQueries.java @@ -0,0 +1,62 @@ +/* + * 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; + +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.util.Optional; + +/** + * + * @author ugnich + */ +public class CrosspostQueries { + + public static Optional> getTwitterTokens(JdbcTemplate sql, int uid) { + try { + return sql.queryForObject("SELECT access_token,access_token_secret FROM twitter WHERE user_id=? AND crosspost=1", + (rs, num) -> { + return Optional.of(Pair.of(rs.getString(1), rs.getString(2))); + }, uid); + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + } + } + + public static Optional getFacebookToken(JdbcTemplate sql, int uid) { + try { + return Optional.of(sql.queryForObject("SELECT access_token FROM facebook WHERE user_id=? AND access_token IS NOT NULL " + + "AND crosspost=1", String.class, uid)); + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + } + } + + public static Optional> getVKTokens(JdbcTemplate sql, int uid) { + try { + return sql.queryForObject("SELECT vk_id,access_token FROM vk WHERE user_id=? AND crosspost=1", + (rs, num) -> { + return Optional.of(Pair.of(rs.getString(1), rs.getString(2))); + }, uid); + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + } + } +} diff --git a/juick-core/src/main/java/com/juick/server/MessagesQueries.java b/juick-core/src/main/java/com/juick/server/MessagesQueries.java new file mode 100644 index 00000000..a0242543 --- /dev/null +++ b/juick-core/src/main/java/com/juick/server/MessagesQueries.java @@ -0,0 +1,663 @@ +/* + * 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; + +import com.juick.Message; +import com.juick.User; +import com.juick.server.helpers.PrivacyOpts; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.jdbc.core.ConnectionCallback; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Logger; + +/** + * + * @author Ugnich Anton + */ +public class MessagesQueries { + + private static final Logger LOGGER = Logger.getLogger(MessagesQueries.class.getName()); + + public static int createMessage(JdbcTemplate sql, int uid, String txt, String attachment, List tags) { + KeyHolder holder = new GeneratedKeyHolder(); + sql.update(con -> { + PreparedStatement stmt = con.prepareStatement("INSERT INTO messages(user_id,attach) VALUES (?,?)", + Statement.RETURN_GENERATED_KEYS); + stmt.setInt(1, uid); + if (attachment != null) { + stmt.setString(2, attachment); + } else { + stmt.setNull(2, Types.VARCHAR); + } + return stmt; + }, holder); + + int mid = holder.getKey().intValue(); + + + if (mid > 0) { + + String tagsNames = ""; + String tagsIDs = ""; + + for (int i = 0; i < tags.size(); i++) { + if (i > 0) { + tagsNames += " "; + tagsIDs += ","; + } + tagsNames += tags.get(i).Name; + tagsIDs += "(" + mid + "," + tags.get(i).TID + ")"; + } + if (tags.size() > 0) { + sql.execute("INSERT INTO messages_tags(message_id,tag_id) VALUES " + tagsIDs); + } + final String finalTagsNames = tagsNames; + sql.update(con -> { + PreparedStatement stmt = con.prepareStatement("INSERT INTO messages_txt(message_id,tags,txt) " + + "VALUES (?,?,?)", Statement.NO_GENERATED_KEYS); + stmt.setInt(1, mid); + if (finalTagsNames.isEmpty()) { + stmt.setNull(2, Types.VARCHAR); + } else { + stmt.setString(2, finalTagsNames); + } + stmt.setString(3, txt); + return stmt; + }); + } + + return mid; + } + + public static int createReply(JdbcTemplate sql, int mid, int rid, int uid, String txt, String attachment) { + int ridnew = getReplyIDIncrement(sql, mid); + + sql.update( con -> { + PreparedStatement stmt = con.prepareStatement("INSERT INTO replies(message_id,reply_id,user_id," + + "replyto,attach,txt) VALUES (?,?,?,?,?,?)", Statement.NO_GENERATED_KEYS); + stmt.setInt(1, mid); + stmt.setInt(2, ridnew); + stmt.setInt(3, uid); + stmt.setInt(4, rid); + if (attachment != null) { + stmt.setString(5, attachment); + } else { + stmt.setNull(5, Types.VARCHAR); + } + stmt.setString(6, txt); + return stmt; + }); + + if (ridnew > 0) { + sql.update("UPDATE messages SET replies=replies+1 WHERE message_id=?", mid); + } + + return ridnew; + } + + public static int getReplyIDIncrement(JdbcTemplate sql, int mid) { + return sql.execute((ConnectionCallback) conn -> { + conn.setAutoCommit(false); + try (PreparedStatement ps = conn.prepareStatement("UPDATE messages SET maxreplyid=maxreplyid+1 WHERE message_id=?")) { + ps.setInt(1, mid); + if (ps.executeUpdate() != 1) { + throw new IncorrectResultSizeDataAccessException("Cannot find a message to update: " + mid, 1, 0); + } + } + try (PreparedStatement ps = conn.prepareStatement("SELECT maxreplyid FROM messages WHERE message_id=?")) { + ps.setInt(1, mid); + try (ResultSet resultSet = ps.executeQuery()) { + if (resultSet.next()) { + int rv = resultSet.getInt(1); + conn.commit(); + return rv; + } else { + throw new IncorrectResultSizeDataAccessException("while getting getReplyIDIncrement, mid=" + mid, 1, 0); + } + } + } + }); + + } + + public static boolean recommendMessage(JdbcTemplate sql, int mid, int vuid) { + boolean res = sql.update("INSERT IGNORE INTO favorites(user_id,message_id) VALUES (" + vuid + "," + mid + ")") == 1; + if (res) { + sql.update("UPDATE messages SET likes=likes+1 WHERE message_id=?", mid); + } + return res; + } + + public static boolean canViewThread(JdbcTemplate sql, int mid, int uid) { + PrivacyOpts privacyOpts; + try { + privacyOpts = sql.queryForObject("SELECT user_id,privacy FROM messages WHERE messages.message_id=?", + (rs, rowNum) -> { + PrivacyOpts res = new PrivacyOpts(); + res.setUid(rs.getInt(1)); + res.setPrivacy(rs.getInt(2)); + return res; + }, mid); + } catch (EmptyResultDataAccessException e) { + return true; + } + return privacyOpts.getPrivacy() >= 0 + || uid == privacyOpts.getUid() + || ((privacyOpts.getPrivacy() == -1 || privacyOpts.getPrivacy() == -2) && uid > 0 + && UserQueries.isInWL(sql, privacyOpts.getUid(), uid)); + } + + public static boolean isReadOnly(JdbcTemplate sql, int mid) { + try { + return sql.queryForObject("SELECT readonly FROM messages WHERE message_id=?", new Object[]{mid}, Integer.class) == 1; + } catch (EmptyResultDataAccessException e) { + return false; + } + } + + public static boolean isSubscribed(JdbcTemplate sql, int uid, int mid) { + try { + return sql.queryForObject("SELECT 1 FROM subscr_messages WHERE suser_id=? AND message_id=?", new Object[]{uid, mid}, Integer.class) == 1; + } catch (EmptyResultDataAccessException e) { + return false; + } + } + + public static int getMessagePrivacy(JdbcTemplate sql, int mid) { + try { + return sql.queryForObject("SELECT privacy FROM messages WHERE message_id=?", new Object[]{mid}, Integer.class); + } catch (EmptyResultDataAccessException e) { + return -4; + } + } + + + public static com.juick.Message getMessage(JdbcTemplate sql, int mid) { + try { + return sql.queryForObject("SELECT messages.user_id,users.nick," + + "TIMESTAMPDIFF(MINUTE,messages.ts,NOW())," + + "messages.ts," + + "messages.readonly,messages.privacy,messages.replies," + + "messages.attach,messages.place_id,messages.lat," + + "messages.lon,messages.likes,messages.hidden," + + "txt.tags,txt.repliesby,txt.txt FROM messages " + + "INNER JOIN users ON messages.user_id=users.id " + + "INNER JOIN messages_txt AS txt " + + "ON messages.message_id=txt.message_id " + + "WHERE messages.message_id=?", new Object[]{mid}, + (rs, rowNum) -> { + Message msg = new Message(); + msg.setMID(mid); + msg.setUser(new User()); + msg.getUser().setUID(rs.getInt(1)); + msg.getUser().setUName(rs.getString(2)); + msg.TimeAgo = rs.getInt(3); + msg.setDate(rs.getTimestamp(4)); + msg.ReadOnly = rs.getBoolean(5); + msg.Privacy = rs.getInt(6); + msg.FriendsOnly = msg.Privacy < 0; + msg.Replies = rs.getInt(7); + msg.AttachmentType = rs.getString(8); + //if (rs.getInt(9) > 0) { + // msg.Place = PlacesQueries.getPlace(sql, rs.getInt(9)); + //} + if (rs.getDouble(10) != 0) { + msg.Place = new com.juick.Place(); + msg.Place.lat = rs.getDouble(10); + msg.Place.lon = rs.getDouble(11); + } + msg.Likes = rs.getInt(12); + msg.Hidden = rs.getBoolean(13); + msg.parseTags(rs.getString(14)); + msg.RepliesBy = rs.getString(15); + msg.setText(rs.getString(16)); + return msg; + }); + } catch (EmptyResultDataAccessException e) { + return null; + } + } + + public static com.juick.Message getReply(JdbcTemplate sql, int mid, int rid) { + try { + return sql.queryForObject("SELECT replies.user_id,users.nick," + + "replies.replyto,replies.ts," + + "replies.attach,replies.txt FROM replies INNER JOIN users " + + "ON replies.user_id=users.id " + + "WHERE replies.message_id=? AND replies.reply_id=?", + new Object[]{mid, rid}, (rs, num) -> { + Message msg = new Message(); + msg.setMID(mid); + msg.setRID(rid); + msg.setUser(new User()); + msg.getUser().setUID(rs.getInt(1)); + msg.getUser().setUName(rs.getString(2)); + msg.ReplyTo = rs.getInt(3); + msg.setDate(rs.getTimestamp(4)); + msg.AttachmentType = rs.getString(5); + msg.setText(rs.getString(6)); + return msg; + }); + } catch (EmptyResultDataAccessException e) { + return null; + } + } + + public static User getMessageAuthor(JdbcTemplate sql, int mid) { + try { + return sql.queryForObject("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.setUName(rs.getString(2)); + return res; + }); + } catch (EmptyResultDataAccessException e) { + return null; + } + } + + public static List getMessageTags(JdbcTemplate sql, int mid) { + return sql.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)", + new Object[]{mid}, (rs, num) -> { + com.juick.Tag t = new com.juick.Tag(); + t.TID = rs.getInt(1); + t.SynonymID = rs.getInt(2); + t.Name = rs.getString(3); + t.UsageCnt = rs.getInt(4); + return t; + }); + } + + public static List getMessageTagsIDs(JdbcTemplate sql, int mid) { + return sql.queryForList("SELECT tag_id FROM messages_tags WHERE message_id=?", new Object[] {mid}, Integer.class); + } + + public static List getMessageRecommendations(JdbcTemplate sql, int mid) { + return sql.queryForList("SELECT users.nick FROM favorites INNER JOIN users " + + "ON (favorites.message_id=? AND favorites.user_id=users.id)", + new Object[] {mid}, String.class); + } + + public static List getAll(JdbcTemplate sql, int visitor_uid, int before) { + if (visitor_uid > 1) { + if (before > 0) { + return sql.queryForList("SELECT message_id FROM messages WHERE message_id0 OR user_id=?) AND user_id NOT IN (SELECT bl_user_id FROM bl_users WHERE user_id=?)" + + " AND user_id NOT IN (SELECT id from users WHERE banned=1) ORDER BY message_id DESC LIMIT 20", + new Object[]{before, visitor_uid, visitor_uid}, Integer.class); + } else { + return sql.queryForList("SELECT message_id FROM messages WHERE hidden=0" + + " AND (privacy>0 OR user_id=?)" + + " AND user_id NOT IN (SELECT bl_user_id FROM bl_users WHERE user_id=?)" + + " AND user_id NOT IN (SELECT id from users WHERE banned=1) ORDER BY message_id DESC LIMIT 20", + new Object[]{visitor_uid, visitor_uid}, Integer.class); + } + } else { + if (before > 0) { + return sql.queryForList("SELECT message_id FROM messages WHERE message_id0 AND user_id NOT IN (SELECT id from users WHERE banned=1) " + + " ORDER BY message_id DESC LIMIT 20", + new Object[]{before}, Integer.class); + } else { + return sql.queryForList("SELECT message_id FROM messages WHERE hidden=0 AND privacy>0" + + " AND user_id NOT IN (SELECT id from users WHERE banned=1) ORDER BY message_id DESC LIMIT 20", + Integer.class); + } + } + } + + public static List getTag(JdbcTemplate sql, int tid, int visitor_uid, int before, int cnt) { + if (before > 0) { + return sql.queryForList("SELECT message_id FROM (tags INNER JOIN messages_tags " + + "ON ((tags.synonym_id=? OR tags.tag_id=?) AND tags.tag_id=messages_tags.tag_id)) " + + "INNER JOIN messages USING(message_id) WHERE messages.message_id0 OR messages.user_id=?) ORDER BY message_id DESC LIMIT ?", + new Object[]{tid, tid, before, visitor_uid, cnt}, Integer.class); + } else { + return sql.queryForList("SELECT message_id FROM (tags INNER JOIN messages_tags " + + "ON ((tags.synonym_id=? OR tags.tag_id=?) AND tags.tag_id=messages_tags.tag_id)) " + + "INNER JOIN messages USING(message_id) WHERE messages.privacy>0 OR messages.user_id=? " + + "ORDER BY message_id DESC LIMIT ?", + new Object[]{tid, tid, visitor_uid, cnt}, Integer.class); + } + } + + public static List getTags(JdbcTemplate sql, String tids, int visitor_uid, int before, int cnt) { + if (before > 0) { + return sql.queryForList("SELECT messages.message_id FROM messages_tags " + + "INNER JOIN messages USING(message_id) WHERE messages_tags.tag_id IN (" + tids + ") " + + "AND messages.message_id0 OR messages.user_id=?) " + + "ORDER BY messages.message_id DESC LIMIT ?", new Object[]{before, visitor_uid, cnt}, Integer.class); + } else { + return sql.queryForList("SELECT messages.message_id FROM messages_tags " + + "INNER JOIN messages USING(message_id) WHERE messages_tags.tag_id IN (" + tids + ") " + + "AND (messages.privacy>0 OR messages.user_id=?) " + + "ORDER BY messages.message_id DESC LIMIT ?", new Object[]{visitor_uid, cnt}, Integer.class); + } + } + + public static List getPlace(JdbcTemplate sql, int place_id, int visitor_uid, int before) { + if (before > 0) { + return sql.queryForList("SELECT message_id FROM messages WHERE place_id=? AND message_id0 OR user_id=?) ORDER BY message_id DESC LIMIT 20", + new Object[]{place_id, before, visitor_uid}, Integer.class); + } else { + return sql.queryForList("SELECT message_id FROM messages WHERE place_id=? AND (privacy>0 OR user_id=?) " + + "ORDER BY message_id DESC LIMIT 20", new Object[]{place_id, visitor_uid}, Integer.class); + } + } + + public static List getMyFeed(JdbcTemplate sql, int uid, int before) { + List mids; + if (before > 0) { + mids = sql.queryForList("SELECT message_id FROM messages " + + "INNER JOIN subscr_users ON (subscr_users.suser_id=? AND subscr_users.user_id=messages.user_id) " + + "WHERE message_id=0 OR (privacy>=-2 AND privacy<=-1 AND messages.user_id " + + "IN (SELECT user_id FROM wl_users WHERE wl_user_id=?))) ORDER BY message_id DESC LIMIT 20", + Integer.class, uid, before, uid); + } else { + mids = sql.queryForList("SELECT message_id FROM messages " + + "INNER JOIN subscr_users ON (subscr_users.suser_id=? " + + "AND subscr_users.user_id=messages.user_id) " + + "WHERE (privacy>=0 OR (privacy>=-2 AND privacy<=-1 AND messages.user_id " + + "IN (SELECT user_id FROM wl_users WHERE wl_user_id=?))) ORDER BY message_id DESC LIMIT 20", + Integer.class, uid, uid); + } + + + if (before > 0) { + mids.addAll(sql.queryForList("SELECT message_id FROM messages " + + "WHERE user_id=? AND message_id getPrivate(JdbcTemplate sql, int uid, int before) { + + if (before > 0) { + return sql.queryForList("SELECT message_id FROM messages WHERE user_id=? AND privacy<0 AND message_id getDiscussions(JdbcTemplate sql, int uid, int before) { + if (before > 0) { + return sql.queryForList("SELECT message_id FROM subscr_messages WHERE suser_id=? AND message_id getRecommended(JdbcTemplate sql, int uid, int before) { + if (before > 0) { + return sql.queryForList("SELECT message_id FROM favorites WHERE user_id " + + "IN (SELECT user_id FROM subscr_users WHERE suser_id=?) AND message_id getPopular(JdbcTemplate sql, int visitor_uid, int before) { + + if (before > 0) { + return sql.queryForList("SELECT message_id FROM messages WHERE message_id0 " + + "AND popular>0 AND user_id NOT IN (SELECT bl_user_id FROM bl_users WHERE user_id=?) " + + "ORDER BY message_id DESC LIMIT 20", Integer.class, before, visitor_uid); + } else { + return sql.queryForList("SELECT message_id FROM messages WHERE privacy>0 " + + "AND popular>0 AND user_id NOT IN (SELECT bl_user_id FROM bl_users WHERE user_id=?) " + + "ORDER BY message_id DESC LIMIT 20", Integer.class, visitor_uid); + } + } + + public static List getPhotos(JdbcTemplate sql, int visitor_uid, int before) { + if (before > 0) { + return sql.queryForList("SELECT message_id FROM messages WHERE message_id0 OR user_id=?) " + + "AND attach IS NOT NULL AND user_id NOT IN (SELECT id from users WHERE banned=1) " + + "AND user_id NOT IN (SELECT bl_user_id FROM bl_users WHERE user_id=?) " + + "ORDER BY message_id DESC LIMIT 20", Integer.class, before, visitor_uid, visitor_uid); + } else { + return sql.queryForList("SELECT message_id FROM messages WHERE (privacy>0 OR user_id=?) " + + "AND attach IS NOT NULL AND user_id NOT IN (SELECT id from users WHERE banned=1) " + + "AND user_id NOT IN (SELECT bl_user_id FROM bl_users WHERE user_id=?) " + + "ORDER BY message_id DESC LIMIT 20", Integer.class, visitor_uid, visitor_uid); + } + } + + public static List getSearch(JdbcTemplate sql, JdbcTemplate sqlSearch, String search, int before) { + List mids; + + if (before > 0) { + mids = sqlSearch.queryForList("SELECT id AS message_id FROM messages WHERE MATCH(?) AND id 0) { + return sql.queryForList("SELECT message_id FROM messages WHERE message_id " + + "IN (" + Utils.convertArrayInt2String(mids) + ") AND privacy>0 ORDER BY message_id DESC LIMIT 20", + Integer.class); + } + return mids; + } + + public static List getUserBlog(JdbcTemplate sql, int UID, int privacy, int before) { + if (before > 0) { + return sql.queryForList("SELECT message_id FROM messages WHERE user_id=? AND message_id=" + + privacy + " ORDER BY message_id DESC LIMIT 20", new Object[]{UID, before}, Integer.class); + } else { + return sql.queryForList("SELECT message_id FROM messages WHERE user_id=? AND privacy>=" + privacy + + " ORDER BY message_id DESC LIMIT 20", new Object[]{UID}, Integer.class); + } + } + + public static List getUserTag(JdbcTemplate sql, int UID, int TID, int privacy, int before) { + if (before > 0) { + return sql.queryForList("SELECT messages.message_id FROM messages_tags INNER JOIN messages " + + "USING(message_id) WHERE messages.user_id=? AND messages_tags.tag_id=? " + + "AND messages.message_id=" + privacy + + " ORDER BY messages.message_id DESC LIMIT 20", + new Object[] {UID, TID, before}, Integer.class); + } else { + return sql.queryForList("SELECT messages.message_id FROM messages_tags INNER JOIN messages " + + "USING(message_id) WHERE messages.user_id=? AND messages_tags.tag_id=? " + + "AND messages.privacy>=" + privacy + " ORDER BY messages.message_id DESC LIMIT 20", + new Object[]{UID, TID}, Integer.class); + } + } + + public static List getUserRecommendations(JdbcTemplate sql, int UID, int before) { + if (before > 0) { + return sql.queryForList("SELECT message_id FROM favorites " + + "WHERE user_id=? AND message_id getUserPhotos(JdbcTemplate sql, int UID, int privacy, int before) { + if (before > 0) { + return sql.queryForList("SELECT message_id FROM messages WHERE user_id=? AND message_id=" + + privacy + " AND attach IS NOT NULL ORDER BY message_id DESC LIMIT 20", + new Object[] {UID, before}, Integer.class); + } else { + return sql.queryForList("SELECT message_id FROM messages WHERE user_id=? AND privacy>=" + privacy + + " AND attach IS NOT NULL ORDER BY message_id DESC LIMIT 20", + new Object[] {UID}, Integer.class); + } + } + + public static List getUserSearch(JdbcTemplate sql, JdbcTemplate sqlSearch, int UID, String search, int privacy, int before) { + List mids; + + + if (before > 0) { + mids = sqlSearch.queryForList("SELECT id AS message_id FROM messages WHERE user_id=? AND MATCH(?) AND id 0) { + return sql.queryForList("SELECT message_id FROM messages WHERE message_id IN (" + + Utils.convertArrayInt2String(mids) + ") AND privacy>=" + privacy + " ORDER BY message_id DESC", + Integer.class); + } + return mids; + } + + public static List getMessages(JdbcTemplate sql, List mids) { + List msgs = new ArrayList<>(20); + + return sql.query("SELECT messages.message_id,messages.user_id,users.nick," + + "messages_txt.tags,messages.readonly,messages.privacy,messages_txt.txt," + + "TIMESTAMPDIFF(MINUTE,messages.ts,NOW())," + + "messages.ts,messages.replies," + + "messages_txt.repliesby,messages.attach,messages.lat," + + "messages.lon,messages.likes " + + "FROM (messages INNER JOIN messages_txt " + + "ON messages.message_id=messages_txt.message_id) " + + "INNER JOIN users ON messages.user_id=users.id " + + "WHERE messages.message_id " + + "IN (" + Utils.convertArrayInt2String(mids) + ") " + + "ORDER BY messages.message_id DESC", (rs, rowNum) -> { + com.juick.Message msg = new com.juick.Message(); + msg.setUser(new User()); + + msg.setMID(rs.getInt(1)); + msg.getUser().setUID(rs.getInt(2)); + msg.getUser().setUName(rs.getString(3)); + if (rs.getString(4) != null) { + msg.parseTags(rs.getString(4)); + } + msg.ReadOnly = rs.getInt(5) == 1; + msg.Privacy = rs.getInt(6); + msg.FriendsOnly = msg.Privacy < 0; + msg.setText(rs.getString(7)); + msg.TimeAgo = rs.getInt(8); + msg.setDate(rs.getTimestamp(9)); + msg.Replies = rs.getInt(10); + msg.RepliesBy = rs.getString(11); + msg.AttachmentType = rs.getString(12); + if (rs.getDouble(13) != 0) { + msg.Place = new com.juick.Place(); + msg.Place.lat = rs.getDouble(13); + msg.Place.lon = rs.getDouble(14); + } + msg.Likes = rs.getInt(15); + return msg; + }); + } + + public static List getReplies(JdbcTemplate sql, int mid) { + return sql.query("SELECT replies.reply_id,replies.replyto,replies.user_id,users.nick,users.banned,replies.txt," + + "TIMESTAMPDIFF(MINUTE,replies.ts,NOW()),replies.ts,replies.attach FROM replies INNER JOIN users " + + "ON replies.user_id=users.id WHERE replies.message_id=? ORDER BY replies.reply_id ASC", + new Object[]{mid}, (rs, num) -> { + com.juick.Message msg = new com.juick.Message(); + msg.setMID(mid); + msg.setRID(rs.getInt(1)); + msg.ReplyTo = rs.getInt(2); + msg.setUser(new User()); + msg.getUser().setUID(rs.getInt(3)); + msg.getUser().setUName(rs.getString(4)); + msg.getUser().Banned = rs.getBoolean(5); + msg.setText(rs.getString(6)); + msg.TimeAgo = rs.getInt(7); + msg.setDate(rs.getTimestamp(8)); + msg.AttachmentType = rs.getString(9); + return msg; + }); + } + + public static boolean setMessagePopular(JdbcTemplate sql, int mid, int popular) { + boolean ret; + switch (popular) { + case -2: + ret = sql.update("UPDATE messages SET hidden=1 WHERE message_id=?", mid) > 0; + break; + case -1: + ret = sql.update("UPDATE messages SET popular=0 WHERE message_id=?", mid) > 0; + break; + default: + ret = sql.update("UPDATE messages SET popular=? WHERE message_id=?", popular, mid) > 0; + break; + } + if (popular == -1) { + ret = sql.update("INSERT INTO top_ignore_messages VALUES (?)", mid) > 0; + } + return ret; + } + + public static boolean setMessagePrivacy(JdbcTemplate sql, int mid) { + return sql.update("UPDATE messages SET privacy=1 WHERE message_id=?", + mid) > 0; + } + public static boolean deleteMessage(JdbcTemplate sql, int uid, int mid) { + if (sql.update("DELETE FROM messages WHERE message_id=? AND user_id=?", mid, uid) > 0) { + sql.update("DELETE FROM messages_txt WHERE message_id=?", mid); + sql.update("DELETE FROM replies WHERE message_id=?", mid); + sql.update("DELETE FROM subscr_messages WHERE message_id=?", mid); + sql.update("DELETE FROM messages_tags WHERE message_id=?", mid); + return true; + } + return false; + } + +} diff --git a/juick-core/src/main/java/com/juick/server/PMQueries.java b/juick-core/src/main/java/com/juick/server/PMQueries.java new file mode 100644 index 00000000..74d5df3e --- /dev/null +++ b/juick-core/src/main/java/com/juick/server/PMQueries.java @@ -0,0 +1,145 @@ +/* + * 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; + +import com.juick.User; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.util.List; + +/** + * + * @author Ugnich Anton + */ +public class PMQueries { + + public static boolean createPM(JdbcTemplate sql, int uid_from, int uid_to, String body) { + boolean ret = sql.update("INSERT INTO pm(user_id,user_id_to,txt) VALUES (?,?,?)", + uid_from, uid_to, body) > 0; + + if (ret) { + sql.update("INSERT INTO pm_streams(user_id,user_id_to,lastmessage,unread) " + + "VALUES (?,?,NOW(),1) " + + "ON DUPLICATE KEY UPDATE lastmessage=NOW(),unread=unread+1", + uid_from, uid_to); + } + return ret; + } + + public static boolean addPMinRoster(JdbcTemplate sql, int uid, String jid) { + return sql.update("INSERT INTO pm_inroster(user_id,jid) VALUES (?,?)", + uid, jid) > 0; + } + + public static boolean removePMinRoster(JdbcTemplate sql, int uid, String jid) { + return sql.update("DELETE FROM pm_inroster WHERE user_id=? AND jid=?", uid, jid) > 0; + } + + public static boolean havePMinRoster(JdbcTemplate sql, int uid, String jid) { + List res = sql.queryForList("SELECT 1 FROM pm_inroster " + + "WHERE user_id=? AND jid=?", Integer.class, + uid, jid); + return res.size() > 0; + } + + public static String getLastView(JdbcTemplate sql, int uid_from, int uid_to) { + try { + return sql.queryForObject("SELECT lastview FROM pm_streams WHERE user_id=? AND user_id_to=?", String.class, + uid_from, uid_to); + } catch (EmptyResultDataAccessException e) { + return null; + } + } + + public static List getPMLastConversationsUsers(JdbcTemplate sql, int uid, int cnt) { + List qusers = sql.query("SELECT user_id,unread FROM pm_streams " + + "WHERE user_id_to=? " + + "ORDER BY unread DESC, lastmessage DESC LIMIT " + cnt, (rs, rowNum) -> { + com.juick.User u = new com.juick.User(); + u.setUID(rs.getInt(1)); + u.MessagesCount = rs.getInt(2); + return u; + }, uid); + if (!qusers.isEmpty()) { + UserQueries.fillUsersByID(sql, qusers); + } + + return qusers; + } + + public static boolean haveUserInArray(List arr, int uid) { + for (User user : arr) { + if (user.getUID() == uid) { + return true; + } + } + return false; + } + + public static List getPMMessages(JdbcTemplate sql, int uid, int uid_to) { + List msgs = sql.query("SELECT user_id,txt,ts FROM pm " + + "WHERE (user_id=? AND user_id_to=?) " + + "OR (user_id_to=? AND user_id=?) ORDER BY ts DESC LIMIT 20", + (rs, rowNum) -> { + com.juick.Message msg = new com.juick.Message(); + int uuid = rs.getInt(1); + msg.setUser(new User()); + msg.getUser().setUID(uuid); + msg.setText(rs.getString(2)); + msg.setDate(rs.getTimestamp(3)); + return msg; + }, uid, uid_to, uid, uid_to); + + sql.update("UPDATE pm_streams SET lastview=NOW(),unread=0 " + + "WHERE user_id_to=? AND user_id=?", uid, uid_to); + + return msgs; + } + + public static List getLastPMInbox(JdbcTemplate sql, int uid) { + return sql.query("SELECT pm.user_id,users.nick,pm.txt,TIMESTAMPDIFF(MINUTE,pm.ts,NOW()),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().setUName(rs.getString(2)); + msg.setText(rs.getString(3)); + msg.TimeAgo = rs.getInt(4); + msg.setDate(rs.getTimestamp(5)); + return msg; + }, uid); + } + + public static List getLastPMSent(JdbcTemplate sql, int uid) { + return sql.query("SELECT pm.user_id_to,users.nick,pm.txt,TIMESTAMPDIFF(MINUTE,pm.ts,NOW())," + + "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().setUName(rs.getString(2)); + msg.setText(rs.getString(3)); + msg.TimeAgo = rs.getInt(4); + msg.setDate(rs.getTimestamp(5)); + return msg; + }, uid); + } +} diff --git a/juick-core/src/main/java/com/juick/server/PushQueries.java b/juick-core/src/main/java/com/juick/server/PushQueries.java new file mode 100644 index 00000000..54528993 --- /dev/null +++ b/juick-core/src/main/java/com/juick/server/PushQueries.java @@ -0,0 +1,74 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.juick.server; + +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.util.StringUtils; + +import java.util.List; +import java.util.Optional; + +/** + * + * @author ugnich + */ +public class PushQueries { + + public static Optional getAndroidRegID(JdbcTemplate sql, int uid) { + try { + return Optional.of(sql.queryForObject("SELECT regid FROM android WHERE user_id=?", String.class, uid)); + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + } + + } + + public static List getAndroidSubscribers(JdbcTemplate sql, int uid) { + return sql.queryForList("SELECT regid FROM android INNER JOIN subscr_users " + + "ON (subscr_users.user_id=? AND android.user_id=subscr_users.suser_id)", String.class, uid); + } + + public static List getAndroidTokens(JdbcTemplate sql, List uids) { + return sql.queryForList("SELECT regid FROM android INNER JOIN users " + + "ON (users.id=android.user_id) WHERE users.id IN (" + StringUtils.collectionToCommaDelimitedString(uids) + ")", String.class); + } + + public static Optional getWinPhoneURL(JdbcTemplate sql, int uid) { + try { + return Optional.of(sql.queryForObject("SELECT url FROM winphone WHERE user_id=?", String.class, uid)); + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + } + } + + public static List getWinPhoneSubscribers(JdbcTemplate sql, int uid) { + return sql.queryForList("SELECT url FROM winphone INNER JOIN subscr_users " + + "ON (subscr_users.user_id=? AND winphone.user_id=subscr_users.suser_id)", String.class, uid); + } + + public static List getWindowsTokens(JdbcTemplate sql, List uids) { + return sql.queryForList("SELECT url FROM winphone INNER JOIN users " + + "ON (users.id=winphone.user_id) WHERE users.id IN (" + StringUtils.collectionToCommaDelimitedString(uids) + ")", String.class); + } + + public static Optional getAPNSToken(JdbcTemplate sql, int uid) { + try { + return Optional.of(sql.queryForObject("SELECT token from ios WHERE user_id=?", String.class, uid)); + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + } + } + + public static List getAPNSSubscribers(JdbcTemplate sql, int uid) { + return sql.queryForList("SELECT token FROM ios INNER JOIN subscr_users " + + "ON (subscr_users.user_id=? AND ios.user_id=subscr_users.suser_id)", String.class, uid); + } + public static List getAPNSTokens(JdbcTemplate sql, List uids) { + return sql.queryForList("SELECT token FROM ios INNER JOIN users " + + "ON (users.id=ios.user_id) WHERE users.id IN (" + StringUtils.collectionToCommaDelimitedString(uids) + ")", String.class); + } + +} diff --git a/juick-core/src/main/java/com/juick/server/SQLHelpers.java b/juick-core/src/main/java/com/juick/server/SQLHelpers.java new file mode 100644 index 00000000..13ebe98a --- /dev/null +++ b/juick-core/src/main/java/com/juick/server/SQLHelpers.java @@ -0,0 +1,168 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.juick.server; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author ugnich + */ +public class SQLHelpers { + + private static final Logger logger = Logger.getLogger(SQLHelpers.class.getName()); + + public static int execute(Connection sql, String query) { + int ret = -1; + PreparedStatement stmt = null; + try { + stmt = sql.prepareStatement(query); + ret = stmt.executeUpdate(); + } catch (SQLException e) { + logger.log(Level.SEVERE, "sql exception", e); + } finally { + Utils.finishSQL(null, stmt); + } + return ret; + } + + public static int executeInt(Connection sql, String query, int param) { + int ret = -1; + PreparedStatement stmt = null; + try { + stmt = sql.prepareStatement(query); + stmt.setInt(1, param); + ret = stmt.executeUpdate(); + } catch (SQLException e) { + logger.log(Level.SEVERE, "sql exception", e); + } finally { + Utils.finishSQL(null, stmt); + } + return ret; + } + + public static int getInt(Connection sql, String query, int defvalue) { + int ret = defvalue; + PreparedStatement stmt = null; + ResultSet rs = null; + try { + stmt = sql.prepareStatement(query); + rs = stmt.executeQuery(); + if (rs.first()) { + ret = rs.getInt(1); + } + } catch (SQLException e) { + logger.log(Level.SEVERE, "sql exception", e); + } finally { + Utils.finishSQL(rs, stmt); + } + return ret; + } + + public static int getInt(Connection sql, String query, int param, int defvalue) { + int ret = defvalue; + PreparedStatement stmt = null; + ResultSet rs = null; + try { + stmt = sql.prepareStatement(query); + stmt.setInt(1, param); + rs = stmt.executeQuery(); + if (rs.first()) { + ret = rs.getInt(1); + } + } catch (SQLException e) { + logger.log(Level.SEVERE, "sql exception", e); + } finally { + Utils.finishSQL(rs, stmt); + } + return ret; + } + + public static int getInt(Connection sql, String query, String param, int defvalue) { + int ret = defvalue; + PreparedStatement stmt = null; + ResultSet rs = null; + try { + stmt = sql.prepareStatement(query); + stmt.setString(1, param); + rs = stmt.executeQuery(); + if (rs.first()) { + ret = rs.getInt(1); + } + } catch (SQLException e) { + logger.log(Level.SEVERE, "sql exception", e); + } finally { + Utils.finishSQL(rs, stmt); + } + return ret; + } + + public static String getString(Connection sql, String query, int param) { + String ret = null; + PreparedStatement stmt = null; + ResultSet rs = null; + try { + stmt = sql.prepareStatement(query); + stmt.setInt(1, param); + rs = stmt.executeQuery(); + if (rs.first()) { + ret = rs.getString(1); + } + } catch (SQLException e) { + logger.log(Level.SEVERE, "sql exception", e); + } finally { + Utils.finishSQL(rs, stmt); + } + return ret; + } + + public static String getString(Connection sql, String query, String param) { + String ret = null; + PreparedStatement stmt = null; + ResultSet rs = null; + try { + stmt = sql.prepareStatement(query); + stmt.setString(1, param); + rs = stmt.executeQuery(); + if (rs.first()) { + ret = rs.getString(1); + } + } catch (SQLException e) { + logger.log(Level.SEVERE, "sql exception", e); + } finally { + Utils.finishSQL(rs, stmt); + } + return ret; + } + + public static List getArrayInteger(Connection sql, String query, int param) { + List ret = new ArrayList<>(); + + PreparedStatement stmt = null; + ResultSet rs = null; + try { + stmt = sql.prepareStatement(query); + stmt.setInt(1, param); + rs = stmt.executeQuery(); + rs.beforeFirst(); + while (rs.next()) { + ret.add(rs.getInt(1)); + } + } catch (SQLException e) { + logger.log(Level.SEVERE, "sql exception", e); + } finally { + Utils.finishSQL(rs, stmt); + } + + return ret; + } +} diff --git a/juick-core/src/main/java/com/juick/server/ShowQueries.java b/juick-core/src/main/java/com/juick/server/ShowQueries.java new file mode 100644 index 00000000..3ddff412 --- /dev/null +++ b/juick-core/src/main/java/com/juick/server/ShowQueries.java @@ -0,0 +1,32 @@ +package com.juick.server; + +import com.juick.User; + +import java.util.List; +import org.springframework.jdbc.core.JdbcTemplate; + +/** + * Created by vt on 10/01/16. + */ +public class ShowQueries { + + public static List getRecommendedUsers(JdbcTemplate sql, User forUser) { + return sql.queryForList("SELECT users.nick FROM subscr_users INNER JOIN users " + + "ON subscr_users.user_id=users.id " + + "WHERE subscr_users.user_id NOT IN (SELECT user_id FROM subscr_users WHERE suser_id=?) " + + "AND subscr_users.suser_id IN (SELECT user_id FROM subscr_users WHERE suser_id=?) " + + "AND subscr_users.user_id NOT IN (SELECT bl_user_id FROM bl_users WHERE user_id=?) " + + "AND subscr_users.user_id!=? AND users.lastmessage>UNIX_TIMESTAMP()-259200 " + + "GROUP BY subscr_users.user_id ORDER BY count(*) DESC LIMIT 10", + String.class, new Object[] {forUser.getUID(), forUser.getUID(), forUser.getUID(), forUser.getUID()}); + } + + public static List getTopUsers(JdbcTemplate sql) { + return sql.queryForList("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", + String.class); + } +} diff --git a/juick-core/src/main/java/com/juick/server/SubscriptionsQueries.java b/juick-core/src/main/java/com/juick/server/SubscriptionsQueries.java new file mode 100644 index 00000000..7c30285b --- /dev/null +++ b/juick-core/src/main/java/com/juick/server/SubscriptionsQueries.java @@ -0,0 +1,104 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.juick.server; + +import com.juick.Tag; +import com.juick.User; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +/** + * + * @author ugnich + */ +public class SubscriptionsQueries { + + private static final Logger logger = Logger.getLogger(SubscriptionsQueries.class.getName()); + + public static List getJIDSubscribedToUser(JdbcTemplate sql, int uid, boolean friendsonly) { + if (friendsonly == false) { + return sql.queryForList("SELECT jids.jid FROM subscr_users INNER JOIN jids " + + "ON (subscr_users.user_id=? AND subscr_users.suser_id=jids.user_id) WHERE jids.active=1", + String.class, uid); + } else { + return sql.queryForList("SELECT jids.jid FROM subscr_users INNER JOIN jids " + + "ON (subscr_users.user_id=? AND subscr_users.suser_id=jids.user_id) WHERE jids.active=1 " + + "AND jids.user_id IN (SELECT wl_user_id FROM wl_users WHERE user_id=?)", String.class, uid, uid); + } + } + + public static List getSubscribedUsers(JdbcTemplate sql, int uid, int mid) { + List userids = UserQueries.getUserReaders(sql, uid); + Set set = new HashSet<>(); + set.addAll(userids.stream().map(User::getUID).collect(Collectors.toList())); + List tags = MessagesQueries.getMessageTagsIDs(sql, mid); + if (tags.size() > 0) { + List tagUsers = sql.queryForList("SELECT suser_id FROM subscr_tags " + + "WHERE tag_id IN (" + StringUtils.arrayToCommaDelimitedString(tags.toArray()) + ") AND suser_id!=" + uid, Integer.class); + set.addAll(tagUsers); + } + return UserQueries.getUsersByID(sql, new ArrayList<>(set)); + } + + public static List getUsersSubscribedToComments(JdbcTemplate sql, int mid, int ignore_uid) { + List userids = sql.queryForList("SELECT suser_id FROM subscr_messages WHERE message_id=? AND suser_id!=?", + new Object[] {mid, ignore_uid}, Integer.class); + if (userids.size() > 0) { + return UserQueries.getUsersByID(sql, userids); + } else { + return new ArrayList<>(); + } + } + + public static List getUsersSubscribedToUserRecommendations(JdbcTemplate sql, int uid, int mid, int muid) { + List tags = MessagesQueries.getMessageTagsIDs(sql, mid); + + String query = "SELECT suser_id FROM subscr_users WHERE user_id=" + uid; + query += " AND user_id NOT IN (SELECT user_id FROM bl_users WHERE bl_user_id=" + muid + ")"; + query += " AND user_id NOT IN (SELECT suser_id FROM subscr_users WHERE user_id=" + muid + ")"; + query += " AND user_id NOT IN (SELECT suser_id FROM subscr_messages WHERE message_id=" + mid + ")"; + query += " AND user_id NOT IN (SELECT user_id FROM favorites WHERE message_id=" + mid + ")"; + query += " AND user_id NOT IN (SELECT subscr_users.suser_id FROM subscr_users INNER JOIN favorites ON (favorites.message_id=" + mid + " AND subscr_users.user_id=favorites.user_id AND favorites.user_id!=" + uid + "))"; + if (!tags.isEmpty()) { + String tagsStr = Utils.convertArrayInt2String(tags); + query += " AND user_id NOT IN (SELECT suser_id FROM subscr_tags WHERE tag_id IN (" + tagsStr + "))"; + query += " AND user_id NOT IN (SELECT user_id FROM bl_tags WHERE tag_id IN (" + tagsStr + "))"; + } + List userids = sql.queryForList(query, Integer.class); + return UserQueries.getUsersByID(sql, userids); + } + + public static boolean subscribeMessage(JdbcTemplate sql, int mid, int vuid) { + return sql.update("INSERT IGNORE INTO subscr_messages(suser_id,message_id) VALUES (" + vuid + "," + mid + ")") == 1; + } + public static boolean unSubscribeMessage(JdbcTemplate sql, int mid, int vuid) { + return sql.update("DELETE FROM subscr_messages WHERE message_id=? AND suser_id=?", + mid, vuid) > 0; + } + public static boolean subscribeUser(JdbcTemplate sql, User user, User toUser) { + return sql.update("INSERT IGNORE INTO subscr_users(user_id,suser_id) VALUES (?,?)", + user.getUID(), toUser.getUID()) == 1; + } + public static boolean unSubscribeUser(JdbcTemplate sql, User user, User fromUser) { + return sql.update("DELETE FROM subscr_users WHERE suser_id=? AND user_id=?", + user.getUID(), fromUser.getUID()) > 0; + } + public static boolean subscribeTag(JdbcTemplate sql, User user, Tag toTag) { + return sql.update("INSERT IGNORE INTO subscr_tags(tag_id,suser_id) VALUES (?,?)", + toTag.TID, user.getUID()) == 1; + } + public static boolean unSubscribeTag(JdbcTemplate sql, User user, Tag toTag) { + return sql.update("DELETE FROM subscr_tags WHERE tag_id=? AND suser_id=?", + toTag.TID, user.getUID()) > 0; + } + +} diff --git a/juick-core/src/main/java/com/juick/server/TagQueries.java b/juick-core/src/main/java/com/juick/server/TagQueries.java new file mode 100644 index 00000000..2258a8ea --- /dev/null +++ b/juick-core/src/main/java/com/juick/server/TagQueries.java @@ -0,0 +1,166 @@ +/* + * 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; + +import com.juick.Tag; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; + +import java.sql.PreparedStatement; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * + * @author Ugnich Anton + */ +public class TagQueries { + + public static com.juick.Tag getTag(JdbcTemplate sql, int tid) { + try { + return sql.queryForObject("SELECT synonym_id,name FROM tags WHERE tag_id=?", + new Object[]{tid}, (rs, num) -> { + Tag ret = new Tag(); + ret.TID = tid; + ret.SynonymID = rs.getInt(1); + ret.Name = rs.getString(2); + return ret; + }); + } catch (EmptyResultDataAccessException e) { + return null; + } + } + + public static com.juick.Tag getTag(JdbcTemplate sql, String tag, boolean autoCreate) { + Tag ret = null; + try { + ret = sql.queryForObject("SELECT tag_id,synonym_id,name FROM tags WHERE name=?", + new Object[]{tag}, (rs, rowNum) -> { + Tag ret1 = new Tag(); + ret1.TID = rs.getInt(1); + ret1.SynonymID = rs.getInt(2); + ret1.Name = rs.getString(3); + return ret1; + }); + } catch (EmptyResultDataAccessException e) { + // tag not found + } + if (ret == null && autoCreate) { + ret = new com.juick.Tag(); + ret.TID = createTag(sql, tag); + ret.Name = tag; + } + + return ret; + } + + public static List getTags(JdbcTemplate sql, String[] tags, boolean autoCreate) { + List ret = new ArrayList<>(); + + for (String tag : tags) { + if (!tag.isEmpty()) { + Tag t = getTag(sql, tag, autoCreate); + if (t != null) { + ret.add(t); + } + } + } + + return ret; + } + + public static boolean getTagNoIndex(JdbcTemplate sql, int tag_id) { + try { + return sql.queryForObject("SELECT noindex FROM tags WHERE tag_id=?", Integer.class, tag_id) == 1; + } catch (EmptyResultDataAccessException e) { + return false; + } + } + + public static int createTag(JdbcTemplate sql, String name) { + KeyHolder holder = new GeneratedKeyHolder(); + sql.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(); + } + + public static List getUserTagsAll(JdbcTemplate sql, int uid) { + return sql.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", + (rs, rowNum) -> { + Tag t = new Tag(); + t.Name = rs.getString(1); + t.UsageCnt = rs.getInt(2); + return t; + }, uid); + } + + public static List getUserBLTags(JdbcTemplate sql, int uid) { + return sql.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); + } + + public static List getPopularTags(JdbcTemplate sql) { + return sql.queryForList("SELECT name FROM tags WHERE top=1 ORDER BY name ASC", String.class); + } + public static List updateTags(JdbcTemplate sql, int mid, List newTags) { + List currentTags = MessagesQueries.getMessageTags(sql, mid); + newTags.stream().filter(currentTags::contains) + .forEach(t -> sql.update("DELETE FROM messages_tags WHERE message_id=? AND tag_id=?", mid, t.TID)); + newTags.stream().filter(t -> !currentTags.contains(t)) + .forEach(t -> sql.update("INSERT INTO messages_tags(message_id,tag_id) VALUES (?,?)", mid, t.TID)); + return MessagesQueries.getMessageTags(sql, mid); + } + + public static List fromString(JdbcTemplate sql, String txt, boolean tagsOnly) { + String patternString = tagsOnly ? "^(?:(?:\\*[^ \\r\\n\\t]+)|\\s)+$" : "^\\*([^ \\r\\n\\t]+)\\s+([\\s\\S]+)"; + Pattern tagsPattern = Pattern.compile(patternString); + if (tagsPattern.matcher(txt).matches()) { + Pattern tagPattern = Pattern.compile("\\*([^ \\r\\n\\t]+)"); + Matcher tagMatcher = tagPattern.matcher(txt); + List tags = new ArrayList<>(); + // TODO: process readonly, private, friends, public + while (tagMatcher.find()) { + for (int i = 1; i <= tagMatcher.groupCount(); i++) { + tags.add(getTag(sql, tagMatcher.group(i), true)); + } + } + return tags; + } + return Collections.emptyList(); + } + public static String toString(List tags) { + return tags.stream().map(t -> " *" + t.Name) + .collect(Collectors.joining()); + } +} diff --git a/juick-core/src/main/java/com/juick/server/UserQueries.java b/juick-core/src/main/java/com/juick/server/UserQueries.java new file mode 100644 index 00000000..db380d7a --- /dev/null +++ b/juick-core/src/main/java/com/juick/server/UserQueries.java @@ -0,0 +1,440 @@ +/* + * 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; + +import com.juick.User; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; +import org.springframework.util.StringUtils; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.*; + +/** + * + * @author Ugnich Anton + */ +public class UserQueries { + + static final String ABCDEF = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + public static class UserMapper implements RowMapper { + @Override + public User mapRow(ResultSet rs, int rowNum) throws SQLException { + User user = new User(); + user.setUID(rs.getInt(1)); + user.setUName(rs.getString(2)); + user.Banned = rs.getBoolean(3); + return user; + } + } + + public static String getSignUpHashByJID(JdbcTemplate sql, String jid) { + String hash = sql.queryForObject("SELECT loginhash FROM jids WHERE jid=? AND user_id IS NULL", + String.class, jid); + if (hash == null) { + hash = UUID.randomUUID().toString(); + sql.update("INSERT INTO jids(jid,loginhash) VALUES (?,?)", jid, hash); + } + return hash; + } + public static String getSignUpHashByTelegramID(JdbcTemplate sql, Long telegramId, String username) { + try { + return sql.queryForObject("SELECT loginhash FROM telegram WHERE tg_id=? AND user_id IS NULL", + String.class, telegramId); + } catch (EmptyResultDataAccessException e) { + String hash = UUID.randomUUID().toString(); + sql.update("INSERT INTO telegram(tg_id, loginhash, tg_name) VALUES (?, ?, ?)", telegramId, hash, username); + return hash; + } + } + + public static int createUser(JdbcTemplate sql, String username, String password) { + KeyHolder holder = new GeneratedKeyHolder(); + sql.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); + + int uid = holder.getKey().intValue(); + + sql.update("INSERT INTO useroptions(user_id) VALUES (?)", uid); + sql.update("INSERT INTO subscr_users(user_id,suser_id) VALUES (2,?)", uid); + + return uid; + } + + public static Optional getUserByUID(JdbcTemplate sql, int uid) { + try { + return Optional.of(sql.queryForObject("SELECT id, nick,banned FROM users WHERE id=?", + new UserMapper(), uid)); + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + } + } + + public static User getUserByName(JdbcTemplate sql, String username) { + try { + return sql.queryForObject("SELECT id,nick,banned FROM users WHERE nick=?", + new UserMapper(), + username); + } catch (EmptyResultDataAccessException e) { + return null; + } + } + + public static User getUserByJID(JdbcTemplate sql, String jid) { + try { + return sql.queryForObject("SELECT id,nick,banned FROM users WHERE id=(SELECT user_id FROM jids WHERE jid=?)", + new UserMapper(), jid); + } catch (EmptyResultDataAccessException e) { + return null; + } + } + + public static List getUsersByName(JdbcTemplate sql, List unames) { + if (!unames.isEmpty()) { + return sql.query("SELECT id,nick,banned FROM users WHERE nick IN (" + Utils.convertArrayString2String(unames) + ")", + new UserMapper()); + } + return Collections.emptyList(); + } + + public static List getUsersByID(JdbcTemplate sql, List uids) { + if (!uids.isEmpty()) { + return sql.query("SELECT id,nick,banned FROM users WHERE id IN (" + Utils.convertArrayInt2String(uids) + ")", + new UserMapper()); + } + return Collections.emptyList(); + } + + public static boolean fillUsersByID(JdbcTemplate sql, List users) { + boolean ret = false; + + String uids = ""; + final int usersSize = users.size(); + for (int i = 0; i < usersSize; i++) { + if (i > 0) { + uids += ","; + } + uids += users.get(i).getUID(); + } + + sql.query("SELECT id,nick,banned FROM users WHERE id IN (" + uids + ")", + (rs, num) -> { + User u = new User(); + u.setUID(rs.getInt(1)); + u.setUName(rs.getString(2)); + return u; + }); + + return true; + } + + public static List getUsersByJID(JdbcTemplate sql, List jids) { + return sql.query("SELECT users.id,users.nick,jids.jid FROM users " + + "INNER JOIN jids ON jids.user_id=users.id " + + "WHERE jids.jid IN (" + Utils.convertArrayString2String(jids) + ")", + (rs, rowNum) -> { + com.juick.User user = new com.juick.User(); + user.setUID(rs.getInt(1)); + user.setUName(rs.getString(2)); + user.setJID(rs.getString(3)); + return user; + }); + } + + public static List getJIDsbyUID(JdbcTemplate sql, int uid) { + return sql.queryForList("SELECT jid FROM jids WHERE user_id=? AND active=1", String.class, uid); + } + + public static int getUIDbyJID(JdbcTemplate sql, String jid) { + return sql.queryForObject("SELECT user_id FROM jids WHERE jid=?", Integer.class, jid); + } + + public static int getUIDbyName(JdbcTemplate sql, String uname) { + try { + return sql.queryForObject("SELECT id FROM users WHERE nick=?", Integer.class, uname); + } catch (EmptyResultDataAccessException e) { + return 0; + } + } + + public static int getUIDbyHash(JdbcTemplate sql, String hash) { + try { + return sql.queryForObject("SELECT user_id FROM logins WHERE hash=?", Integer.class, hash); + } catch (EmptyResultDataAccessException e) { + return 0; + } + } + + public static com.juick.User getUserByHash(JdbcTemplate sql, String hash) { + try { + User user = sql.queryForObject("SELECT logins.user_id,users.nick, users.banned FROM logins " + + "INNER JOIN users ON logins.user_id=users.id WHERE logins.hash=?", + new UserMapper(), hash); + user.setAuthHash(hash); + return user; + } catch (EmptyResultDataAccessException e) { + return new User(); + } + } + + public static String getHashByUID(JdbcTemplate sql, int uid) { + try { + return sql.queryForObject("SELECT hash FROM logins WHERE user_id=?", String.class, uid); + } catch (EmptyResultDataAccessException e){ + String hash = generateHash(16); + sql.update(con -> { + PreparedStatement stmt = con.prepareStatement("INSERT INTO logins(user_id,hash) VALUES (?,?)"); + stmt.setInt(1, uid); + stmt.setString(2, hash); + return stmt; + }); + return hash; + } + } + + public static String generateHash(int len) { + Random rnd = new Random(); + StringBuilder sb = new StringBuilder(len); + for (int i = 0; i < len; i++) { + sb.append(ABCDEF.charAt(rnd.nextInt(ABCDEF.length()))); + } + return sb.toString(); + } + + public static boolean checkUserNameValid(String uname) { + return uname != null && uname.length() >= 2 && uname.length() <= 16 && uname.matches("[a-zA-Z0-9\\-]+"); + } + + public static int checkPassword(JdbcTemplate sql, String username, String password) { + try { + String realPassword = sql.queryForObject("SELECT passw FROM users WHERE nick=?", String.class, username); + if (realPassword.equals(password)) { + User user = UserQueries.getUserByName(sql, username); + if (user != null) { + return user.getUID(); + } else { + return -1; + } + } else { + return -1; + } + } catch (EmptyResultDataAccessException e) { + return -1; + } + } + + public static int getUserOptionInt(JdbcTemplate sql, int uid, String option, int defaultValue) { + try { + return sql.queryForObject("SELECT " + option + " FROM useroptions WHERE user_id=?", Integer.class, uid); + } catch (EmptyResultDataAccessException e) { + return defaultValue; + } + } + + public static void setUserOptionInt(JdbcTemplate sql, int uid, String option, int value) { + sql.update("UPDATE useroptions SET " + option + "=? WHERE user_id=?", value, uid); + } + + public static boolean getCanMedia(JdbcTemplate sql, int uid) { + try { + int res = sql.queryForObject("SELECT users.lastphoto-UNIX_TIMESTAMP() FROM users WHERE id=?", + Integer.class, uid); + return res < 3600; + } catch (EmptyResultDataAccessException e) { + return false; + } + } + + public static boolean isInWL(JdbcTemplate sql, int uid, int check) { + try { + return sql.queryForObject("SELECT 1 FROM wl_users WHERE user_id=? AND wl_user_id=?", + Integer.class, uid, check) == 1; + } catch (EmptyResultDataAccessException e) { + return false; + } + } + + public static boolean isInBL(JdbcTemplate sql, int uid, int check) { + try { + return sql.queryForObject("SELECT 1 FROM bl_users WHERE user_id=? AND bl_user_id=?", + Integer.class, uid, check) == 1; + } catch (EmptyResultDataAccessException e) { + return false; + } + } + + public static boolean isInBLAny(JdbcTemplate sql, int uid, int uid2) { + try { + return sql.queryForObject("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) == 1; + } catch (EmptyResultDataAccessException e) { + return false; + } + } + + public static List checkBL(JdbcTemplate sql, int visitor, List uids) { + if (!uids.isEmpty()) { + return sql.queryForList("SELECT user_id FROM bl_users WHERE bl_user_id=? and user_id IN (" + + StringUtils.collectionToCommaDelimitedString(uids) + ")", Integer.class, visitor); + } else { + return new ArrayList<>(); + } + } + + public static boolean isSubscribed(JdbcTemplate sql, int uid, int check) { + try { + return sql.queryForObject("SELECT 1 FROM subscr_users WHERE suser_id=? AND user_id=?", + Integer.class, uid, check) == 1; + } catch (EmptyResultDataAccessException e) { + return false; + } + } + + public static List getUserRead(JdbcTemplate sql, int uid) { + return sql.queryForList("SELECT user_id FROM subscr_users WHERE suser_id=?", Integer.class, uid); + } + + public static List getUserReadLeastPopular(JdbcTemplate sql, int uid, int cnt) { + return sql.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.setUName(rs.getString(2)); + return u; + }, uid, cnt); + } + + public static List getUserReaders(JdbcTemplate sql, int uid) { + return sql.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.setUName(rs.getString(2)); + return u; + }, uid); + } + + public static List getUserFriends(JdbcTemplate sql, int uid) { + return sql.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.setUName(rs.getString(2)); + return u; + }, uid, uid); + } + + public static List getUserBLUsers(JdbcTemplate sql, int uid) { + return sql.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.setUName(rs.getString(2)); + return u; + }, uid); + } + + public static boolean linkTwitterAccount(JdbcTemplate sql, User user, String accessToken, + String accessTokenSecret, String screenName) { + if (sql.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) { + return sql.update("INSERT INTO subscr_users(user_id,suser_id,jid) " + + "VALUES (?,1741,'juick\\@twitter.juick.com')", user.getUID()) > 0; + } + return false; + + } + + public static int getStatsIRead(JdbcTemplate sql, int uid) { + try { + return sql.queryForObject("SELECT COUNT(*) FROM subscr_users WHERE suser_id=?", Integer.class, uid); + } catch (EmptyResultDataAccessException e) { + return 0; + } + } + + public static int getStatsMyReaders(JdbcTemplate sql, int uid) { + try { + return sql.queryForObject("SELECT COUNT(*) FROM subscr_users WHERE user_id=?", Integer.class, uid); + } catch (EmptyResultDataAccessException e) { + return 0; + } + } + + public static int getStatsMessages(JdbcTemplate sql, int uid) { + try { + return sql.queryForObject("SELECT COUNT(*) FROM messages WHERE user_id=?", Integer.class, uid); + } catch (EmptyResultDataAccessException e) { + return 0; + } + } + + public static int getStatsReplies(JdbcTemplate sql, int uid) { + try { + return sql.queryForObject("SELECT COUNT(*) FROM replies WHERE user_id=?", Integer.class, uid); + } catch (EmptyResultDataAccessException e) { + return 0; + } + } + + public enum ActiveStatus { + Inactive, + Active + } + + public static boolean setActiveStatusForJID(JdbcTemplate sql, String JID, ActiveStatus jidStatus) { + User user = getUserByJID(sql, JID); + if (user != null) { + return sql.update(con -> { + PreparedStatement preparedStatement = con.prepareStatement( + "UPDATE jids SET active=? WHERE user_id=? AND jid=?"); + int newStatus = jidStatus == ActiveStatus.Active ? 1 : 0; + preparedStatement.setInt(1, newStatus); + preparedStatement.setInt(2, user.getUID()); + preparedStatement.setString(3, JID); + return preparedStatement; + + }) >= 0; + } + return false; + } +} diff --git a/juick-core/src/main/java/com/juick/server/Utils.java b/juick-core/src/main/java/com/juick/server/Utils.java new file mode 100644 index 00000000..1968150e --- /dev/null +++ b/juick-core/src/main/java/com/juick/server/Utils.java @@ -0,0 +1,86 @@ +/* + * 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; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; + +/** + * + * @author Ugnich Anton + */ +public class Utils { + + public static String convertArrayInt2String(List mids) { + String q = ""; + for (int i = 0; i < mids.size(); i++) { + if (i > 0) { + q += ","; + } + q += mids.get(i); + } + return q; + } + + public static String convertArrayString2String(List unames) { + String q = ""; + for (int i = 0; i < unames.size(); i++) { + if (i > 0) { + q += ","; + } + q += "\"" + unames.get(i) + "\""; + } + return q; + } + + public static String buildQueryArray(String query1, int length, String query2) { + String ret = query1; + for (int i = 0; i < length; i++) { + if (i > 0) { + ret += ","; + } + ret += "?"; + } + ret += query2; + return ret; + } + + public static void stmtSetStringArray(PreparedStatement stmt, int offset, String strs[]) throws SQLException { + for (int i = 0; i < strs.length; i++) { + stmt.setString(offset + i, strs[i]); + } + } + + public static void finishSQL(ResultSet rs, Statement stmt) { + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + } + } + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException e) { + } + } + } +} diff --git a/juick-core/src/main/java/com/juick/server/helpers/PrivacyOpts.java b/juick-core/src/main/java/com/juick/server/helpers/PrivacyOpts.java new file mode 100644 index 00000000..66cf9410 --- /dev/null +++ b/juick-core/src/main/java/com/juick/server/helpers/PrivacyOpts.java @@ -0,0 +1,29 @@ +package com.juick.server.helpers; + +/** + * Created by vt on 16/01/16. + */ +public class PrivacyOpts { + private int uid; + private int privacy; + + public PrivacyOpts() { + + } + + public int getUid() { + return uid; + } + + public void setUid(int uid) { + this.uid = uid; + } + + public int getPrivacy() { + return privacy; + } + + public void setPrivacy(int privacy) { + this.privacy = privacy; + } +} diff --git a/juick-core/src/main/java/com/juick/xmpp/extensions/JuickMessage.java b/juick-core/src/main/java/com/juick/xmpp/extensions/JuickMessage.java new file mode 100644 index 00000000..885b2375 --- /dev/null +++ b/juick-core/src/main/java/com/juick/xmpp/extensions/JuickMessage.java @@ -0,0 +1,184 @@ +/* + * 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.xmpp.extensions; + +import com.juick.xmpp.utils.XmlUtils; +import com.juick.xmpp.*; +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.TimeZone; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +/** + * + * @author Ugnich Anton + */ +public class JuickMessage extends com.juick.Message implements StanzaChild { + + public final static String XMLNS = "http://juick.com/message"; + public final static String TagName = "juick"; + private SimpleDateFormat df; + + public JuickMessage() { + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + public JuickMessage(com.juick.Message msg) { + super(msg); + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + @Override + public String getXMLNS() { + return XMLNS; + } + + @Override + public JuickMessage parse(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException { + JuickMessage jmsg = new JuickMessage(); + + final String sMID = parser.getAttributeValue(null, "mid"); + if (sMID != null) { + jmsg.setMID(Integer.parseInt(sMID)); + } + final String sRID = parser.getAttributeValue(null, "rid"); + if (sRID != null) { + jmsg.setRID(Integer.parseInt(sRID)); + } + final String sReplyTo = parser.getAttributeValue(null, "replyto"); + if (sReplyTo != null) { + jmsg.ReplyTo = Integer.parseInt(sReplyTo); + } + final String sPrivacy = parser.getAttributeValue(null, "privacy"); + if (sPrivacy != null) { + jmsg.Privacy = Integer.parseInt(sPrivacy); + } + final String sFriendsOnly = parser.getAttributeValue(null, "friendsonly"); + if (sFriendsOnly != null) { + jmsg.FriendsOnly = true; + } + final String sReadOnly = parser.getAttributeValue(null, "readonly"); + if (sReadOnly != null) { + jmsg.ReadOnly = true; + } + String ts = parser.getAttributeValue(null, "timestamp"); + if (ts != null) { + jmsg.setDate(df.parse(ts)); + } + jmsg.AttachmentType = parser.getAttributeValue(null, "attach"); + + while (parser.next() == XmlPullParser.START_TAG) { + final String tag = parser.getName(); + final String xmlns = parser.getNamespace(); + if (tag.equals("body")) { + jmsg.setText(XmlUtils.getTagText(parser)); + } else if (tag.equals(JuickUser.TagName) && xmlns != null && xmlns.equals(JuickUser.XMLNS)) { + jmsg.setUser(new JuickUser().parse(parser)); + } else if (tag.equals("tag")) { + jmsg.Tags.add(XmlUtils.getTagText(parser)); + } else { + XmlUtils.skip(parser); + } + } + return jmsg; + } + + @Override + public String toString() { + String ret = ""; + + ret = "<" + TagName + " xmlns=\"" + XMLNS + "\""; + if (getMID() > 0) { + ret += " mid=\"" + getMID() + "\""; + } + if (getRID() > 0) { + ret += " rid=\"" + getRID() + "\""; + } + if (ReplyTo > 0) { + ret += " replyto=\"" + ReplyTo + "\""; + } + ret += " privacy=\"" + Privacy + "\""; + if (FriendsOnly) { + ret += " friendsonly=\"1\""; + } + if (ReadOnly) { + ret += " readonly=\"1\""; + } + if (getDate() != null) { + ret += " ts=\"" + df.format(getDate()) + "\""; + } + if (AttachmentType != null) { + ret += " attach=\"" + AttachmentType + "\""; + } + ret += ">"; + if (getUser() != null) { + ret += JuickUser.toString(getUser()); + } + if (getText() != null) { + ret += "" + XmlUtils.escape(getText()) + ""; + } + if (!Tags.isEmpty()) { + for (int i = 0; i < Tags.size(); i++) { + ret += "" + XmlUtils.escape(Tags.get(i)) + ""; + } + } + ret += ""; + + return ret; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof JuickMessage)) { + return false; + } + JuickMessage jmsg = (JuickMessage) obj; + return (this.getMID() == jmsg.getMID() && this.getRID() == jmsg.getRID()); + } + + @Override + public int compareTo(Object obj) throws ClassCastException { + if (!(obj instanceof JuickMessage)) { + throw new ClassCastException(); + } + JuickMessage jmsg = (JuickMessage) obj; + + if (this.getMID() != jmsg.getMID()) { + if (this.getMID() > jmsg.getMID()) { + return -1; + } else { + return 1; + } + } + + if (this.getRID() != jmsg.getRID()) { + if (this.getRID() < jmsg.getRID()) { + return -1; + } else { + return 1; + } + } + + return 0; + } +} diff --git a/juick-core/src/main/java/com/juick/xmpp/extensions/JuickUser.java b/juick-core/src/main/java/com/juick/xmpp/extensions/JuickUser.java new file mode 100644 index 00000000..edc6749a --- /dev/null +++ b/juick-core/src/main/java/com/juick/xmpp/extensions/JuickUser.java @@ -0,0 +1,75 @@ +/* + * 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.xmpp.extensions; + +import com.juick.xmpp.utils.XmlUtils; +import com.juick.xmpp.*; +import java.io.IOException; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +/** + * + * @author Ugnich Anton + */ +public class JuickUser extends com.juick.User implements StanzaChild { + + public final static String XMLNS = "http://juick.com/user"; + public final static String TagName = "user"; + + public JuickUser() { + } + + public JuickUser(com.juick.User user) { + super(user); + } + + @Override + public String getXMLNS() { + return XMLNS; + } + + @Override + public JuickUser parse(final XmlPullParser parser) throws XmlPullParserException, IOException { + JuickUser juser = new JuickUser(); + String strUID = parser.getAttributeValue(null, "uid"); + if (strUID != null) { + juser.setUID(Integer.parseInt(strUID)); + } + juser.setUName(parser.getAttributeValue(null, "uname")); + XmlUtils.skip(parser); + return juser; + } + + public static String toString(com.juick.User user) { + String str = "<" + TagName + " xmlns='" + XMLNS + "'"; + if (user.getUID() > 0) { + str += " uid='" + user.getUID() + "'"; + } + if (user.getUName() != null && user.getUName().length() > 0) { + str += " uname='" + XmlUtils.escape(user.getUName()) + "'"; + } + str += "/>"; + return str; + } + + @Override + public String toString() { + return toString(this); + } +} diff --git a/juick-www/build.gradle b/juick-www/build.gradle new file mode 100644 index 00000000..c71b682f --- /dev/null +++ b/juick-www/build.gradle @@ -0,0 +1,88 @@ +buildscript { + repositories { + mavenCentral() + jcenter() + } + dependencies { + classpath 'com.eriwen:gradle-css-plugin:1.11.1' + classpath 'com.eriwen:gradle-js-plugin:1.12.1' + classpath 'org.akhikhl.gretty:gretty:+' + classpath 'com.tkruse.gradle:gradle-groovysh-plugin:1.0.8' + } +} +apply plugin: 'java' +apply plugin: 'war' +apply plugin: 'css' +apply plugin: 'org.akhikhl.gretty' +apply plugin: 'com.eriwen.gradle.js' +apply plugin: 'com.github.tkruse.groovysh' + +repositories { + mavenCentral() +} + +dependencies { + compile project(':juick-core') + compile project(':deps:com.juick.xmpp') + compile 'org.slf4j:slf4j-jdk14:1.7.13' + compile 'com.ganyo:gcm-server:1.0.+' + compile 'com.notnoop.apns:apns:1.0.0.Beta6' + compile 'com.github.scribejava:scribejava-apis:2.7.3' + compile 'org.apache.httpcomponents:httpclient:4.5.1' + compile 'org.apache.commons:commons-lang3:3.4' + compile "org.springframework:spring-jdbc:4.3.1.RELEASE" + compile 'org.json:json:20151123' + testCompile 'junit:junit:4.12' + providedCompile 'javax.servlet:javax.servlet-api:3.1.0' + providedRuntime 'mysql:mysql-connector-java:5.1.39' +} + +javascript.source { + dev { + js { + srcDir "src/main/webapp" + include "*.js" + } + } +} + +combineJs { + source = javascript.source.dev.js.files + dest = file("${buildDir}/scripts.all.js") +} + +minifyJs { + source = combineJs + dest = file("${buildDir}/scripts.min.js") + sourceMap = file("${buildDir}/scripts.sourcemap.json") +} + +css.source { + dev { + css { + srcDir "src/main/webapp" + include "*.css" + } + } +} + +combineCss { + source = css.source.dev.css.files + dest = "${buildDir}/style.all.css" +} + +minifyCss { + source = combineCss + dest = "${buildDir}/style.min.css" +} + +assemble.dependsOn 'minifyCss' +assemble.dependsOn 'minifyJs' + +compileJava.options.encoding = 'UTF-8' + +gretty { + httpPort = 8080 + contextPath = '' + servletContainer = 'tomcat8' +} \ No newline at end of file diff --git a/juick-www/src/main/java/com/juick/server/protocol/JuickProtocol.java b/juick-www/src/main/java/com/juick/server/protocol/JuickProtocol.java new file mode 100644 index 00000000..1a2e5333 --- /dev/null +++ b/juick-www/src/main/java/com/juick/server/protocol/JuickProtocol.java @@ -0,0 +1,375 @@ +package com.juick.server.protocol; + +import com.juick.*; +import com.juick.json.MessageSerializer; +import com.juick.server.*; +import com.juick.server.protocol.annotation.UserCommand; +import com.juick.xmpp.extensions.JuickMessage; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * Created by oxpa on 22.03.16. + */ + +public class JuickProtocol { + MessageSerializer json = new MessageSerializer(); + JdbcTemplate sql; + String baseUri; + + public JuickProtocol(JdbcTemplate sql, String baseUri) { + this.sql = sql; + this.baseUri = baseUri; + } + + /** + * find command by pattern and invoke + * @param user who send command + * @param userInput given by user + * @return command result + * @throws InvocationTargetException + * @throws IllegalAccessException + * @throws NoSuchMethodException + */ + public ProtocolReply getReply(User user, String userInput) throws InvocationTargetException, + IllegalAccessException, NoSuchMethodException { + Optional cmd = Arrays.asList(getClass().getDeclaredMethods()).stream() + .filter(m -> m.isAnnotationPresent(UserCommand.class)) + .filter(m -> Pattern.compile(m.getAnnotation(UserCommand.class).pattern(), + m.getAnnotation(UserCommand.class).patternFlags()).matcher(userInput).matches()) + .findFirst(); + if (!cmd.isPresent()) { + // default command - post as new message + return postMessage(user, userInput); + } else { + Matcher matcher = Pattern.compile(cmd.get().getAnnotation(UserCommand.class).pattern(), + cmd.get().getAnnotation(UserCommand.class).patternFlags()).matcher(userInput); + List groups = new ArrayList<>(); + while (matcher.find()) { + for (int i = 1; i <= matcher.groupCount(); i++) { + groups.add(matcher.group(i)); + } + } + return (ProtocolReply) getClass().getMethod(cmd.get().getName(), User.class, String[].class) + .invoke(this, user, groups.toArray(new String[groups.size()])); + } + } + + public ProtocolReply postMessage(User user, String input) { + List tags = TagQueries.fromString(sql, input, false); + String body = input.substring(TagQueries.toString(tags).length()); + int mid = MessagesQueries.createMessage(sql, user.getUID(), body, null, tags); + //app.events().publishEvent(new JuickMessageEvent(app.messages().getMessage(mid))); + return new ProtocolReply("New message posted.\n#" + mid + " " + baseUri + mid, + Optional.of(json.serializeList(Collections.singletonList(MessagesQueries.getMessage(sql, mid))))); + } + + @UserCommand(pattern = "^#(\\++)$", help = "#+ - Show last Juick messages (#++ - second page, ...)") + public ProtocolReply commandLast(User user, String... arguments) { + // number of + is the page count + int page = arguments[0].length(); + List mids = MessagesQueries.getAll(sql, user.getUID(), page); + List messages = MessagesQueries.getMessages(sql, mids); + // TODO: message toString + return new ProtocolReply("Last messages: \n" + String.join("\n", messages.stream().map(Object::toString) + .collect(Collectors.toList())), Optional.of(json.serializeList(messages))); + } + + @UserCommand(pattern = "^\\s*bl\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, + help = "BL - Show your blacklist") + public ProtocolReply commandBL(User user_from, String... arguments) { + List blusers; + List bltags; + + blusers = UserQueries.getUserBLUsers(sql, user_from.getUID()); + bltags = TagQueries.getUserBLTags(sql, user_from.getUID()); + + + String txt = ""; + 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.getUName() + "\n"; + } + } + if (txt.isEmpty()) { + txt = "You don't have any users or tags in your blacklist."; + } + return new ProtocolReply(txt, Optional.empty()); + } + + @UserCommand(pattern = "^\\@([^\\s\\n\\+]+)(\\+?)$", + help = "@username+ - Show user's info and last 10 messages (@username++ - second page, ..)") + public ProtocolReply commandUser(User user, String... arguments) { + User blogUser = UserQueries.getUserByName(sql, arguments[0]); + int page = arguments[1].length(); + if (blogUser.getUID() > 0) { + List mids = MessagesQueries.getUserBlog(sql, blogUser.getUID(), 0, page); + List messages = MessagesQueries.getMessages(sql, mids); + return new ProtocolReply(String.format("Last messages from @%s:\n%s", arguments[0], + String.join("\n", messages.stream() + .map(Object::toString).collect(Collectors.toList()))), + Optional.of(json.serializeList(messages))); + } + return new ProtocolReply("User not found", Optional.empty()); + } + + @UserCommand(pattern = "^\\s*d\\s*\\#([0-9]+)\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, + help = "D #12345 - delete the message") + public ProtocolReply commandDel(User user, String... args) { + try { + int mid = Integer.parseInt(args[0]); + if (MessagesQueries.deleteMessage(sql, user.getUID(), mid)) { + return new ProtocolReply(String.format("Message %s deleted", mid), Optional.empty()); + } + } catch (NumberFormatException e) { + return new ProtocolReply("Error", Optional.empty()); + } + return new ProtocolReply("Error", Optional.empty()); + } + + @UserCommand(pattern = "^\\s*login\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, + help = "LOGIN - log in to Juick website") + public ProtocolReply commandLogin(User user, String... arguments) { + return new ProtocolReply(baseUri + "?" + UserQueries.getHashByUID(sql, user.getUID()), + Optional.empty()); + } + + @UserCommand(pattern = "^(#+)$", help = "# - Show last messages from your feed (## - second page, ...)") + public ProtocolReply commandMyFeed(User user, String... arguments) { + // number of # is the page count + int page = arguments[0].length(); + List mids = MessagesQueries.getMyFeed(sql, user.getUID(), page); + List messages = MessagesQueries.getMessages(sql, mids); + // TODO: add instructions for empty feed + return new ProtocolReply("Your feed: \n" + String.join("\n", + messages.stream().map(Object::toString).collect(Collectors.toList())), + Optional.of(json.serializeList(messages))); + } + + @UserCommand(pattern = "^\\s*(on|off)\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, + help = "ON/OFF - Enable/disable subscriptions delivery") + public ProtocolReply commandOnOff(User user, String[] input) { + UserQueries.ActiveStatus newStatus; + String retValUpdated; + if (input[0].toLowerCase().equals("on")) { + newStatus = UserQueries.ActiveStatus.Active; + retValUpdated = "Notifications are activated for " + user.getJID(); + } else { + newStatus = UserQueries.ActiveStatus.Inactive; + retValUpdated = "Notifications are disabled for " + user.getJID(); + } + + if (UserQueries.setActiveStatusForJID(sql, user.getJID(), newStatus)) { + return new ProtocolReply(retValUpdated, Optional.empty()); + } else { + return new ProtocolReply(String.format("Subscriptions status for %s was not changed", user.getJID()), + Optional.empty()); + } + } + + @UserCommand(pattern = "^\\s*ping\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, + help = "PING - returns you a PONG") + public ProtocolReply commandPing(User user, String[] input) { + return new ProtocolReply("PONG", Optional.empty()); + } + + @UserCommand(pattern = "^\\@(\\S+)\\s+([\\s\\S]+)$", help = "@username message - send PM to username") + public ProtocolReply commandPM(User user_from, String... arguments) { + String user_to = arguments[0]; + String body = arguments[1]; + int ret = 0; + + int uid_to = 0; + String jid_to = null; + boolean haveInRoster = false; + + if (user_to.indexOf('@') > 0) { + uid_to = UserQueries.getUIDbyJID(sql, user_to); + } else { + uid_to = UserQueries.getUIDbyName(sql, user_to); + } + + if (uid_to > 0) { + if (!UserQueries.isInBLAny(sql, uid_to, user_from.getUID())) { + if (PMQueries.createPM(sql, user_from.getUID(), uid_to, body)) { + //jid_to = UserQueries.getJIDsbyUID(sql, uid_to); + if (jid_to != null) { + haveInRoster = PMQueries.havePMinRoster(sql, user_from.getUID(), jid_to); + } + ret = 200; + } else { + ret = 500; + } + } else { + ret = 403; + } + } else { + ret = 404; + } + + + if (ret == 200) { + JuickMessage jmsg = new JuickMessage(); + jmsg.setUser(user_from); + jmsg.setText(body); + // TODO: add PM payload + //app.events().publishEvent(new JuickMessageEvent(jmsg)); + /* TODO: move to XMPP component + if (jid_to != null) { + Message mm = new Message(); + mm.to = new JID(jid_to); + mm.type = Message.Type.chat; + if (haveInRoster) { + mm.from = new JID(user_from.getUName(), getDomain(), "Juick"); + mm.body = body; + } else { + mm.from = new JID("juick", getDomain(), "Juick"); + mm.body = "Private message from @" + user_from.getUName() + ":\n" + body; + } + return Collections.singletonList(mm); + } + */ + } + if (ret == 200) { + return new ProtocolReply("Private message sent", Optional.empty()); + } else { + return new ProtocolReply("Error " + ret, Optional.empty()); + } + } + + @UserCommand(pattern = "^#(\\d+)(\\+?)$", help = "#1234 - Show message (#1234+ - message with replies)") + public ProtocolReply commandShow(User user, String... arguments) { + boolean showReplies = arguments[1].length() > 0; + int mid; + try { + mid = Integer.parseInt(arguments[0]); + } catch (NumberFormatException e) { + return new ProtocolReply("Error", Optional.empty()); + } + Message msg = MessagesQueries.getMessage(sql, mid); + if (showReplies) { + List replies = MessagesQueries.getReplies(sql, mid); + replies.add(0, msg); + return new ProtocolReply(String.join("\n", + replies.stream().map(Object::toString).collect(Collectors.toList())), + Optional.of(json.serializeList(replies))); + } + return new ProtocolReply(msg.toString(), Optional.of(json.serializeList(Collections.singletonList(msg)))); + } + @UserCommand(pattern = "^(#|\\.)(\\d+)((\\.|\\-|\\/)(\\d+))?\\s([\\s\\S]+)", + help = "#1234 *tag *tag2 - edit tags\n#1234 text - reply to message") + public ProtocolReply EditOrReply(User user, String... args) { + int mid; + try { + mid = Integer.parseInt(args[1]); + } catch (NumberFormatException e) { + return new ProtocolReply("Error", Optional.empty()); + } + int rid; + try { + rid = Integer.parseInt(args[4]); + } catch (NumberFormatException e) { + rid = 0; + } + String txt = args[5]; + List messageTags = TagQueries.fromString(sql, txt, true); + if (messageTags.size() > 0) { + if (user.getUID() != MessagesQueries.getMessageAuthor(sql, mid).getUID()) { + return new ProtocolReply("It is not your message", Optional.empty()); + } + TagQueries.updateTags(sql, mid, messageTags); + return new ProtocolReply("Tags are updated", Optional.empty()); + } else { + int newrid = MessagesQueries.createReply(sql, mid, rid, user.getUID(), txt, null); + return new ProtocolReply("Reply posted.\n#" + mid + "/" + newrid + " " + + baseUri + mid + "/" + newrid, + Optional.of(json.serializeList(Collections.singletonList(MessagesQueries.getReply(sql, mid, newrid))))); + } + } + + @UserCommand(pattern = "^(s|u)\\s+#(\\d+)$", help = "S #1234 - subscribe to comments", + patternFlags = Pattern.CASE_INSENSITIVE) + public ProtocolReply commandSubscribeMessage(User user, String... args) { + boolean subscribe = args[0].equalsIgnoreCase("s"); + int mid; + try { + mid = Integer.parseInt(args[1]); + } catch (NumberFormatException e) { + return new ProtocolReply("Error", Optional.empty()); + } + if (subscribe) { + if (SubscriptionsQueries.subscribeMessage(sql, mid, user.getUID())) { + return new ProtocolReply("Subscribed", Optional.empty()); + } + } else { + if (SubscriptionsQueries.unSubscribeMessage(sql, mid, user.getUID())) { + return new ProtocolReply("Unsubscribed from #" + mid, Optional.empty()); + } + return new ProtocolReply("You was not subscribed to #" + mid, Optional.empty()); + } + return new ProtocolReply("Error", Optional.empty()); + } + @UserCommand(pattern = "^(s|u)\\s+\\@(\\S+)$", help = "S @user - subscribe to user's posts", + patternFlags = Pattern.CASE_INSENSITIVE) + public ProtocolReply commandSubscribeUser(User user, String... args) { + boolean subscribe = args[0].equalsIgnoreCase("s"); + User toUser = UserQueries.getUserByName(sql, args[1]); + if (toUser.getUID() > 0) { + if (subscribe) { + if (SubscriptionsQueries.subscribeUser(sql, user, toUser)) { + return new ProtocolReply("Subscribed", Optional.empty()); + // TODO: notification + // TODO: already subscribed case + } + } else { + if (SubscriptionsQueries.unSubscribeUser(sql, user, toUser)) { + return new ProtocolReply("Unsubscribed from @" + toUser.getUName(), Optional.empty()); + } + return new ProtocolReply("You was not subscribed to @" + toUser.getUName(), Optional.empty()); + } + } + return new ProtocolReply("Error", Optional.empty()); + } + @UserCommand(pattern = "^(s|u)\\s+\\*(\\S+)$", help = "S *tag - subscribe to tag" + + "\nU *tag - unsubscribe from tag", patternFlags = Pattern.CASE_INSENSITIVE) + public ProtocolReply commandSubscribeTag(User user, String... args) { + boolean subscribe = args[0].equalsIgnoreCase("s"); + Tag tag = TagQueries.getTag(sql, args[1], true); + if (subscribe) { + if (SubscriptionsQueries.subscribeTag(sql, user, tag)) { + return new ProtocolReply("Subscribed", Optional.empty()); + } + } else { + if (SubscriptionsQueries.unSubscribeTag(sql, user, tag)) { + return new ProtocolReply("Unsubscribed from " + tag.Name, Optional.empty()); + } + return new ProtocolReply("You was not subscribed to " + tag.Name, Optional.empty()); + } + return new ProtocolReply("Error", Optional.empty()); + } + + @UserCommand(pattern = "^\\s*help\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, + help = "HELP - returns this help message") + public ProtocolReply commandHelp(User user, String[] input) { + List commandsHelp = Arrays.asList(getClass().getDeclaredMethods()).stream() + .filter(m -> m.isAnnotationPresent(UserCommand.class)) + .map(m -> m.getAnnotation(UserCommand.class).help()) + .collect(Collectors.toList()); + return new ProtocolReply(String.join("\n", commandsHelp), Optional.empty()); + } +} diff --git a/juick-www/src/main/java/com/juick/server/protocol/ProtocolReply.java b/juick-www/src/main/java/com/juick/server/protocol/ProtocolReply.java new file mode 100644 index 00000000..d9d36a5d --- /dev/null +++ b/juick-www/src/main/java/com/juick/server/protocol/ProtocolReply.java @@ -0,0 +1,23 @@ +package com.juick.server.protocol; + +import java.util.Optional; + +/** + * Created by vitalyster on 08.04.2016. + */ +public class ProtocolReply { + + private Optional json; + private String description; + + public ProtocolReply(String text, Optional json) { + this.description = text; + this.json = json; + } + public String getDescription() { + return description; + } + public Optional getJson() { + return json; + } +} diff --git a/juick-www/src/main/java/com/juick/server/protocol/annotation/UserCommand.java b/juick-www/src/main/java/com/juick/server/protocol/annotation/UserCommand.java new file mode 100644 index 00000000..af7c4924 --- /dev/null +++ b/juick-www/src/main/java/com/juick/server/protocol/annotation/UserCommand.java @@ -0,0 +1,31 @@ +package com.juick.server.protocol.annotation; + +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 ""; + + /** + * + * @return pattern flags + */ + int patternFlags() default 0; + + /** + * + * @return a string used in HELP command output. Basically, only 1 string + */ + String help() default ""; +} diff --git a/juick-www/src/main/java/com/juick/www/CrosspostComponent.java b/juick-www/src/main/java/com/juick/www/CrosspostComponent.java new file mode 100644 index 00000000..e2ddbfa5 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/CrosspostComponent.java @@ -0,0 +1,323 @@ +/* + * Juick + * Copyright (C) 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.www; + +import com.juick.server.CrosspostQueries; +import com.juick.xmpp.JID; +import com.juick.xmpp.Message; +import com.juick.xmpp.Stream; +import com.juick.xmpp.StreamComponent; +import com.juick.xmpp.extensions.JuickMessage; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.net.ssl.HttpsURLConnection; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import java.io.*; +import java.net.Socket; +import java.net.URL; +import java.net.URLEncoder; +import java.security.Key; +import java.util.Properties; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; + +/** + * + * @author Ugnich Anton + */ +public class CrosspostComponent implements ServletContextListener, Stream.StreamListener, Message.MessageListener { + + private static Logger logger = Logger.getLogger(CrosspostComponent.class.getName()); + + private ExecutorService executorService; + + public final static String TWITTERURL = "https://api.twitter.com/1.1/statuses/update.json"; + public final static String FBURL = "https://graph.facebook.com/me/feed"; + public final static String VKURL = "https://api.vk.com/method/wall.post"; + JdbcTemplate sql; + Stream xmpp; + String twitter_consumer_key; + String twitter_consumer_secret; + + @Override + public void contextInitialized(final ServletContextEvent sce) { + logger.info("component initialized"); + executorService = Executors.newSingleThreadExecutor(); + executorService.submit((Runnable) () -> { + try { + Properties conf = new Properties(); + conf.load(sce.getServletContext().getResourceAsStream("/WEB-INF/juick.conf")); + + LogManager.getLogManager().readConfiguration( + sce.getServletContext().getResourceAsStream("/WEB-INF/logging.properties")); + twitter_consumer_key = conf.getProperty("twitter_consumer_key", ""); + twitter_consumer_secret = conf.getProperty("twitter_consumer_secret", ""); + + setupSql(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver"), conf.getProperty("datasource_url", "")); + setupXmppComponent(conf.getProperty("xmpp_password", "")); + } catch (Exception e) { + logger.log(Level.SEVERE, e.getMessage(), e); + } + }); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + executorService.shutdown(); + logger.info("component destroyed"); + } + + public void setupSql(String driver, String url) { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(driver); + dataSource.setUrl(url); + sql = new JdbcTemplate(dataSource); + } + + public void setupXmppComponent(String password) { + try { + Socket socket = new Socket("localhost", 5347); + xmpp = new StreamComponent(new JID("", "crosspost.juick.com", ""), socket.getInputStream(), socket.getOutputStream(), password); + xmpp.addChildParser(new JuickMessage()); + xmpp.addListener((Stream.StreamListener) this); + xmpp.addListener((Message.MessageListener) this); + xmpp.startParsing(); + } catch (IOException e) { + logger.log(Level.SEVERE, e.getMessage(), e); + } + } + + @Override + public void onStreamReady() { + logger.info("XMPP STREAM READY"); + } + + @Override + public void onStreamFail(Exception e) {logger.log(Level.SEVERE, "XMPP STREAM FAIL", e);} + @Override + public void onMessage(com.juick.xmpp.Message msg) { + JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); + if (msg.to != null && msg.to.Username != null && jmsg != null && jmsg.getRID() == 0) { + if (msg.to.Username.equals("twitter")) { + twitterPost(jmsg); + } else if (msg.to.Username.equals("fb")) { + facebookPost(jmsg); + } else if (msg.to.Username.equals("vk")) { + vkontaktePost(jmsg); + } + } + } + + public boolean facebookPost(com.juick.Message jmsg) { + String token = CrosspostQueries.getFacebookToken(sql, jmsg.getUser().getUID()).orElse(""); + if (token.isEmpty()) { + return false; + } + + logger.info("FB: #" + jmsg.getMID()); + + String status = getMessageHashTags(jmsg) + "\n" + jmsg.getText(); + + boolean ret = false; + try { + String body = "access_token=" + URLEncoder.encode(token, "UTF-8") + "&message=" + URLEncoder.encode(status, "UTF-8") + "&link=http%3A%2F%2Fjuick.com%2F" + jmsg.getMID(); + + HttpsURLConnection conn = (HttpsURLConnection) new URL(FBURL).openConnection(); + conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + conn.setRequestProperty("User-Agent", "Juick"); + conn.setRequestProperty("Content-Length", Integer.toString(body.length())); + conn.setUseCaches(false); + conn.setDoInput(true); + conn.setDoOutput(true); + conn.setRequestMethod("POST"); + conn.connect(); + + OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream()); + wr.write(body); + wr.close(); + + ret = streamToString(conn.getInputStream()) != null; + + conn.disconnect(); + } catch (Exception e) { + logger.log(Level.SEVERE, "fbPost: " + e.getMessage(), e); + } + return ret; + } + + public boolean vkontaktePost(com.juick.Message jmsg) { + Pair tokens = CrosspostQueries.getVKTokens(sql, jmsg.getUser().getUID()).orElse(Pair.of("", "")); + if (tokens.getLeft().isEmpty() || tokens.getRight().isEmpty()) { + return false; + } + + logger.info("VK: #" + jmsg.getMID()); + + String status = getMessageHashTags(jmsg) + "\n" + jmsg.getText() + "\nhttp://juick.com/" + jmsg.getMID(); + + boolean ret = false; + try { + String body = "owner_id=" + tokens.getLeft() + "&access_token=" + URLEncoder.encode(tokens.getRight(), "UTF-8") + "&from_group=1&message=" + URLEncoder.encode(status, "UTF-8"); + + HttpsURLConnection conn = (HttpsURLConnection) new URL(VKURL).openConnection(); + conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + conn.setRequestProperty("User-Agent", "Juick"); + conn.setRequestProperty("Content-Length", Integer.toString(body.length())); + conn.setUseCaches(false); + conn.setDoInput(true); + conn.setDoOutput(true); + conn.setRequestMethod("POST"); + conn.connect(); + + OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream()); + wr.write(body); + wr.close(); + + ret = streamToString(conn.getInputStream()) != null; + + conn.disconnect(); + } catch (Exception e) { + logger.log(Level.SEVERE, "vkPost: " + e.getMessage(), e); + } + return ret; + } + + public boolean twitterPost(com.juick.Message jmsg) { + Pair tokens = CrosspostQueries.getTwitterTokens(sql, jmsg.getUser().getUID()).orElse(Pair.of("", "")); + if (tokens.getLeft().isEmpty() || tokens.getRight().isEmpty()) { + return false; + } + String token = percentEncode(tokens.getLeft()); + String token_secret = percentEncode(tokens.getRight()); + + logger.info("TWITTER: #" + jmsg.getMID()); + + String status = getMessageHashTags(jmsg) + jmsg.getText(); + if (status.length() > 115) { + status = status.substring(0, 114) + "…"; + } + status += " http://juick.com/" + jmsg.getMID(); + status = percentEncode(status); + + boolean ret = false; + try { + String nonce = UUID.randomUUID().toString(); + String timestamp = Long.toString(System.currentTimeMillis() / 1000L); + String signature = percentEncode(twitterSignature(status, nonce, timestamp, token, token_secret)); + String auth = "OAuth " + + "oauth_consumer_key=\"" + twitter_consumer_key + "\", " + + "oauth_nonce=\"" + nonce + "\", " + + "oauth_signature=\"" + signature + "\", " + + "oauth_signature_method=\"HMAC-SHA1\", " + + "oauth_timestamp=\"" + timestamp + "\", " + + "oauth_token=\"" + token + "\", " + + "oauth_version=\"1.0\""; + + HttpsURLConnection conn = (HttpsURLConnection) new URL(TWITTERURL).openConnection(); + conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + conn.setRequestProperty("User-Agent", "Juick"); + conn.setRequestProperty("Content-Length", Integer.toString(status.length() + 7)); + conn.setRequestProperty("Authorization", auth); + conn.setUseCaches(false); + conn.setDoInput(true); + conn.setDoOutput(true); + conn.setRequestMethod("POST"); + conn.connect(); + + OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream()); + wr.write("status=" + status); + wr.close(); + + ret = streamToString(conn.getInputStream()) != null; + + conn.disconnect(); + } catch (Exception e) { + logger.log(Level.SEVERE, "twitterPost: " + e.getMessage(), e); + } + return ret; + } + + public String twitterSignature(String status, String nonce, String timestamp, String token, String token_secret) { + try { + // ALPHABET-SORTED + String params = "oauth_consumer_key=" + twitter_consumer_key + + "&oauth_nonce=" + nonce + + "&oauth_signature_method=HMAC-SHA1" + + "&oauth_timestamp=" + timestamp + + "&oauth_token=" + token + + "&oauth_version=1.0" + + "&status=" + status; + + String base = "POST&" + percentEncode(TWITTERURL) + "&" + percentEncode(params); + String key = twitter_consumer_secret + "&" + token_secret; + + Key signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA1"); + Mac mac = Mac.getInstance("HmacSHA1"); + mac.init(signingKey); + byte[] rawHmac = mac.doFinal(base.getBytes()); + return Base64.encodeBase64String(rawHmac); + + } catch (Exception e) { + logger.log(Level.SEVERE, "twitterSignature: " + e.getMessage(), e); + } + return null; + } + + public String streamToString(InputStream is) { + try { + BufferedReader buf = new BufferedReader(new InputStreamReader(is)); + StringBuilder str = new StringBuilder(); + String line; + do { + line = buf.readLine(); + str.append(line).append("\n"); + } while (line != null); + return str.toString(); + } catch (Exception e) { + logger.log(Level.SEVERE, "streamToString: " + e.getMessage(), e); + } + return null; + } + + public String getMessageHashTags(com.juick.Message jmsg) { + String hashtags = ""; + for (int i = 0; i < jmsg.Tags.size(); i++) { + hashtags += "#" + jmsg.Tags.get(i) + " "; + } + return hashtags; + } + + public static String percentEncode(String s) { + String ret = ""; + try { + ret = URLEncoder.encode(s, "UTF-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~"); + } catch (UnsupportedEncodingException e) { + } + return ret; + } +} diff --git a/juick-www/src/main/java/com/juick/www/Discover.java b/juick-www/src/main/java/com/juick/www/Discover.java new file mode 100644 index 00000000..238af0fb --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/Discover.java @@ -0,0 +1,118 @@ +/* + * 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.www; + +import com.juick.server.AdsQueries; +import com.juick.server.MessagesQueries; +import com.juick.server.TagQueries; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.List; + +/** + * + * @author Ugnich Anton + */ +public class Discover { + + protected void doGet(JdbcTemplate sql, JdbcTemplate sqlSearch, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + + String paramTagStr = URLDecoder.decode(request.getRequestURI().substring(5), "UTF-8"); + com.juick.Tag paramTag = TagQueries.getTag(sql, paramTagStr, false); + if (paramTag == null) { + Errors.doGet404(sql, request, response); + return; + } else if (paramTag.SynonymID > 0 && paramTag.TID != paramTag.SynonymID) { + com.juick.Tag synTag = TagQueries.getTag(sql, paramTag.SynonymID); + String url = "/tag/" + URLEncoder.encode(synTag.Name, "UTF-8"); + if (request.getQueryString() != null) { + url += "?" + request.getQueryString(); + } + Utils.sendPermanentRedirect(response, url); + return; + } else if (!paramTag.Name.equals(paramTagStr)) { + String url = "/tag/" + URLEncoder.encode(paramTag.Name, "UTF-8"); + if (request.getQueryString() != null) { + url += "?" + request.getQueryString(); + } + Utils.sendPermanentRedirect(response, url); + return; + } + + int paramBefore = 0; + String paramBeforeStr = request.getParameter("before"); + if (paramBeforeStr != null) { + try { + paramBefore = Integer.parseInt(paramBeforeStr); + } catch (NumberFormatException e) { + } + } + + int visitor_uid = visitor != null ? visitor.getUID() : 0; + + String title = "*" + Utils.encodeHTML(paramTag.Name); + List mids = MessagesQueries.getTag(sql, paramTag.TID, visitor_uid, paramBefore, (visitor == null) ? 40 : 20); + + response.setContentType("text/html; charset=UTF-8"); + try (PrintWriter out = response.getWriter()) { + String head = ""; + if (TagQueries.getTagNoIndex(sql, paramTag.TID)) { + head = ""; + } else if (paramBefore > 0 || mids.size() < 5) { + head = ""; + } + PageTemplates.pageHead(out, title, head); + PageTemplates.pageNavigation(out, visitor, null); + PageTemplates.pageHomeColumn(out, sql, visitor); + + out.println("
"); + + if (mids.size() > 0) { + int vuid = visitor != null ? visitor.getUID() : 0; + int ad_mid = AdsQueries.getAdMID(sql, vuid); + if (ad_mid > 0 && mids.indexOf(ad_mid) == -1) { + mids.add(0, ad_mid); + AdsQueries.logAdMID(sql, vuid, ad_mid); + } else { + ad_mid = 0; + } + + PageTemplates.printMessages(out, sql, null, mids, visitor, visitor == null ? 2 : 3, ad_mid); + } + + if (mids.size() >= 20) { + String nextpage = "/tag/" + URLEncoder.encode(paramTag.Name, "UTF-8") + "?before=" + mids.get(mids.size() - 1); + out.println("

Читать дальше →

"); + } + + out.println("
"); + + PageTemplates.pageFooter(request, out, visitor, true); + + PageTemplates.pageEnd(out); + } + } +} diff --git a/juick-www/src/main/java/com/juick/www/Errors.java b/juick-www/src/main/java/com/juick/www/Errors.java new file mode 100644 index 00000000..45fa3d51 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/Errors.java @@ -0,0 +1,42 @@ +package com.juick.www; + +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * + * @author ugnich + */ +public class Errors { + + public static String tagsHTML = null; + + public static void doGet404(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + + if (tagsHTML == null) { + tagsHTML = PageTemplates.formatPopularTags(sql, 80); + } + + response.setStatus(404); + response.setContentType("text/html; charset=UTF-8"); + try (PrintWriter out = response.getWriter()) { + PageTemplates.pageHead(out, "404 Страница не найдена", null); + PageTemplates.pageNavigation(out, visitor, null); + PageTemplates.pageHomeColumn(out, sql, visitor); + + out.println("
"); + out.println("

Страница не найдена

"); + out.println("

Сожалеем, но страницу с этим адресом удалил её автор, либо её никогда не существовало.

"); + out.println("
"); + + PageTemplates.pageFooter(request, out, visitor, false); + PageTemplates.pageEnd(out); + } + } +} diff --git a/juick-www/src/main/java/com/juick/www/FacebookLogin.java b/juick-www/src/main/java/com/juick/www/FacebookLogin.java new file mode 100644 index 00000000..22b081a5 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/FacebookLogin.java @@ -0,0 +1,152 @@ +/* + * 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.www; + +import com.juick.server.UserQueries; +import org.json.JSONObject; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URLEncoder; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author Ugnich Anton + */ +public class FacebookLogin { + + private static final Logger logger = Logger.getLogger(FacebookLogin.class.getName()); + + private static final String FACEBOOK_APPID = "130568668304"; + private static final String FACEBOOK_SECRET = "95813bfb6ab8f473410c50d4f971649e"; + private static final String FACEBOOK_REDIRECT = "http://juick.com/_fblogin"; + + protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String fbstate; + + String code = request.getParameter("code"); + if (code == null || code.equals("")) { + fbstate = UUID.randomUUID().toString(); + + Cookie c = new Cookie("fbstate", fbstate); + response.addCookie(c); + + response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); + response.setHeader("Location", "https://www.facebook.com/dialog/oauth?scope=publish_stream&client_id=" + FACEBOOK_APPID + "&redirect_uri=" + URLEncoder.encode(FACEBOOK_REDIRECT, "utf-8") + "&state=" + fbstate); + return; + } + + fbstate = Utils.getCookie(request, "fbstate"); + if (fbstate == null || fbstate.isEmpty() || !fbstate.equals(request.getParameter("state"))) { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } else { + Cookie c = new Cookie("fbstate", "-"); + c.setMaxAge(0); + response.addCookie(c); + } + + String token = Utils.fetchURL("https://graph.facebook.com/oauth/access_token?client_id=" + FACEBOOK_APPID + "&redirect_uri=" + URLEncoder.encode(FACEBOOK_REDIRECT, "utf-8") + "&client_secret=" + FACEBOOK_SECRET + "&code=" + URLEncoder.encode(code, "utf-8")); + if (token == null || token.isEmpty() || !token.startsWith("access_token=")) { + logger.log(Level.SEVERE, "FACEBOOK TOKEN ERROR: " + token); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return; + } + token = token.substring(13); // access_token=... + int tokenamp = token.indexOf('&'); // &expires= + if (tokenamp > 0) { + token = token.substring(0, tokenamp); + } + + String graph = Utils.fetchURL("https://graph.facebook.com/me?access_token=" + token); + if (graph == null || graph.isEmpty()) { + System.err.println("FACEBOOK GRAPH ERROR"); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return; + } + + try { + JSONObject json = new JSONObject(graph); + String fbIDStr = json.getString("id"); + String fbName = json.getString("name"); + String fbLink = json.getString("link"); + boolean fbVerified = json.getBoolean("verified"); + + long fbID = 0; + if (fbIDStr != null && !fbIDStr.isEmpty()) { + fbID = Long.parseLong(fbIDStr); + } + + if (fbID == 0 || fbName == null || fbLink == null || fbName.isEmpty() || fbLink.isEmpty()) { + throw new Exception(); + } + + int uid = getUIDbyFBID(sql, fbID); + if (uid > 0) { + if (!updateDB(sql, fbID, token, fbName, fbLink)) { + throw new Exception(); + } + Cookie c = new Cookie("hash", UserQueries.getHashByUID(sql, uid)); + c.setMaxAge(50 * 24 * 60 * 60); + response.addCookie(c); + response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); + response.setHeader("Location", "/"); + } else if (fbVerified) { + String loginhash = UUID.randomUUID().toString(); + if (!insertDB(sql, fbID, loginhash, token, fbName, fbLink)) { + throw new Exception(); + } + response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); + response.setHeader("Location", "/signup?type=fb&hash=" + loginhash); + } else { + throw new Exception(); + } + } catch (Exception e) { + logger.log(Level.WARNING, "fb error", e); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return; + } + } + + private int getUIDbyFBID(JdbcTemplate sql, long fbID) { + try { + return sql.queryForObject("SELECT user_id FROM facebook WHERE fb_id=? AND user_id IS NOT NULL", + Integer.class, fbID); + } catch (EmptyResultDataAccessException e) { + return 0; + } + } + + private boolean insertDB(JdbcTemplate sql, long fbID, String loginhash, String token, String fbName, String fbLink) { + return sql.update("INSERT INTO facebook(fb_id,loginhash,access_token,fb_name,fb_link) VALUES (?,?,?,?,?)", + fbID, loginhash, token, fbName, fbLink) > 0; + } + + private boolean updateDB(JdbcTemplate sql, long fbID, String token, String fbName, String fbLink) { + return sql.update("UPDATE facebook SET access_token=?,fb_name=?,fb_link=? WHERE fb_id=?", + token, fbName, fbLink, fbID) > 0; + } +} diff --git a/juick-www/src/main/java/com/juick/www/Help.java b/juick-www/src/main/java/com/juick/www/Help.java new file mode 100644 index 00000000..e0ecab2b --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/Help.java @@ -0,0 +1,90 @@ +/* + * 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.www; + +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; + +/** + * + * @author Ugnich Anton + */ +public class Help { + + protected void doRedirectToHelpIndex(HttpServletResponse response) throws ServletException, IOException { + Utils.sendTemporaryRedirect(response, "/help/ru/"); + } + + protected void doGetHelp(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + + String path[] = request.getRequestURI().split("/"); + String page; + if (path.length < 3 || path.length > 4 || path[2].length() != 2 || !path[2].matches("^[a-z]+$")) { + Errors.doGet404(sql, request, response); + return; + } + + if (path.length == 4) { + page = path[3]; + if (!page.matches("^[a-zA-Z0-9\\-]*$") || page.equals("navigation") || page.equals("index")) { + Errors.doGet404(sql, request, response); + return; + } + } else { + page = "index"; + } + + File f = new File("/var/www/juick.com/help/" + path[2] + "/" + page); + if (!f.isFile()) { + Errors.doGet404(sql, request, response); + return; + } + + response.setContentType("text/html; charset=UTF-8"); + try (PrintWriter out = response.getWriter()) { + PageTemplates.pageHead(out, "Помощь", null); + PageTemplates.pageNavigation(out, visitor, null); + + out.println(""); + + out.println("
"); + out.println("
"); + printFile(out, f); + out.println("
"); + out.println("
"); + + PageTemplates.pageFooter(request, out, visitor, false); + PageTemplates.pageEnd(out); + } + } + + private void printFile(PrintWriter out, File f) throws IOException { + BufferedReader br = new BufferedReader(new FileReader(f)); + String str; + while ((str = br.readLine()) != null) { + out.println(str); + } + } +} diff --git a/juick-www/src/main/java/com/juick/www/Home.java b/juick-www/src/main/java/com/juick/www/Home.java new file mode 100644 index 00000000..9241c5e6 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/Home.java @@ -0,0 +1,170 @@ +/* + * 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.www; + +import com.juick.server.AdsQueries; +import com.juick.server.MessagesQueries; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URLEncoder; +import java.util.List; + +/** + * + * @author Ugnich Anton + */ +public class Home { + + protected void doGet(JdbcTemplate sql, JdbcTemplate sqlSearch, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { + int paramBefore = 0; + String paramBeforeStr = request.getParameter("before"); + if (paramBeforeStr != null) { + try { + paramBefore = Integer.parseInt(paramBeforeStr); + } catch (NumberFormatException e) { + } + } + + String paramSearch = request.getParameter("search"); + if (paramSearch != null && paramSearch.length() > 64) { + paramSearch = null; + } + + String title; + List mids; + + String paramShow = request.getParameter("show"); + if (paramSearch != null) { + title = "Поиск: " + Utils.encodeHTML(paramSearch); + mids = MessagesQueries.getSearch(sql, sqlSearch, Utils.encodeSphinx(paramSearch), paramBefore); + } else if (paramShow == null) { + if (visitor != null) { + title = "Популярные"; + mids = MessagesQueries.getPopular(sql, visitor.getUID(), paramBefore); + } else { + title = "Микроблоги Juick: популярные записи"; + mids = MessagesQueries.getPopular(sql, 0, paramBefore); + } + + } else if (paramShow.equals("top")) { + Utils.sendPermanentRedirect(response, "/"); + return; + } else if (paramShow.equals("my") && visitor != null) { + title = "Моя лента"; + mids = MessagesQueries.getMyFeed(sql, visitor.getUID(), paramBefore); + } else if (paramShow.equals("private") && visitor != null) { + title = "Приватные"; + mids = MessagesQueries.getPrivate(sql, visitor.getUID(), paramBefore); + } else if (paramShow.equals("discuss") && visitor != null) { + title = "Обсуждения"; + mids = MessagesQueries.getDiscussions(sql, visitor.getUID(), paramBefore); + } else if (paramShow.equals("recommended") && visitor != null) { + title = "Рекомендации"; + mids = MessagesQueries.getRecommended(sql, visitor.getUID(), paramBefore); + } else if (paramShow.equals("photos")) { + title = "Фотографии"; + if (visitor != null) { + mids = MessagesQueries.getPhotos(sql, visitor.getUID(), paramBefore); + } else { + mids = MessagesQueries.getPhotos(sql, 0, paramBefore); + } + } else if (paramShow.equals("all")) { + title = "Все сообщения"; + if (visitor != null) { + mids = MessagesQueries.getAll(sql, visitor.getUID(), paramBefore); + } else { + mids = MessagesQueries.getAll(sql, 0, paramBefore); + } + } else { + Errors.doGet404(sql, request, response); + return; + } + + response.setContentType("text/html; charset=UTF-8"); + try (PrintWriter out = response.getWriter()) { + String head = ""; + if (paramBefore > 0 || paramShow != null) { + head = ""; + } + PageTemplates.pageHead(out, title, head); + PageTemplates.pageNavigation(out, visitor, paramSearch); + PageTemplates.pageHomeColumn(out, sql, visitor, paramShow == null && paramBefore == 0 && paramSearch == null && visitor == null); + + out.println("
"); + + if (paramShow == null && paramBefore == 0) { + out.println(""); + } + + if (visitor != null) { + out.println("
"); + out.println("
"); + out.println(" "); + out.println("
"); + out.println(" или загрузить
"); + out.println("
"); + out.println(" "); + out.println("
"); + out.println("
"); + out.println("
"); + } + + if (mids.size() > 0) { + int ad_mid = 0; + if (paramShow == null || paramShow.equals("top") || paramShow.equals("all")) { + int vuid = visitor != null ? visitor.getUID() : 0; + ad_mid = AdsQueries.getAdMID(sql, vuid); + if (ad_mid > 0 && mids.indexOf(ad_mid) == -1) { + mids.add(0, ad_mid); + AdsQueries.logAdMID(sql, vuid, ad_mid); + } else { + ad_mid = 0; + } + } + + PageTemplates.printMessages(out, sql, null, mids, visitor, visitor == null ? 2 : 3, ad_mid); + } + + if (mids.size() >= 20) { + String nextpage = "?before=" + mids.get(mids.size() - 1); + if (paramShow != null) { + nextpage += "&show=" + paramShow; + } + if (paramSearch != null) { + nextpage += "&search=" + URLEncoder.encode(paramSearch, "UTF-8"); + } + + out.println("

Читать дальше →

"); + } + + if (paramShow == null && paramBefore == 0) { + out.println(""); + } + + out.println("
"); + + PageTemplates.pageFooter(request, out, visitor, true); + PageTemplates.pageEnd(out); + } + } +} diff --git a/juick-www/src/main/java/com/juick/www/Login.java b/juick-www/src/main/java/com/juick/www/Login.java new file mode 100644 index 00000000..155f58d5 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/Login.java @@ -0,0 +1,246 @@ +/* + * 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.www; + +import org.springframework.jdbc.core.JdbcTemplate; + +import java.io.IOException; +import java.io.PrintWriter; +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * + * @author Ugnich Anton + */ +public class Login { + + protected void doGetLoginForm(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + if (visitor != null) { + Utils.sendTemporaryRedirect(response, "/"); + return; + } + + response.setContentType("text/html; charset=UTF-8"); + try (PrintWriter out = response.getWriter()) { + out.println(""); + out.println(""); + out.println(""); + out.println("Juick"); + out.println(""); + out.println(""); + out.println(""); + out.println(""); + + out.println(""); + + out.println(""); + + out.println("
juick.com © 2008-2014   Контакты · Помощь
"); + + out.println("
"); + out.println(" Зарегистрироваться:"); + out.println(" "); + out.println(" "); + out.println("
XMPP"); + out.println("
Отправьте LOGIN на juick@juick.com
"); + out.println("
"); + out.println("
"); + out.println("
Уже зарегистрированы?"); + out.println("
"); + out.println(""); + out.println(""); + out.println(""); + out.println("
"); + out.println("
"); + + out.println(""); + out.println(""); + } + } + + protected void doGetLogin(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String hash = request.getQueryString(); + if (hash.length() > 32) { + response.sendError(400); + return; + } + + if (com.juick.server.UserQueries.getUIDbyHash(sql, hash) > 0) { + Cookie c = new Cookie("hash", hash); + c.setMaxAge(365 * 24 * 60 * 60); + response.addCookie(c); + response.sendRedirect("/"); + } else { + response.sendError(403); + } + } + + protected void doPostLogin(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String username = request.getParameter("username"); + String password = request.getParameter("password"); + if (username == null || password == null || username.length() > 32 || password.isEmpty()) { + response.sendError(400); + return; + } + + int uid = com.juick.server.UserQueries.checkPassword(sql, username, password); + if (uid > 0) { + String hash = com.juick.server.UserQueries.getHashByUID(sql, uid); + Cookie c = new Cookie("hash", hash); + c.setMaxAge(365 * 24 * 60 * 60); + response.addCookie(c); + + String referer = request.getHeader("Referer"); + if (referer != null && referer.startsWith("http://juick.com/") && !referer.equals("http://juick.com/login")) { + response.sendRedirect(referer); + } else { + response.sendRedirect("/"); + } + } else { + response.sendError(403); + } + } + + protected void doGetLogout(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + if (visitor != null) { + sql.update("DELETE FROM logins WHERE user_id=?", visitor.getUID()); + } + + Cookie c = new Cookie("hash", "-"); + c.setDomain(".juick.com"); + c.setMaxAge(0); + response.addCookie(c); + + Cookie c2 = new Cookie("hash", "-"); + c2.setMaxAge(0); + response.addCookie(c2); + + response.sendRedirect("/"); + } +} diff --git a/juick-www/src/main/java/com/juick/www/Main.java b/juick-www/src/main/java/com/juick/www/Main.java new file mode 100644 index 00000000..8602447a --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/Main.java @@ -0,0 +1,310 @@ +/* + * 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.www; + +import com.juick.server.UserQueries; +import com.juick.xmpp.JID; +import com.juick.xmpp.Stream; +import com.juick.xmpp.StreamComponent; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import ru.sape.Sape; + +import javax.servlet.ServletException; +import javax.servlet.annotation.MultipartConfig; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.Socket; +import java.net.URLEncoder; +import java.util.Properties; + +/** + * + * @author Ugnich Anton + */ +@WebServlet(name = "Main", urlPatterns = {"/"}) +@MultipartConfig(fileSizeThreshold = 1024 * 1024, maxRequestSize = 1024 * 1024 * 10) +public class Main extends HttpServlet implements Stream.StreamListener { + + JdbcTemplate sql; + JdbcTemplate sqlSearch; + String sqlSearchConnStr = "jdbc:mysql://127.0.0.1:9306?autoReconnect=true&useUnicode=yes&characterEncoding=utf8&maxAllowedPacket=512000"; + Stream xmpp; + Home home = new Home(); + Discover discover = new Discover(); + PM pm = new PM(); + Login login = new Login(); + Help help = new Help(); + User pagesUser = new User(); + UserThread pagesUserThread = new UserThread(); + NewMessage pagesNewMessage = new NewMessage(); + FacebookLogin loginFacebook = new FacebookLogin(); + VKontakteLogin loginVK = new VKontakteLogin(); + TwitterAuth twitterAuth; + SignUp signup = new SignUp(); + Settings settings = new Settings(); + RSS rss = new RSS(); + + @Override + public void init() throws ServletException { + + super.init(); + try { + Properties conf = new Properties(); + conf.load(getServletContext().getResourceAsStream("/WEB-INF/juick.conf")); + + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver")); + dataSource.setUrl(conf.getProperty("datasource_url")); + DriverManagerDataSource dataSourceSearch = new DriverManagerDataSource(); + dataSourceSearch.setDriverClassName(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver")); + dataSourceSearch.setUrl(sqlSearchConnStr); + sql = new JdbcTemplate(dataSource); + sqlSearch = new JdbcTemplate(dataSourceSearch); + + setupXmppComponent(conf.getProperty("xmpp_password")); + twitterAuth = new TwitterAuth(conf.getProperty("twitter_consumer_key"), + conf.getProperty("twitter_consumer_secret")); + PageTemplates.sape = new Sape(conf.getProperty("sape_user"), "juick.com", 2000, 3600); + } catch (Exception e) { + log(null, e); + } + } + + public void setupXmppComponent(final String password) { + Thread thr = new Thread(() -> { + try { + Socket socket = new Socket("localhost", 5347); + xmpp = new StreamComponent(new JID("", "www.juick.com", ""), socket.getInputStream(), socket.getOutputStream(), password); + xmpp.addListener(Main.this); + xmpp.startParsing(); + } catch (IOException e) { + log("xmpp exception", e); + } + }); + thr.start(); + } + + @Override + public void onStreamFail(Exception e) {log("XMPP STREAM FAIL:" + e);} + + @Override + public void onStreamReady() { + log("XMPP STREAM READY"); + } + + + /** + * Handles the HTTP GET method. + * @param request servlet request + * @param response servlet response + * @throws ServletException if a servlet-specific error occurs + * @throws IOException if an I/O error occurs + */ + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + if (request.getCharacterEncoding() == null) { + request.setCharacterEncoding("UTF-8"); + } + String uri = request.getRequestURI(); + + if (uri.equals("/")) { + String tag = request.getParameter("tag"); + if (tag != null) { + Utils.sendPermanentRedirect(response, "/tag/" + URLEncoder.encode(tag, "UTF-8")); + } else { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + home.doGet(sql, sqlSearch, request, response, visitor); + } + } else if (uri.equals("/post")) { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + if (visitor != null) { + pagesNewMessage.doGetNewMessage(sql, request, response, visitor); + } else { + Utils.sendTemporaryRedirect(response, "/login"); + } + } else if (uri.equals("/login")) { + if (request.getQueryString() == null) { + login.doGetLoginForm(sql, request, response); + } else { + login.doGetLogin(sql, request, response); + } + } else if (uri.startsWith("/pm/")) { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + if (visitor == null) { + Utils.sendTemporaryRedirect(response, "/login"); + } else { + switch (uri) { + case "/pm/inbox": + pm.doGetInbox(sql, request, response, visitor); + break; + case "/pm/sent": + pm.doGetSent(sql, request, response, visitor); + break; + default: + Errors.doGet404(sql, request, response); + break; + } + } + } else if (uri.startsWith("/rss/")) { + String uname = uri.substring(5); + int uid = UserQueries.getUIDbyName(sql, uname); + if (uid > 0) { + rss.doGet(sql, request, response, uid, uname); + } else { + response.sendError(404); + } + } else if (uri.equals("/logout")) { + login.doGetLogout(sql, request, response); + } else if (uri.equals("/settings")) { + settings.doGet(sql, request, response); + } else if (uri.equals("/_fblogin")) { + loginFacebook.doGet(sql, request, response); + } else if (uri.equals("/_vklogin")) { + loginVK.doGet(sql, request, response); + } else if (uri.startsWith("/_twitter")) { + twitterAuth.doGet(sql, request, response); + } else if (uri.equals("/signup")) { + signup.doGet(sql, request, response); + } else if (uri.equals("/help") || uri.equals("/help/")) { + help.doRedirectToHelpIndex(response); + } else if (uri.startsWith("/help/")) { + help.doGetHelp(sql, request, response); + } else if (uri.startsWith("/tag/")) { + discover.doGet(sql, sqlSearch, request, response); + } else if (uri.matches("^/\\d+$")) { + String strID = request.getRequestURI().substring(1); + int mid = 0; + try { + mid = Integer.parseInt(strID); + } catch (NumberFormatException e) { + } + if (mid > 0) { + com.juick.User author = com.juick.server.MessagesQueries.getMessageAuthor(sql, mid); + if (author != null) { + Utils.sendPermanentRedirect(response, "/" + author.getUName() + "/" + mid); + return; + } + } + Errors.doGet404(sql, request, response); + } else if (uri.matches("^/[^/]+$")) { + com.juick.User user = com.juick.server.UserQueries.getUserByName(sql, request.getRequestURI().substring(1)); + if (user != null) { + Utils.sendPermanentRedirect(response, "/" + user.getUName() + "/"); + } else { + Errors.doGet404(sql, request, response); + } + } else if (uri.matches("^/.+/.*")) { + String uriparts[] = uri.split("/"); + com.juick.User user = com.juick.server.UserQueries.getUserByName(sql, uriparts[1]); + if (user != null && user.getUName().equals(uriparts[1]) && !user.Banned) { + if (uriparts.length == 2) { // http://juick.com/username/ + pagesUser.doGetBlog(sql, sqlSearch, request, response, user); + } else if (uriparts[2].equals("tags")) { + pagesUser.doGetTags(sql, request, response, user); + } else if (uriparts[2].equals("friends")) { + pagesUser.doGetFriends(sql, request, response, user); + } else if (uriparts[2].equals("readers")) { + pagesUser.doGetReaders(sql, request, response, user); + } else { + int mid = 0; + try { + mid = Integer.parseInt(uriparts[2]); + } catch (NumberFormatException e) { + } + if (mid > 0) { + com.juick.User author = com.juick.server.MessagesQueries.getMessageAuthor(sql, mid); + if (author != null) { + if (!author.getUName().equals(user.getUName())) { + Utils.sendPermanentRedirect(response, "/" + author.getUName() + "/" + mid); + } else { + pagesUserThread.doGetThread(sql, request, response, mid); + } + } else { + Errors.doGet404(sql, request, response); + } + } else { + Errors.doGet404(sql, request, response); + } + } + } else if (user != null && !user.Banned) { + Utils.sendPermanentRedirect(response, "/" + user.getUName() + "/" + (uriparts.length > 2 ? uriparts[2] : "")); + } else { + Errors.doGet404(sql, request, response); + } + } else { + Errors.doGet404(sql, request, response); + } + } + + /** + * Handles the HTTP POST method. + * @param request servlet request + * @param response servlet response + * @throws ServletException if a servlet-specific error occurs + * @throws IOException if an I/O error occurs + */ + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + if (request.getCharacterEncoding() == null) { + request.setCharacterEncoding("UTF-8"); + } + + String uri = request.getRequestURI(); + if (uri.equals("/post")) { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + if (visitor != null && !visitor.Banned) { + pagesNewMessage.doPostMessage(sql, request, response, xmpp, visitor); + } else { + response.sendError(403); + } + } else if (uri.equals("/comment")) { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + if (visitor != null && !visitor.Banned) { + pagesNewMessage.doPostComment(sql, request, response, xmpp, visitor); + } else { + response.sendError(403); + } + } else if (uri.equals("/like")) { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + if (visitor != null && !visitor.Banned) { + pagesNewMessage.doPostRecomm(sql, request, response, xmpp, visitor); + } else { + response.sendError(403); + } + } else if (uri.equals("/pm/send")) { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + if (visitor != null && !visitor.Banned) { + pm.doPostPM(sql, request, response, xmpp, visitor); + } else { + response.sendError(403); + } + } else if (uri.equals("/login")) { + login.doPostLogin(sql, request, response); + } else if (uri.equals("/signup")) { + signup.doPost(sql, request, response); + } else if (uri.equals("/settings")) { + settings.doPost(sql, request, response); + } else { + response.sendError(405); + } + } +} diff --git a/juick-www/src/main/java/com/juick/www/NewMessage.java b/juick-www/src/main/java/com/juick/www/NewMessage.java new file mode 100644 index 00000000..dc1b25f7 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/NewMessage.java @@ -0,0 +1,413 @@ +/* + * 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.www; + +import com.juick.Tag; +import com.juick.server.*; +import com.juick.xmpp.JID; +import com.juick.xmpp.Message; +import com.juick.xmpp.Stream; +import com.juick.xmpp.extensions.JuickMessage; +import com.juick.xmpp.extensions.JuickUser; +import com.juick.xmpp.extensions.Nickname; +import com.juick.xmpp.extensions.XOOB; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Ugnich Anton + */ +public class NewMessage { + + protected void doGetNewMessage(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { + response.setContentType("text/html; charset=UTF-8"); + try (PrintWriter out = response.getWriter()) { + PageTemplates.pageHead(out, "Написать", "" + + "" + + "" + + ""); + PageTemplates.pageNavigation(out, visitor, null); + + out.println("
"); + out.println("
"); + out.println("

Место: Отменить

"); + out.println("

Фото: (JPG, PNG, до 10Мб)

"); + + String body = request.getParameter("body"); + if (body == null) { + body = ""; + } else { + if (body.length() > 4096) { + body = body.substring(0, 4096); + } + body = Utils.encodeHTML(body); + } + out.println("


"); + + out.println("" + "" + "

"); + out.println("
"); + out.println("
"); + out.println("

Теги:

"); + printUserTags(sql, out, visitor); + out.println("
"); + + PageTemplates.pageFooter(request, out, visitor, false); + PageTemplates.pageEnd(out); + } + } + + void printUserTags(JdbcTemplate sql, PrintWriter out, com.juick.User visitor) { + List tags = TagQueries.getUserTagsAll(sql, visitor.getUID()); + + if (tags.isEmpty()) { + return; + } + + int min = tags.get(0).UsageCnt; + int max = tags.get(0).UsageCnt; + for (int i = 1; i < tags.size(); i++) { + int usagecnt = tags.get(i).UsageCnt; + if (usagecnt < min) { + min = usagecnt; + } + if (usagecnt > max) { + max = usagecnt; + } + } + max -= min; + + out.print("

"); + for (int i = 0; i < tags.size(); i++) { + if (i > 0) { + out.print(" "); + } + String taglink = ""; + try { + taglink = "" + Utils.encodeHTML(tags.get(i).Name) + ""; + } catch (UnsupportedEncodingException e) { + } + int usagecnt = tags.get(i).UsageCnt; + if (usagecnt <= max / 5 + min) { + out.print("" + taglink + ""); + } else if (usagecnt <= max / 5 * 2 + min) { + out.print(taglink); + } else if (usagecnt <= max / 5 * 3 + min) { + out.print("" + taglink + ""); + } else if (usagecnt <= max / 5 * 4 + min) { + out.print("" + taglink + ""); + } else { + out.print("" + taglink + ""); + } + } + out.println("

"); + } + + public void doPostMessage(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, Stream xmpp, com.juick.User visitor) throws ServletException, IOException { + String body = request.getParameter("body"); + if (body == null || body.length() < 1 || body.length() > 4096) { + response.sendError(400); + return; + } + body = body.replace("\r", ""); + + String tagsStr = request.getParameter("tags"); + List tags = new ArrayList(); + String tagsArr[] = new String[1]; + if (tagsStr != null && !tagsStr.isEmpty()) { + tagsArr = tagsStr.split("[ \\,]"); + for (int i = 0; i < tagsArr.length; i++) { + if (tagsArr[i].startsWith("*")) { + tagsArr[i] = tagsArr[i].substring(1); + } + if (tagsArr[i].length() > 64) { + tagsArr[i] = tagsArr[i].substring(0, 64); + } + } + tags = TagQueries.getTags(sql, tagsArr, true); + while (tags.size() > 5) { + tags.remove(5); + } + } + + String attachmentFName = null; + try { + attachmentFName = Utils.receiveMultiPartFile(request, "attach"); + } catch (Exception e) { + System.out.println("MULTIPART ERROR: " + e.toString()); + response.sendError(400); + return; + } + + String paramImg = request.getParameter("img"); + if (attachmentFName == null && paramImg != null && paramImg.length() > 10 ) { + try { + URL imgUrl = new URL(paramImg); + attachmentFName = Utils.downloadImage(imgUrl); + } catch (Exception e) { + System.out.println("DOWNLOAD ERROR: " + e.toString()); + response.sendError(500); + return; + } + } + + String attachmentType = attachmentFName != null ? attachmentFName.substring(attachmentFName.length() - 3) : null; + int mid = MessagesQueries.createMessage(sql, visitor.getUID(), body, attachmentType, tags); + SubscriptionsQueries.subscribeMessage(sql, mid, visitor.getUID()); + + Message xmsg = new Message(); + xmsg.from = new JID("juick", "juick.com", null); + xmsg.type = Message.Type.chat; + xmsg.thread = "juick-" + mid; + + JuickMessage jmsg = new JuickMessage(MessagesQueries.getMessage(sql, mid)); + xmsg.addChild(jmsg); + + Nickname nick = new Nickname(); + nick.Nickname = "@" + jmsg.getUser().getUName(); + xmsg.addChild(nick); + + if (attachmentFName != null) { + String fname = mid + "." + attachmentType; + String attachmentURL = "http://i.juick.com/photos-1024/" + fname; + + Runtime.getRuntime().exec("/var/www/juick.com/cgi/p-convert.sh /var/www/juick.com/i/tmp/" + attachmentFName + " " + fname); + + body = attachmentURL + "\n" + body; + XOOB xoob = new XOOB(); + xoob.URL = attachmentURL; + xmsg.addChild(xoob); + } + + String tagsStr2 = ""; + for (String tag : tagsArr) { + tagsStr2 += " *" + tag; + } + xmsg.body = "@" + jmsg.getUser().getUName() + ":" + tagsStr2 + "\n" + body + "\n\n#" + mid + " http://juick.com/" + mid; + + xmsg.to = new JID("juick", "s2s.juick.com", null); + xmpp.send(xmsg); + + xmsg.to.Host = "ws.juick.com"; + xmpp.send(xmsg); + + xmsg.to.Host = "push.juick.com"; + xmpp.send(xmsg); + + xmsg.to.Host = "crosspost.juick.com"; + xmsg.to.Username = "twitter"; + xmpp.send(xmsg); + xmsg.to.Username = "fb"; + xmpp.send(xmsg); + + xmsg.to.Host = "nologin.ru"; + xmsg.to.Username = "jubo"; + xmpp.send(xmsg); + + // + + response.setContentType("text/html; charset=UTF-8"); + try (PrintWriter out = response.getWriter()) { + PageTemplates.pageHead(out, "Сообщение опубликовано", null); + PageTemplates.pageNavigation(out, visitor, null); + PageTemplates.pageHomeColumn(out, sql, visitor); + + String hashtags = ""; + String tagscomma = ""; + for (int i = 0; i < tagsArr.length; i++) { + if (i > 0) { + hashtags += " "; + tagscomma += ","; + } + hashtags += "#" + tagsArr[i]; + tagscomma += tagsArr[i]; + } + + String url = URLEncoder.encode("http://juick.com/" + mid, "utf-8"); + String sharetwi = hashtags + " " + body; + if (sharetwi.length() > 115) { + sharetwi = sharetwi.substring(0, 114) + "…"; + } + sharetwi += " http://juick.com/" + mid; + String sharelj = URLEncoder.encode(body + "\n", "utf-8") + url; + + out.println("
"); + out.println("

Сообщение опубликовано

"); + out.println("

Поделитесь своим новым постом в социальных сетях:

"); + if (CrosspostQueries.getTwitterTokens(sql, visitor.getUID()).isPresent()) { + out.println("

Отправить в Twitter

"); + } + out.println("

Отправить в LiveJournal

"); + out.println("

Отправить в ВКонтакте

"); + if (CrosspostQueries.getFacebookToken(sql, visitor.getUID()).isPresent()) { + out.println("

Отправить в Facebook

"); + } + out.println("

Отправить в Google+

"); + out.println("

Ссылка на сообщение: http://juick.com/" + mid + "

"); + out.println("
"); + + PageTemplates.pageFooter(request, out, visitor, false); + PageTemplates.pageEnd(out); + } + } + + public void doPostComment(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, Stream xmpp, com.juick.User visitor) throws ServletException, IOException { + int mid = Utils.parseInt(request.getParameter("mid"), 0); + if (mid == 0) { + response.sendError(400); + return; + } + com.juick.Message msg = MessagesQueries.getMessage(sql, mid); + if (msg == null) { + response.sendError(404); + return; + } + + int rid = Utils.parseInt(request.getParameter("rid"), 0); + com.juick.Message reply = null; + if (rid > 0) { + reply = MessagesQueries.getReply(sql, mid, rid); + if (reply == null) { + response.sendError(404); + return; + } + } + + String body = request.getParameter("body"); + if (body == null || body.length() < 1 || body.length() > 4096) { + response.sendError(400); + return; + } + body = body.replace("\r", ""); + + if ((msg.ReadOnly && msg.getUser().getUID() != visitor.getUID()) || UserQueries.isInBLAny(sql, msg.getUser().getUID(), visitor.getUID()) || (reply != null && UserQueries.isInBLAny(sql, reply.getUser().getUID(), visitor.getUID()))) { + response.sendError(403); + return; + } + + String attachmentFName = null; + try { + attachmentFName = Utils.receiveMultiPartFile(request, "attach"); + } catch (Exception e) { + System.out.println("MULTIPART ERROR: " + e.toString()); + response.sendError(400); + return; + } + + String paramImg = request.getParameter("img"); + if (attachmentFName == null && paramImg != null && paramImg.length() > 10) { + try { + attachmentFName = Utils.downloadImage(new URL(paramImg)); + } catch (Exception e) { + System.out.println("DOWNLOAD ERROR: " + e.toString()); + response.sendError(500); + return; + } + } + + String attachmentType = attachmentFName != null ? attachmentFName.substring(attachmentFName.length() - 3) : null; + int ridnew = MessagesQueries.createReply(sql, mid, rid, visitor.getUID(), body, attachmentType); + SubscriptionsQueries.subscribeMessage(sql, mid, visitor.getUID()); + + Message xmsg = new Message(); + xmsg.from = new JID("juick", "juick.com", null); + xmsg.type = Message.Type.chat; + xmsg.thread = "juick-" + mid; + + JuickMessage jmsg = new JuickMessage(MessagesQueries.getReply(sql, mid, ridnew)); + xmsg.addChild(jmsg); + + String quote = reply != null ? reply.getText() : msg.getText(); + if (quote.length() >= 50) { + quote = quote.substring(0, 47) + "..."; + } + + Nickname nick = new Nickname(); + nick.Nickname = "@" + jmsg.getUser().getUName(); + xmsg.addChild(nick); + + if (attachmentFName != null) { + String fname = mid + "-" + ridnew + "." + attachmentType; + String attachmentURL = "http://i.juick.com/photos-1024/" + fname; + + Runtime.getRuntime().exec("/var/www/juick.com/cgi/p-convert.sh /var/www/juick.com/i/tmp/" + attachmentFName + " " + fname); + + body = attachmentURL + "\n" + body; + XOOB xoob = new XOOB(); + xoob.URL = attachmentURL; + xmsg.addChild(xoob); + } + + xmsg.body = "Reply by @" + jmsg.getUser().getUName() + ":\n>" + quote + "\n" + body + "\n\n#" + mid + "/" + ridnew + " http://juick.com/" + mid + "#" + ridnew; + + xmsg.to = new JID("juick", "s2s.juick.com", null); + xmpp.send(xmsg); + + xmsg.to.Host = "ws.juick.com"; + xmpp.send(xmsg); + + xmsg.to.Host = "push.juick.com"; + xmpp.send(xmsg); + + Utils.sendTemporaryRedirect(response, "/" + msg.getUser().getUName() + "/" + mid + "#" + ridnew); + } + + public void doPostRecomm(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, Stream xmpp, com.juick.User visitor) throws ServletException, IOException { + int mid = Utils.parseInt(request.getParameter("mid"), 0); + if (mid == 0) { + response.sendError(400); + return; + } + com.juick.Message msg = MessagesQueries.getMessage(sql, mid); + if (msg == null) { + response.sendError(404); + return; + } + if (msg.getUser().getUID() == visitor.getUID()) { + response.sendError(403); + return; + } + + boolean res = MessagesQueries.recommendMessage(sql, mid, visitor.getUID()); + + if (res) { + Message xmsg = new Message(); + xmsg.from = new JID("juick", "juick.com", null); + xmsg.to = new JID("recomm", "s2s.juick.com", null); + JuickMessage jmsg = new JuickMessage(); + jmsg.setMID(mid); + jmsg.setUser(new JuickUser(visitor)); + xmsg.addChild(jmsg); + xmpp.send(xmsg); + + Utils.replyJSON(request, response, "{\"status\":\"ok\"}"); + } else { + response.sendError(500); + } + } +} diff --git a/juick-www/src/main/java/com/juick/www/PM.java b/juick-www/src/main/java/com/juick/www/PM.java new file mode 100644 index 00000000..5adef7b3 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/PM.java @@ -0,0 +1,224 @@ +/* + * 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.www; + +import com.juick.server.PMQueries; +import com.juick.server.UserQueries; +import com.juick.xmpp.JID; +import com.juick.xmpp.Message; +import com.juick.xmpp.Stream; +import com.juick.xmpp.extensions.JuickMessage; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; + +/** + * + * @author Ugnich Anton + */ +public class PM { + + protected void doGetInbox(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { + /* + int paramBefore = 0; + String paramBeforeStr = request.getParameter("before"); + if (paramBeforeStr != null) { + try { + paramBefore = Integer.parseInt(paramBeforeStr); + } catch (NumberFormatException e) { + } + } + */ + + String title = "PM: Inbox"; + List msgs = PMQueries.getLastPMInbox(sql, visitor.getUID()); + + response.setContentType("text/html; charset=UTF-8"); + try (PrintWriter out = response.getWriter()) { + PageTemplates.pageHead(out, title, null); + PageTemplates.pageNavigation(out, visitor, null); + PageTemplates.pageHomeColumn(out, sql, visitor); + + out.println("
"); + + if (!msgs.isEmpty()) { + out.println(""); + } + + /* + if (msgs.size() >= 20) { + String nextpage = "?before=" + msgs.get(msgs.size() - 1); + out.println("

Читать дальше →

"); + } + */ + + out.println("
"); + + PageTemplates.pageFooter(request, out, visitor, false); + PageTemplates.pageEnd(out); + } + } + + protected void doGetSent(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { + /* + int paramBefore = 0; + String paramBeforeStr = request.getParameter("before"); + if (paramBeforeStr != null) { + try { + paramBefore = Integer.parseInt(paramBeforeStr); + } catch (NumberFormatException e) { + } + } + */ + + String title = "PM: Sent"; + List msgs = PMQueries.getLastPMSent(sql, visitor.getUID()); + + String uname = request.getParameter("uname"); + if (!UserQueries.checkUserNameValid(uname)) { + uname = ""; + } + + response.setContentType("text/html; charset=UTF-8"); + try (PrintWriter out = response.getWriter()) { + PageTemplates.pageHead(out, title, null); + PageTemplates.pageNavigation(out, visitor, null); + PageTemplates.pageHomeColumn(out, sql, visitor); + + out.println("
"); + + out.println("
"); + out.println("
"); + out.println("
To:
"); + out.println("
"); + out.println("
"); + out.println("
"); + out.println("
"); + + if (!msgs.isEmpty()) { + out.println(""); + } + + /* + if (msgs.size() >= 20) { + String nextpage = "?before=" + msgs.get(msgs.size() - 1); + out.println("

Читать дальше →

"); + } + */ + + out.println("
"); + + PageTemplates.pageFooter(request, out, visitor, false); + PageTemplates.pageEnd(out); + } + } + + public void doPostPM(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, Stream xmpp, com.juick.User visitor) throws ServletException, IOException { + String uname = request.getParameter("uname"); + if (uname.startsWith("@")) { + uname = uname.substring(1); + } + int uid = 0; + if (UserQueries.checkUserNameValid(uname)) { + uid = UserQueries.getUIDbyName(sql, uname); + } + + String body = request.getParameter("body"); + if (uid == 0 || body == null || body.length() < 1 || body.length() > 10240) { + response.sendError(400); + return; + } + + if (UserQueries.isInBLAny(sql, uid, visitor.getUID())) { + response.sendError(403); + return; + } + + if (PMQueries.createPM(sql, visitor.getUID(), uid, body)) { + Message msg = new Message(); + msg.from = new JID("juick", "juick.com", null); + msg.to = new JID(Integer.toString(uid), "push.juick.com", null); + JuickMessage jmsg = new JuickMessage(); + jmsg.setUser(visitor); + jmsg.setText(body); + msg.childs.add(jmsg); + xmpp.send(msg); + + msg.to.Host = "ws.juick.com"; + xmpp.send(msg); + + List jids = UserQueries.getJIDsbyUID(sql, uid); + for (String jid : jids) { + Message mm = new Message(); + mm.to = new JID(jid); + mm.type = Message.Type.chat; + if (PMQueries.havePMinRoster(sql, visitor.getUID(), jid)) { + mm.from = new JID(jmsg.getUser().getUName(), "juick.com", "Juick"); + mm.body = body; + } else { + mm.from = new JID("juick", "juick.com", "Juick"); + mm.body = "Private message from @" + jmsg.getUser().getUName() + ":\n" + body; + } + xmpp.send(mm); + } + + Utils.sendTemporaryRedirect(response, "/pm/sent"); + + } else { + response.sendError(500); + } + } +} diff --git a/juick-www/src/main/java/com/juick/www/PageTemplates.java b/juick-www/src/main/java/com/juick/www/PageTemplates.java new file mode 100644 index 00000000..f2483ac0 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/PageTemplates.java @@ -0,0 +1,479 @@ +/* + * 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.www; + +import com.juick.Message; +import com.juick.Tag; +import com.juick.server.MessagesQueries; +import com.juick.server.TagQueries; +import com.juick.server.UserQueries; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.util.StringUtils; +import ru.sape.Sape; + +import javax.servlet.http.HttpServletRequest; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * + * @author Ugnich Anton + */ +public class PageTemplates { + + public static Sape sape = null; + protected static final SimpleDateFormat sdfSQL = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private static SimpleDateFormat sdfSimple = new SimpleDateFormat("d MMM"); + private static SimpleDateFormat sdfFull = new SimpleDateFormat("d MMM yyyy"); + private static String tagsHTML = null; + + public static void pageHead(PrintWriter out, String title, String headers) { + out.println(""); + out.print(""); + out.print(""); + out.println(""); + out.print(""); + out.print(""); + out.print(""); + if (headers != null) { + out.print(headers); + } + out.print("" + title + ""); + out.println(""); + out.println(""); + out.println(""); + out.println(""); + out.flush(); + out.println(""); + } + + public static void pageNavigation(PrintWriter out, com.juick.User visitor, String search) { + out.println("
"); + out.println(" "); + out.print(" "); + out.print("
"); + out.println("
"); + if (visitor != null) { + out.print(" "); + out.print(" "); + } else { + out.println("

Чтобы добавлять сообщения и комментарии, представьтесь.

"); + } + out.println("
"); + out.println("
"); + } + + public static void pageHomeColumn(PrintWriter out, JdbcTemplate sql, com.juick.User visitor) { + pageHomeColumn(out, sql, visitor, false); + } + + public static void pageHomeColumn(PrintWriter out, JdbcTemplate sql, com.juick.User visitor, boolean showAdv) { + if (tagsHTML == null) { + tagsHTML = PageTemplates.formatPopularTags(sql, 80); + } + + out.println(""); + } + + public static String formatPopularTags(JdbcTemplate sql, int cnt) { + List popularTags = TagQueries.getPopularTags(sql).stream() + .map(t -> "" + Utils.encodeHTML(t) + "").collect(Collectors.toList()); + return StringUtils.collectionToDelimitedString(popularTags, " "); + } + + public static void pageFooter(HttpServletRequest request, PrintWriter out, com.juick.User visitor, boolean sapeon) { + out.println("
"); + out.println(" "); + out.print("
"); + out.print("Twitter"); + out.print("ВКонтакте"); + out.print("Facebook"); + out.println("
"); + out.print("
juick.com © 2008-2016"); + + String queryString = request.getQueryString(); + String requestURI = request.getRequestURI(); + if (sapeon && sape != null && (visitor == null || visitor.getUID() == 1) && queryString == null) { + String links = sape.getPageLinks(requestURI, request.getCookies()).render(); + if (links != null && !links.isEmpty()) { + out.print("
Спонсоры: " + links); + } + } + + out.println("
"); + out.println("
"); + + if (visitor != null) { + out.println(""); + } + + out.println(""); + } + + public static void pageEnd(PrintWriter out) { + out.println(""); + } + + public static String formatTags(List tags) { + String ret = ""; + for (Tag tag : tags) { + String tagName = tag.Name.replaceAll("<", "<").replaceAll(">", ">"); + try { + ret += " *"; + } catch (UnsupportedEncodingException e) { + } + } + + return ret; + } + + public static String formatTags(List tags, com.juick.User user) { + String ret = ""; + for (String tag : tags) { + tag = tag.replaceAll("<", "<"); + tag = tag.replaceAll(">", ">"); + try { + ret += " *" + tag + ""; + } catch (UnsupportedEncodingException e) { + } + } + + return ret; + } + + public static String formatDate(int minutes, Date fulldate) { + if (minutes < 1) { + return "сейчас"; + } else if (minutes < 60) { + String unit; + int ld = minutes % 10; + if ((minutes < 10 || minutes > 20) && ld == 1) { + unit = "минуту"; + } else if ((minutes < 10 || minutes > 20) && ld > 1 && ld < 5) { + unit = "минуты"; + } else { + unit = "минут"; + } + return minutes + " " + unit + " назад"; + } else if (minutes < 1440) { + int hours = (minutes / 60); + String unit; + int ld = hours % 10; + if ((hours < 10 || hours > 20) && ld == 1) { + unit = "час"; + } else if ((hours < 10 || hours > 20) && ld > 1 && ld < 5) { + unit = "часа"; + } else { + unit = "часов"; + } + return hours + " " + unit + " назад"; + } else if (minutes < 20160) { + int days = (minutes / 1440); + String unit; + int ld = days % 10; + if ((days < 10 || days > 20) && ld == 1) { + unit = "день"; + } else if ((days < 10 || days > 20) && ld > 1 && ld < 5) { + unit = "дня"; + } else { + unit = "дней"; + } + return days + " " + unit + " назад"; + } else { + String ret = sdfFull.format(fulldate); + synchronized (sdfSQL) { + try { + Calendar c = Calendar.getInstance(); + int curyear = c.get(Calendar.YEAR); + c.setTime(fulldate); + if (c.get(Calendar.YEAR) == curyear) { + ret = sdfSimple.format(fulldate); + } else { + ret = sdfFull.format(fulldate); + } + } catch (Exception e) { + System.err.println("PARSE EXCEPTION: " + fulldate); + } + } + return ret; + } + } + + public static String formatJSLocalTime(Date ts) { + return ""; + } + + public static String formatReplies(int replies) { + int ld = replies % 10; + int lh = replies % 100; + if ((lh < 10 || lh > 20) && ld == 1) { + return replies + " ответ"; + } else if ((lh < 10 || lh > 20) && ld > 1 && ld < 5) { + return replies + " ответа"; + } else { + return replies + " ответов"; + } + } + private static Pattern regexLinks2 = Pattern.compile("((?<=\\s)|(?<=\\A))([\\[\\{]|<)((?:ht|f)tps?://(?:www\\.)?([^\\/\\s\\\"\\)\\!]+)/?(?:[^\\]\\}](?", ">"); + + // http://juick.com/last?page=2 + // http://juick.com/last?page=2 + msg = msg.replaceAll("((?<=\\s)|(?<=\\A))((?:ht|f)tps?://(?:www\\.)?([^\\/\\s\\n\\\"]+)/?[^\\s\\n\\\"]*)", "$1$2"); + + // (http://juick.com/last?page=2) + // (http://juick.com/last?page=2) + Matcher m = regexLinks2.matcher(msg); + StringBuffer sb = new StringBuffer(); + while (m.find()) { + String url = m.group(3).replace(" ", "%20").replaceAll("\\s+", ""); + m.appendReplacement(sb, "$1$2" + url + "$5"); + } + m.appendTail(sb); + msg = sb.toString(); + + return "
" + msg + "
"; + } + + public static String formatMessage(String msg) { + msg = msg.replaceAll("&", "&"); + msg = msg.replaceAll("<", "<"); + msg = msg.replaceAll(">", ">"); + + // -- + // — + msg = msg.replaceAll("((?<=\\s)|(?<=\\A))\\-\\-?((?=\\s)|(?=\\Z))", "$1—$2"); + + // http://juick.com/last?page=2 + // juick.com + msg = msg.replaceAll("((?<=\\s)|(?<=\\A))((?:ht|f)tps?://(?:www\\.)?([^\\/\\s\\n\\\"]+)/?[^\\s\\n\\\"]*)", "$1$3"); + + // [link text][http://juick.com/last?page=2] + // link text + msg = msg.replaceAll("\\[([^\\]]+)\\]\\[((?:ht|f)tps?://[^\\]]+)\\]", "$1"); + msg = msg.replaceAll("\\[([^\\]]+)\\]\\(((?:ht|f)tps?://[^\\)]+)\\)", "$1"); + + // #12345 + // #12345 + msg = msg.replaceAll("((?<=\\s)|(?<=\\A)|(?<=\\p{Punct}))#(\\d+)((?=\\s)|(?=\\Z)|(?=\\))|(?=\\.)|(?=\\,))", "$1#$2$3"); + + // #12345/65 + // #12345/65 + msg = msg.replaceAll("((?<=\\s)|(?<=\\A)|(?<=\\p{Punct}))#(\\d+)/(\\d+)((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1#$2/$3$4"); + + // *bold* + // bold + msg = msg.replaceAll("((?<=\\s)|(?<=\\A)|(?<=\\p{Punct}))\\*([^\\*\\n<>]+)\\*((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1$2$3"); + + // /italic/ + // italic + msg = msg.replaceAll("((?<=\\s)|(?<=\\A))/([^\\/\\n<>]+)/((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1$2$3"); + + // _underline_ + // underline + msg = msg.replaceAll("((?<=\\s)|(?<=\\A))_([^\\_\\n<>]+)_((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1$2$3"); + + // /12 + // /12 + msg = msg.replaceAll("((?<=\\s)|(?<=\\A))\\/(\\d+)((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1/$2$3"); + + // @username@jabber.org + // @username@jabber.org + msg = msg.replaceAll("((?<=\\s)|(?<=\\A))@([\\w\\-\\.]+@[\\w\\-\\.]+)((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1@$2$3"); + + // @username + // @username + msg = msg.replaceAll("((?<=\\s)|(?<=\\A))@([\\w\\-]{2,16})((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1@$2$3"); + + // (http://juick.com/last?page=2) + // (juick.com) + Matcher m = regexLinks2.matcher(msg); + StringBuffer sb = new StringBuffer(); + while (m.find()) { + String url = m.group(3).replace(" ", "%20").replaceAll("\\s+", ""); + m.appendReplacement(sb, "$1$2$4$5"); + } + m.appendTail(sb); + msg = sb.toString(); + + // > citate + msg = msg.replaceAll("(?:(?<=\\n)|(?<=\\A))> *(.*)?(\\n|(?=\\Z))", "$1"); + msg = msg.replaceAll("", "\n"); + + msg = msg.replaceAll("\n", "
\n"); + return msg; + } + + public static void printMessages(PrintWriter out, JdbcTemplate sql, com.juick.User user, List mids, com.juick.User visitor, int YandexID, int ad_mid) { + List msgs = MessagesQueries.getMessages(sql, mids); + + for (int i = 0; i < msgs.size(); i++) { + com.juick.Message msg = msgs.get(i); + if (msg.getMID() == ad_mid) { + msgs.remove(i); + msgs.add(0, msg); + break; + } + } + + List blUIDs = new ArrayList(20); + if (visitor != null) { + for (Message msg : msgs) { + blUIDs.add(msg.getUser().getUID()); + } + blUIDs = UserQueries.checkBL(sql, visitor.getUID(), blUIDs); + } + + for (int i = 0; i < msgs.size(); i++) { + + com.juick.Message msg = msgs.get(i); + + List tags = MessagesQueries.getMessageTags(sql, msg.getMID()); + String tagsStr = formatTags(tags); + if (msg.ReadOnly) { + tagsStr += " *readonly"; + } + if (msg.Privacy < 0) { + tagsStr += " *friends"; + } + if (msg.getMID() == ad_mid) { + tagsStr += " *реклама"; + } + + String txt; + if (!msg.Tags.isEmpty() && msg.Tags.contains("code")) { + txt = formatMessageCode(msg.getText()); + } else { + txt = formatMessage(msg.getText()); + } + + out.println("
"); + out.println(" "); + out.println("
@" + msg.getUser().getUName() + ":" + tagsStr + "
"); + out.println("
"); + if (msg.AttachmentType != null) { + String fname = msg.getMID() + "." + msg.AttachmentType; + out.println("

\"\"/

"); + } + out.println("

" + txt + "

"); + if (msg.AttachmentType != null) { + out.println("
"); + } + out.print(" "); + + out.print(" "); + out.print("
"); + } + } +} diff --git a/juick-www/src/main/java/com/juick/www/PushComponent.java b/juick-www/src/main/java/com/juick/www/PushComponent.java new file mode 100644 index 00000000..67366628 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/PushComponent.java @@ -0,0 +1,311 @@ +/* + * Juick + * Copyright (C) 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.www; + +import com.google.android.gcm.server.Message; +import com.google.android.gcm.server.MulticastResult; +import com.google.android.gcm.server.Result; +import com.google.android.gcm.server.Sender; +import com.juick.json.MessageSerializer; +import com.juick.server.PushQueries; +import com.juick.server.SubscriptionsQueries; +import com.juick.xmpp.JID; +import com.juick.xmpp.Message.MessageListener; +import com.juick.xmpp.Stream; +import com.juick.xmpp.StreamComponent; +import com.juick.xmpp.extensions.JuickMessage; +import com.juick.xmpp.utils.XmlUtils; +import com.notnoop.apns.APNS; +import com.notnoop.apns.ApnsService; +import org.apache.http.Consts; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.HttpClient; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.apache.http.util.TextUtils; +import org.json.JSONObject; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import java.io.IOException; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +/** + * + * @author Ugnich Anton + */ +public class PushComponent implements ServletContextListener, Stream.StreamListener, MessageListener { + + private static Logger logger = Logger.getLogger(PushComponent.class.getName()); + + private ExecutorService executorService; + String wns_application_sip; + String wns_client_secret; + JdbcTemplate sql; + Socket socket; + Stream xmpp; + Sender GCMSender; + + @Override + public void contextInitialized(final ServletContextEvent sce) { + logger.info("component initialized"); + executorService = Executors.newSingleThreadExecutor(); + executorService.submit((Runnable) () -> { + Properties conf = new Properties(); + try { + conf.load(sce.getServletContext().getResourceAsStream("/WEB-INF/juick.conf")); + LogManager.getLogManager().readConfiguration( + sce.getServletContext().getResourceAsStream("/WEB-INF/logging.properties")); + wns_application_sip = conf.getProperty("wns_application_sip", ""); + wns_client_secret = conf.getProperty("wns_client_secret", ""); + GCMSender = new Sender(conf.getProperty("gcm_key")); + + setupSql(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver"), conf.getProperty("datasource_url", "")); + setupXmppComponent(new JID("", conf.getProperty("push_jid"), ""), conf.getProperty("xmpp_host", "localhost"), + Integer.parseInt(conf.getProperty("xmpp_port", "5347")), conf.getProperty("push_xmpp_password", "")); + } catch (IOException e) { + logger.log(Level.SEVERE, e.getMessage(), e); + } + }); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + executorService.shutdown(); + logger.info("component destroyed"); + } + + public void setupSql(String driver, String url) { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(driver); + dataSource.setUrl(url); + sql = new JdbcTemplate(dataSource); + } + + public void setupXmppComponent(JID jid, String host, int port, String password) { + try { + socket = new Socket(host, port); + xmpp = new StreamComponent(jid, socket.getInputStream(), socket.getOutputStream(), password); + xmpp.addChildParser(new JuickMessage()); + xmpp.addListener((Stream.StreamListener) this); + xmpp.addListener((MessageListener) this); + xmpp.startParsing(); + } catch (IOException e) { + logger.log(Level.SEVERE, e.getMessage(), e); + } + } + + @Override + public void onStreamReady() { + logger.info("XMPP STREAM READY"); + } + + @Override + public void onStreamFail(Exception e) {logger.log(Level.SEVERE, "XMPP STREAM FAIL", e);} + + @Override + public void onMessage(com.juick.xmpp.Message msg) { + JuickMessage jmsg = (JuickMessage)msg.getChild(JuickMessage.XMLNS); + List subscribedUsers = new ArrayList<>(); + boolean isPM = jmsg.getMID() == 0; + boolean isReply = jmsg.getRID() > 0; + int pmTo = 0; + if (isPM) { + pmTo = Integer.parseInt(msg.to.Username); + } else { + if (isReply) { + subscribedUsers = + SubscriptionsQueries.getUsersSubscribedToComments(sql, jmsg.getMID(), jmsg.getUser().getUID()); + } else { + // new message + subscribedUsers = SubscriptionsQueries.getSubscribedUsers(sql, jmsg.getUser().getUID(), jmsg.getMID()); + } + } + + /*** ANDROID ***/ + final List regids = new ArrayList<>(); + if (isPM) { + PushQueries.getAndroidRegID(sql, pmTo).ifPresent(regids::add); + } else { + List uids = subscribedUsers.stream().map(com.juick.User::getUID).collect(Collectors.toList()); + if (uids.size() > 0) { + regids.addAll(PushQueries.getAndroidTokens(sql, uids)); + } + } + + if (!regids.isEmpty()) { + MessageSerializer messageSerializer = new MessageSerializer(); + String json = messageSerializer.serialize(jmsg).toString(); + logger.info(json); + Message message = new Message.Builder().addData("message", json).build(); + try { + MulticastResult result = GCMSender.send(message, regids, 3); + List results = result.getResults(); + for (int i = 0; i < results.size(); i++) { + logger.info("RES " + i + ": " + results.get(i).toString()); + } + } catch (IOException e) { + logger.log(Level.SEVERE, e.getMessage(), e); + } catch (IllegalArgumentException err) { + logger.warning("Android: Invalid API Key"); + } + } else { + logger.info("GMS: no recipients"); + } + + /*** WinPhone ***/ + final List urls = new ArrayList<>(); + if (isPM) { + PushQueries.getWinPhoneURL(sql, pmTo).ifPresent(urls::add); + } else { + List uids = subscribedUsers.stream().map(com.juick.User::getUID).collect(Collectors.toList()); + if (uids.size() > 0) { + urls.addAll(PushQueries.getWindowsTokens(sql, uids)); + } + } + + + if (urls.isEmpty()) { + logger.info("WNS: no recipients"); + } else { + try { + String wnsToken = getWnsAccessToken(); + String text1 = "@" + jmsg.getUser().getUName(); + if (!jmsg.Tags.isEmpty()) { + text1 += ":" + XmlUtils.escape(jmsg.getTagsString()); + } + String text2 = XmlUtils.escape(jmsg.getText()); + String xml = "" + + "" + + "" + + "" + + "" + + "" + text1 + "" + + "" + text2 + "" + + "" + + "" + + "" + + "" + + "" + + ""; + logger.fine(xml); + for (String url : urls) { + logger.info("WNS: " + url); + sendWNS(wnsToken, url, xml); + } + } catch (IOException | IllegalStateException e) { + logger.log(Level.SEVERE, "WNS: ", e); + } + } + + /*** iOS ***/ + final List tokens = new ArrayList<>(); + if (isPM) { + PushQueries.getAPNSToken(sql, pmTo).ifPresent(tokens::add); + } else { + List uids = subscribedUsers.stream().map(com.juick.User::getUID).collect(Collectors.toList()); + if (uids.size() > 0) { + tokens.addAll(PushQueries.getAPNSTokens(sql, uids)); + } + } + if (!tokens.isEmpty()) { + ApnsService service = APNS.newService().withCert("/etc/juick/ios.p12", "juick") + .withSandboxDestination().build(); + for (String token : tokens) { + String payload = APNS.newPayload().alertTitle("@" + jmsg.getUser().getUName()).alertBody(jmsg.getText()).build(); + logger.info("APNS: " + token); + service.push(token, payload); + } + } else { + logger.info("APNS: no recipients"); + } + } + + String getWnsAccessToken() throws IOException, IllegalStateException { + if(TextUtils.isEmpty(wns_application_sip)) { + throw new IllegalStateException("'wns_application_sip' is not initialized"); + } + if(TextUtils.isEmpty(wns_client_secret)) { + throw new IllegalStateException("'wns_client_secret' is not initialized"); + } + HttpClient client = HttpClientBuilder.create().build(); + String url = "https://login.live.com/accesstoken.srf"; + List formParams = new ArrayList<>(); + formParams.add(new BasicNameValuePair("grant_type", "client_credentials")); + formParams.add(new BasicNameValuePair("client_id", wns_application_sip)); + formParams.add(new BasicNameValuePair("client_secret", wns_client_secret)); + formParams.add(new BasicNameValuePair("scope", "notify.windows.com")); + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8); + HttpPost httppost = new HttpPost(url); + httppost.setEntity(entity); + HttpResponse response = client.execute(httppost); + int statusCode = response.getStatusLine().getStatusCode(); + String responseContent = EntityUtils.toString(response.getEntity(), Consts.UTF_8); + JSONObject json = new JSONObject(responseContent); + if(statusCode != 200) { + throw new IOException(json.opt("error") + ": " + json.opt("error_description")); + } + String tokenType = (String)json.get("token_type"); + if(tokenType.length() >= 1) { + tokenType = Character.toUpperCase(tokenType.charAt(0)) + tokenType.substring(1); + } + return tokenType + " " + json.get("access_token"); + } + + void sendWNS(String wnsToken, String url, String xml) throws IOException { + HttpClient client = HttpClientBuilder.create().build(); + StringEntity entity = new StringEntity(xml, Consts.UTF_8); + HttpPost httpPost = new HttpPost(url); + httpPost.setHeader("Content-Type", "text/xml"); + httpPost.setHeader("Authorization", wnsToken); + httpPost.setHeader("X-WNS-Type", "wns/toast"); + httpPost.setEntity(entity); + HttpResponse response = client.execute(httpPost); + int statusCode = response.getStatusLine().getStatusCode(); + if(statusCode != 200) { + String headersContent = stringifyWnsHttpHeaders(response.getAllHeaders()); + throw new IOException(headersContent); + } + } + + static String stringifyWnsHttpHeaders(Header[] allHeaders) { + String[] wnsHeaders = Arrays.stream(allHeaders) + .filter(x -> x.getName().startsWith("X-WNS-") || x.getName().startsWith("WWW-")) + .map(x -> x.getName() + ": " + x.getValue()) + .toArray(String[]::new); + return String.join("\n", wnsHeaders); + } +} diff --git a/juick-www/src/main/java/com/juick/www/RSS.java b/juick-www/src/main/java/com/juick/www/RSS.java new file mode 100644 index 00000000..48510ce3 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/RSS.java @@ -0,0 +1,101 @@ +/* + * 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.www; + +import com.juick.Message; +import com.juick.server.MessagesQueries; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +/** + * + * @author ugnich + */ +public class RSS { + + private static final SimpleDateFormat sdfRSS = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z"); + + protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, int uid, String uname) throws ServletException, IOException { + List mids = MessagesQueries.getUserBlog(sql, uid, 0, 0); + if (mids.isEmpty()) { + response.sendError(404); + return; + } + + List msgs = MessagesQueries.getMessages(sql, mids); + + response.setContentType("application/rss+xml; charset=UTF-8"); + try (PrintWriter out = response.getWriter()) { + out.println(""); + out.println(""); + out.println(""); + out.println(""); + out.println("" + uname + " - Juick"); + out.println("http://juick.com/" + uname + "/"); + out.println("The latest messages by @" + uname + " at Juick"); + out.println("http://i.juick.com/a/" + uid + ".png" + uname + " - Juickhttp://juick.com/" + uname + "/"); + + for (Message msg : msgs) { + out.println(""); + out.println("http://juick.com/" + msg.getUser().getUName() + "/" + msg.getMID() + ""); + out.println("http://juick.com/" + msg.getUser().getUName() + "/" + msg.getMID() + ""); + + out.print("<![CDATA[@" + msg.getUser().getUName() + ":"); + if (!msg.Tags.isEmpty()) { + for (int n = 0; n < msg.Tags.size(); n++) { + out.print(" *" + msg.Tags.get(n)); + } + } + out.println("]]>"); + out.println(""); + + Date date = msg.getDate(); + out.println("" + sdfRSS.format(date) + ""); + + + out.println("http://juick.com/" + msg.getUser().getUName() + "/" + msg.getMID() + ""); + if (!msg.Tags.isEmpty()) { + for (int n = 0; n < msg.Tags.size(); n++) { + out.println("" + msg.Tags.get(n) + ""); + } + } + if (msg.AttachmentType != null) { + if (msg.AttachmentType.equals("jpg")) { + out.println(""); + out.println(""); + } else if (msg.AttachmentType.equals("png")) { + out.println(""); + out.println(""); + } + } + out.println(""); + out.println(""); + } + + out.println(""); + } + } +} diff --git a/juick-www/src/main/java/com/juick/www/Settings.java b/juick-www/src/main/java/com/juick/www/Settings.java new file mode 100644 index 00000000..af1591bf --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/Settings.java @@ -0,0 +1,91 @@ +/* + * 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.www; + +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * + * @author Ugnich Anton + */ +public class Settings { + + protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + + response.setContentType("text/html; charset=UTF-8"); + try (PrintWriter out = response.getWriter()) { + PageTemplates.pageHead(out, "Логин", ""); + PageTemplates.pageNavigation(out, visitor, null); + + out.println("
"); + out.println("
"); + out.println("
"); + out.println("
"); + out.println("

Имя пользователя:

"); + out.println("

Пароль:

"); + out.println("

"); + out.println("
"); + out.println("
"); + out.println("
"); + out.println("
"); // topwrapper + + PageTemplates.pageFooter(request, out, visitor, false); + PageTemplates.pageEnd(out); + } + } + + protected void doPost(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String username = request.getParameter("username"); + String password = request.getParameter("password"); + if (username == null || password == null || username.length() > 32 || password.isEmpty()) { + response.sendError(400); + return; + } + + int uid = com.juick.server.UserQueries.checkPassword(sql, username, password); + if (uid > 0) { + String hash = com.juick.server.UserQueries.getHashByUID(sql, uid); + Cookie c = new Cookie("hash", hash); + c.setDomain(".juick.com"); + c.setMaxAge(365 * 24 * 60 * 60); + response.addCookie(c); + + + if (uid > 0) { + throw new IOException("Settings"); + } + + String referer = request.getHeader("Referer"); + if (referer != null && referer.startsWith("http://juick.com/") && !referer.equals("http://juick.com/login")) { + response.sendRedirect(referer); + } else { + response.sendRedirect("/"); + } + } else { + response.sendError(403); + } + } +} diff --git a/juick-www/src/main/java/com/juick/www/SignUp.java b/juick-www/src/main/java/com/juick/www/SignUp.java new file mode 100644 index 00000000..bba1d34e --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/SignUp.java @@ -0,0 +1,258 @@ +/* + * 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.www; + +import com.juick.server.UserQueries; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; + +/** + * + * @author Ugnich Anton + */ +public class SignUp { + + protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + + String type = request.getParameter("type"); + String hash = request.getParameter("hash"); + if (type == null || type.isEmpty() || hash == null || hash.isEmpty() || hash.length() > 36 || !type.matches("^[a-zA-Z0-9\\-]+$") || !hash.matches("^[a-zA-Z0-9\\-]+$")) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + String account = null; + if (type.equals("fb")) { + account = getFacebookNameByHash(sql, hash); + } else if (type.equals("vk")) { + account = getVKNameByHash(sql, hash); + } else if (type.equals("xmpp")) { + account = getJIDByHash(sql, hash); + } else if (type.equals("durov")) { + account = getTelegramNameByHash(sql, hash); + } + if (account == null) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + response.setContentType("text/html; charset=UTF-8"); + try (PrintWriter out = response.getWriter()) { + PageTemplates.pageHead(out, "Новый пользователь", null); + PageTemplates.pageNavigation(out, visitor, null); + + out.println("
"); + + out.print("

"); + if (type.charAt(0) == 'f') { + out.print("\"Facebook\"/"); + } else if (type.charAt(0) == 'v') { + out.print("\"VKontakte\"/"); + } else if (type.charAt(0) == 'x') { + out.print("\"XMPP\"/"); + } else if (type.charAt(0) == 'd') { + out.print("\"Telegram\"/"); + } + out.println(account + "

"); + + out.println("

Связать с существующим аккаунтом Juick

"); + out.println("
"); + out.println(""); + out.println(""); + out.println(""); + if (visitor != null) { + out.println(""); + } else { + out.println("

Имя пользователя:

"); + out.println("

Пароль:

"); + out.println("

"); + } + out.println("
"); + + out.println("
"); + + out.println("

Создать новый аккаунт Juick

"); + out.println("
"); + out.println(""); + out.println(""); + out.println(""); + out.println("

Имя пользователя:
(От 2-х до 16-и латинских символов и/или цифр, дефис)

"); + out.println("

Пароль:
(от 6-и до 32-х символов)

"); + out.println("

"); + out.println("
"); + + out.println("
"); + + PageTemplates.pageFooter(request, out, visitor, false); + PageTemplates.pageEnd(out); + } + } + + protected void doPost(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + int uid = 0; + + String type = request.getParameter("type"); + String hash = request.getParameter("hash"); + if (type == null || type.isEmpty() || hash == null || hash.isEmpty() || hash.length() > 36 || !type.matches("^[a-zA-Z0-9\\-]+$") || !hash.matches("^[a-zA-Z0-9\\-]+$")) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + String action = request.getParameter("action"); + if (action.charAt(0) == 'l') { + + if (visitor == null) { + String username = request.getParameter("username"); + String password = request.getParameter("password"); + if (username == null || password == null || username.length() > 32 || password.isEmpty()) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return; + } + uid = com.juick.server.UserQueries.checkPassword(sql, username, password); + } else { + uid = visitor.getUID(); + } + + if (uid <= 0) { + response.sendError(HttpServletResponse.SC_FORBIDDEN); + return; + } + + if (!(type.charAt(0) == 'f' && setFacebookUser(sql, hash, uid)) + && !(type.charAt(0) == 'v' && setVKUser(sql, hash, uid)) + && !(type.charAt(0) == 'd' && setTelegramUser(sql, hash, uid)) + && !(type.charAt(0) == 'x' && setJIDUser(sql, hash, uid))) { + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return; + } + + } else { // Create new account + String username = request.getParameter("username"); + String password = request.getParameter("password"); + if (username == null || password == null || username.length() < 2 || username.length() > 16 || !username.matches("^[a-zA-Z0-9\\-]+$") || password.length() < 6 || password.length() > 32) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + // CHECK USERNAME + + uid = UserQueries.createUser(sql, username, password); + if (uid <= 0) { + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return; + } + + if (!(type.charAt(0) == 'f' && setFacebookUser(sql, hash, uid)) + && !(type.charAt(0) == 'v' && setVKUser(sql, hash, uid)) + && !(type.charAt(0) == 'd' && setTelegramUser(sql, hash, uid)) + && !(type.charAt(0) == 'x' && setJIDUser(sql, hash, uid))) { + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return; + } + + int ref = 0; + String sRef = Utils.getCookie(request, "ref"); + if (sRef != null) { + try { + ref = Integer.parseInt(sRef); + } catch (Exception e) { + } + } + + if (ref > 0) { + setUserRef(sql, uid, ref); + } + + visitor = null; + } + + if (visitor == null) { + hash = com.juick.server.UserQueries.getHashByUID(sql, uid); + Cookie c = new Cookie("hash", hash); + c.setMaxAge(365 * 24 * 60 * 60); + response.addCookie(c); + } + + response.sendRedirect("/"); + } + + private boolean setUserRef(JdbcTemplate sql, int uid, int ref) { + return sql.update("INSERT INTO users_refs(user_id,ref) VALUES (?,?)", uid, ref) > 0; + } + + private String getFacebookNameByHash(JdbcTemplate sql, String hash) { + try { + return sql.queryForObject("SELECT fb_name,fb_link FROM facebook WHERE loginhash=?", String.class, hash); + } catch (EmptyResultDataAccessException e) { + return null; + } + } + private String getTelegramNameByHash(JdbcTemplate sql, String hash) { + try { + String name = sql.queryForObject("SELECT tg_name FROM telegram WHERE loginhash=?", String.class, hash); + return "" + name + ""; + } catch (EmptyResultDataAccessException e) { + return null; + } + } + + private boolean setFacebookUser(JdbcTemplate sql, String hash, int uid) { + return sql.update("UPDATE facebook SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; + } + + private String getVKNameByHash(JdbcTemplate sql, String hash) { + List> logins = sql.query("SELECT vk_name,vk_link FROM vk WHERE loginhash=?", + (rs, num) -> { + return Pair.of(rs.getString(1), rs.getString(2)); + }, hash); + if (logins.size() > 0) { + return "" + logins.get(0).getLeft() + ""; + } + return null; + } + + private boolean setVKUser(JdbcTemplate sql, String hash, int uid) { + return sql.update("UPDATE vk SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; + } + private boolean setTelegramUser(JdbcTemplate sql, String hash, int uid) { + return sql.update("UPDATE telegram SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; + } + + private String getJIDByHash(JdbcTemplate sql, String hash) { + try { + return sql.queryForObject("SELECT jid FROM jids WHERE loginhash=?", String.class, hash); + } catch (EmptyResultDataAccessException e) { + return null; + } + } + + private boolean setJIDUser(JdbcTemplate sql, String hash, int uid) { + return sql.update("UPDATE jids SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; + } +} diff --git a/juick-www/src/main/java/com/juick/www/TwitterAuth.java b/juick-www/src/main/java/com/juick/www/TwitterAuth.java new file mode 100644 index 00000000..d4be4335 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/TwitterAuth.java @@ -0,0 +1,87 @@ +package com.juick.www; + +import com.github.scribejava.apis.TwitterApi; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.model.OAuth1AccessToken; +import com.github.scribejava.core.model.OAuth1RequestToken; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth.OAuth10aService; +import com.juick.server.UserQueries; +import org.json.JSONObject; +import org.springframework.jdbc.core.JdbcTemplate; + +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 vt on 01.12.2015. + */ +public class TwitterAuth { + + private final static String VERIFY_URL = "https://api.twitter.com/1.1/account/verify_credentials.json"; + + private String consumerKey, consumerSecret; + + public TwitterAuth(String consumerKey, String consumerSecret) { + this.consumerKey = consumerKey; + this.consumerSecret = consumerSecret; + } + + protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String hash = "", request_token = "", request_token_secret = ""; + 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 = UserQueries.getUserByHash(sql, hash); + if ( user == null || user.getUID() == 0) { + response.sendError(403); + return; + } + OAuth10aService oAuthService = new ServiceBuilder() + .apiKey(consumerKey) + .apiSecret(consumerSecret) + .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, VERIFY_URL, oAuthService); + oAuthService.signRequest(accessToken, oAuthRequest); + JSONObject jsonResponse = new JSONObject(oAuthRequest.send().getBody()); + String screenName = jsonResponse.getString("screen_name"); + if (UserQueries.linkTwitterAccount(sql, user, accessToken.getToken(), accessToken.getTokenSecret(), + screenName)) { + response.setStatus(HttpServletResponse.SC_FOUND); + response.setHeader("Location", "http://juick.com/settings"); + } else { + response.sendError(500); + } + } + } + } +} diff --git a/juick-www/src/main/java/com/juick/www/User.java b/juick-www/src/main/java/com/juick/www/User.java new file mode 100644 index 00000000..549e38af --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/User.java @@ -0,0 +1,343 @@ +/* + * 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.www; + +import com.juick.Tag; +import com.juick.server.MessagesQueries; +import com.juick.server.TagQueries; +import com.juick.server.UserQueries; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.List; + +/** + * + * @author Ugnich Anton + */ +public class User { + + protected void doGetBlog(JdbcTemplate sql, JdbcTemplate sqlSearch, HttpServletRequest request, HttpServletResponse response, com.juick.User user) throws ServletException, IOException { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + + List mids; + + String paramShow = request.getParameter("show"); + + com.juick.Tag paramTag = null; + String paramTagStr = request.getParameter("tag"); + if (paramTagStr != null) { + if (paramTagStr.length() < 64) { + paramTag = TagQueries.getTag(sql, paramTagStr, false); + } + if (paramTag == null) { + Errors.doGet404(sql, request, response); + return; + } else if (!paramTag.Name.equals(paramTagStr)) { + String url = "/" + user.getUName() + "/?tag=" + URLEncoder.encode(paramTag.Name, "UTF-8"); + Utils.sendPermanentRedirect(response, url); + return; + } + } + + int paramBefore = 0; + String paramBeforeStr = request.getParameter("before"); + if (paramBeforeStr != null) { + try { + paramBefore = Integer.parseInt(paramBeforeStr); + } catch (NumberFormatException e) { + } + } + + String paramSearch = request.getParameter("search"); + if (paramSearch != null && paramSearch.length() > 64) { + paramSearch = null; + } + + int privacy = 0; + if (visitor != null) { + if (user.getUID() == visitor.getUID() || visitor.getUID() == 1) { + privacy = -3; + } else if (UserQueries.isInWL(sql, user.getUID(), visitor.getUID())) { + privacy = -2; + } + } + + String title; + if (paramShow == null) { + if (paramTag != null) { + title = "Блог " + user.getUName() + ": *" + Utils.encodeHTML(paramTag.Name); + mids = MessagesQueries.getUserTag(sql, user.getUID(), paramTag.TID, privacy, paramBefore); + } else if (paramSearch != null) { + title = "Блог " + user.getUName() + ": " + Utils.encodeHTML(paramSearch); + mids = MessagesQueries.getUserSearch(sql, sqlSearch, user.getUID(), Utils.encodeSphinx(paramSearch), privacy, paramBefore); + } else { + title = "Блог " + user.getUName(); + mids = MessagesQueries.getUserBlog(sql, user.getUID(), privacy, paramBefore); + } + } else if (paramShow.equals("recomm")) { + title = "Рекомендации " + user.getUName(); + mids = MessagesQueries.getUserRecommendations(sql, user.getUID(), paramBefore); + } else if (paramShow.equals("photos")) { + title = "Фотографии " + user.getUName(); + mids = MessagesQueries.getUserPhotos(sql, user.getUID(), privacy, paramBefore); + } else { + Errors.doGet404(sql, request, response); + return; + } + + if (visitor == null) { + pageUserRefCookie(request, response, user.getUID()); + } + + response.setContentType("text/html; charset=UTF-8"); + try (PrintWriter out = response.getWriter()) { + String head = ""; + if (paramTag != null && TagQueries.getTagNoIndex(sql, paramTag.TID)) { + head += ""; + } else if (paramBefore > 0 || paramShow != null) { + head += ""; + } + PageTemplates.pageHead(out, title, head); + PageTemplates.pageNavigation(out, visitor, null); + pageUserColumn(out, sql, user, visitor); + + if (mids.size() > 0) { + out.println("
"); + + if (paramTag != null) { + out.println("

← Все записи с тегом " + Utils.encodeHTML(paramTag.Name) + "

"); + } + + PageTemplates.printMessages(out, sql, user, mids, visitor, visitor == null ? 4 : 5, 0); + + if (mids.size() >= 20) { + String nextpage = "?before=" + mids.get(mids.size() - 1); + if (paramShow != null) { + nextpage += "&show=" + paramShow; + } + if (paramTag != null) { + nextpage += "&tag=" + URLEncoder.encode(paramTag.Name, "UTF-8"); + } + if (paramSearch != null) { + nextpage += "&search=" + URLEncoder.encode(paramSearch, "UTF-8"); + } + out.println("

Читать дальше →

"); + } + + out.println("
"); + } + + PageTemplates.pageFooter(request, out, visitor, true); + PageTemplates.pageEnd(out); + } + } + + protected void doGetTags(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User user) throws ServletException, IOException { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + + if (visitor == null) { + pageUserRefCookie(request, response, user.getUID()); + } + + response.setContentType("text/html; charset=UTF-8"); + try (PrintWriter out = response.getWriter()) { + String head = ""; + PageTemplates.pageHead(out, "Теги " + user.getUName(), head); + PageTemplates.pageNavigation(out, visitor, null); + pageUserColumn(out, sql, user, visitor); + + out.println("
"); + out.println("

" + pageUserTags(sql, user, visitor, 0) + "

"); + out.println("
"); + + PageTemplates.pageFooter(request, out, visitor, false); + PageTemplates.pageEnd(out); + } + } + + protected void doGetFriends(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User user) throws ServletException, IOException { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + + if (visitor == null) { + pageUserRefCookie(request, response, user.getUID()); + } + + response.setContentType("text/html; charset=UTF-8"); + try (PrintWriter out = response.getWriter()) { + String head = ""; + PageTemplates.pageHead(out, "Подписки " + user.getUName(), head); + PageTemplates.pageNavigation(out, visitor, null); + pageUserColumn(out, sql, user, visitor); + + out.println("
"); + out.println(""); + + List friends = UserQueries.getUserFriends(sql, user.getUID()); + for (int i = 0; i < friends.size(); i++) { + if (i % 3 == 0 && i > 0) { + out.print(""); + } + out.print(""); + } + + out.println("
" + + friends.get(i).getUName() + "
"); + out.println("
"); + + PageTemplates.pageFooter(request, out, visitor, false); + PageTemplates.pageEnd(out); + } + } + + protected void doGetReaders(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User user) throws ServletException, IOException { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + + if (visitor == null) { + pageUserRefCookie(request, response, user.getUID()); + } + + response.setContentType("text/html; charset=UTF-8"); + try (PrintWriter out = response.getWriter()) { + String head = ""; + PageTemplates.pageHead(out, "Читатели " + user.getUName(), head); + PageTemplates.pageNavigation(out, visitor, null); + pageUserColumn(out, sql, user, visitor); + + out.println("
"); + out.println(""); + + List readers = UserQueries.getUserReaders(sql, user.getUID()); + for (int i = 0; i < readers.size(); i++) { + if (i % 3 == 0 && i > 0) { + out.print(""); + } + out.print(""); + } + + out.println("
" + + readers.get(i).getUName() + "
"); + out.println("
"); + + PageTemplates.pageFooter(request, out, visitor, false); + PageTemplates.pageEnd(out); + } + } + + public static void pageUserRefCookie(HttpServletRequest request, HttpServletResponse response, int uid) { + String hReferer = request.getHeader("Referer"); + String ref = Utils.getCookie(request, "ref"); + + if (ref == null && (hReferer == null || !(hReferer.startsWith("http://juick.com/") || hReferer.startsWith("https://juick.com/")))) { + Cookie c = new Cookie("ref", Integer.toString(uid)); + c.setMaxAge(7 * 24 * 60 * 60); + c.setPath("/"); + response.addCookie(c); + } + } + + public static void pageUserColumn(PrintWriter out, JdbcTemplate sql, com.juick.User user, com.juick.User visitor) { + out.println(""); + } + + public static String pageUserTags(JdbcTemplate sql, com.juick.User user, com.juick.User visitor, int cnt) { + List tags = TagQueries.getUserTagsAll(sql, user.getUID()); + int maxUsageCnt = tags.stream().map(t -> t.UsageCnt).max(Integer::max).orElse(0); + String ret = ""; + int count = Math.min(tags.size(), cnt); + for (int i = 0; i < count; i++) { + String tag = Utils.encodeHTML(tags.get(i).Name); + try { + tag = "" + tag + ""; + } catch (UnsupportedEncodingException e) { + } + + if (tags.get(i).UsageCnt > maxUsageCnt / 3 * 2) { + ret += "" + tag + " "; + } else if (tags.get(i).UsageCnt > maxUsageCnt / 3) { + ret += "" + tag + " "; + } else { + ret += tag + " "; + } + } + return ret; + } +} diff --git a/juick-www/src/main/java/com/juick/www/UserThread.java b/juick-www/src/main/java/com/juick/www/UserThread.java new file mode 100644 index 00000000..249cf342 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/UserThread.java @@ -0,0 +1,364 @@ +/* + * 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.www; + +import com.juick.Message; +import com.juick.Tag; +import com.juick.server.MessagesQueries; +import com.juick.server.UserQueries; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * + * @author Ugnich Anton + */ +public class UserThread { + + protected void doGetThread(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, int MID) throws ServletException, IOException { + com.juick.User visitor = Utils.getVisitorUser(sql, request, response); + + if (!MessagesQueries.canViewThread(sql, MID, visitor != null ? visitor.getUID() : 0)) { + response.sendError(403); + return; + } + + com.juick.Message msg = MessagesQueries.getMessage(sql, MID); + + boolean listview = false; + String paramView = request.getParameter("view"); + if (paramView != null) { + if (paramView.equals("list")) { + listview = true; + if (visitor != null) { + UserQueries.setUserOptionInt(sql, visitor.getUID(), "repliesview", 1); + } + } else if (paramView.equals("tree") && visitor != null) { + UserQueries.setUserOptionInt(sql, visitor.getUID(), "repliesview", 0); + } + } else if (visitor != null && UserQueries.getUserOptionInt(sql, visitor.getUID(), "repliesview", 0) == 1) { + listview = true; + } + + String title = msg.getUser().getUName() + ": " + msg.getTagsString(); + + if (visitor == null) { + User.pageUserRefCookie(request, response, msg.getUser().getUID()); + } + + response.setContentType("text/html; charset=UTF-8"); + PrintWriter out = response.getWriter(); + try { + String headers = ""; + if (paramView != null) { + headers += ""; + } + if (msg.Hidden) { + headers += ""; + } + PageTemplates.pageHead(out, title, headers); + PageTemplates.pageNavigation(out, visitor, null); + + out.println("
"); + printMessage(out, sql, msg, visitor); + printReplies(out, sql, msg, visitor, listview); + out.println("
"); + + PageTemplates.pageFooter(request, out, visitor, false); + + out.println(""); + + PageTemplates.pageEnd(out); + } finally { + out.close(); + } + } + + public static com.juick.Message printMessage(PrintWriter out, JdbcTemplate sql, com.juick.Message msg, com.juick.User visitor) { + msg.VisitorCanComment = visitor != null; + + List tags = MessagesQueries.getMessageTags(sql, msg.getMID()); + String tagsStr = PageTemplates.formatTags(tags); + if (msg.ReadOnly) { + tagsStr += " *readonly"; + msg.VisitorCanComment = false; + } + if (msg.Privacy < 0) { + tagsStr += " *friends"; + } + + String txt; + if (!msg.Tags.isEmpty() && msg.Tags.contains("code")) { + txt = PageTemplates.formatMessageCode(msg.getText()); + } else { + txt = PageTemplates.formatMessage(msg.getText()); + } + + if (!tags.isEmpty()) { + tagsStr = "" + tagsStr + ""; + } + + out.println("
    "); + out.println("
  • "); + out.println("
    \""
    "); + out.println("
    "); + out.println("
    "); + out.println(" "); + out.println("
    " + PageTemplates.formatJSLocalTime(msg.getDate()) + "
    "); + out.println("
    " + txt + "
    "); + + if (msg.AttachmentType != null) { + out.println("
    \"\"/
    "); + } + + boolean visitorInBL = false; + if (visitor != null) { + if (visitor.getUID() == msg.getUser().getUID()) { + msg.VisitorCanComment = true; + } else { + visitorInBL = UserQueries.isInBL(sql, msg.getUser().getUID(), visitor.getUID()); + if (visitorInBL) { + msg.VisitorCanComment = false; + } + } + } + + if (msg.VisitorCanComment) { + out.println("
    "); + out.println("
    "); + out.println("
    "); + } + + List recomm = MessagesQueries.getMessageRecommendations(sql, msg.getMID()); + if (!recomm.isEmpty()) { + out.print("
    Рекомендовали (" + recomm.size() + "): "); + for (int i = 0; i < recomm.size(); i++) { + if (i > 0) { + out.print(", "); + } + out.print("@" + recomm.get(i) + ""); + } + out.println("
    "); + } + out.println("
    "); + out.println("
  • "); + + out.println("
  • "); + out.println("
"); + + return msg; + } + + public static void printReplies(PrintWriter out, JdbcTemplate sql, com.juick.Message msg, com.juick.User visitor, boolean listview) { + List replies = MessagesQueries.getReplies(sql, msg.getMID()); + + List blUIDs = new ArrayList(); + for (int i = 0; i < replies.size(); i++) { + com.juick.Message reply = replies.get(i); + if (reply.getUser().getUID() != msg.getUser().getUID() && !blUIDs.contains(reply.getUser().getUID())) { + blUIDs.add(reply.getUser().getUID()); + } + if (reply.ReplyTo > 0) { + boolean added = false; + for (int n = 0; n < replies.size(); n++) { + if (replies.get(n).getRID() == reply.ReplyTo) { + replies.get(n).childs.add(reply); + added = true; + break; + } + } + if (!added) { + reply.ReplyTo = 0; + } + } + } + + if (!replies.isEmpty()) { + if (visitor != null && msg.getUser().getUID() == visitor.getUID()) { + for (Message reply : replies) { + reply.VisitorCanComment = true; + } + } else if (visitor != null && msg.VisitorCanComment) { + blUIDs = UserQueries.checkBL(sql, visitor.getUID(), blUIDs); + for (Message reply : replies) { + reply.VisitorCanComment = reply.getUser().getUID() == visitor.getUID() || !blUIDs.contains(reply.getUser().getUID()); + } + } else { + for (Message reply : replies) { + reply.VisitorCanComment = false; + } + } + + boolean foldable = false; + if (replies.size() > 10) { + for (int i = 0; i < replies.size() - 1; i++) { + if (replies.get(i).getChildsCount() > 1) { + foldable = true; + break; + } + } + } + + out.println("
"); + out.print("
"); + if (listview) { + out.print("Показать деревом"); + } else { + if (foldable) { + out.print("li').show(); $('#replies .msg-comments').hide(); $('#unfoldall').hide(); return false\">Раскрыть все · "); + } + out.print("Показать списком"); + } + out.print("
"); + out.println("

Ответы (" + replies.size() + ")

"); + out.println("
"); + + out.println("
    "); + if (listview) { + printList(out, replies, visitor); + } else { + printTree(out, replies, visitor, 0, 0, false); + } + out.println("
"); + + for (Message reply : replies) { + reply.cleanupChilds(); + } + replies.clear(); + } + } + + public static void printTree(PrintWriter out, List replies, com.juick.User visitor, int ReplyTo, int margin, boolean hidden) { + if (margin > 240) { + margin = 240; + } + + for (int i = 0; i < replies.size(); i++) { + com.juick.Message msg = replies.get(i); + if (msg.ReplyTo == ReplyTo) { + + out.print("
  • 0) { + out.print("margin-left: " + margin + "px;"); + } + if (hidden) { + out.print("display:none;"); + } + out.println("\">"); + if (!msg.getUser().Banned) { + out.println("
    \""
    "); + } else { + out.println("
    "); + } + out.println("
    "); + out.println("
    "); + if (!msg.getUser().Banned) { + out.println(" "); + } else { + out.println("
    [удалено]:
    "); + } + out.println(" "); + out.println("
    " + PageTemplates.formatMessage(msg.getText()) + "
    "); + if (msg.AttachmentType != null) { + out.println("
    \"\"/
    "); + } + if (msg.VisitorCanComment) { + out.println(" "); + out.println("
    "); + } else if (visitor == null) { + out.println(" "); + } + + int childs = msg.getChildsCount(); + if (ReplyTo == 0 && childs > 1 && replies.size() > 10) { + out.println(" "); + + } + out.println("
    "); + out.println("
  • "); + + if (ReplyTo == 0 && childs > 1 && replies.size() > 10) { + printTree(out, msg.childs, visitor, msg.getRID(), margin + 20, true); + } else if (childs > 0) { + printTree(out, msg.childs, visitor, msg.getRID(), margin + 20, hidden); + } + } + } + } + + public static void printList(PrintWriter out, List replies, com.juick.User visitor) { + for (Message msg : replies) { + out.print("
  • "); + if (!msg.getUser().Banned) { + out.println("
    \""
    "); + } else { + out.println("
    "); + } + out.println("
    "); + out.println("
    "); + if (!msg.getUser().Banned) { + out.println(" "); + } else { + out.println("
    [удалено]:
    "); + } + out.println(" "); + out.println("
    " + PageTemplates.formatMessage(msg.getText()) + "
    "); + if (msg.AttachmentType != null) { + out.println("
    \"\"/
    "); + } + out.print("
    /" + msg.getRID()); + if (msg.ReplyTo > 0) { + out.print(" в ответ на /" + msg.ReplyTo + ""); + } + if (msg.VisitorCanComment) { + out.println(" · Ответить
    "); + out.println("
    "); + } else if (visitor == null) { + out.println(" "); + } + out.println("
    "); + out.println("
  • "); + } + } +} diff --git a/juick-www/src/main/java/com/juick/www/Utils.java b/juick-www/src/main/java/com/juick/www/Utils.java new file mode 100644 index 00000000..c98bddf9 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/Utils.java @@ -0,0 +1,242 @@ +/* + * 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.www; + +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.Part; +import java.io.*; +import java.net.URL; +import java.net.URLConnection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.UUID; + +/** + * + * @author Ugnich Anton + */ +public class Utils { + + public static String getCookie(HttpServletRequest request, String name) { + Cookie cookies[] = request.getCookies(); + if (cookies != null) { + for (int i = 0; i < cookies.length; i++) { + if (cookies[i].getName().equals(name)) { + return cookies[i].getValue(); + } + } + } + return null; + } + + public static String receiveMultiPartFile(HttpServletRequest request, String name) throws Exception { + String attachmentFName = null; + + Part filePart = request.getPart("attach"); + if (filePart != null) { + String partname = Utils.getPartFilename(filePart); + if (partname != null && partname.length() > 0) { + String attachmentType = partname.substring(partname.length() - 3).toLowerCase(); + if (attachmentType.equals("jpg") || attachmentType.equals("peg") || attachmentType.equals("png")) { + if (attachmentType.equals("peg")) { + attachmentType = "jpg"; + } + attachmentFName = UUID.randomUUID().toString() + "." + attachmentType; + filePart.write("/var/www/juick.com/i/tmp/" + attachmentFName); + } else { + throw new Exception("Wrong file type"); + } + } + } + + return attachmentFName; + } + + public static com.juick.User getVisitorUser(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) { + String hash = getCookie(request, "hash"); + if (hash != null) { + com.juick.User visitor = com.juick.server.UserQueries.getUserByHash(sql, hash); + if (response != null && visitor != null) { + response.setHeader("X-Username", visitor.getUName()); + } + return visitor; + } else { + return null; + } + } + + public static void sendTemporaryRedirect(HttpServletResponse response, String location) { + response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); + response.setHeader("Location", location); + } + + public static void sendPermanentRedirect(HttpServletResponse response, String location) { + response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); + response.setHeader("Location", location); + } + + public static String getPartFilename(Part part) { + for (String cd : part.getHeader("content-disposition").split(";")) { + if (cd.trim().startsWith("filename")) { + String filename = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", ""); + return filename.substring(filename.lastIndexOf('/') + 1).substring(filename.lastIndexOf('\\') + 1); // MSIE fix. + } + } + return null; + } + + public static void finishSQL(ResultSet rs, Statement stmt) { + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + } + } + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException e) { + } + } + } + + public static void replyJSON(HttpServletRequest request, HttpServletResponse response, String json) throws IOException { + response.setContentType("application/json; charset=UTF-8"); + response.setHeader("Access-Control-Allow-Origin", "*"); + + String callback = request.getParameter("callback"); + if (callback != null && (callback.length() > 64 || !callback.matches("[a-zA-Z0-9\\-\\_]+"))) { + callback = null; + } + + PrintWriter out = response.getWriter(); + try { + if (callback != null) { + out.print(callback + "("); + out.print(json); + out.print(")"); + } else { + out.print(json); + } + } finally { + out.close(); + } + } + + public static String convertArray2String(ArrayList mids) { + String q = ""; + for (int i = 0; i < mids.size(); i++) { + if (i > 0) { + q += ","; + } + q += mids.get(i); + } + return q; + } + + public static String encodeHTML(String str) { + return str.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("'", "'").replaceAll("\"", """).replaceAll("\n", " "); + } + + public static String encodeSphinx(String str) { + return str.replaceAll("@", "\\\\@"); + } + + public static int parseInt(String str, int def) { + int ret = def; + if (str != null) { + try { + ret = Integer.parseInt(str); + } catch (Exception e) { + } + } + return ret; + } + + public static String fetchURL(String url) { + try { + URLConnection c = new URL(url).openConnection(); + BufferedReader in = new BufferedReader(new InputStreamReader(c.getInputStream())); + String inputLine; + StringBuilder b = new StringBuilder(); + while ((inputLine = in.readLine()) != null) { + b.append(inputLine).append("\n"); + } + in.close(); + return b.toString(); + } catch (Exception e) { + System.err.println("fetchURL: "+e.toString()); + return null; + } + } + + public static String downloadImage(URL url) throws Exception { + String attachmentFName = null; + Exception ex = null; + + InputStream is = null; + FileOutputStream fos = null; + try { + URLConnection urlConn = url.openConnection(); + is = urlConn.getInputStream(); + String mime = urlConn.getContentType(); + + String attachmentType; + if (mime != null && mime.equals("image/jpeg")) { + attachmentType = "jpg"; + } else if (mime != null && mime.equals("image/png")) { + attachmentType = "png"; + } else { + throw new Exception("Wrong file type"); + } + + attachmentFName = UUID.randomUUID().toString() + "." + attachmentType; + fos = new FileOutputStream("/var/www/juick.com/i/tmp/" + attachmentFName); + byte[] buffer = new byte[10240]; + int len; + while ((len = is.read(buffer)) > 0) { + fos.write(buffer, 0, len); + } + } catch (Exception e) { + ex = e; + attachmentFName = null; + } finally { + try { + if (is != null) { + is.close(); + } + } finally { + if (fos != null) { + fos.close(); + } + } + } + + if (ex != null) { + throw ex; + } else { + return attachmentFName; + } + } +} diff --git a/juick-www/src/main/java/com/juick/www/VKontakteLogin.java b/juick-www/src/main/java/com/juick/www/VKontakteLogin.java new file mode 100644 index 00000000..509187a9 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/VKontakteLogin.java @@ -0,0 +1,128 @@ +/* + * 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.www; + +import com.juick.server.UserQueries; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URLEncoder; +import java.util.UUID; + +/** + * + * @author Ugnich Anton + */ +public class VKontakteLogin { + + private static final String VK_APPID = "3544101"; + private static final String VK_SECRET = "z2afNI8jA5lIpZ2jsTm1"; + private static final String VK_REDIRECT = "http://juick.com/_vklogin"; + + protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String code = request.getParameter("code"); + if (code == null || code.equals("")) { + response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); + response.setHeader("Location", "https://oauth.vk.com/authorize?client_id=" + VK_APPID + "&redirect_uri=" + URLEncoder.encode(VK_REDIRECT, "utf-8") + "&scope=friends,wall,offline&response_type=code"); + return; + } + + + String tokenjson = Utils.fetchURL("https://oauth.vk.com/access_token?client_id=" + VK_APPID + "&redirect_uri=" + URLEncoder.encode(VK_REDIRECT, "utf-8") + "&client_secret=" + VK_SECRET + "&code=" + URLEncoder.encode(code, "utf-8")); + if (tokenjson == null || tokenjson.isEmpty()) { + System.err.println("VK TOKEN EMPTY"); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return; + } + String token = null; + long vkID = 0; + try { + JSONObject json = new JSONObject(tokenjson); + token = json.getString("access_token"); + vkID = json.getLong("user_id"); + } catch (JSONException e) { + System.err.println("VK TOKEN EXCEPTION: " + e); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return; + } + if (token == null || vkID == 0) { + System.err.println("VK TOKEN EMPTY: " + tokenjson); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return; + } + + + + String graph = Utils.fetchURL("https://api.vk.com/method/users.get?uids=" + vkID + "&fields=screen_name&access_token=" + token); + if (graph == null || graph.isEmpty()) { + System.err.println("VK GRAPH ERROR"); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return; + } + + try { + JSONObject json = new JSONObject(graph).getJSONArray("response").getJSONObject(0); + String vkName = json.getString("first_name") + " " + json.getString("last_name"); + String vkLink = json.getString("screen_name"); + + if (vkName == null || vkLink == null || vkName.isEmpty() || vkName.length() == 1 || vkLink.isEmpty()) { + throw new Exception(); + } + + int uid = getUIDbyVKID(sql, vkID); + if (uid > 0) { + Cookie c = new Cookie("hash", UserQueries.getHashByUID(sql, uid)); + c.setMaxAge(50 * 24 * 60 * 60); + response.addCookie(c); + response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); + response.setHeader("Location", "/"); + } else { + String loginhash = UUID.randomUUID().toString(); + if (!insertDB(sql, vkID, loginhash, token, vkName, vkLink)) { + throw new Exception(); + } + response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); + response.setHeader("Location", "/signup?type=vk&hash=" + loginhash); + } + } catch (Exception e) { + System.err.println("JSON ERROR: " + e); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return; + } + } + + private int getUIDbyVKID(JdbcTemplate sql, long vkID) { + try { + return sql.queryForObject("SELECT user_id FROM vk WHERE vk_id=? AND user_id IS NOT NULL", Integer.class, vkID); + } catch (EmptyResultDataAccessException e) { + return 0; + } + } + + private boolean insertDB(JdbcTemplate sql, long vkID, String loginhash, String token, String vkName, String vkLink) { + return sql.update("INSERT INTO vk(vk_id,loginhash,access_token,vk_name,vk_link) VALUES (?,?,?,?,?)", + vkID, loginhash, token, vkName, vkLink) > 0; + } +} diff --git a/juick-www/src/main/java/com/juick/xmpp/s2s/CacheEntry.java b/juick-www/src/main/java/com/juick/xmpp/s2s/CacheEntry.java new file mode 100644 index 00000000..7cdb18ab --- /dev/null +++ b/juick-www/src/main/java/com/juick/xmpp/s2s/CacheEntry.java @@ -0,0 +1,19 @@ +package com.juick.xmpp.s2s; + +/** + * + * @author ugnich + */ +public class CacheEntry { + + public String hostname; + public long tsCreated; + public long tsUpdated; + public String xml; + + public CacheEntry(String hostname, String xml) { + this.hostname = hostname; + this.tsCreated = this.tsUpdated = System.currentTimeMillis(); + this.xml = xml; + } +} diff --git a/juick-www/src/main/java/com/juick/xmpp/s2s/CleaningUp.java b/juick-www/src/main/java/com/juick/xmpp/s2s/CleaningUp.java new file mode 100644 index 00000000..14d97ed8 --- /dev/null +++ b/juick-www/src/main/java/com/juick/xmpp/s2s/CleaningUp.java @@ -0,0 +1,107 @@ +package com.juick.xmpp.s2s; + +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.util.Iterator; + +/** + * + * @author ugnich + */ +public class CleaningUp implements Runnable { + XMPPComponent xmpp; + + public CleaningUp(XMPPComponent xmpp) { + this.xmpp = xmpp; + } + + @Override + public void run() { + while (true) { + try { + PrintWriter statsFile = new PrintWriter(xmpp.STATSFILE, "UTF-8"); + statsFile.write("

    Threads: " + Thread.activeCount() + "

    "); + statsFile.write("

    Out (" + xmpp.outConnections.size() + ")

    "); + + long now = System.currentTimeMillis(); + + synchronized (xmpp.outConnections) { + for (Iterator i = xmpp.outConnections.iterator(); i.hasNext();) { + ConnectionOut c = i.next(); + int inactive = (int) ((double) (now - c.tsLocalData) / 1000.0); + if (inactive > 900) { + c.closeConnection(); + i.remove(); + } else { + statsFile.write(" "); + statsFile.write(" \n"); + statsFile.write(" \n"); + statsFile.write(" \n"); + statsFile.write(" \n"); + statsFile.write(" \n"); + statsFile.write(" "); + } + } + } + + statsFile.write("
    tosidinactiveout packetsout bytes
    " + c.to + "" + c.streamID + "" + inactive + "" + c.packetsLocal + "" + c.bytesLocal + "

    In (" + xmpp.inConnections.size() + ")

    "); + + synchronized (xmpp.inConnections) { + for (Iterator i = xmpp.inConnections.iterator(); i.hasNext();) { + ConnectionIn c = i.next(); + int inactive = (int) ((double) (now - c.tsRemoteData) / 1000.0); + if (inactive > 900) { + c.closeConnection(); + i.remove(); + } else { + statsFile.write(" "); + if (c.from.isEmpty()) { + statsFile.write(" \n"); + } else if (c.from.size() == 1) { + statsFile.write(" \n"); + } else { + String out = " \n"); + } + statsFile.write(" \n"); + statsFile.write(" \n"); + statsFile.write(" \n"); + statsFile.write(" "); + } + } + } + + statsFile.write("
    fromsidinactivein packets
     " + c.from.get(0) + ""; + for (int n = 0; n < c.from.size(); n++) { + if (n > 0) { + out += "
    "; + } + out += c.from.get(n); + } + statsFile.write(out + "
    " + c.streamID + "" + inactive + "" + c.packetsRemote + "

    Cache (" + xmpp.outCache.size() + ")

    "); + + synchronized (xmpp.outCache) { + for (Iterator i = xmpp.outCache.iterator(); i.hasNext();) { + CacheEntry c = i.next(); + int inactive = (int) ((double) (now - c.tsCreated) / 1000.0); + if (inactive > 600) { + i.remove(); + } else { + statsFile.write(""); + } + } + } + + statsFile.write("
    hostlivesize
    " + c.hostname + "" + inactive + "" + c.xml.length() + "
    "); + statsFile.close(); + + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + } + } catch (FileNotFoundException e) { + } catch (UnsupportedEncodingException e) { + } + } + } +} diff --git a/juick-www/src/main/java/com/juick/xmpp/s2s/Connection.java b/juick-www/src/main/java/com/juick/xmpp/s2s/Connection.java new file mode 100644 index 00000000..eae6efaa --- /dev/null +++ b/juick-www/src/main/java/com/juick/xmpp/s2s/Connection.java @@ -0,0 +1,148 @@ +package com.juick.xmpp.s2s; + +import org.xmlpull.mxp1.MXParser; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.*; +import java.net.Socket; +import java.security.*; +import java.security.cert.CertificateException; +import java.util.UUID; +import java.util.logging.Logger; + +/** + * + * @author ugnich + */ +public class Connection { + + protected static final Logger LOGGER = Logger.getLogger(Connection.class.getName()); + + public String streamID; + public long tsCreated = 0; + public long tsLocalData = 0; + public long bytesLocal = 0; + public long packetsLocal = 0; + XMPPComponent xmpp; + 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_STREAM = "http://etherx.jabber.org/streams"; + XmlPullParser parser = new MXParser(); + OutputStreamWriter writer; + private boolean secured = false; + SSLContext sc; + 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 null; + } + } + }; + + + public Connection(XMPPComponent xmpp) throws XmlPullParserException, KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException, KeyManagementException { + this.xmpp = xmpp; + tsCreated = System.currentTimeMillis(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + KeyStore ks = KeyStore.getInstance("JKS"); + try (InputStream ksIs = new FileInputStream(xmpp.keystore)) { + ks.load(ksIs, xmpp.keystorePassword.toCharArray()); + } + + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory + .getDefaultAlgorithm()); + kmf.init(ks, xmpp.keystorePassword.toCharArray()); + sc = SSLContext.getInstance("TLSv1.2"); + + sc.init(kmf.getKeyManagers(), trustAllCerts, new SecureRandom()); + + } + + 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.fine(tag); + } + + public void sendStanza(String xml) throws IOException { + if (streamID != null) { + LOGGER.fine("OUT: " + xml + "\n"); + } + writer.write(xml); + writer.flush(); + tsLocalData = System.currentTimeMillis(); + bytesLocal += xml.length(); + packetsLocal++; + } + + void closeConnection() { + if (streamID != null) { + LOGGER.info(String.format("CLOSING STREAM %s", streamID)); + } + + try { + writer.write(""); + } catch (Exception e) { + } + + try { + writer.close(); + } catch (Exception e) { + } + + try { + socket.close(); + } catch (Exception e) { + } + } + + static String generateDialbackKey(String to, String from, String id) throws Exception { + Mac hmacSha256 = Mac.getInstance("hmacSHA256"); + + SecretKeySpec secret_key = new SecretKeySpec("$UppPerSeCCret4".getBytes(), "SHA-256"); + hmacSha256.init(secret_key); + byte key[] = hmacSha256.doFinal((to + " " + from + " " + id).getBytes()); + + StringBuilder hexkey = new StringBuilder(); + for (int i = 0; i < key.length; i++) { + hexkey.append(Integer.toHexString(0xFF & key[i])); + } + + return hexkey.toString(); + } + + public boolean isSecured() { + return secured; + } + + public void setSecured(boolean secured) { + this.secured = secured; + } + + public void restartParser() throws XmlPullParserException, IOException { + parser = new MXParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + parser.setInput(new InputStreamReader(socket.getInputStream())); + writer = new OutputStreamWriter(socket.getOutputStream()); + streamID = UUID.randomUUID().toString(); + } +} diff --git a/juick-www/src/main/java/com/juick/xmpp/s2s/ConnectionIn.java b/juick-www/src/main/java/com/juick/xmpp/s2s/ConnectionIn.java new file mode 100644 index 00000000..345caea9 --- /dev/null +++ b/juick-www/src/main/java/com/juick/xmpp/s2s/ConnectionIn.java @@ -0,0 +1,222 @@ +package com.juick.xmpp.s2s; + +import com.juick.xmpp.Iq; +import com.juick.xmpp.JID; +import com.juick.xmpp.Message; +import com.juick.xmpp.Presence; +import com.juick.xmpp.utils.XmlUtils; +import org.xmlpull.v1.XmlPullParser; + +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSocket; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.Socket; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author ugnich + */ +public class ConnectionIn extends Connection implements Runnable { + + private static final Logger LOGGER = Logger.getLogger(ConnectionIn.class.getName()); + + final public List from = new ArrayList<>(); + public long tsRemoteData = 0; + public long packetsRemote = 0; + JuickBot bot; + + public ConnectionIn(XMPPComponent xmpp, JuickBot bot, Socket socket) throws Exception { + super(xmpp); + this.bot = bot; + this.socket = socket; + streamID = UUID.randomUUID().toString(); + restartParser(); + } + + @Override + public void run() { + LOGGER.info("STREAM FROM ? " + streamID + " START"); + 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("STREAM FROM ? " + streamID + " INVALID FIRST PACKET"); + } + boolean xmppversionnew = parser.getAttributeValue(null, "version") != null; + + sendOpenStream(parser.getAttributeValue(null, "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.info("STREAM FROM " + dfrom + " TO " + to + " " + streamID + " ASKING FOR DIALBACK"); + if (dfrom.endsWith(xmpp.HOSTNAME) && (dfrom.equals(xmpp.HOSTNAME) || dfrom.endsWith("." + xmpp.HOSTNAME))) { + break; + } + if (to != null && to.equals(xmpp.HOSTNAME)) { + String dbKey = XmlUtils.getTagText(parser); + updateTsRemoteData(); + + ConnectionOut c = xmpp.getConnectionOut(dfrom, false); + if (c != null) { + c.sendDialbackVerify(streamID, dbKey); + } else { + c = new ConnectionOut(xmpp, dfrom, streamID, dbKey); + xmpp.executorService.submit(c); + } + } else { + throw new HostUnknownException("STREAM FROM " + dfrom + " " + streamID + " INVALID TO " + to); + } + } 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(); + boolean valid = false; + if (vfrom != null && vto != null && vid != null && vkey != null) { + String vkey2 = generateDialbackKey(vfrom, vto, vid); + valid = vkey.equals(vkey2); + } + if (valid) { + sendStanza(""); + LOGGER.info("STREAM FROM " + vfrom + " " + streamID + " DIALBACK VERIFY VALID"); + } else { + sendStanza(""); + LOGGER.warning("STREAM FROM " + vfrom + " " + streamID + " DIALBACK VERIFY INVALID"); + } + } else if (tag.equals("presence") && checkFromTo(parser)) { + Presence p = Presence.parse(parser, null); + if (p != null && (p.type == null || !p.type.equals(Presence.Type.error))) { + bot.incomingPresence(p); + } + } else if (tag.equals("message") && checkFromTo(parser)) { + updateTsRemoteData(); + Message msg = Message.parse(parser, xmpp.childParsers); + if (msg != null && (msg.type == null || !msg.type.equals(Message.Type.error))) { + LOGGER.info("STREAM " + streamID + ": " + msg.toString()); + if (!bot.incomingMessage(msg)) { + xmpp.router.send(msg.toString()); + } + } + } else if (tag.equals("iq") && checkFromTo(parser)) { + updateTsRemoteData(); + String type = parser.getAttributeValue(null, "type"); + String xml = XmlUtils.parseToString(parser, true); + if (type == null || !type.equals(Iq.Type.error)) { + LOGGER.info("STREAM " + streamID + ": " + xml); + xmpp.router.send(xml); + } + } else if (!isSecured() && tag.equals("starttls")) { + LOGGER.info("STREAM " + streamID + " SECURING"); + sendStanza(""); + try { + socket = sc.getSocketFactory().createSocket(socket, socket.getInetAddress().getHostAddress(), + socket.getPort(), true); + ((SSLSocket) socket).setUseClientMode(false); + ((SSLSocket) socket).startHandshake(); + setSecured(true); + LOGGER.info("STREAM " + streamID + " SECURED"); + restartParser(); + } catch (SSLException sex) { + LOGGER.warning("STREAM " + streamID + " SSL ERROR"); + sendStanza(""); + xmpp.removeConnectionIn(this); + closeConnection(); + } + } else if (isSecured() && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) { + sendOpenStream(null, true); + } else { + LOGGER.info("STREAM " + streamID + ": " + XmlUtils.parseToString(parser, true)); + } + } + LOGGER.warning("STREAM " + streamID + " FINISHED"); + xmpp.removeConnectionIn(this); + closeConnection(); + } catch (EOFException ex) { + LOGGER.info(String.format("STREAM %s CLOSED (dirty)", streamID)); + xmpp.removeConnectionIn(this); + closeConnection(); + } catch (HostUnknownException e) { + LOGGER.warning(e.getMessage()); + } catch (Exception e) { + LOGGER.log(Level.WARNING, "STREAM " + streamID + " ERROR", e); + xmpp.removeConnectionIn(this); + closeConnection(); + } + } + + void updateTsRemoteData() { + tsRemoteData = System.currentTimeMillis(); + } + + void sendOpenStream(String from, boolean xmppversionnew) throws IOException { + String openStream = ""; + if (xmppversionnew) { + openStream += ""; + if (!isSecured() && !xmpp.brokenSSLhosts.contains(from)) { + openStream += ""; + } + openStream += ""; + } + sendStanza(openStream); + } + + public void sendDialbackResult(String sfrom, String type) { + try { + sendStanza(""); + if (type.equals("valid")) { + from.add(sfrom); + LOGGER.info("STREAM FROM " + sfrom + " " + streamID + " READY"); + } + } catch (IOException e) { + LOGGER.warning("STREAM FROM " + sfrom + " " + streamID + " ERROR: " + e.toString()); + } + } + + boolean checkFromTo(XmlPullParser parser) throws Exception { + String cfrom = parser.getAttributeValue(null, "from"); + String cto = parser.getAttributeValue(null, "to"); + if (cfrom != null && cto != null && !cfrom.isEmpty() && !cto.isEmpty()) { + JID jidto = new JID(cto); + if (jidto.Host != null && jidto.Username != null && jidto.Host.equals(xmpp.HOSTNAME) && jidto.Username.matches("^[a-zA-Z0-9\\-]{2,16}$")) { + JID jidfrom = new JID(cfrom); + int size = from.size(); + for (int i = 0; i < size; i++) { + if (from.get(i).equals(jidfrom.Host)) { + return true; + } + } + } + } + return false; + } + class HostUnknownException extends Exception { + public HostUnknownException(String message) { + super(message); + } + } +} diff --git a/juick-www/src/main/java/com/juick/xmpp/s2s/ConnectionOut.java b/juick-www/src/main/java/com/juick/xmpp/s2s/ConnectionOut.java new file mode 100644 index 00000000..e604a5b4 --- /dev/null +++ b/juick-www/src/main/java/com/juick/xmpp/s2s/ConnectionOut.java @@ -0,0 +1,166 @@ +package com.juick.xmpp.s2s; + +import com.juick.xmpp.extensions.StreamFeatures; +import com.juick.xmpp.utils.XmlUtils; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSocket; +import java.io.EOFException; +import java.io.IOException; +import java.net.Socket; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.UUID; +import java.util.logging.Level; + +/** + * @author ugnich + */ +public class ConnectionOut extends Connection implements Runnable { + + public boolean streamReady = false; + public String to; + String checkSID = null; + String dbKey = null; + + public ConnectionOut(XMPPComponent xmpp, String hostname) throws CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException, XmlPullParserException, KeyManagementException, KeyStoreException, IOException { + super(xmpp); + to = hostname; + } + + public ConnectionOut(XMPPComponent xmpp, String hostname, String checkSID, String dbKey) throws Exception { + super(xmpp); + to = hostname; + this.checkSID = checkSID; + this.dbKey = dbKey; + streamID = UUID.randomUUID().toString(); + } + + void sendOpenStream() throws IOException { + sendStanza(""); + } + + void processDialback() throws Exception { + if (checkSID != null) { + sendDialbackVerify(checkSID, dbKey); + } + sendStanza("" + + generateDialbackKey(to, xmpp.HOSTNAME, streamID) + ""); + } + + @Override + public void run() { + LOGGER.info("STREAM TO " + to + " START"); + try { + socket = new Socket(); + socket.connect(DNSQueries.getServerAddress(to)); + restartParser(); + + 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.info("STREAM TO " + to + " " + streamID + " OPEN"); + xmpp.addConnectionOut(ConnectionOut.this); + boolean xmppversionnew = parser.getAttributeValue(null, "version") != null; + if (!xmppversionnew) { + processDialback(); + } + + while (parser.next() != XmlPullParser.END_DOCUMENT) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + logParser(); + + 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; + LOGGER.info("STREAM TO " + to + " " + streamID + " READY"); + + String cache = xmpp.getFromCache(to); + if (cache != null) { + LOGGER.info("STREAM TO " + to + " " + streamID + " SENDING CACHE"); + sendStanza(cache); + } + + } else { + LOGGER.info("STREAM TO " + to + " " + streamID + " DIALBACK FAIL"); + } + 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"); + if (from != null && from.equals(to) && sid != null && !sid.isEmpty() && type != null) { + ConnectionIn c = xmpp.getConnectionIn(sid); + if (c != null) { + c.sendDialbackResult(from, type); + } + } + XmlUtils.skip(parser); + } else if (tag.equals("features") && parser.getNamespace().equals(NS_STREAM)) { + StreamFeatures features = StreamFeatures.parse(parser); + if (!isSecured() && features.STARTTLS >= 0 && !xmpp.brokenSSLhosts.contains(to)) { + System.out.println("STREAM TO " + to + " " + streamID + " SECURING"); + sendStanza(""); + } else { + processDialback(); + } + } else if (tag.equals("proceed") && parser.getNamespace().equals(NS_TLS)) { + try { + socket = sc.getSocketFactory().createSocket(socket, socket.getInetAddress().getHostAddress(), + socket.getPort(), true); + ((SSLSocket) socket).startHandshake(); + setSecured(true); + System.out.println("STREAM " + streamID + " SECURED"); + restartParser(); + sendOpenStream(); + } catch (SSLException sex) { + LOGGER.log(Level.SEVERE, String.format("s2s ssl error: %s %s", to, streamID), sex); + sendStanza(""); + xmpp.removeConnectionOut(this); + closeConnection(); + } + } else if (isSecured() && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) { + streamID = parser.getAttributeValue(null, "id"); + } else { + LOGGER.info("STREAM TO " + to + " " + streamID + ": " + XmlUtils.parseToString(parser, true)); + } + } + + LOGGER.warning("STREAM TO " + to + " " + streamID + " FINISHED"); + xmpp.removeConnectionOut(ConnectionOut.this); + closeConnection(); + } catch (EOFException eofex) { + LOGGER.info(String.format("STREAM %s %s CLOSED (dirty)", to, streamID)); + xmpp.removeConnectionOut(ConnectionOut.this); + closeConnection(); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, String.format("s2s out exception: %s %s", to, streamID), e); + xmpp.removeConnectionOut(ConnectionOut.this); + closeConnection(); + } + } + + public void sendDialbackVerify(String sid, String key) { + try { + sendStanza("" + key + ""); + } catch (IOException e) { + LOGGER.log(Level.WARNING, "STREAM TO " + to + " " + streamID + " ERROR", e); + } + } +} diff --git a/juick-www/src/main/java/com/juick/xmpp/s2s/DNSQueries.java b/juick-www/src/main/java/com/juick/xmpp/s2s/DNSQueries.java new file mode 100644 index 00000000..e4c5f085 --- /dev/null +++ b/juick-www/src/main/java/com/juick/xmpp/s2s/DNSQueries.java @@ -0,0 +1,47 @@ +package com.juick.xmpp.s2s; + +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +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 Random rand = new Random(); + + public static InetSocketAddress getServerAddress(String hostname) throws UnknownHostException { + + String host = hostname; + int port = 5269; + + try { + Hashtable env = new Hashtable(5); + env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory"); + 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()); + try { + String srv[] = att.get(i).toString().split(" "); + port = Integer.parseInt(srv[2]); + host = srv[3]; + } catch (Exception e) { + } + } + + ctx.close(); + } catch (NamingException e) { + } + + return new InetSocketAddress(host, port); + } +} diff --git a/juick-www/src/main/java/com/juick/xmpp/s2s/JuickBot.java b/juick-www/src/main/java/com/juick/xmpp/s2s/JuickBot.java new file mode 100644 index 00000000..f0b71689 --- /dev/null +++ b/juick-www/src/main/java/com/juick/xmpp/s2s/JuickBot.java @@ -0,0 +1,378 @@ +package com.juick.xmpp.s2s; + +import com.juick.User; +import com.juick.server.PMQueries; +import com.juick.server.TagQueries; +import com.juick.server.UserQueries; +import com.juick.xmpp.JID; +import com.juick.xmpp.Message; +import com.juick.xmpp.Presence; +import com.juick.xmpp.extensions.Error; +import com.juick.xmpp.extensions.JuickMessage; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * @author ugnich + */ +public class JuickBot { + XMPPComponent xmpp; + public JuickBot(XMPPComponent xmpp, JID JuickJID) { + this.xmpp = xmpp; + this.JuickJID = JuickJID; + } + + public final JID JuickJID; + private static final String HELPTEXT = + "@username text - Send private message\n" + + "*tagname Blah-blah-blah - Post a message with tag 'tagname'\n" + + "#1234 Blah-blah-blah - Answer to message #1234\n" + + "#1234/5 Blah - Answer to reply #1234/5\n" + + "! #1234 - Recommend post\n" + + "\n" + + "# - Show last messages from your feed (## - second page, ...)\n" + + "@ - Show recomendations and popular personal blogs\n" + + "* - Show your tags\n" + + "#1234 - Show message\n" + + "#1234+ - Show message with replies\n" + + "@username - Show user's info\n" + + "@username+ - Show user's info and last 10 messages\n" + + "@username *tag - User's messages with this tag\n" + + "*tag - Show last 10 messages with this tag\n" + + "? blah - Search posts for 'blah'\n" + + "? @username blah - Searching among user\'s posts for 'blah'\n" + + "D #123 - Delete message\n" + + "D #123/45 - Delete reply\n" + + "DL - Delete last message/reply\n" + + "S - Show your subscriptions\n" + + "S #123 - Subscribe to message replies\n" + + "S @username - Subscribe to user's blog\n" + + "U #123 - Unsubscribe from comments\n" + + "U @username - Unsubscribe from user's blog\n" + + "BL - Show your blacklist\n" + + "BL @username - Add/delete user to/from your blacklist\n" + + "BL *tag - Add/delete tag to/from your blacklist\n" + + "ON / OFF - Enable/disable subscriptions delivery\n" + + "PING - Pong\n" + + "\n" + + "Read more: http://juick.com/help/"; + + public boolean incomingPresence(Presence p) throws Exception { + final String username = p.to.Username.toLowerCase(); + final boolean toJuick = username.equals("juick"); + + if (p.type == null) { + Presence reply = new Presence(); + reply.from = new JID(p.to.Username, p.to.Host, null); + reply.to = new JID(p.from.Username, p.from.Host, null); + reply.type = Presence.Type.unsubscribe; + xmpp.sendOut(reply); + return true; + } else if (p.type.equals(Presence.Type.probe)) { + int uid_to = 0; + if (!toJuick) { + uid_to = UserQueries.getUIDbyName(xmpp.sql, username); + } + + if (toJuick || uid_to > 0) { + Presence reply = new Presence(); + reply.from = p.to; + reply.from.Resource = "Juick"; + reply.to = p.from; + reply.priority = 10; + xmpp.sendOut(reply); + } else { + Presence reply = new Presence(p.to, p.from, Presence.Type.error); + reply.id = p.id; + reply.addChild(new Error(Error.Type.cancel, "item-not-found")); + xmpp.sendOut(reply); + return true; + } + return true; + } else if (p.type.equals(Presence.Type.subscribe)) { + boolean canSubscribe = false; + if (toJuick) { + canSubscribe = true; + } else { + int uid_to = UserQueries.getUIDbyName(xmpp.sql, username); + if (uid_to > 0) { + PMQueries.addPMinRoster(xmpp.sql, uid_to, p.from.Bare()); + canSubscribe = true; + } + } + + if (canSubscribe) { + Presence reply = new Presence(p.to, p.from, Presence.Type.subscribed); + xmpp.sendOut(reply); + + reply.from.Resource = "Juick"; + reply.priority = 10; + reply.type = null; + xmpp.sendOut(reply); + + return true; + } else { + Presence reply = new Presence(p.to, p.from, Presence.Type.error); + reply.id = p.id; + reply.addChild(new Error(Error.Type.cancel, "item-not-found")); + xmpp.sendOut(reply); + return true; + } + } else if (p.type.equals(Presence.Type.unsubscribe)) { + if (!toJuick) { + int uid_to = UserQueries.getUIDbyName(xmpp.sql, username); + if (uid_to > 0) { + PMQueries.removePMinRoster(xmpp.sql, uid_to, p.from.Bare()); + } + } + + Presence reply = new Presence(p.to, p.from, Presence.Type.unsubscribed); + xmpp.sendOut(reply); + } + + return false; + } + + public boolean incomingMessage(Message msg) throws Exception { + if (msg.body == null || msg.body.isEmpty()) { + return true; + } + + String username = msg.to.Username.toLowerCase(); + + User user_from = null; + String signuphash = ""; + user_from = UserQueries.getUserByJID(xmpp.sql, msg.from.Bare()); + if (user_from == null) { + signuphash = UserQueries.getSignUpHashByJID(xmpp.sql, msg.from.Bare()); + } + + if (user_from == null) { + Message reply = new Message(msg.to, msg.from, Message.Type.chat); + if (username.equals("juick")) { + reply.body = "Для того, чтобы начать пользоваться сервисом, пожалуйста пройдите быструю регистрацию: 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."; + } else { + reply.body = "Внимание, системное сообщение!\nВаш JabberID не обнаружен в списке доверенных. Для того, чтобы отправить сообщение пользователю " + username + "@juick.com, пожалуйста зарегистрируйте свой JabberID в системе: http://juick.com/signup?type=xmpp&hash=" + signuphash + "\nЕсли у вас уже есть учетная запись на Juick, вы сможете присоединить этот JabberID к ней.\n\nWarning, system message!\nYour JabberID is not found in our server's white list. To send a message to " + username + "@juick.com, 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."; + } + xmpp.sendOut(reply); + return true; + } + + if (username.equals("juick")) { + return incomingMessageJuick(user_from, msg); + } + + int uid_to = UserQueries.getUIDbyName(xmpp.sql, username); + + if (uid_to == 0) { + Message reply = new Message(msg.to, msg.from, Message.Type.error); + reply.id = msg.id; + reply.addChild(new Error(Error.Type.cancel, "item-not-found")); + xmpp.sendOut(reply); + return true; + } + + boolean success = false; + if (!UserQueries.isInBLAny(xmpp.sql, uid_to, user_from.getUID())) { + success = PMQueries.createPM(xmpp.sql, user_from.getUID(), uid_to, msg.body); + } + + if (success) { + Message m = new Message(); + m.from = new JID("juick", "juick.com", null); + m.to = new JID(Integer.toString(uid_to), "push.juick.com", null); + JuickMessage jmsg = new JuickMessage(); + jmsg.setUser(user_from); + jmsg.setText(msg.body); + m.childs.add(jmsg); + xmpp.router.send(m.toString()); + + m.to.Host = "ws.juick.com"; + xmpp.router.send(m.toString()); + + List jids; + boolean inroster = false; + jids = UserQueries.getJIDsbyUID(xmpp.sql, uid_to); + for (String jid : jids) { + Message mm = new Message(); + mm.to = new JID(jid); + mm.type = Message.Type.chat; + inroster = PMQueries.havePMinRoster(xmpp.sql, user_from.getUID(), jid); + if (inroster) { + mm.from = new JID(jmsg.getUser().getUName(), "juick.com", "Juick"); + mm.body = msg.body; + } else { + mm.from = new JID("juick", "juick.com", "Juick"); + mm.body = "Private message from @" + jmsg.getUser().getUName() + ":\n" + msg.body; + } + xmpp.sendOut(mm); + } + } else { + Message reply = new Message(msg.to, msg.from, Message.Type.error); + reply.id = msg.id; + reply.addChild(new Error(Error.Type.cancel, "not-allowed")); + xmpp.sendOut(reply); + } + + return true; + } + private static Pattern regexPM = Pattern.compile("^\\@(\\S+)\\s+([\\s\\S]+)$"); + + public boolean incomingMessageJuick(User user_from, Message msg) throws Exception { + String command = msg.body.trim(); + int commandlen = command.length(); + + // COMPATIBILITY + if (commandlen > 7 && command.substring(0, 3).equalsIgnoreCase("PM ")) { + command = command.substring(3).trim(); + commandlen = command.length(); + } + + if (commandlen == 4) { + if (command.equalsIgnoreCase("PING")) { + commandPing(msg); + return true; + } else if (command.equalsIgnoreCase("HELP")) { + commandHelp(msg); + return true; + } + } else if (commandlen == 5 && command.equalsIgnoreCase("LOGIN")) { + commandLogin(msg, user_from); + return true; + } else if (command.charAt(0) == '@') { + Matcher matchPM = regexPM.matcher(command); + if (matchPM.find()) { + String user_to = matchPM.group(1); + String msgtxt = matchPM.group(2); + commandPM(msg, user_from, user_to, msgtxt); + return true; + } + } else if (commandlen == 2 && command.equalsIgnoreCase("BL")) { + commandBLShow(msg, user_from); + return true; + } + + return false; + } + + private void commandPing(Message m) throws Exception { + Presence p = new Presence(JuickJID, m.from); + p.priority = 10; + xmpp.sendOut(p); + + Message reply = new Message(JuickJID, m.from, Message.Type.chat); + reply.body = "PONG"; + xmpp.sendOut(reply); + } + + private void commandHelp(Message m) throws Exception { + Message reply = new Message(JuickJID, m.from, Message.Type.chat); + reply.body = HELPTEXT; + xmpp.sendOut(reply); + } + + private void commandLogin(Message m, User user_from) throws Exception { + Message reply = new Message(JuickJID, m.from, Message.Type.chat); + reply.body = "http://juick.com/login?" + UserQueries.getHashByUID(xmpp.sql, user_from.getUID()); + xmpp.sendOut(reply); + } + + private void commandPM(Message m, User user_from, String user_to, String body) throws Exception { + int ret = 0; + + int uid_to = 0; + List jids_to = null; + boolean haveInRoster = false; + + if (user_to.indexOf('@') > 0) { + uid_to = UserQueries.getUIDbyJID(xmpp.sql, user_to); + } else { + uid_to = UserQueries.getUIDbyName(xmpp.sql, user_to); + } + + if (uid_to > 0) { + if (!UserQueries.isInBLAny(xmpp.sql, uid_to, user_from.getUID())) { + if (PMQueries.createPM(xmpp.sql, user_from.getUID(), uid_to, body)) { + jids_to = UserQueries.getJIDsbyUID(xmpp.sql, uid_to); + ret = 200; + } else { + ret = 500; + } + } else { + ret = 403; + } + } else { + ret = 404; + } + + if (ret == 200) { + Message msg = new Message(); + msg.from = new JID("juick", "juick.com", null); + msg.to = new JID(Integer.toString(uid_to), "push.juick.com", null); + JuickMessage jmsg = new JuickMessage(); + jmsg.setUser(user_from); + jmsg.setText(body); + msg.childs.add(jmsg); + xmpp.router.send(msg.toString()); + + msg.to.Host = "ws.juick.com"; + xmpp.router.send(msg.toString()); + + for (String jid : jids_to) { + Message mm = new Message(); + mm.to = new JID(jid); + mm.type = Message.Type.chat; + haveInRoster = PMQueries.havePMinRoster(xmpp.sql, user_from.getUID(), jid); + if (haveInRoster) { + mm.from = new JID(user_from.getUName(), "juick.com", "Juick"); + mm.body = body; + } else { + mm.from = new JID("juick", "juick.com", "Juick"); + mm.body = "Private message from @" + user_from.getUName() + ":\n" + body; + } + xmpp.sendOut(mm); + } + } + + Message reply = new Message(m.to, m.from); + if (ret == 200) { + reply.type = m.type; + reply.body = "Private message sent"; + } else { + reply.type = Message.Type.error; + reply.body = "Error " + ret; + } + xmpp.sendOut(reply); + } + + private void commandBLShow(Message m, User user_from) throws Exception { + List blusers = UserQueries.getUserBLUsers(xmpp.sql, user_from.getUID()); + List bltags = TagQueries.getUserBLTags(xmpp.sql, user_from.getUID()); + + String txt = ""; + 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.getUName() + "\n"; + } + } + if (txt.isEmpty()) { + txt = "You don't have any users or tags in your blacklist."; + } + + Message reply = new Message(JuickJID, m.from, Message.Type.chat); + reply.body = txt; + xmpp.sendOut(reply); + } +} diff --git a/juick-www/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java b/juick-www/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java new file mode 100644 index 00000000..d0b231e2 --- /dev/null +++ b/juick-www/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java @@ -0,0 +1,443 @@ +package com.juick.xmpp.s2s; + +import com.juick.User; +import com.juick.server.MessagesQueries; +import com.juick.server.SubscriptionsQueries; +import com.juick.server.UserQueries; +import com.juick.xmpp.*; +import com.juick.xmpp.extensions.JuickMessage; +import com.juick.xmpp.extensions.Nickname; +import com.juick.xmpp.extensions.XOOB; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.xmlpull.v1.XmlPullParserException; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author ugnich + */ +public class XMPPComponent implements ServletContextListener, Stream.StreamListener, + Message.MessageListener, Iq.IqListener, Presence.PresenceListener { + + private static final Logger logger = Logger.getLogger(XMPPComponent.class.getName()); + + public final ExecutorService executorService = Executors.newCachedThreadPool(); + StreamComponent router; + JuickBot bot; + + public String HOSTNAME, componentName; + public String STATSFILE = null; + public String keystore; + public String keystorePassword; + public List brokenSSLhosts; + final List inConnections = Collections.synchronizedList(new ArrayList<>()); + final List outConnections = Collections.synchronizedList(new ArrayList<>()); + final List outCache = Collections.synchronizedList(new ArrayList<>()); + JdbcTemplate sql; + final public HashMap childParsers = new HashMap<>(); + + public void addConnectionIn(ConnectionIn c) { + synchronized (inConnections) { + inConnections.add(c); + } + } + + public void addConnectionOut(ConnectionOut c) { + synchronized (outConnections) { + outConnections.add(c); + } + } + + public void removeConnectionIn(ConnectionIn c) { + synchronized (inConnections) { + inConnections.remove(c); + } + } + + public void removeConnectionOut(ConnectionOut c) { + synchronized (outConnections) { + outConnections.remove(c); + } + } + + public String getFromCache(String hostname) { + CacheEntry ret = null; + synchronized (outCache) { + for (Iterator i = outCache.iterator(); i.hasNext();) { + CacheEntry c = i.next(); + if (c.hostname != null && c.hostname.equals(hostname)) { + ret = c; + i.remove(); + break; + } + } + } + return (ret != null) ? ret.xml : null; + } + + public ConnectionOut getConnectionOut(String hostname, boolean needReady) { + synchronized (outConnections) { + for (ConnectionOut c : outConnections) { + if (c.to != null && c.to.equals(hostname) && (!needReady || c.streamReady)) { + return c; + } + } + } + return null; + } + + public ConnectionIn getConnectionIn(String streamID) { + synchronized (inConnections) { + for (ConnectionIn c : inConnections) { + if (c.streamID != null && c.streamID.equals(streamID)) { + return c; + } + } + } + return null; + } + + public void sendOut(Stanza s) { + sendOut(s.to.Host, s.toString()); + } + + public void sendOut(String hostname, String xml) { + boolean haveAnyConn = false; + + ConnectionOut connOut = null; + synchronized (outConnections) { + for (ConnectionOut c : outConnections) { + if (c.to != null && c.to.equals(hostname)) { + if (c.streamReady) { + connOut = c; + break; + } else { + haveAnyConn = true; + break; + } + } + } + } + if (connOut != null) { + try { + connOut.sendStanza(xml); + } catch (IOException e) { + logger.warning("STREAM TO " + connOut.to + " " + connOut.streamID + " ERROR: " + e.toString()); + } + return; + } + + boolean haveCache = false; + synchronized (outCache) { + for (CacheEntry c : outCache) { + if (c.hostname != null && c.hostname.equals(hostname)) { + c.xml += xml; + c.tsUpdated = System.currentTimeMillis(); + haveCache = true; + break; + } + } + if (!haveCache) { + outCache.add(new CacheEntry(hostname, xml)); + } + } + + if (!haveAnyConn) { + try { + ConnectionOut connectionOut = new ConnectionOut(this, hostname); + executorService.submit(connectionOut); + } catch (CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | XmlPullParserException | KeyStoreException | KeyManagementException | IOException e) { + logger.log(Level.SEVERE, "s2s out error", e); + } + } + } + + @Override + public void contextInitialized(ServletContextEvent sce) { + + logger.info("component initialized"); + Properties conf = new Properties(); + try { + conf.load(sce.getServletContext().getResourceAsStream("/WEB-INF/juick.conf")); + HOSTNAME = conf.getProperty("hostname"); + componentName = conf.getProperty("componentname"); + JID Jid = new JID(conf.getProperty("xmppbot_jid")); + STATSFILE = conf.getProperty("statsfile"); + keystore = conf.getProperty("keystore"); + keystorePassword = conf.getProperty("keystore_password"); + brokenSSLhosts = Arrays.asList(conf.getProperty("broken_ssl_hosts", "").split(",")); + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver")); + dataSource.setUrl(conf.getProperty("datasource_url")); + sql = new JdbcTemplate(dataSource); + bot = new JuickBot(this, Jid); + + childParsers.put(JuickMessage.XMLNS, new JuickMessage()); + + executorService.submit(() -> { + Socket routerSocket = null; + try { + routerSocket = new Socket("localhost", 5347); + router = new StreamComponent(new JID("s2s"), routerSocket.getInputStream(), routerSocket.getOutputStream(), conf.getProperty("xmpp_password")); + router.addChildParser(new JuickMessage()); + router.addListener((Stream.StreamListener) this); + router.addListener((Message.MessageListener) this); + router.addListener((Iq.IqListener) this); + router.startParsing(); + } catch (IOException e) { + logger.log(Level.SEVERE, "router error", e); + } + }); + executorService.submit(() -> { + final ServerSocket listener = new ServerSocket(5269); + logger.info("s2s listener ready"); + while (true) { + try { + Socket socket = listener.accept(); + ConnectionIn client = new ConnectionIn(this, bot, socket); + addConnectionIn(client); + executorService.submit(client); + } catch (Exception e) { + logger.log(Level.SEVERE, "s2s error", e); + } + } + }); + executorService.submit(new CleaningUp(this)); + + } catch (Exception e) { + logger.log(Level.SEVERE, "XMPPComponent error", e); + } + } + + + + @Override + public void contextDestroyed(ServletContextEvent sce) { + synchronized (outConnections) { + for (Iterator i = outConnections.iterator(); i.hasNext();) { + ConnectionOut c = i.next(); + c.closeConnection(); + i.remove(); + } + } + + synchronized (inConnections) { + for (Iterator i = inConnections.iterator(); i.hasNext();) { + ConnectionIn c = i.next(); + c.closeConnection(); + i.remove(); + } + } + + try { + closeRouterConnection(); + } catch (IOException e) { + logger.log(Level.WARNING, "router warning", e); + } + executorService.shutdown(); + logger.info("component destroyed"); + } + public void closeRouterConnection() throws IOException { + router.logoff(); + } + + public void sendJuickMessage(JuickMessage jmsg) { + List jids = new ArrayList<>(); + + if (jmsg.FriendsOnly) { + jids = SubscriptionsQueries.getJIDSubscribedToUser(sql, jmsg.getUser().getUID(), jmsg.FriendsOnly); + } else { + List users = SubscriptionsQueries.getSubscribedUsers(sql, jmsg.getUser().getUID(), jmsg.getMID()); + for (User user : users) { + for (String jid : UserQueries.getJIDsbyUID(sql, user.getUID())) { + jids.add(jid); + } + } + } + + String txt = "@" + jmsg.getUser().getUName() + ":" + jmsg.getTagsString() + "\n"; + String attachment = jmsg.getAttachmentURL(); + if (attachment != null) { + txt += attachment + "\n"; + } + txt += jmsg.getText() + "\n\n"; + txt += "#" + jmsg.getMID() + " http://juick.com/" + jmsg.getMID(); + + Nickname nick = new Nickname(); + nick.Nickname = "@" + jmsg.getUser().getUName(); + + com.juick.xmpp.Message msg = new com.juick.xmpp.Message(); + msg.from = bot.JuickJID; + msg.body = txt; + msg.type = Message.Type.chat; + msg.thread = "juick-" + jmsg.getMID(); + msg.addChild(jmsg); + msg.addChild(nick); + if (attachment != null) { + XOOB oob = new XOOB(); + oob.URL = attachment; + msg.addChild(oob); + } + + for (String jid : jids) { + msg.to = new JID(jid); + sendOut(msg); + } + } + + public void sendJuickComment(JuickMessage jmsg) { + List users; + String replyQuote; + String replyTo; + + users = SubscriptionsQueries.getUsersSubscribedToComments(sql, jmsg.getMID(), jmsg.getUser().getUID()); + com.juick.Message replyMessage = jmsg.ReplyTo > 0 ? MessagesQueries.getReply(sql, jmsg.getMID(), jmsg.ReplyTo) + : MessagesQueries.getMessage(sql, jmsg.getMID()); + replyTo = replyMessage.getUser().getUName(); + replyQuote = getReplyQuote(replyMessage); + + String txt = "Reply by @" + jmsg.getUser().getUName() + ":\n" + replyQuote + "\n@" + replyTo + " "; + String attachment = jmsg.getAttachmentURL(); + if (attachment != null) { + txt += attachment + "\n"; + } + txt += jmsg.getText() + "\n\n" + "#" + jmsg.getMID() + "/" + jmsg.getRID() + " http://juick.com/" + jmsg.getMID() + "#" + jmsg.getRID(); + + com.juick.xmpp.Message msg = new com.juick.xmpp.Message(); + msg.from = bot.JuickJID; + msg.body = txt; + msg.type = Message.Type.chat; + msg.addChild(jmsg); + for (User user : users) { + for (String jid : UserQueries.getJIDsbyUID(sql, user.getUID())) { + msg.to = new JID(jid); + sendOut(msg); + } + } + } + + private String getReplyQuote(com.juick.Message q) { + String quote = q.getText(); + if (quote.length() > 50) { + quote = ">" + quote.substring(0, 47).replace('\n', ' ') + "...\n"; + } else if (quote.length() > 0) { + quote = ">" + quote.replace('\n', ' ') + "\n"; + } + return quote; + } + + public void sendJuickRecommendation(JuickMessage recomm) { + List users; + JuickMessage jmsg; + jmsg = new JuickMessage(MessagesQueries.getMessage(sql, recomm.getMID())); + users = SubscriptionsQueries.getUsersSubscribedToUserRecommendations(sql, + recomm.getUser().getUID(), recomm.getMID(), jmsg.getUser().getUID()); + + String txt = "Recommended by @" + recomm.getUser().getUName() + ":\n"; + txt += "@" + jmsg.getUser().getUName() + ":" + jmsg.getTagsString() + "\n"; + String attachment = jmsg.getAttachmentURL(); + if (attachment != null) { + txt += attachment + "\n"; + } + txt += jmsg.getText() + "\n\n"; + txt += "#" + jmsg.getMID(); + if (jmsg.Replies > 0) { + if (jmsg.Replies % 10 == 1 && jmsg.Replies % 100 != 11) { + txt += " (" + jmsg.Replies + " reply)"; + } else { + txt += " (" + jmsg.Replies + " replies)"; + } + } + txt += " http://juick.com/" + jmsg.getMID(); + + Nickname nick = new Nickname(); + nick.Nickname = "@" + jmsg.getUser().getUName(); + + com.juick.xmpp.Message msg = new com.juick.xmpp.Message(); + msg.from = bot.JuickJID; + msg.body = txt; + msg.type = Message.Type.chat; + msg.thread = "juick-" + jmsg.getMID(); + msg.addChild(jmsg); + msg.addChild(nick); + if (attachment != null) { + XOOB oob = new XOOB(); + oob.URL = attachment; + msg.addChild(oob); + } + + for (User user : users) { + for (String jid : UserQueries.getJIDsbyUID(sql, user.getUID())) { + msg.to = new JID(jid); + sendOut(msg); + } + } + } + + @Override + public boolean onIq(Iq iq) { + JID jid = iq.to; + if (!jid.Host.equals(componentName)) { + logger.info("STREAM ROUTER (IQ): " + iq.toString()); + sendOut(iq); + } + return false; + } + + @Override + public void onMessage(Message xmsg) { + logger.info("STREAM ROUTER (PROCESS): " + xmsg.toString()); + JuickMessage jmsg = (JuickMessage) xmsg.getChild(JuickMessage.XMLNS); + JID jid = xmsg.to; + if (jid.Host.equals(componentName)) { + if (jmsg != null) { + if (jid.Username != null && jid.Username.equals("recomm")) { + sendJuickRecommendation(jmsg); + } else { + if (jmsg.getRID() > 0) { + sendJuickComment(jmsg); + } else if (jmsg.getMID() > 0) { + sendJuickMessage(jmsg); + } + } + } + } else { + sendOut(xmsg); + } + } + + @Override + public void onPresence(Presence presence) { + JID jid = presence.to; + if (!jid.Host.equals(componentName)) { + logger.info("STREAM ROUTER (PRESENCE): " + presence.toString()); + sendOut(presence); + } + } + + @Override + public void onStreamReady() { + logger.info("STREAM ROUTER (READY)"); + } + + @Override + public void onStreamFail(Exception ex) { + logger.log(Level.SEVERE, "STREAM ROUTER (FAIL)", ex); + } +} diff --git a/juick-www/src/main/java/ru/sape/Sape.java b/juick-www/src/main/java/ru/sape/Sape.java new file mode 100644 index 00000000..c00054ae --- /dev/null +++ b/juick-www/src/main/java/ru/sape/Sape.java @@ -0,0 +1,25 @@ +/* + * http://code.google.com/p/javasape/ + */ +package ru.sape; + +import javax.servlet.http.Cookie; + +public class Sape { + + private final String sapeUser; + private final SapeConnection sapePageLinkConnection; + + public Sape(String sapeUser, String host, int socketTimeout, int cacheLifeTime) { + this.sapeUser = sapeUser; + + this.sapePageLinkConnection = new SapeConnection( + "/code.php?user=" + sapeUser + "&host=" + host, + "SAPE_Client PHP", socketTimeout, cacheLifeTime); + } + public boolean debug = false; + + public SapePageLinks getPageLinks(String requestUri, Cookie[] cookies) { + return new SapePageLinks(sapePageLinkConnection, sapeUser, requestUri, cookies, debug); + } +} diff --git a/juick-www/src/main/java/ru/sape/SapeConnection.java b/juick-www/src/main/java/ru/sape/SapeConnection.java new file mode 100644 index 00000000..8c794b08 --- /dev/null +++ b/juick-www/src/main/java/ru/sape/SapeConnection.java @@ -0,0 +1,107 @@ +package ru.sape; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SapeConnection { + + private final String version = "1.0.3"; + private final List serverList = Arrays.asList("dispenser-01.sape.ru", "dispenser-02.sape.ru"); + private final String dispenserPath; + private final String userAgent; + private final int socketTimeout; + private final int cacheLifeTime; + + public SapeConnection(String dispenserPath, String userAgent, int socketTimeout, int cacheLifeTime) { + this.dispenserPath = dispenserPath; + this.userAgent = userAgent; + this.socketTimeout = socketTimeout; + this.cacheLifeTime = cacheLifeTime; + } + + protected String fetchRemoteFile(String host, String path) throws IOException { + Reader r = null; + + try { + HttpURLConnection connection = (HttpURLConnection) ((new URL(("http://" + host + path)).openConnection())); + + if (socketTimeout > 0) { + connection.setConnectTimeout(socketTimeout); + connection.setReadTimeout(socketTimeout); + } + + connection.addRequestProperty("User-Agent", userAgent + ' ' + version); + + connection.setDoOutput(true); + connection.setDoInput(true); + connection.setUseCaches(false); + connection.setRequestMethod("GET"); + connection.connect(); + + r = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8")); + + StringWriter sw = new StringWriter(); + + int b; + + while ((b = r.read()) != -1) { + sw.write(b); + } + + return sw.toString(); + } finally { + if (r != null) { + r.close(); + } + } + } + Map cached; + long cacheUpdated; + + @SuppressWarnings("unchecked") + public Map getData() { + if (cacheLifeTime <= (System.currentTimeMillis() - cacheUpdated) / 1000) { + for (String server : serverList) { + String data; + + try { + data = fetchRemoteFile(server, dispenserPath + "&charset=UTF-8"); + } catch (IOException e1) { + continue; + } + + if (data.startsWith("FATAL ERROR:")) { + System.err.println("Sape responded with error: " + data); + + continue; + } + + try { + cached = (Map) new SerializedPhpParser(data).parse(); + } catch (Exception e) { + System.err.println("Can't parse Sape data: " + e); + continue; + } + + cacheUpdated = System.currentTimeMillis(); + + return cached; + } + + System.err.println("Unable to fetch Sape data"); + + return new HashMap(); + } + + return cached; + } +} diff --git a/juick-www/src/main/java/ru/sape/SapePageLinks.java b/juick-www/src/main/java/ru/sape/SapePageLinks.java new file mode 100644 index 00000000..498aeac0 --- /dev/null +++ b/juick-www/src/main/java/ru/sape/SapePageLinks.java @@ -0,0 +1,95 @@ +package ru.sape; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.servlet.http.Cookie; + +public class SapePageLinks { + + private boolean showCode; + + public SapePageLinks(SapeConnection sapeConnection, String sapeUser, String requestUri, Cookie[] cookies) { + this(sapeConnection, sapeUser, requestUri, cookies, false); + } + + @SuppressWarnings("unchecked") + public SapePageLinks(SapeConnection sapeConnection, String sapeUser, String requestUri, Cookie[] cookies, boolean showCode) { + if (sapeUser.equals(getCookieValue(cookies, "sape_cookie"))) { + showCode = true; + } + + Map data = sapeConnection.getData(); + + if (data.containsKey("__sape_delimiter__")) { + linkDelimiter = (String) data.get("__sape_delimiter__"); + } + + if (data.containsKey(requestUri)) { + pageLinks = new ArrayList(((Map) data.get(requestUri)).values()); + } + + if (data.containsKey("__sape_new_url__")) { + if (showCode) { + Object newUrl = data.get("__sape_new_url__"); + + if (newUrl instanceof Map) { + pageLinks = new ArrayList(((Map) newUrl).values()); + } else { + pageLinks = new ArrayList(Arrays.asList((String) newUrl)); + } + } + } + + this.showCode = showCode; + } + private String linkDelimiter = "."; + private List pageLinks = new ArrayList(); + + public String render() { + return render(-1); + } + + public String render(int count) { + StringBuilder s = new StringBuilder(); + + if (count < 0) { + count = pageLinks.size(); + } + + for (Iterator i = pageLinks.iterator(); i.hasNext() && count > 0; count--) { + if (s.length() > 0) { + s.append(linkDelimiter); + } + + String l = i.next(); + + s.append(l); + + i.remove(); + } + + if (showCode) { + s.insert(0, ""); + s.append(""); + } + + return s.toString(); + } + + private static String getCookieValue(Cookie[] cookies, String name) { + if (cookies == null) { + return null; + } + + for (Cookie cookie : cookies) { + if (cookie.getName().equals(name)) { + return cookie.getValue(); + } + } + + return null; + } +} diff --git a/juick-www/src/main/java/ru/sape/SerializedPhpParser.java b/juick-www/src/main/java/ru/sape/SerializedPhpParser.java new file mode 100644 index 00000000..a24551b9 --- /dev/null +++ b/juick-www/src/main/java/ru/sape/SerializedPhpParser.java @@ -0,0 +1,221 @@ +/* +Copyright (c) 2007 Zsolt Szász + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package ru.sape; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * Deserializes a serialized PHP data structure into corresponding Java objects. It supports + * the integer, float, boolean, string primitives that are mapped to their Java + * equivalent, plus arrays that are parsed into Map instances and objects + * that are represented by {@link SerializedPhpParser.PhpObject} instances. + *

    + * Example of use: + *

    + *      String input = "O:8:"TypeName":1:{s:3:"foo";s:3:"bar";}";
    + *      SerializedPhpParser serializedPhpParser = new SerializedPhpParser(input);
    + *      Object result = serializedPhpParser.parse();
    + * 
    + * + * The result object will be a PhpObject with the name "TypeName" and + * the attribute "foo" = "bar". + */ +class SerializedPhpParser { + + private final String input; + private int index; + private boolean assumeUTF8 = true; + private Pattern acceptedAttributeNameRegex = null; + + public SerializedPhpParser(String input) { + this.input = input; + } + + public Object parse() { + char type = input.charAt(index); + switch (type) { + case 'i': + index += 2; + return parseInt(); + case 'd': + index += 2; + return parseFloat(); + case 'b': + index += 2; + return parseBoolean(); + case 's': + index += 2; + return parseString(); + case 'a': + index += 2; + return parseArray(); + case 'O': + index += 2; + return parseObject(); + case 'N': + index += 2; + return NULL; + default: + throw new IllegalStateException("Encountered unknown type [" + type + "], str=" + input.substring(index)); + } + } + + private Object parseObject() { + PhpObject phpObject = new PhpObject(); + int strLen = readLength(); + phpObject.name = input.substring(index, index + strLen); + index = index + strLen + 2; + int attrLen = readLength(); + for (int i = 0; i < attrLen; i++) { + Object key = parse(); + Object value = parse(); + if (isAcceptedAttribute(key)) { + phpObject.attributes.put(key, value); + } + } + index++; + return phpObject; + } + + private Map parseArray() { + int arrayLen = readLength(); + Map result = new LinkedHashMap(); + for (int i = 0; i < arrayLen; i++) { + Object key = parse(); + Object value = parse(); + if (isAcceptedAttribute(key)) { + result.put(key, value); + } + } + index++; + return result; + } + + private boolean isAcceptedAttribute(Object key) { + if (acceptedAttributeNameRegex == null) { + return true; + } + if (!(key instanceof String)) { + return true; + } + return acceptedAttributeNameRegex.matcher((String) key).matches(); + } + + private int readLength() { + int delimiter = input.indexOf(':', index); + int arrayLen = Integer.valueOf(input.substring(index, delimiter)); + index = delimiter + 2; + return arrayLen; + } + + /** + * Assumes strings are utf8 encoded + * + * @return + */ + private String parseString() { + int strLen = readLength(); + + int utfStrLen = 0; + int byteCount = 0; + while (byteCount != strLen) { + char ch = input.charAt(index + utfStrLen++); + + /* + if (ch == '\'') { + utfStrLen -= 1; + break; + } + */ + + if (assumeUTF8) { + if ((ch >= 0x0001) && (ch <= 0x007F)) { + byteCount++; + } else if (ch > 0x07FF) { + byteCount += 3; + } else { + byteCount += 2; + } + } else { + byteCount++; + } + } + String value = input.substring(index, index + utfStrLen); + index = index + utfStrLen + 2; + return value; + } + + private Boolean parseBoolean() { + int delimiter = input.indexOf(';', index); + String value = input.substring(index, delimiter); + if (value.equals("1")) { + value = "true"; + } else if (value.equals("0")) { + value = "false"; + } + index = delimiter + 1; + return Boolean.valueOf(value); + } + + private Double parseFloat() { + int delimiter = input.indexOf(';', index); + String value = input.substring(index, delimiter); + index = delimiter + 1; + return Double.valueOf(value); + } + + private Integer parseInt() { + int delimiter = input.indexOf(';', index); + String value = input.substring(index, delimiter); + index = delimiter + 1; + return Integer.valueOf(value); + } + + public void setAcceptedAttributeNameRegex(String acceptedAttributeNameRegex) { + this.acceptedAttributeNameRegex = Pattern.compile(acceptedAttributeNameRegex); + } + public static final Object NULL = new Object() { + + @Override + public String toString() { + return "NULL"; + } + }; + + /** + * Represents an object that has a name and a map of attributes + */ + public static class PhpObject { + + public String name; + public Map attributes = new HashMap(); + + @Override + public String toString() { + return "\"" + name + "\" : " + attributes.toString(); + } + } +} diff --git a/juick-www/src/webapp/WEB-INF/juick.conf.example b/juick-www/src/webapp/WEB-INF/juick.conf.example new file mode 100644 index 00000000..54ac862a --- /dev/null +++ b/juick-www/src/webapp/WEB-INF/juick.conf.example @@ -0,0 +1,5 @@ +mysql_username=username +xmpp_password=secret +sape_user=usertoken +wns_application_sip=ms-app://x-1-11-1-1111111111-... +wns_client_secret=secret \ No newline at end of file diff --git a/juick-www/src/webapp/WEB-INF/logging.properties.example b/juick-www/src/webapp/WEB-INF/logging.properties.example new file mode 100644 index 00000000..7bdaa72e --- /dev/null +++ b/juick-www/src/webapp/WEB-INF/logging.properties.example @@ -0,0 +1,5 @@ +handlers = java.util.logging.FileHandler +java.util.logging.FileHandler.pattern = www.log +java.util.logging.FileHandler.limit = 1000000 +java.util.logging.FileHandler.count = 5 +java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter diff --git a/juick-www/src/webapp/WEB-INF/web.xml b/juick-www/src/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..4db19af6 --- /dev/null +++ b/juick-www/src/webapp/WEB-INF/web.xml @@ -0,0 +1,50 @@ + + + + Main + com.juick.www.Main + 1 + + + Main + / + + + default + /scripts.js + + + default + /style.css + + + js + application/javascript;charset=UTF-8 + + + css + text/css;charset=UTF-8 + + + + 30 + + + + APNS/GCM/MPNS module + PushComponent + com.juick.www.PushComponent + + + Crossposting module + CrosspostComponent + com.juick.www.CrosspostComponent + + + XMPP module + XMPPComponent + com.juick.xmpp.s2s.XMPPComponent + + diff --git a/juick-www/src/webapp/favicon.png b/juick-www/src/webapp/favicon.png new file mode 100644 index 00000000..bc7161e2 Binary files /dev/null and b/juick-www/src/webapp/favicon.png differ diff --git a/juick-www/src/webapp/logo.png b/juick-www/src/webapp/logo.png new file mode 100644 index 00000000..933f6099 Binary files /dev/null and b/juick-www/src/webapp/logo.png differ diff --git a/juick-www/src/webapp/scripts.js b/juick-www/src/webapp/scripts.js new file mode 100644 index 00000000..32427e1e --- /dev/null +++ b/juick-www/src/webapp/scripts.js @@ -0,0 +1,684 @@ +var ws=null; +var pageTitle; + +function initWS() { + if(typeof(pageMID)!="undefined" && pageMID>0) { + var url; + var protocolPrefix = (window.location.protocol === 'https:') ? 'wss:' : 'ws:'; + if(typeof(juickDebug)!="undefined") { + url= protocolPrefix + "//ws.juick.com/_replies"; + } else { + url= protocolPrefix + "//ws.juick.com/"+pageMID; + } + if(typeof(hash)!="undefined" && hash) { + url+="?hash="+hash; + } + + ws = new WebSocket(url); + ws.onopen = function() { + console.log('online'); + if($('#wsthread').length==0) { + var d=$('
    '); + d.on('click',onclickNextReply); + d.appendTo("body"); + pageTitle=document.title; + } + }; + ws.onclose = function() { + console.log('offline'); + ws=null; + setTimeout(function() { + initWS(); + },2000); + }; + ws.onmessage = function(msg) { + if(msg.data==' ') { + ws.send(' '); + } else { + try { + var jsonMsg=$.parseJSON(msg.data); + console.log('data: '+msg.data); + wsIncomingReply(jsonMsg); + } catch(err) { + console.log(err); + } + } + }; + setInterval(wsSendKeepAlive, 90000); + } +} + +function wsSendKeepAlive() { + if(ws) { + ws.send(' '); + } +} + +function wsShutdown() { + if(ws) { + ws.onclose=function(){}; + ws.close(); + } +} + +function wsIncomingReply(msg) { + var p; + if(msg.replyto>0) { + p=$('#'+msg.replyto); + if(p.length==0) { + p=null; + } + } + + var li=$('
  • '); + li.html('
    '+ + '
    '+ + '
    '+ + ''+ + ''+ + '
    '+msg.body+'
    '+ + ''+ + ''+ + '
    '); + + if(p) { + li.css('margin-left',parseInt(p.css('margin-left'))+20+'px'); + p.after(li); + } else { + $('#replies').append(li); + } + + updateRepliesCounter(); +} + +function onclickNewReply(e) { + var li=$(e); + li.removeClass('reply-new'); + li.off('click'); + li.off('mouseover'); + updateRepliesCounter(); +} + +function onclickNextReply() { + var li=$('#replies>li.reply-new:first'); + if(li.length) { + li.removeClass('reply-new'); + li.off('click'); + li.get(0).scrollIntoView(); + updateRepliesCounter(); + } +} + +function updateRepliesCounter() { + var replies=$('#replies>li.reply-new').length; + if(replies>0) { + $('#wsthread').text(replies).css('display','block'); + document.title='['+replies+'] '+pageTitle; + } else { + $('#wsthread').css('display','none'); + document.title=pageTitle; + } +} + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ + +function postformListener(formEl,ev) { + if(ev.ctrlKey && (ev.keyCode==10 || ev.keyCode==13)) { + if(!formEl.onsubmit || formEl.onsubmit()) { + formEl.submit(); + } + } +} + +function unfoldPostForm() { + if(window.location.pathname==="/" && window.location.hash==="#post") { + $('#newmessage>div').css('display','block'); + $('#newmessage textarea').css('min-height','70px'); + $('#newmessage textarea')[0].focus(); + } +} + +function onsubmitNewMessage() { + if($('#newmessage textarea').val().length==0) { + openDialog('

    Пожалуйста, введите текст сообщения

    '); + return false; + } + return true; +} + +function showMoreReplies(id) { + $('#'+id+' .msg-comments').hide(); + + var replies=$('#replies>li'); + var flagshow=0; + for(var i=0; i'); + c.before(''); + c.append('
    '); + } + // $('#replies #'+rid+' .msg-links').hide(); + $('#replies #'+rid+' .msg-comment').show(); + $('#replies #'+rid+' textarea')[0].focus(); + $('#replies #'+rid+' textarea').autoResize({ + extraSpace: 0, + minHeight: 1 + }); + return false; +} + +function showCommentFooter(e) { + var a=$(e).closest("article"); + if(a.find("footer.comm").length==0) { + a.append('
    '); + a.find('textarea').autoResize({ + extraSpace: 0, + minHeight: 1 + }); + } + a.find('textarea')[0].focus(); + return false; +} + +function attachCommentPhoto(div) { + if($(div).children().length===0) { + var inp=$(''); + inp.on('change',function() { + $(this).parent().attr('class','attach-photo-active'); + }); + inp.trigger('click'); + $(div).append(inp); + } else { + $(div).empty(); + $(div).attr('class','attach-photo'); + } +} + +function attachMessagePhoto(div) { + var f=$(div).closest('form'); + if(f.find('input:file').length===0) { + var inp=$(''); + inp.on('change',function() { + $(div).text("загрузить (✓)"); + }); + f.append(inp); + inp.trigger('click'); + } else { + f.find('input:file').remove(); + $(div).text("загрузить"); + } +} + +function unfoldReply() { + if((0+window.location.hash.substring(1))>0) { + var el=$(window.location.hash); + while(el.is(":hidden")) { + el=el.prev(); + } + showMoreReplies(el.attr('id')); + window.location.replace(window.location.hash); + } +} + +function showMessageLinksDialog(mid,rid) { + var hlink=window.location.protocol+"//juick.com/"+mid; + var mlink="#"+mid; + if(rid>0) { + hlink+="#"+rid; + mlink+="/"+rid; + } + var hlinkenc=encodeURIComponent(hlink); + + var html="
    Ссылка на сообщение:"; + html+="
    "+hlink+"
    "; + html+="Номер сообщения:"; + html+="
    "+mlink+"
    "; + html+="Поделиться:
      "; + html+="
    • "; + html+="
    • "; + html+="
    • "; + html+="
    • "; + html+="
    "; + + openDialog(html); +} + +function showPhotoDialog(fname) { + var width=$(window).width(); + var height=$(window).height()*0.9; + if(width<640) { + return true; + } else if(width<1280) { + openDialog(""); + $('#dialogw img').css('max-height',height+'px'); + return false; + } else { + openDialog(""); + $('#dialogw img').css('max-height',height+'px'); + return false; + } +} + +function openDialog(html) { + var dhtml="
    "; + dhtml+=html; + dhtml+="
    "; + $('body').append(dhtml); +} + +function closeDialog() { + $('#dialogb').remove(); + $('#dialogt').remove(); +} + +function openSocialWindow(a) { + var w=window.open(a.href,'juickshare','width=640,height=400'); + if(window.focus) w.focus(); + return false; +} + +function checkUsername() { + var uname=$('#username').val(); + $.ajax('//api.juick.com/users?uname='+uname).done(function() { + $('#username').css('background','#FFCCCC'); + }).fail(function() { + $('#username').css('background','#CCFFCC'); + }); +} + +/******************************************************************************/ + +function openDialogLogin() { + var html='

    Пожалуйста, представьтесь:' + +'Facebook ' + +'ВКонтакте

    ' + +'

    Уже зарегистрированы?

    ' + +'
    ' + +'
    ' + +'
    ' + +'' + +'
    '; + openDialog(html); + return false; +} + +/******************************************************************************/ + +function likeMessage(e,mid) { + if (confirm("Are you sure?")) { + $.ajax({ + url: '//juick.com/like?mid='+mid, + type: 'POST' + }).done(function() { + $(e).closest("article").append("

    OK!

    "); + }).fail(function() { + $(e).closest("article").append("

    Ошибка

    "); + }); + } + return false; +} + +/******************************************************************************/ + +function setPopular(e,mid,popular) { + $.ajax('//api.juick.com/messages/set_popular?mid='+mid+'&popular='+popular+'&hash='+hash).done(function() { + var a=$(e).closest("article"); + a.append("

    OK!

    "); + }); + return false; +} + +function setPrivacy(e,mid) { + $.ajax('//api.juick.com/messages/set_privacy?mid='+mid+'&hash='+hash).done(function() { + var a=$(e).closest("article"); + a.append("

    OK!

    "); + }); + return false; +} + +/******************************************************************************/ + +function readerLinkReplace(e) { + var a=$(e); + a.attr('href','/_out?lid='+a.data('lid')); +} + +/******************************************************************************/ + +jQuery.fn.selectText = function(){ + var d = document; + if (d.body.createTextRange) { + var range = d.body.createTextRange(); + range.moveToElementText(this[0]); + range.select(); + } else if (window.getSelection) { + var selection = window.getSelection(); + var range = d.createRange(); + range.selectNodeContents(this[0]); + selection.removeAllRanges(); + selection.addRange(range); + } +}; + +/* + * jQuery.fn.autoResize 1.14 + */ + +(function($){ + + var uid = 'ar' + +new Date, + + defaults = autoResize.defaults = { + onResize: function(){}, + onBeforeResize: function(){ + return 123 + }, + onAfterResize: function(){ + return 555 + }, + animate: { + duration: 200, + complete: function(){} + }, + extraSpace: 50, + minHeight: 'original', + maxHeight: 500, + minWidth: 'original', + maxWidth: 500 + }; + + autoResize.cloneCSSProperties = [ + 'lineHeight', 'textDecoration', 'letterSpacing', + 'fontSize', 'fontFamily', 'fontStyle', 'fontWeight', + 'textTransform', 'textAlign', 'direction', 'wordSpacing', 'fontSizeAdjust', + 'paddingTop', 'paddingLeft', 'paddingBottom', 'paddingRight', 'width' + ]; + + autoResize.cloneCSSValues = { + position: 'absolute', + top: -9999, + left: -9999, + opacity: 0, + overflow: 'hidden' + }; + + autoResize.resizableFilterSelector = [ + 'textarea:not(textarea.' + uid + ')', + 'input:not(input[type])', + 'input[type=text]', + 'input[type=password]', + 'input[type=email]', + 'input[type=url]' + ].join(','); + + autoResize.AutoResizer = AutoResizer; + + $.fn.autoResize = autoResize; + + function autoResize(config) { + this.filter(autoResize.resizableFilterSelector).each(function(){ + new AutoResizer( $(this), config ); + }); + return this; + } + + function AutoResizer(el, config) { + + if (el.data('AutoResizer')) { + el.data('AutoResizer').destroy(); + } + + config = this.config = $.extend({}, autoResize.defaults, config); + this.el = el; + + this.nodeName = el[0].nodeName.toLowerCase(); + + this.originalHeight = el.height(); + this.previousScrollTop = null; + + this.value = el.val(); + + if (config.maxWidth === 'original') config.maxWidth = el.width(); + if (config.minWidth === 'original') config.minWidth = el.width(); + if (config.maxHeight === 'original') config.maxHeight = el.height(); + if (config.minHeight === 'original') config.minHeight = el.height(); + + if (this.nodeName === 'textarea') { + el.css({ + resize: 'none', + overflowY: 'hidden' + }); + } + + el.data('AutoResizer', this); + + // Make sure onAfterResize is called upon animation completion + config.animate.complete = (function(f){ + return function() { + config.onAfterResize.call(el); + return f.apply(this, arguments); + }; + }(config.animate.complete)); + + this.bind(); + + } + + AutoResizer.prototype = { + + bind: function() { + + var check = $.proxy(function(){ + this.check(); + return true; + }, this); + + this.unbind(); + + this.el + .bind('keyup.autoResize', check) + //.bind('keydown.autoResize', check) + .bind('change.autoResize', check) + .bind('paste.autoResize', function() { + setTimeout(function() { + check(); + }, 0); + }); + + if (!this.el.is(':hidden')) { + this.check(null, true); + } + + }, + + unbind: function() { + this.el.unbind('.autoResize'); + }, + + createClone: function() { + + var el = this.el, + clone = this.nodeName === 'textarea' ? el.clone() : $(''); + + this.clone = clone; + + $.each(autoResize.cloneCSSProperties, function(i, p){ + clone[0].style[p] = el.css(p); + }); + + clone + .removeAttr('name') + .removeAttr('id') + .addClass(uid) + .attr('tabIndex', -1) + .css(autoResize.cloneCSSValues); + + if (this.nodeName === 'textarea') { + clone.height('auto'); + } else { + clone.width('auto').css({ + whiteSpace: 'nowrap' + }); + } + + }, + + check: function(e, immediate) { + + if (!this.clone) { + this.createClone(); + this.injectClone(); + } + + var config = this.config, + clone = this.clone, + el = this.el, + value = el.val(); + + // Do nothing if value hasn't changed + if (value === this.prevValue) { + return true; + } + this.prevValue = value; + + if (this.nodeName === 'input') { + + clone.text(value); + + // Calculate new width + whether to change + var cloneWidth = clone.width(), + newWidth = (cloneWidth + config.extraSpace) >= config.minWidth ? + cloneWidth + config.extraSpace : config.minWidth, + currentWidth = el.width(); + + newWidth = Math.min(newWidth, config.maxWidth); + + if ( + (newWidth < currentWidth && newWidth >= config.minWidth) || + (newWidth >= config.minWidth && newWidth <= config.maxWidth) + ) { + + config.onBeforeResize.call(el); + config.onResize.call(el); + + el.scrollLeft(0); + + if (config.animate && !immediate) { + el.stop(1,1).animate({ + width: newWidth + }, config.animate); + } else { + el.width(newWidth); + config.onAfterResize.call(el); + } + + } + + return; + + } + + // TEXTAREA + + clone.width(el.width()).height(0).val(value).scrollTop(10000); + + var scrollTop = clone[0].scrollTop; + + // Don't do anything if scrollTop hasen't changed: + if (this.previousScrollTop === scrollTop) { + return; + } + + this.previousScrollTop = scrollTop; + + if (scrollTop + config.extraSpace >= config.maxHeight) { + el.css('overflowY', ''); + scrollTop = config.maxHeight; + immediate = true; + } else if (scrollTop <= config.minHeight) { + scrollTop = config.minHeight; + } else { + el.css('overflowY', 'hidden'); + scrollTop += config.extraSpace; + } + + config.onBeforeResize.call(el); + config.onResize.call(el); + + // Either animate or directly apply height: + if (config.animate && !immediate) { + el.stop(1,1).animate({ + height: scrollTop + }, config.animate); + } else { + el.height(scrollTop); + config.onAfterResize.call(el); + } + + }, + + destroy: function() { + this.unbind(); + this.el.removeData('AutoResizer'); + this.clone.remove(); + delete this.el; + delete this.clone; + }, + + injectClone: function() { + ( + autoResize.cloneContainer || + (autoResize.cloneContainer = $('').appendTo('body')) + ).append(this.clone); + } + + }; + +})(jQuery); + +/******************************************************************************/ + +$(document).ready(function() { + $('textarea').autoResize({ + extraSpace: 0, + minHeight: 1 + }); + + $('textarea.reply').click(function () { + $(this).addClass("narrow"); + $(this).after('
    '); + $(this).parent().after(''); + $(this).off('click'); + }); + + $('textarea.replypm').click(function () { + $(this).addClass("narrowpm"); + $(this).parent().after(''); + $(this).off('click'); + }); + + unfoldPostForm(); + unfoldReply(); + $(window).bind('hashchange',unfoldPostForm); + $(window).bind('hashchange',unfoldReply); + + $(window).on('pagehide',wsShutdown); +}); diff --git a/juick-www/src/webapp/style.css b/juick-www/src/webapp/style.css new file mode 100644 index 00000000..5548b79a --- /dev/null +++ b/juick-www/src/webapp/style.css @@ -0,0 +1,263 @@ +html,body,div,h1,h2,ul,li,p,form,input,textarea,pre { margin: 0; padding: 0; } +html,input,textarea { font-family: sans-serif; font-size: 12pt; } +html { background: #EEEEE5; color: #000; } +body { width: 1024px; margin: 0 auto; } +h1,h2 { font-weight: normal; } +ul { list-style-type: none; } +a { text-decoration: none; color: #069; } +img,hr { border: none; } +hr { height: 1px; background: #CCC; margin: 10px 0; } +pre { white-space: pre-wrap; white-space: -moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; word-wrap: break-word; } +span.u { text-decoration: underline; } + +#content { width: 728px; margin: 15px 0 0 286px; } +#topwrapper { position: relative; clear: both; } + +/********/ + +body>header { width: 1024px; } +body>header a { color: #000; border-bottom: 1px dotted #666; font-size: 13pt; } + +#logo { float: left; width: 110px; height: 36px; margin: 7px 25px 0 20px; } +#logo a { display: block; width: 110px; height: 36px; text-indent: 100%; white-space: nowrap; overflow: hidden; border: 0; +background: url("//i.juick.com/logo.png") no-repeat; } +@media screen and (-webkit-min-device-pixel-ratio: 2), + (min-resolution: 192dpi) { + #logo a { + background: url("//i.juick.com/logo_2x.png") no-repeat; + background-size: cover; + } +} +nav#global { float: left; } +nav#global li { display: inline-block; margin: 14px 12px 0 0; } +#search { float: right; margin: 12px 20px 12px 0; } +#search input { background: #FFF; border: 1px solid #DDDDD5; padding: 4px; } + +#headdiv { clear: both; margin: 0 0 5px 0; padding: 0 20px; background: #DDDDD5; border-top: 1px solid #D5D5D0; border-bottom: 1px solid #D5D5D0; position: relative; } +#headdiv li { display: inline-block; margin: 12px 12px 12px 0; } +nav#actions { top: 0; right: 8px; position: absolute; } + +body>header nav li:after { display: inline-block; content: "/"; margin-left: 12px; color: #AAA; } +body>header nav li:last-child:after { display: none; } + +body>header p { color: #000; font-size: 13pt; margin: 12px 0; text-align: center; } + +/********/ + +#content>p, #content>h1, #content>h2 { margin: 1em 0; } + +#newmessage { background: #E5E5E0; padding: 15px; margin-bottom: 20px; } +#newmessage textarea { border: 1px solid #CCC; padding: 4px; width: 688px; resize: vertical; min-height: 14pt; height: 14pt; margin: 0 0 5px 0; } +#newmessage input { border: 1px solid #CCC; padding: 2px 4px; margin: 5px 0; } +#newmessage>div { display: none; } +#newmessage .img { width: 500px; } +#newmessage .tags { width: 500px; } +#newmessage .subm { width: 150px; background: #EEEEE5; } + +article { margin: 10px 0 20px 58px; background: #FFF; padding: 12px 13px; } +article>aside { margin: -12px 0 0 -71px; width: 48px; height: 48px; float: left; } +article>aside img { width: 48px; height: 48px; } +article>header.u { overflow: hidden; display: inline-block; width: 460px; } +article>header.t { width: 140px; text-align: right; float: right; } +article time { color: #999; font-size: 10pt; border-bottom: 1px dotted #999; } +article p { margin: 10px 0 15px 0; } +article p.i { text-align: center; } +article p.ir { float: right; margin-left: 10px; margin-bottom: 10px; } +article p.ir a { cursor: -webkit-zoom-in; cursor: -moz-zoom-in; } +article p.ir img { max-width: 200px; max-height: 200px; } +article .irbr { clear: right; } +article>nav.l { display: inline-block; font-size: 10pt; } +article>nav.l a { color: #888; border-bottom: 1px dotted #AAA; margin-right: 15px; } +article>nav.s { display: inline-block; text-align: right; float: right; } +article>nav.s a { font-weight: bold; color: #222; } +article a.likes { padding-left: 20px; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAAXNSR0IArs4c6QAAAMRJREFUGBmtwbsuQ2EAAOCPX6LlKSRGD2E0YCKRGDqLQReDSVhIjI1r49bRdiZJn0GIQcPSxFO4HPHLOe1J2rpMvo//MaMl1batZNSWtlTLrK4JL5qqTqQO1aTqqppeTcrtexRklkTRskzw4Eju1rmOIIqCjrp7uRsNhQWLCmfu5C48Kxk05MmV3JR3xwatiaZ1rYg29Zr34VSPHdG6wpw3iaDPnmhDpiKVKPtmV7Sq4tOlET86EEUNw34RJK4Ffxgzrs8XpvA41+ECiwcAAAAASUVORK5CYII=") no-repeat 0px 1px; } +article a.replies { margin-left: 18px; padding-left: 20px; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAANdJREFUOMvN069OgmEUBvAfSCUxNicFKTQ3bkCLyRvwDrgHZ5Q7MFlobAY3G9UrsOi0iIGNBIGgG00o53PvCMAHBJ7thPecPc/5+3IoKKKDIeZrbIi74CiFQBs3eMTbmmRnuMUID5nzG90cFXcxyEqHU3zlEBigkQoUor9N8Recf4Gdpr8XgV+Uc/DK+Ekdz/hAcwNyE594Sp0NTJYOZobriB/FjUwjNkE9m36GKq5wHO9zXOICtch4j1f0Q2QlSnjBGL10dXlQxXvSVmWbTZ3E/5ijtRxcAIj4MflVC0WJAAAAAElFTkSuQmCC") no-repeat 0px 1px; } +article footer.comm { margin: 13px 0 0 0; } +article textarea { width: 530px; padding: 2px; resize: vertical; vertical-align: top; min-height: 12pt; height: 12pt; border: 0; } +article input { width: 50px; margin-left: 6px; vertical-align: top; border: 1px solid #CCC; background: #EEE; color: #999; } + +#yandex_ad_728 { width: 728px; height: 90px; margin: 20px 0; padding: 15px 0; background: #FFF; } +.adslot1 { display: inline-block; width: 320px; height: 90px; } + +@media (min-width:500px) {.adslot1 {width: 468px; height: 60px;}} +@media (min-width:800px) {.adslot1 {width: 728px; height: 90px; margin: 10px 0 20px 0; }} + + +#geomap { width: 700px; height: 300px; margin-top: 1em; overflow: hidden; } + +.msg { margin: 10px 0 20px 0; } +.ads { padding: 13px 10px 5px 10px; margin: 8px 0 16px 58px; background: #FFF; } +.msgthread { margin-bottom: 0; } +.msg-avatar { float: left; width: 48px; height: 48px; } +.msg-avatar img { width: 48px; height: 48px; vertical-align: top; } +.msg-cont { background: #FFF; margin-left: 58px; padding: 12px 15px; width: 640px; } +.msg-menu { float: right; width: 16px; height: 16px; } +.msg-menu>a { display: block; width: 16px; height: 16px; vertical-align: top; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAAXNSR0IArs4c6QAAALRQTFRFAAAAldX/ncT/ksj/ltL/nMb/lMn/mcz/l9H/lcr/mZmZmcz/m83/mJiYmZmZmcz/mM7/mpqamsr/l8v/m83/mJiYmM7/l8v/mZmZmcz/mpqamMv/mcz/mM3/ms3/msv/mMv/mMz/mcz/msz/mc3/mMz/mcz/msz/mc3/mcv/mc3/mcv/mcz/mcz/mcz/mcz/mZmZmZmZmcz/mcz/mcz/mcz/mcz/mcz/mcz/mcz/mcz/mZmZiZsGCAAAADx0Uk5TAAwNDhESExQWGC0tLi8yMjQ1NTY4OTk7PDxESktNUVNUd3h5eoGCg4mKmMvMzc7P29zd3uLj7O3u8vT2+A5wCAAAAJFJREFUGBkFwQlCgkAAAMDRxEySkryyzDxSAc+gkvX//2oGAAB42J5COG46AJ+/+SiKxvl1DsyqJyCtZnBXxgDxdxPrHQDZCvvUNIFkalAgtCSXHr1LIqpRt+ifu91zn6hG8YLh7TbEoMAyg8kEsgWaZQzwWDbgvXoG0p83YH7Nx+32a/73AdDZHEIovu4BAMA/t6QMuyHliCkAAAAASUVORK5CYII=") no-repeat; } +.msg-header { overflow: hidden; } +.msg-ts { font-size: small; vertical-align: top; margin: 5px 0; } +.msg-ts, .msg-ts>a { color: #999; } +.msg-place { font-size: small; } +.msg-place>a { color: #999; } +.msg-txt { overflow: hidden; margin: 10px 0 12px 0; } +.msg-media { text-align: center; } +.msg-links { font-size: small; color: #999; margin: 5px 0 0 0; } +.msg-comments { overflow: hidden; font-size: small; color: #AAA; text-indent: 10px; margin-top: 10px; } +.msg-comment { margin: 5px 0; } +.ta-wrapper { display: inline-block; border: 1px solid #DDD; } +.msg-comment textarea { width: 634px; padding: 2px; resize: vertical; vertical-align: top; min-height: 12pt; height: 12pt; border: 0; } +.msg-comment .narrow { width: 554px; } +.msg-comment .narrowpm { width: 580px; } +.attach-photo { display: inline-block; padding: 2px 4px; cursor: pointer; width: 16px; height: 13px; overflow: hidden; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAANCAQAAAAKsiavAAAAAXNSR0IArs4c6QAAAKRJREFUGNNjZICC6Q5M+xlQwD/HzAMMDEwwLnM9AxqAiDDO/M+AFzCh8B4zhrDwsfAxBjHcggkhm/CY2SDlHYQ5TZD5EoMMmgmMhSnvZnrPfDLzyQyvrPf/CzGs+L2TgYFhBoM0gzTjDAaGX7uwuQG/I1ldGRj+pzM8YXjMmMrAwOGK6cibfy2z3sMdeYpBBTMcHjMU/9zJzsjo8r8DIk1yQGEBAFzpL+AuTCqZAAAAAElFTkSuQmCC") no-repeat 3px 4px; } +.attach-photo-active { display: inline-block; padding: 2px 4px; cursor: pointer; width: 16px; height: 13px; overflow: hidden; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAANCAMAAACXZR4WAAAAAXNSR0IArs4c6QAAAEVQTFRFAAAAAJUAAJ0AAJIAAJkAAJYAAJwAAJcAAJoAAJgAAJkAAJoAAJoAAJgAAJkAAJoAAJkAAJkAAJkAAJkAAJkAAJkAAJkA9z3GXQAAABZ0Uk5TAAwNDg8REkBHSktRU1RVv9jZ2+Lj5OeV7PgAAABSSURBVAhbdcjBFkAgFADRIUlE9Or9/6daUC0cs5pzAXD65niKFSJQv/aChHHcrg4yA9jcILCW4tkbDGRVwfzDhs+yEBqcFsCmBiqHmULSDr0P3JdgDbuscEckAAAAAElFTkSuQmCC") no-repeat 3px 4px; } +.msg-comment input { width: 50px; margin-left: 6px; vertical-align: top; border: 1px solid #CCC; background: #EEE; color: #999; } +.msg-recomms { margin-top: 10px; overflow: hidden; font-size: small; color: #AAA; text-indent: 10px; } +.reply-new .msg-cont { border-right: 5px solid #0C0; } +q:before, q:after { content: "";} +q { border-left: 1px dashed #CCC; margin: 10px 0 10px 10px; padding-left: 10px; display: block; color: #666; } + +#mtoolbar { width: 670px; margin-left: 58px; background: #E5E5DD; border-top: 1px solid #CCC; } +#mtoolbar ul, #mtoolbar a { padding: 5px; } +#mtoolbar li { display: inline; } +#mtoolbar div { display: inline-block; width: 16px; height: 16px; background: url(//static.juick.com/toolbar-icons.png) no-repeat; vertical-align: middle; margin: 5px; } + +.newmessage { width: 695px; padding: 2px; resize: vertical; border: 1px solid #DDD; } /* textarea */ + +.users { width: 100%; margin: 10px 0; } /* table */ +.users td { width: 33%; padding: 6px 0; overflow: hidden; } /* table */ +.users img { width: 32px; height: 32px; vertical-align: middle; margin-right: 6px; } /* table */ + +.title2 { padding: 10px 20px; margin: 20px 0; background: #DDDDD0; } +.title2-right { float: right; line-height: 24px; } +#content .title2 h2 { font-size: x-large; margin: 0; } + +.page { text-align: center; padding: 5px; background: #E5E5DD; } + +/* signup form */ +.signup-h1>img { vertical-align: middle; margin-right: 10px; } +.signup-h1 { margin: 20px 0 10px 0; font-size: x-large; } +.signup-h2 { font-size: large; margin: 10px 0 5px 0; } +.signup-hr { margin: 20px 0; } + +/********/ + +#readerlinks li { margin: 15px 0; } +#readerlinks img { vertical-align: top; margin: 1px 7px 0 0; } +#readerlinks a { color: #000; border-bottom: 1px dotted #666; } +#readerlinks a:visited { color: #999; } + +/********/ + +.newpm { margin: 20px 60px 30px 60px; } +.newpm textarea { width: 100%; resize: vertical; } +.newpm-send input { width: 100px; } + +/********/ + +#column { width: 240px; padding-top: 10px; overflow: hidden; float: left; margin-left: 10px; } +#column ul, #column p, #column hr { margin: 10px 0; } +#column li { margin: 6px 0; } +#column .margtop { margin-top: 15px; } +#column p { font-size: 10pt; line-height: 140%; } +#column .tags { text-align: justify; } +#column .inp { width: 222px; padding: 3px; border: 1px solid #CCC; border-radius: 3px; background: #F5F5E9; } +#ctitle { font-size: 14pt; } +#ctitle img { vertical-align: middle; margin-right: 5px; } +#ctoolbar { margin: 10px 0; padding: 5px; line-height: 0; background: #E5E5DD; } +#ctoolbar li { display: inline; } +#ctoolbar a { padding: 5px 10px;} +#ctoolbar div { display: inline-block; width: 16px; height: 16px; background: url(//static.juick.com/toolbar-icons.png) no-repeat; vertical-align: middle; margin: 5px 0; } +#ustats li { margin: 3px 0; font-size: 10pt; } +#column table.iread { width: 100%; } +#column table.iread td { text-align: center; } +#column table.iread img { width: 48px; height: 48px; } + +/********/ + +#dialogb { position: fixed; top: 0; left: 0; width: 100%; height: 100%; opacity: 0.6; background: #000; z-index: 10; } +#dialogt { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 10; } +#dialogt td { vertical-align: middle; text-align: center; } +#dialogw { position: relative; display: inline-block; text-align: left; z-index: 11; } +#dialogc { position: absolute; top: -15px; right: -15px; width: 30px; height: 30px; z-index: 12; cursor: pointer; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAQAAACROWYpAAAEEElEQVQ4y32Va2yTVRzGf23fd10vUNqxsY1dGIyba7BsTgiwhcRkgxHkFkJAuQzIggY0IXGYkKA4DeAQMQTIiIB+MyRAJn7hAxJRJKIRiJgQIhESZxYFEuja9f74oe1aoPh/vrx5z3nO/3qeA0+aBSvW+UaT2VBUb+91HXY3FDUUNZnzDaxYsfBcs2DDpHjqqC1ln07YU79t0oGX+mY1V3dWv1/VWVrqphgTW+EDLBjYS91tZZ/5v+34d1/sijIWv/bw2IXFy2urfKVu7BjP0q0YOCaMWVlzfF7wdJY2mLybzH4/+mpq3cxKrwcHBtanvTqrfK/X97+avCvFUn2xtjDBNJpDn8SGU1Ls+oXVTTUeL8587xYMHKN97fWnlikqnU/UDmWJWUwL/ZKQpPOvTZvg8eLI0W3Y8cyo+3hB4p50MmYLUgC24EdRSTq6rLIaD/Z06SyYuMdWvjnv4TfS+URhahpHYlLwR16gHDcmVrBSPNrXOv2LTimUrMoE3BiaO5Jz2dCmiDNIkGBRcCAuffde1WR8FGMDKy5n5cp5f34pHYqlt/tDkrQ7SpDgxNBASurLrHRFpPsXmUEFbgyw4fFM7FgQvi7NznirHQorTZ8cGkhJUm80vVIylJA0ppU6xlAEBiXj/EvWSI+TuewWhdP0xylJOhPPVeJGVNqxbdQ0xlIMprusoXnzW9LtRH5xFoUjmfHIpxL8Oiod3ed6kXE4wHRW+Od0dku/J57s7P1UmvxBNP//2ah0+CCNVOICk8qyOS3bpAeJfOpgSpISypUujZ+GpXd6HC8zPkM2Z9Vtiv0jZRvlGRrM5Lo4k/vWSHZUHsWlGW+YI+QKs7lh/Z1L0tuZLbPDuVzTpTsVT6+0haXB31hNUzbsMjMwZdWBz6XBuDOY7efWiG3kYuyOZmO6FJLOnWM5mYIZlDDd3lHVPXAt18/C2DAsBf8u2cECpqdbZcNDndHi27S5T5I2DD+POjccT0qHz9JJS3ZIrLioIGBbNu7dI6claVdB72uHI0np8lW6WUIgO55WivFRT6ttvffDQ/2SdCuyatiZR2wLXwlL0uVf6WEdrdRnL4YFEzfl+Gm3dXn2bun/67YkRZI3oscjxyM/Rx7FJenBwP6L7KWLdvy5K5kRA6qZSYety9nDiYM//HFLeXbvzsmr5kl66KKDmeSJQUaG8FJDgHbW0e3YzwnPmVe+33lz582Oy+P7OcF+ullHOwFqeEKGMgKIl2r8tLCUjWxnl7HH6KXX2GPdxXY2spQW/FTzlACOSC8eyplEgBYWsoI1rGUta1jBQloIMIlyCkhv9r2w48JLObVMwU+ARhoJ4GcKtZTjxZXL9Vm6BSsGJnYcOHFl4MSBHRMDK5b/e69yxzyNAvYf9TCL+HAwka4AAAAASUVORK5CYII="); } + +.dialoglogin { width: 300px; background: #EEEEE5; padding: 25px; } +#signfb,#signvk { display: block; width: 100%; height: 32px; line-height: 32px; text-indent: 37px; text-decoration: none; overflow: hidden; margin: 10px 0; } +#signfb { color: #FFF; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAADNQTFRFO1edX3ewl6bLnKrOoK3QrrrYvMXe2N7r3OLu3+Tv5urz7O/29vf6+Pn7+vv9/Pz9////ykQjsQAAAEZJREFUOMtjYBgFuAATO68ADxdOaUYuATDAqYBbAL8CFgECCjiBcqz4XMiPz3oQEKCtAgEkwEdIAQchBWyEFDAPkDdHsAIAhZkIwz/VK/UAAAAASUVORK5CYII=") no-repeat #3A569C; } +#signvk { margin-bottom: 30px; color: #FFF; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAHJQTFRFbY+zbo+zbpCzb5C0cpO1c5O2dZW3dpa4e5m6gJ29gZ69lq/In7bNo7jPrcDUs8XXvs3dv87dy9fkztnlz9rm0Nrm093o1N7o1+Dq3OTt3ubu4Ofv5Orw7fH27vL28PP38vX49Pb5+vv8+/z9/Pz9////2jSYlQAAAG5JREFUOMvtkEcOgDAMBE3vvXdIyP+/iMMRKfYHmMtcRtE6AD8f1Is8pyKgAs0RGYO2HSWqMQaoBHVRgYsS3AsrtyFlrqgdJlCLb95gxQO6IkZCqL+KCjz0TQU5ejOf2a3aJXPF7BOB2PvMhp8PDzGRFgEe7xvEAAAAAElFTkSuQmCC") no-repeat #6d8fb3; } +.dialoglogin form { margin-top: 7px; } +.signinput,.signsubmit { border: 1px solid #CCC; margin: 3px 0; padding: 3px; } +.signinput { width: 292px; } +.signsubmit { width: 70px; } + +.dialogshare { padding: 20px; background: #EEEEE5; border: 1px solid #999; min-width: 300px; overflow: auto; } +.dialogl { padding: 5px; margin: 3px 0 20px; border: 1px solid #DDD; background: #F5F5E9; } +.dialogshare li { float: left; margin: 5px 10px 0 0; } +.dialogshare a { display: block; width: 32px; height: 32px; background-image: url(//static.juick.com/sharesocial.png); } + +.dialogtxt { background: #EEEEE5; padding: 20px; } + +/********/ + +#wsthread { position: fixed; bottom: 20px; right: 20px; background: #CCC; cursor: pointer; padding: 5px 10px; display: none; } + +/********/ + +#footer { clear: both; font-size: 10pt; padding: 10px 0; color: #999; width: 1004px; margin: 0 auto 20px 0; } +#footer-social { float: left; } +#footer-social a { display: inline-block; width: 32px; height: 32px; text-indent: 100%; white-space: nowrap; overflow: hidden; border: 0; margin: 0 15px 0 0; } +#footer-left { margin-left: 286px; margin-right: 350px; } +#footer-right { float: right; } + +/******************************************************************************/ + +.sharenew { display: inline-block; padding: 0 12px 0 37px; min-height: 32px; line-height: 32px; min-width: 200px; color: #FFF; } + +.ico32-twi { background: #55acee url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAM9QTFRFVazuVqzuV6zuWK3uWq7uXK/uXrDuYLHvY7PvZLPvZrTvZ7Tva7bwbLfwbbjwbrjwcbnwc7rxdbvxer7xfL/xf8Dyg8LyhcPyhsTyh8TzisXzi8bzjcfzjsfzksn0qNT1rNb2udz3v+D4weD4weH4w+H5w+L5yuT5yuX5y+X5zuf60ej51er61uv62uz62+374O/75fL76PP86vT87PX87vb87/f98Pf88Pf98fj99Pn99vr99/v++Pv9+vz9+/z9/P39/f39/f7+/v7+////bJnt2AAAAKlJREFUGBntwUdWAkEABNAaQJIOkpMSJCcDKEEJ2lTd/0wyD1bdbFiw43/g5kIJHJXf/2b1u2fY/IWPQO1botiBraBFNQqE2zIy3L/CFl9Tg6dUbiyjAx+2WE8kl29zBfZwZD5+xYDRwTQEW2RCUkfkiwdHerilTpiHK9zXCTWKwOWVVjSUZPiTxVnJJimRu6oH1/1DpbURjfRZDOGMx+7WkNRXI4Gba/gHx4AiZYtLseAAAAAASUVORK5CYII=") no-repeat; } +.ico32-vk { background: #6d8fb3 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAHJQTFRFbY+zbo+zbpCzb5C0cpO1c5O2dZW3dpa4e5m6gJ29gZ69lq/In7bNo7jPrcDUs8XXvs3dv87dy9fkztnlz9rm0Nrm093o1N7o1+Dq3OTt3ubu4Ofv5Orw7fH27vL28PP38vX49Pb5+vv8+/z9/Pz9////2jSYlQAAAG5JREFUOMvtkEcOgDAMBE3vvXdIyP+/iMMRKfYHmMtcRtE6AD8f1Is8pyKgAs0RGYO2HSWqMQaoBHVRgYsS3AsrtyFlrqgdJlCLb95gxQO6IkZCqL+KCjz0TQU5ejOf2a3aJXPF7BOB2PvMhp8PDzGRFgEe7xvEAAAAAElFTkSuQmCC") no-repeat; } +.ico32-fb { background: #3b579d url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAADNQTFRFO1edX3ewl6bLnKrOoK3QrrrYvMXe2N7r3OLu3+Tv5urz7O/29vf6+Pn7+vv9/Pz9////ykQjsQAAAEZJREFUOMtjYBgFuAATO68ADxdOaUYuATDAqYBbAL8CFgECCjiBcqz4XMiPz3oQEKCtAgEkwEdIAQchBWyEFDAPkDdHsAIAhZkIwz/VK/UAAAAASUVORK5CYII=") no-repeat; } +.ico32-lj { background: #888888 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAlJQTFRFAAAAAAAAAFVVADNmACRtAEBgADNmADdtADBgADljADNmADFiADFjADVpADJjADRpADRoADFnADNmADJkADVlADJnADRnADJlADJnADJlADRnADNmADJnADNmADJmADRmADNlADRmADNnADNmADJmADRmADNmADRmADJmADNlADNmADNmADJmADNnADNlADNnADNmADNlADNmADNmADNmBTRmADNmADNmADNmWGh4ADNmBDZnK1J6LWCURmiIRmmLc4mfADNmKlyPADNmGkNsLF+SKFiIR3qtL1Z+ATRnDT9xE0Z5Gk2BN2ueOVh1UIO2DkFzE0Z5JVeLK1J5M2WYPlp1T3ehJEpuADNmK16QPG+iQHOmRFxze35/ADNmLV+SjIeCMVl/TmJ4BThqBzZnR3mtBjpsWo7AZ4KeFENxH0h0bJ7SADNmAzZpHz9sAzdqCzppEkBvF0RyO1+EP3OmBDZodKbafWR+ATRoDjlpQ3epADNmATNlDDdoQWOGaJvOBDVmBDZoQWWKRnmrUoS3AzZqBzdoWFZ4cKTVc4mgADNmBjdmCzhoVom5W47CADNmADNnAzRnBjVoWlh5opSHADNmATRlATRmCDlqcaXYh7rtADNmWYy/XI/CXpLFYZTHY5bJZZjLZpjLZpnMb2B9cmB8hbjrhp2zibvvjL7ylMj6l8r9l8v9mY6Fmcz/qneIrba+snqKuaKLwKeMxMbIx8jJy8vLzMzM1LOQ1dXV1d3m2YqR4LmS4uju7sGW78KV8cSX+fn5+smY/cqZ/f39/8yZQ9icRAAAAJt0Uk5TAAEDBQcICg4QEhkaHyIkJyw0QUJESE9RUlZeX2Fpa2x5e4GHiYqMmZ2rr7S2uL/Cw8nNzs/R19/g4OPj4+fn5+fo6Onp6erq6+zs7Ozs7Ozt7e3t7e3t7u/v7+/v7/Dw8PHx8vLy8/Pz9PT09fX19vb29vb29/f3+Pj4+fn5+fn6+vr6+vv7+/v7/Pz8/Pz9/f39/f3+/v7+/v7uuPuRAAABVElEQVQ4y83QU3sDURCA4U2dOrVtpbZt27Ztt2lTY6tTnxpb2/pf3dxPcp3vdt5n5jyHIIQpKSOXZS91/nPN4p6FvckuFr+5Hrn/wpmvbHBnwnMWt+1qdPD563LAhgECu9qLufGn37fDRmQIgm5Oh//DXflKaFkyVxkCDqT3w23ByCpFnUQFyQBAsTr8vjeyM46iqIM6e1FAaKGmlmkc00qLPtIMOmKOMjDeDTymRRapAQBxx6INjDd9zijqNDpXHhBy6Qk7GA+H0SuOSp0lAKFG5mGMM6toMTZrAT3DhByiRcgaLUqQDgBE2DXrGG9V+KX6FgazoRXSAfHbGMe+P378eXiCP67ETcQ45Y8uf0YMFAaoeSqHB66RAggY1u1JnzzwilRBQDDdlrJvaPDTrwsDQlLfFaUtfp9PmBL8YqhYcesRsiQEJGscgZwIgYlp2xJC0z8XJH2ZPhmkKgAAAABJRU5ErkJggg==") no-repeat; } +.ico32-gp { background: #dc4a38 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAAXNSR0IArs4c6QAAAa9JREFUSMftlTFoU1EUhr/3EguZFFzSIVVehyq03dxEJy3iIiJCi4Nr6eQmLoKggg46CaKj6FBB0OIgXcRJiQiKiFIf2KEJVsSCTWlN+Bx8eaQtaa5Dt57tP+/+/33n3P/cCzuxPeFh7/nRFdvxxtFw8m4f+tojJl62qT4xsRBOj511yT0Zuqjq5P/8/Lj6LEd9LqoLRt0Z8QY8Dqy2QbTGI6CfSrjAADDYgd8BUAgXWABG3Z/jH8Ai81lJE97pJfAYiJnK8RBwJWplaISTvZpYcEZteh7AQ9a90fH1ut96n0Ofl0zVOT/Y8mZurapVa65atert7iUQrUXXooQS9xkm5m6WbpCS8osmKSn1EEdcVfVWpweCSsgXH7Ch6rSlrQSKm9vICY4xxF5qVNjFGcqORQ0AXrLUa98x55x30oPG4LCfVDubtjX9tE2/tEcpm81XasNiCL1oTT23Idvvb7USYuURypmZO4+1xnv+8DNE4N8UHt20Zh9Po+WwHrxVGx5fl7tg3YHunHVXhYO8IEFmeE7KCglnKTMRfQ63T8kpZ/1uy2W/+sBTxjuPzLbHX6Ju9rcCQGEmAAAAAElFTkSuQmCC") no-repeat; } + +/******************************************************************************/ + +@media screen and (max-width: 850px) { + body { text-size-adjust: 100%; -webkit-text-size-adjust: 100%;} + body,body>header,#topwrapper,#content,#footer,#mtoolbar { width: auto; min-width: 310px; margin: 0 auto; } + + body>header { margin-bottom: 15px; } + body>header a { font-size: 12pt; } + #logo { display: none; } + nav#global { margin-left: 10px; } + nav#global li { margin-right: 10px; } + #search { float: none; display: inline-block; margin: 10px 10px; } + #headdiv { padding: 0 10px; } + #headdiv li { margin-right: 10px; } + nav#actions { position: relative; right: auto; } + #newmessage .img, #newmessage .tags { width: 100%; } + #column { float: none; padding-top: 0; width: auto; margin: 0 10px; } + + article { margin-left: 0; overflow: auto; } + article>aside { margin: 0 10px 0 0; width: 40px; height: 40px; } + article>aside img { width: 40px; height: 40px; } + article>header.u { margin: 0; width: auto; display: block; } + article>header.t { float: none; text-align: left; margin: 0; width: auto; display: block; } + article p { margin: 10px 0 8px 0; } + article>nav.l { display: block; float: left; width: 80%; line-height: 15pt; } + article>nav.s { display: block; } + article textarea { width: 205px; } + article footer { float: left; } + + #content textarea { width: 100%; } + + .msg,.msg-cont { width: auto; min-width: 280px; } + .msg-cont,.ads { margin-left: 0; } + .msg-avatar { margin: 10px 10px 0 10px; width: 40px; height: 40px; } + .msg-avatar img { width: 40px; height: 40px; } + .msg-comment textarea { width: 100%; } + .msg-txt { margin: 8px 0 0 0; } + .msg-media { overflow: auto; } + + .title2 h2 { font-size: large; } + + #footer { margin: 0 10px; } + #footer div { float: none; margin: 10px 0; } +} diff --git a/settings.gradle b/settings.gradle index 31018a72..96f2c888 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':deps:com.juick.xmpp' +include ':deps:com.juick.xmpp', ':juick-core', 'juick-api', 'juick-www', 'juick-rss', 'juick-ws' diff --git a/src/main/java/com/juick/CrosspostComponent.java b/src/main/java/com/juick/CrosspostComponent.java deleted file mode 100644 index 8b07583f..00000000 --- a/src/main/java/com/juick/CrosspostComponent.java +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Juick - * Copyright (C) 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; - -import com.juick.server.CrosspostQueries; -import com.juick.xmpp.JID; -import com.juick.xmpp.Message; -import com.juick.xmpp.Stream; -import com.juick.xmpp.StreamComponent; -import com.juick.xmpp.extensions.JuickMessage; -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.lang3.tuple.Pair; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.datasource.DriverManagerDataSource; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import javax.net.ssl.HttpsURLConnection; -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; -import java.io.*; -import java.net.Socket; -import java.net.URL; -import java.net.URLEncoder; -import java.security.Key; -import java.util.Properties; -import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.logging.Level; -import java.util.logging.LogManager; -import java.util.logging.Logger; - -/** - * - * @author Ugnich Anton - */ -public class CrosspostComponent implements ServletContextListener, Stream.StreamListener, Message.MessageListener { - - private static Logger logger = Logger.getLogger(CrosspostComponent.class.getName()); - - private ExecutorService executorService; - - public final static String TWITTERURL = "https://api.twitter.com/1.1/statuses/update.json"; - public final static String FBURL = "https://graph.facebook.com/me/feed"; - public final static String VKURL = "https://api.vk.com/method/wall.post"; - JdbcTemplate sql; - Stream xmpp; - String twitter_consumer_key; - String twitter_consumer_secret; - - @Override - public void contextInitialized(final ServletContextEvent sce) { - logger.info("component initialized"); - executorService = Executors.newSingleThreadExecutor(); - executorService.submit((Runnable) () -> { - try { - Properties conf = new Properties(); - conf.load(sce.getServletContext().getResourceAsStream("/WEB-INF/juick.conf")); - - LogManager.getLogManager().readConfiguration( - sce.getServletContext().getResourceAsStream("/WEB-INF/logging.properties")); - twitter_consumer_key = conf.getProperty("twitter_consumer_key", ""); - twitter_consumer_secret = conf.getProperty("twitter_consumer_secret", ""); - - setupSql(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver"), conf.getProperty("datasource_url", "")); - setupXmppComponent(conf.getProperty("xmpp_password", "")); - } catch (Exception e) { - logger.log(Level.SEVERE, e.getMessage(), e); - } - }); - } - - @Override - public void contextDestroyed(ServletContextEvent sce) { - executorService.shutdown(); - logger.info("component destroyed"); - } - - public void setupSql(String driver, String url) { - DriverManagerDataSource dataSource = new DriverManagerDataSource(); - dataSource.setDriverClassName(driver); - dataSource.setUrl(url); - sql = new JdbcTemplate(dataSource); - } - - public void setupXmppComponent(String password) { - try { - Socket socket = new Socket("localhost", 5347); - xmpp = new StreamComponent(new JID("", "crosspost.juick.com", ""), socket.getInputStream(), socket.getOutputStream(), password); - xmpp.addChildParser(new JuickMessage()); - xmpp.addListener((Stream.StreamListener) this); - xmpp.addListener((Message.MessageListener) this); - xmpp.startParsing(); - } catch (IOException e) { - logger.log(Level.SEVERE, e.getMessage(), e); - } - } - - @Override - public void onStreamReady() { - logger.info("XMPP STREAM READY"); - } - - @Override - public void onStreamFail(Exception e) {logger.log(Level.SEVERE, "XMPP STREAM FAIL", e);} - @Override - public void onMessage(com.juick.xmpp.Message msg) { - JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); - if (msg.to != null && msg.to.Username != null && jmsg != null && jmsg.getRID() == 0) { - if (msg.to.Username.equals("twitter")) { - twitterPost(jmsg); - } else if (msg.to.Username.equals("fb")) { - facebookPost(jmsg); - } else if (msg.to.Username.equals("vk")) { - vkontaktePost(jmsg); - } - } - } - - public boolean facebookPost(com.juick.Message jmsg) { - String token = CrosspostQueries.getFacebookToken(sql, jmsg.getUser().getUID()).orElse(""); - if (token.isEmpty()) { - return false; - } - - logger.info("FB: #" + jmsg.getMID()); - - String status = getMessageHashTags(jmsg) + "\n" + jmsg.getText(); - - boolean ret = false; - try { - String body = "access_token=" + URLEncoder.encode(token, "UTF-8") + "&message=" + URLEncoder.encode(status, "UTF-8") + "&link=http%3A%2F%2Fjuick.com%2F" + jmsg.getMID(); - - HttpsURLConnection conn = (HttpsURLConnection) new URL(FBURL).openConnection(); - conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - conn.setRequestProperty("User-Agent", "Juick"); - conn.setRequestProperty("Content-Length", Integer.toString(body.length())); - conn.setUseCaches(false); - conn.setDoInput(true); - conn.setDoOutput(true); - conn.setRequestMethod("POST"); - conn.connect(); - - OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream()); - wr.write(body); - wr.close(); - - ret = streamToString(conn.getInputStream()) != null; - - conn.disconnect(); - } catch (Exception e) { - logger.log(Level.SEVERE, "fbPost: " + e.getMessage(), e); - } - return ret; - } - - public boolean vkontaktePost(com.juick.Message jmsg) { - Pair tokens = CrosspostQueries.getVKTokens(sql, jmsg.getUser().getUID()).orElse(Pair.of("", "")); - if (tokens.getLeft().isEmpty() || tokens.getRight().isEmpty()) { - return false; - } - - logger.info("VK: #" + jmsg.getMID()); - - String status = getMessageHashTags(jmsg) + "\n" + jmsg.getText() + "\nhttp://juick.com/" + jmsg.getMID(); - - boolean ret = false; - try { - String body = "owner_id=" + tokens.getLeft() + "&access_token=" + URLEncoder.encode(tokens.getRight(), "UTF-8") + "&from_group=1&message=" + URLEncoder.encode(status, "UTF-8"); - - HttpsURLConnection conn = (HttpsURLConnection) new URL(VKURL).openConnection(); - conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - conn.setRequestProperty("User-Agent", "Juick"); - conn.setRequestProperty("Content-Length", Integer.toString(body.length())); - conn.setUseCaches(false); - conn.setDoInput(true); - conn.setDoOutput(true); - conn.setRequestMethod("POST"); - conn.connect(); - - OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream()); - wr.write(body); - wr.close(); - - ret = streamToString(conn.getInputStream()) != null; - - conn.disconnect(); - } catch (Exception e) { - logger.log(Level.SEVERE, "vkPost: " + e.getMessage(), e); - } - return ret; - } - - public boolean twitterPost(com.juick.Message jmsg) { - Pair tokens = CrosspostQueries.getTwitterTokens(sql, jmsg.getUser().getUID()).orElse(Pair.of("", "")); - if (tokens.getLeft().isEmpty() || tokens.getRight().isEmpty()) { - return false; - } - String token = percentEncode(tokens.getLeft()); - String token_secret = percentEncode(tokens.getRight()); - - logger.info("TWITTER: #" + jmsg.getMID()); - - String status = getMessageHashTags(jmsg) + jmsg.getText(); - if (status.length() > 115) { - status = status.substring(0, 114) + "…"; - } - status += " http://juick.com/" + jmsg.getMID(); - status = percentEncode(status); - - boolean ret = false; - try { - String nonce = UUID.randomUUID().toString(); - String timestamp = Long.toString(System.currentTimeMillis() / 1000L); - String signature = percentEncode(twitterSignature(status, nonce, timestamp, token, token_secret)); - String auth = "OAuth " - + "oauth_consumer_key=\"" + twitter_consumer_key + "\", " - + "oauth_nonce=\"" + nonce + "\", " - + "oauth_signature=\"" + signature + "\", " - + "oauth_signature_method=\"HMAC-SHA1\", " - + "oauth_timestamp=\"" + timestamp + "\", " - + "oauth_token=\"" + token + "\", " - + "oauth_version=\"1.0\""; - - HttpsURLConnection conn = (HttpsURLConnection) new URL(TWITTERURL).openConnection(); - conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - conn.setRequestProperty("User-Agent", "Juick"); - conn.setRequestProperty("Content-Length", Integer.toString(status.length() + 7)); - conn.setRequestProperty("Authorization", auth); - conn.setUseCaches(false); - conn.setDoInput(true); - conn.setDoOutput(true); - conn.setRequestMethod("POST"); - conn.connect(); - - OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream()); - wr.write("status=" + status); - wr.close(); - - ret = streamToString(conn.getInputStream()) != null; - - conn.disconnect(); - } catch (Exception e) { - logger.log(Level.SEVERE, "twitterPost: " + e.getMessage(), e); - } - return ret; - } - - public String twitterSignature(String status, String nonce, String timestamp, String token, String token_secret) { - try { - // ALPHABET-SORTED - String params = "oauth_consumer_key=" + twitter_consumer_key - + "&oauth_nonce=" + nonce - + "&oauth_signature_method=HMAC-SHA1" - + "&oauth_timestamp=" + timestamp - + "&oauth_token=" + token - + "&oauth_version=1.0" - + "&status=" + status; - - String base = "POST&" + percentEncode(TWITTERURL) + "&" + percentEncode(params); - String key = twitter_consumer_secret + "&" + token_secret; - - Key signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA1"); - Mac mac = Mac.getInstance("HmacSHA1"); - mac.init(signingKey); - byte[] rawHmac = mac.doFinal(base.getBytes()); - return Base64.encodeBase64String(rawHmac); - - } catch (Exception e) { - logger.log(Level.SEVERE, "twitterSignature: " + e.getMessage(), e); - } - return null; - } - - public String streamToString(InputStream is) { - try { - BufferedReader buf = new BufferedReader(new InputStreamReader(is)); - StringBuilder str = new StringBuilder(); - String line; - do { - line = buf.readLine(); - str.append(line).append("\n"); - } while (line != null); - return str.toString(); - } catch (Exception e) { - logger.log(Level.SEVERE, "streamToString: " + e.getMessage(), e); - } - return null; - } - - public String getMessageHashTags(com.juick.Message jmsg) { - String hashtags = ""; - for (int i = 0; i < jmsg.Tags.size(); i++) { - hashtags += "#" + jmsg.Tags.get(i) + " "; - } - return hashtags; - } - - public static String percentEncode(String s) { - String ret = ""; - try { - ret = URLEncoder.encode(s, "UTF-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~"); - } catch (UnsupportedEncodingException e) { - } - return ret; - } -} diff --git a/src/main/java/com/juick/Group.java b/src/main/java/com/juick/Group.java deleted file mode 100644 index 008d3af2..00000000 --- a/src/main/java/com/juick/Group.java +++ /dev/null @@ -1,29 +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; - -/** - * - * @author Ugnich Anton - */ -public class Group { - - public String Name = null; - public int GID = 0; - public int UsersCnt = 0; -} diff --git a/src/main/java/com/juick/Message.java b/src/main/java/com/juick/Message.java deleted file mode 100644 index c128b4f2..00000000 --- a/src/main/java/com/juick/Message.java +++ /dev/null @@ -1,244 +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; - -import java.util.*; - -/** - * - * @author Ugnich Anton - */ -public class Message implements Comparable { - - private int MID = 0; - - private int RID = 0; - - public int ReplyTo = 0; - private String Text = null; - private User User = null; - public List Tags = new ArrayList<>(); - private Date date; - public int TimeAgo = 0; - public int Privacy = 1; - public boolean FriendsOnly = false; - public boolean ReadOnly = false; - public boolean Hidden = false; - public boolean VisitorCanComment = true; - public int Replies = 0; - public String RepliesBy = null; - public String AttachmentType = null; - public String Photo = null; - public String Video = null; - public Place Place = null; - public int Likes = 0; - private boolean liked = false; - public List childs = new ArrayList<>(); - private Optional PM = Optional.empty(); - private Optional Recommendation = Optional.empty(); - - public Message() { - } - - public Message(Message msg) { - setMID(msg.getMID()); - setRID(msg.getRID()); - ReplyTo = msg.ReplyTo; - setText(msg.getText()); - setUser(msg.getUser()); - Tags = msg.Tags; - setDate(msg.getDate()); - TimeAgo = msg.TimeAgo; - Privacy = msg.Privacy; - FriendsOnly = msg.FriendsOnly; - ReadOnly = msg.ReadOnly; - Hidden = msg.Hidden; - Replies = msg.Replies; - AttachmentType = msg.AttachmentType; - Photo = msg.Photo; - Video = msg.Video; - Place = msg.Place; - Likes = msg.Likes; - setLiked(msg.isLiked()); - childs = msg.childs; - PM = msg.PM; - Recommendation = msg.Recommendation; - } - - public void parseTags(String strTags) { - if (strTags != null) { - Tags.addAll(Arrays.asList(strTags.split(" "))); - } - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Message)) { - return false; - } - Message jmsg = (Message) obj; - return (this.getMID() == jmsg.getMID() && this.getRID() == jmsg.getRID()); - } - - @Override - public int compareTo(Object obj) throws ClassCastException { - if (!(obj instanceof Message)) { - throw new ClassCastException(); - } - Message jmsg = (Message) obj; - - if (this.getMID() != jmsg.getMID()) { - if (this.getMID() > jmsg.getMID()) { - return -1; - } else { - return 1; - } - } - - if (this.getRID() != jmsg.getRID()) { - if (this.getRID() < jmsg.getRID()) { - return -1; - } else { - return 1; - } - } - - return 0; - } - - public int getChildsCount() { - int cnt = childs.size(); - for (Message child : childs) { - cnt += child.getChildsCount(); - } - return cnt; - } - - public void cleanupChilds() { - if (!childs.isEmpty()) { - for (Message child : childs) { - child.cleanupChilds(); - } - childs.clear(); - } - } - - public String getAttachmentURL() { - if (AttachmentType != null) { - String url = "http://i.juick.com/"; - url += AttachmentType.equals("mp4") ? "video" : "photos-1024"; - url += "/" + getMID(); - if (getRID() > 0) { - url += "-" + getRID(); - } - url += "." + AttachmentType; - return url; - } else { - return null; - } - } - - public String getTagsString() { - String ret = ""; - if (!Tags.isEmpty()) { - for (String Tag : Tags) { - ret += " *" + Tag; - } - if (FriendsOnly) { - ret += " *friends"; - } - if (Privacy == -2) { - ret += " *private"; - } - if (Privacy == -1) { - ret += " *friends"; - } - if (Privacy == 2) { - ret += " *public"; - } - if (ReadOnly) { - ret += " *readonly"; - } - } - return ret; - } - - public int getMID() { - return MID; - } - - public void setMID(int MID) { - this.MID = MID; - } - - public int getRID() { - return RID; - } - - public void setRID(int RID) { - this.RID = RID; - } - - public com.juick.User getUser() { - return User; - } - - public void setUser(com.juick.User user) { - User = user; - } - - public String getText() { - return Text; - } - - public void setText(String text) { - Text = text; - } - - public Date getDate() { - return date; - } - - public void setDate(Date date) { - - this.date = date; - } - - public Optional getPM() { - return PM; - } - - public void setPM(com.juick.PM PM) { - this.PM = Optional.ofNullable(PM); - } - - public Optional getRecommendation() { - return Recommendation; - } - public void setRecommendation(Recommendation recommendation) { - this.Recommendation = Optional.ofNullable(recommendation); - } - - public boolean isLiked() { - return liked; - } - - public void setLiked(boolean liked) { - this.liked = liked; - } -} diff --git a/src/main/java/com/juick/PM.java b/src/main/java/com/juick/PM.java deleted file mode 100644 index 658cafc9..00000000 --- a/src/main/java/com/juick/PM.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.juick; - -/** - * Created by vt on 08/02/16. - */ -public class PM { - private User to; - - public User getTo() { - return to; - } - - public PM(User to) { - this.to = to; - } -} diff --git a/src/main/java/com/juick/Place.java b/src/main/java/com/juick/Place.java deleted file mode 100644 index 7174ed6e..00000000 --- a/src/main/java/com/juick/Place.java +++ /dev/null @@ -1,33 +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; - -/** - * - * @author Ugnich Anton - */ -public class Place { - - public int pid = 0; - public double lat = 0; - public double lon = 0; - public String name = null; - public int users = 0; - public int messages = 0; - public int distance = 0; -} diff --git a/src/main/java/com/juick/PushComponent.java b/src/main/java/com/juick/PushComponent.java deleted file mode 100644 index 3bb7cdc5..00000000 --- a/src/main/java/com/juick/PushComponent.java +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Juick - * Copyright (C) 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; - -import com.google.android.gcm.server.Message; -import com.google.android.gcm.server.MulticastResult; -import com.google.android.gcm.server.Result; -import com.google.android.gcm.server.Sender; -import com.juick.json.MessageSerializer; -import com.juick.server.PushQueries; -import com.juick.server.SubscriptionsQueries; -import com.juick.xmpp.JID; -import com.juick.xmpp.Message.MessageListener; -import com.juick.xmpp.Stream; -import com.juick.xmpp.StreamComponent; -import com.juick.xmpp.extensions.JuickMessage; -import com.juick.xmpp.utils.XmlUtils; -import com.notnoop.apns.APNS; -import com.notnoop.apns.ApnsService; -import org.apache.http.Consts; -import org.apache.http.Header; -import org.apache.http.HttpResponse; -import org.apache.http.NameValuePair; -import org.apache.http.client.HttpClient; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.util.EntityUtils; -import org.apache.http.util.TextUtils; -import org.json.JSONObject; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.datasource.DriverManagerDataSource; - -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; -import java.io.IOException; -import java.net.Socket; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Properties; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.logging.Level; -import java.util.logging.LogManager; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -/** - * - * @author Ugnich Anton - */ -public class PushComponent implements ServletContextListener, Stream.StreamListener, MessageListener { - - private static Logger logger = Logger.getLogger(PushComponent.class.getName()); - - private ExecutorService executorService; - String wns_application_sip; - String wns_client_secret; - JdbcTemplate sql; - Socket socket; - Stream xmpp; - Sender GCMSender; - - @Override - public void contextInitialized(final ServletContextEvent sce) { - logger.info("component initialized"); - executorService = Executors.newSingleThreadExecutor(); - executorService.submit((Runnable) () -> { - Properties conf = new Properties(); - try { - conf.load(sce.getServletContext().getResourceAsStream("/WEB-INF/juick.conf")); - LogManager.getLogManager().readConfiguration( - sce.getServletContext().getResourceAsStream("/WEB-INF/logging.properties")); - wns_application_sip = conf.getProperty("wns_application_sip", ""); - wns_client_secret = conf.getProperty("wns_client_secret", ""); - GCMSender = new Sender(conf.getProperty("gcm_key")); - - setupSql(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver"), conf.getProperty("datasource_url", "")); - setupXmppComponent(new JID("", conf.getProperty("push_jid"), ""), conf.getProperty("xmpp_host", "localhost"), - Integer.parseInt(conf.getProperty("xmpp_port", "5347")), conf.getProperty("push_xmpp_password", "")); - } catch (IOException e) { - logger.log(Level.SEVERE, e.getMessage(), e); - } - }); - } - - @Override - public void contextDestroyed(ServletContextEvent sce) { - executorService.shutdown(); - logger.info("component destroyed"); - } - - public void setupSql(String driver, String url) { - DriverManagerDataSource dataSource = new DriverManagerDataSource(); - dataSource.setDriverClassName(driver); - dataSource.setUrl(url); - sql = new JdbcTemplate(dataSource); - } - - public void setupXmppComponent(JID jid, String host, int port, String password) { - try { - socket = new Socket(host, port); - xmpp = new StreamComponent(jid, socket.getInputStream(), socket.getOutputStream(), password); - xmpp.addChildParser(new JuickMessage()); - xmpp.addListener((Stream.StreamListener) this); - xmpp.addListener((MessageListener) this); - xmpp.startParsing(); - } catch (IOException e) { - logger.log(Level.SEVERE, e.getMessage(), e); - } - } - - @Override - public void onStreamReady() { - logger.info("XMPP STREAM READY"); - } - - @Override - public void onStreamFail(Exception e) {logger.log(Level.SEVERE, "XMPP STREAM FAIL", e);} - - @Override - public void onMessage(com.juick.xmpp.Message msg) { - JuickMessage jmsg = (JuickMessage)msg.getChild(JuickMessage.XMLNS); - List subscribedUsers = new ArrayList<>(); - boolean isPM = jmsg.getMID() == 0; - boolean isReply = jmsg.getRID() > 0; - int pmTo = 0; - if (isPM) { - pmTo = Integer.parseInt(msg.to.Username); - } else { - if (isReply) { - subscribedUsers = - SubscriptionsQueries.getUsersSubscribedToComments(sql, jmsg.getMID(), jmsg.getUser().getUID()); - } else { - // new message - subscribedUsers = SubscriptionsQueries.getSubscribedUsers(sql, jmsg.getUser().getUID(), jmsg.getMID()); - } - } - - /*** ANDROID ***/ - final List regids = new ArrayList<>(); - if (isPM) { - PushQueries.getAndroidRegID(sql, pmTo).ifPresent(regids::add); - } else { - List uids = subscribedUsers.stream().map(User::getUID).collect(Collectors.toList()); - if (uids.size() > 0) { - regids.addAll(PushQueries.getAndroidTokens(sql, uids)); - } - } - - if (!regids.isEmpty()) { - MessageSerializer messageSerializer = new MessageSerializer(); - String json = messageSerializer.serialize(jmsg).toString(); - logger.info(json); - Message message = new Message.Builder().addData("message", json).build(); - try { - MulticastResult result = GCMSender.send(message, regids, 3); - List results = result.getResults(); - for (int i = 0; i < results.size(); i++) { - logger.info("RES " + i + ": " + results.get(i).toString()); - } - } catch (IOException e) { - logger.log(Level.SEVERE, e.getMessage(), e); - } catch (IllegalArgumentException err) { - logger.warning("Android: Invalid API Key"); - } - } else { - logger.info("GMS: no recipients"); - } - - /*** WinPhone ***/ - final List urls = new ArrayList<>(); - if (isPM) { - PushQueries.getWinPhoneURL(sql, pmTo).ifPresent(urls::add); - } else { - List uids = subscribedUsers.stream().map(User::getUID).collect(Collectors.toList()); - if (uids.size() > 0) { - urls.addAll(PushQueries.getWindowsTokens(sql, uids)); - } - } - - - if (urls.isEmpty()) { - logger.info("WNS: no recipients"); - } else { - try { - String wnsToken = getWnsAccessToken(); - String text1 = "@" + jmsg.getUser().getUName(); - if (!jmsg.Tags.isEmpty()) { - text1 += ":" + XmlUtils.escape(jmsg.getTagsString()); - } - String text2 = XmlUtils.escape(jmsg.getText()); - String xml = "" - + "" - + "" - + "" - + "" - + "" + text1 + "" - + "" + text2 + "" - + "" - + "" - + "" - + "" - + "" - + ""; - logger.fine(xml); - for (String url : urls) { - logger.info("WNS: " + url); - sendWNS(wnsToken, url, xml); - } - } catch (IOException | IllegalStateException e) { - logger.log(Level.SEVERE, "WNS: ", e); - } - } - - /*** iOS ***/ - final List tokens = new ArrayList<>(); - if (isPM) { - PushQueries.getAPNSToken(sql, pmTo).ifPresent(tokens::add); - } else { - List uids = subscribedUsers.stream().map(User::getUID).collect(Collectors.toList()); - if (uids.size() > 0) { - tokens.addAll(PushQueries.getAPNSTokens(sql, uids)); - } - } - if (!tokens.isEmpty()) { - ApnsService service = APNS.newService().withCert("/etc/juick/ios.p12", "juick") - .withSandboxDestination().build(); - for (String token : tokens) { - String payload = APNS.newPayload().alertTitle("@" + jmsg.getUser().getUName()).alertBody(jmsg.getText()).build(); - logger.info("APNS: " + token); - service.push(token, payload); - } - } else { - logger.info("APNS: no recipients"); - } - } - - String getWnsAccessToken() throws IOException, IllegalStateException { - if(TextUtils.isEmpty(wns_application_sip)) { - throw new IllegalStateException("'wns_application_sip' is not initialized"); - } - if(TextUtils.isEmpty(wns_client_secret)) { - throw new IllegalStateException("'wns_client_secret' is not initialized"); - } - HttpClient client = HttpClientBuilder.create().build(); - String url = "https://login.live.com/accesstoken.srf"; - List formParams = new ArrayList<>(); - formParams.add(new BasicNameValuePair("grant_type", "client_credentials")); - formParams.add(new BasicNameValuePair("client_id", wns_application_sip)); - formParams.add(new BasicNameValuePair("client_secret", wns_client_secret)); - formParams.add(new BasicNameValuePair("scope", "notify.windows.com")); - UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8); - HttpPost httppost = new HttpPost(url); - httppost.setEntity(entity); - HttpResponse response = client.execute(httppost); - int statusCode = response.getStatusLine().getStatusCode(); - String responseContent = EntityUtils.toString(response.getEntity(), Consts.UTF_8); - JSONObject json = new JSONObject(responseContent); - if(statusCode != 200) { - throw new IOException(json.opt("error") + ": " + json.opt("error_description")); - } - String tokenType = (String)json.get("token_type"); - if(tokenType.length() >= 1) { - tokenType = Character.toUpperCase(tokenType.charAt(0)) + tokenType.substring(1); - } - return tokenType + " " + json.get("access_token"); - } - - void sendWNS(String wnsToken, String url, String xml) throws IOException { - HttpClient client = HttpClientBuilder.create().build(); - StringEntity entity = new StringEntity(xml, Consts.UTF_8); - HttpPost httpPost = new HttpPost(url); - httpPost.setHeader("Content-Type", "text/xml"); - httpPost.setHeader("Authorization", wnsToken); - httpPost.setHeader("X-WNS-Type", "wns/toast"); - httpPost.setEntity(entity); - HttpResponse response = client.execute(httpPost); - int statusCode = response.getStatusLine().getStatusCode(); - if(statusCode != 200) { - String headersContent = stringifyWnsHttpHeaders(response.getAllHeaders()); - throw new IOException(headersContent); - } - } - - static String stringifyWnsHttpHeaders(Header[] allHeaders) { - String[] wnsHeaders = Arrays.stream(allHeaders) - .filter(x -> x.getName().startsWith("X-WNS-") || x.getName().startsWith("WWW-")) - .map(x -> x.getName() + ": " + x.getValue()) - .toArray(String[]::new); - return String.join("\n", wnsHeaders); - } -} diff --git a/src/main/java/com/juick/Recommendation.java b/src/main/java/com/juick/Recommendation.java deleted file mode 100644 index 4cfa67c2..00000000 --- a/src/main/java/com/juick/Recommendation.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.juick; - -/** - * Created by vt on 08/02/16. - */ -public class Recommendation { - User from; - - public User getFrom() { - return from; - } - - public Recommendation(User from) { - this.from = from; - } -} diff --git a/src/main/java/com/juick/Tag.java b/src/main/java/com/juick/Tag.java deleted file mode 100644 index 119aad99..00000000 --- a/src/main/java/com/juick/Tag.java +++ /dev/null @@ -1,41 +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; - -import java.util.Objects; - -/** - * - * @author Ugnich Anton - */ -public class Tag { - - public String Name = null; - public int TID = 0; - public int SynonymID = 0; - public int UsageCnt = 0; - - @Override - public boolean equals(Object o) { - boolean equal = false; - if (o != null && o instanceof Tag) { - equal = Objects.equals(Name, ((Tag) o).Name); - } - return equal; - } -} diff --git a/src/main/java/com/juick/User.java b/src/main/java/com/juick/User.java deleted file mode 100644 index b9111d8f..00000000 --- a/src/main/java/com/juick/User.java +++ /dev/null @@ -1,96 +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; - -/** - * - * @author Ugnich Anton - */ -public class User { - - private int UID = 0; - private String UName = null; - public Object Avatar = null; - private String FullName = null; - private String JID = null; - public int MessagesCount = 0; - private String AuthHash = null; - public boolean Banned = false; - - public User() { - } - - public User(User u) { - setUID(u.getUID()); - setUName(u.getUName()); - Avatar = u.Avatar; - setFullName(u.getFullName()); - setJID(u.getJID()); - MessagesCount = u.MessagesCount; - setAuthHash(u.getAuthHash()); - Banned = u.Banned; - } - - @Override - public boolean equals(Object obj) { - return (obj instanceof User && ((User) obj).getUID() == this.getUID()); - } - - public int getUID() { - return UID; - } - - public void setUID(int UID) { - this.UID = UID; - } - - public String getUName() { - return UName; - } - - public void setUName(String UName) { - this.UName = UName; - } - - public String getFullName() { - return FullName; - } - - public void setFullName(String fullName) { - FullName = fullName; - } - - public String getJID() { - return JID; - } - - public void setJID(String JID) { - this.JID = JID; - } - - public String getAuthHash() { - return AuthHash; - } - - public void setAuthHash(String authHash) { - AuthHash = authHash; - } - public Integer getUnreadCount() { - return MessagesCount; - } -} diff --git a/src/main/java/com/juick/http/www/Discover.java b/src/main/java/com/juick/http/www/Discover.java deleted file mode 100644 index 4ae3156b..00000000 --- a/src/main/java/com/juick/http/www/Discover.java +++ /dev/null @@ -1,118 +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.http.www; - -import com.juick.server.AdsQueries; -import com.juick.server.MessagesQueries; -import com.juick.server.TagQueries; -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.util.List; - -/** - * - * @author Ugnich Anton - */ -public class Discover { - - protected void doGet(JdbcTemplate sql, JdbcTemplate sqlSearch, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - - String paramTagStr = URLDecoder.decode(request.getRequestURI().substring(5), "UTF-8"); - com.juick.Tag paramTag = TagQueries.getTag(sql, paramTagStr, false); - if (paramTag == null) { - Errors.doGet404(sql, request, response); - return; - } else if (paramTag.SynonymID > 0 && paramTag.TID != paramTag.SynonymID) { - com.juick.Tag synTag = TagQueries.getTag(sql, paramTag.SynonymID); - String url = "/tag/" + URLEncoder.encode(synTag.Name, "UTF-8"); - if (request.getQueryString() != null) { - url += "?" + request.getQueryString(); - } - Utils.sendPermanentRedirect(response, url); - return; - } else if (!paramTag.Name.equals(paramTagStr)) { - String url = "/tag/" + URLEncoder.encode(paramTag.Name, "UTF-8"); - if (request.getQueryString() != null) { - url += "?" + request.getQueryString(); - } - Utils.sendPermanentRedirect(response, url); - return; - } - - int paramBefore = 0; - String paramBeforeStr = request.getParameter("before"); - if (paramBeforeStr != null) { - try { - paramBefore = Integer.parseInt(paramBeforeStr); - } catch (NumberFormatException e) { - } - } - - int visitor_uid = visitor != null ? visitor.getUID() : 0; - - String title = "*" + Utils.encodeHTML(paramTag.Name); - List mids = MessagesQueries.getTag(sql, paramTag.TID, visitor_uid, paramBefore, (visitor == null) ? 40 : 20); - - response.setContentType("text/html; charset=UTF-8"); - try (PrintWriter out = response.getWriter()) { - String head = ""; - if (TagQueries.getTagNoIndex(sql, paramTag.TID)) { - head = ""; - } else if (paramBefore > 0 || mids.size() < 5) { - head = ""; - } - PageTemplates.pageHead(out, title, head); - PageTemplates.pageNavigation(out, visitor, null); - PageTemplates.pageHomeColumn(out, sql, visitor); - - out.println("
    "); - - if (mids.size() > 0) { - int vuid = visitor != null ? visitor.getUID() : 0; - int ad_mid = AdsQueries.getAdMID(sql, vuid); - if (ad_mid > 0 && mids.indexOf(ad_mid) == -1) { - mids.add(0, ad_mid); - AdsQueries.logAdMID(sql, vuid, ad_mid); - } else { - ad_mid = 0; - } - - PageTemplates.printMessages(out, sql, null, mids, visitor, visitor == null ? 2 : 3, ad_mid); - } - - if (mids.size() >= 20) { - String nextpage = "/tag/" + URLEncoder.encode(paramTag.Name, "UTF-8") + "?before=" + mids.get(mids.size() - 1); - out.println("

    Читать дальше →

    "); - } - - out.println("
    "); - - PageTemplates.pageFooter(request, out, visitor, true); - - PageTemplates.pageEnd(out); - } - } -} diff --git a/src/main/java/com/juick/http/www/Errors.java b/src/main/java/com/juick/http/www/Errors.java deleted file mode 100644 index 0044c209..00000000 --- a/src/main/java/com/juick/http/www/Errors.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.juick.http.www; - -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; - -/** - * - * @author ugnich - */ -public class Errors { - - public static String tagsHTML = null; - - public static void doGet404(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - - if (tagsHTML == null) { - tagsHTML = PageTemplates.formatPopularTags(sql, 80); - } - - response.setStatus(404); - response.setContentType("text/html; charset=UTF-8"); - try (PrintWriter out = response.getWriter()) { - PageTemplates.pageHead(out, "404 Страница не найдена", null); - PageTemplates.pageNavigation(out, visitor, null); - PageTemplates.pageHomeColumn(out, sql, visitor); - - out.println("
    "); - out.println("

    Страница не найдена

    "); - out.println("

    Сожалеем, но страницу с этим адресом удалил её автор, либо её никогда не существовало.

    "); - out.println("
    "); - - PageTemplates.pageFooter(request, out, visitor, false); - PageTemplates.pageEnd(out); - } - } -} diff --git a/src/main/java/com/juick/http/www/FacebookLogin.java b/src/main/java/com/juick/http/www/FacebookLogin.java deleted file mode 100644 index cf444abc..00000000 --- a/src/main/java/com/juick/http/www/FacebookLogin.java +++ /dev/null @@ -1,152 +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.http.www; - -import com.juick.server.UserQueries; -import org.json.JSONObject; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.net.URLEncoder; -import java.util.UUID; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author Ugnich Anton - */ -public class FacebookLogin { - - private static final Logger logger = Logger.getLogger(FacebookLogin.class.getName()); - - private static final String FACEBOOK_APPID = "130568668304"; - private static final String FACEBOOK_SECRET = "95813bfb6ab8f473410c50d4f971649e"; - private static final String FACEBOOK_REDIRECT = "http://juick.com/_fblogin"; - - protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - String fbstate; - - String code = request.getParameter("code"); - if (code == null || code.equals("")) { - fbstate = UUID.randomUUID().toString(); - - Cookie c = new Cookie("fbstate", fbstate); - response.addCookie(c); - - response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); - response.setHeader("Location", "https://www.facebook.com/dialog/oauth?scope=publish_stream&client_id=" + FACEBOOK_APPID + "&redirect_uri=" + URLEncoder.encode(FACEBOOK_REDIRECT, "utf-8") + "&state=" + fbstate); - return; - } - - fbstate = Utils.getCookie(request, "fbstate"); - if (fbstate == null || fbstate.isEmpty() || !fbstate.equals(request.getParameter("state"))) { - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - return; - } else { - Cookie c = new Cookie("fbstate", "-"); - c.setMaxAge(0); - response.addCookie(c); - } - - String token = Utils.fetchURL("https://graph.facebook.com/oauth/access_token?client_id=" + FACEBOOK_APPID + "&redirect_uri=" + URLEncoder.encode(FACEBOOK_REDIRECT, "utf-8") + "&client_secret=" + FACEBOOK_SECRET + "&code=" + URLEncoder.encode(code, "utf-8")); - if (token == null || token.isEmpty() || !token.startsWith("access_token=")) { - logger.log(Level.SEVERE, "FACEBOOK TOKEN ERROR: " + token); - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - token = token.substring(13); // access_token=... - int tokenamp = token.indexOf('&'); // &expires= - if (tokenamp > 0) { - token = token.substring(0, tokenamp); - } - - String graph = Utils.fetchURL("https://graph.facebook.com/me?access_token=" + token); - if (graph == null || graph.isEmpty()) { - System.err.println("FACEBOOK GRAPH ERROR"); - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - - try { - JSONObject json = new JSONObject(graph); - String fbIDStr = json.getString("id"); - String fbName = json.getString("name"); - String fbLink = json.getString("link"); - boolean fbVerified = json.getBoolean("verified"); - - long fbID = 0; - if (fbIDStr != null && !fbIDStr.isEmpty()) { - fbID = Long.parseLong(fbIDStr); - } - - if (fbID == 0 || fbName == null || fbLink == null || fbName.isEmpty() || fbLink.isEmpty()) { - throw new Exception(); - } - - int uid = getUIDbyFBID(sql, fbID); - if (uid > 0) { - if (!updateDB(sql, fbID, token, fbName, fbLink)) { - throw new Exception(); - } - Cookie c = new Cookie("hash", UserQueries.getHashByUID(sql, uid)); - c.setMaxAge(50 * 24 * 60 * 60); - response.addCookie(c); - response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); - response.setHeader("Location", "/"); - } else if (fbVerified) { - String loginhash = UUID.randomUUID().toString(); - if (!insertDB(sql, fbID, loginhash, token, fbName, fbLink)) { - throw new Exception(); - } - response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); - response.setHeader("Location", "/signup?type=fb&hash=" + loginhash); - } else { - throw new Exception(); - } - } catch (Exception e) { - logger.log(Level.WARNING, "fb error", e); - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - } - - private int getUIDbyFBID(JdbcTemplate sql, long fbID) { - try { - return sql.queryForObject("SELECT user_id FROM facebook WHERE fb_id=? AND user_id IS NOT NULL", - Integer.class, fbID); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - - private boolean insertDB(JdbcTemplate sql, long fbID, String loginhash, String token, String fbName, String fbLink) { - return sql.update("INSERT INTO facebook(fb_id,loginhash,access_token,fb_name,fb_link) VALUES (?,?,?,?,?)", - fbID, loginhash, token, fbName, fbLink) > 0; - } - - private boolean updateDB(JdbcTemplate sql, long fbID, String token, String fbName, String fbLink) { - return sql.update("UPDATE facebook SET access_token=?,fb_name=?,fb_link=? WHERE fb_id=?", - token, fbName, fbLink, fbID) > 0; - } -} diff --git a/src/main/java/com/juick/http/www/Help.java b/src/main/java/com/juick/http/www/Help.java deleted file mode 100644 index 761c6ce1..00000000 --- a/src/main/java/com/juick/http/www/Help.java +++ /dev/null @@ -1,90 +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.http.www; - -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.*; - -/** - * - * @author Ugnich Anton - */ -public class Help { - - protected void doRedirectToHelpIndex(HttpServletResponse response) throws ServletException, IOException { - Utils.sendTemporaryRedirect(response, "/help/ru/"); - } - - protected void doGetHelp(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - - String path[] = request.getRequestURI().split("/"); - String page; - if (path.length < 3 || path.length > 4 || path[2].length() != 2 || !path[2].matches("^[a-z]+$")) { - Errors.doGet404(sql, request, response); - return; - } - - if (path.length == 4) { - page = path[3]; - if (!page.matches("^[a-zA-Z0-9\\-]*$") || page.equals("navigation") || page.equals("index")) { - Errors.doGet404(sql, request, response); - return; - } - } else { - page = "index"; - } - - File f = new File("/var/www/juick.com/help/" + path[2] + "/" + page); - if (!f.isFile()) { - Errors.doGet404(sql, request, response); - return; - } - - response.setContentType("text/html; charset=UTF-8"); - try (PrintWriter out = response.getWriter()) { - PageTemplates.pageHead(out, "Помощь", null); - PageTemplates.pageNavigation(out, visitor, null); - - out.println(""); - - out.println("
    "); - out.println("
    "); - printFile(out, f); - out.println("
    "); - out.println("
    "); - - PageTemplates.pageFooter(request, out, visitor, false); - PageTemplates.pageEnd(out); - } - } - - private void printFile(PrintWriter out, File f) throws IOException { - BufferedReader br = new BufferedReader(new FileReader(f)); - String str; - while ((str = br.readLine()) != null) { - out.println(str); - } - } -} diff --git a/src/main/java/com/juick/http/www/Home.java b/src/main/java/com/juick/http/www/Home.java deleted file mode 100644 index 1c7cdc30..00000000 --- a/src/main/java/com/juick/http/www/Home.java +++ /dev/null @@ -1,170 +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.http.www; - -import com.juick.server.AdsQueries; -import com.juick.server.MessagesQueries; -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; -import java.net.URLEncoder; -import java.util.List; - -/** - * - * @author Ugnich Anton - */ -public class Home { - - protected void doGet(JdbcTemplate sql, JdbcTemplate sqlSearch, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { - int paramBefore = 0; - String paramBeforeStr = request.getParameter("before"); - if (paramBeforeStr != null) { - try { - paramBefore = Integer.parseInt(paramBeforeStr); - } catch (NumberFormatException e) { - } - } - - String paramSearch = request.getParameter("search"); - if (paramSearch != null && paramSearch.length() > 64) { - paramSearch = null; - } - - String title; - List mids; - - String paramShow = request.getParameter("show"); - if (paramSearch != null) { - title = "Поиск: " + Utils.encodeHTML(paramSearch); - mids = MessagesQueries.getSearch(sql, sqlSearch, Utils.encodeSphinx(paramSearch), paramBefore); - } else if (paramShow == null) { - if (visitor != null) { - title = "Популярные"; - mids = MessagesQueries.getPopular(sql, visitor.getUID(), paramBefore); - } else { - title = "Микроблоги Juick: популярные записи"; - mids = MessagesQueries.getPopular(sql, 0, paramBefore); - } - - } else if (paramShow.equals("top")) { - Utils.sendPermanentRedirect(response, "/"); - return; - } else if (paramShow.equals("my") && visitor != null) { - title = "Моя лента"; - mids = MessagesQueries.getMyFeed(sql, visitor.getUID(), paramBefore); - } else if (paramShow.equals("private") && visitor != null) { - title = "Приватные"; - mids = MessagesQueries.getPrivate(sql, visitor.getUID(), paramBefore); - } else if (paramShow.equals("discuss") && visitor != null) { - title = "Обсуждения"; - mids = MessagesQueries.getDiscussions(sql, visitor.getUID(), paramBefore); - } else if (paramShow.equals("recommended") && visitor != null) { - title = "Рекомендации"; - mids = MessagesQueries.getRecommended(sql, visitor.getUID(), paramBefore); - } else if (paramShow.equals("photos")) { - title = "Фотографии"; - if (visitor != null) { - mids = MessagesQueries.getPhotos(sql, visitor.getUID(), paramBefore); - } else { - mids = MessagesQueries.getPhotos(sql, 0, paramBefore); - } - } else if (paramShow.equals("all")) { - title = "Все сообщения"; - if (visitor != null) { - mids = MessagesQueries.getAll(sql, visitor.getUID(), paramBefore); - } else { - mids = MessagesQueries.getAll(sql, 0, paramBefore); - } - } else { - Errors.doGet404(sql, request, response); - return; - } - - response.setContentType("text/html; charset=UTF-8"); - try (PrintWriter out = response.getWriter()) { - String head = ""; - if (paramBefore > 0 || paramShow != null) { - head = ""; - } - PageTemplates.pageHead(out, title, head); - PageTemplates.pageNavigation(out, visitor, paramSearch); - PageTemplates.pageHomeColumn(out, sql, visitor, paramShow == null && paramBefore == 0 && paramSearch == null && visitor == null); - - out.println("
    "); - - if (paramShow == null && paramBefore == 0) { - out.println(""); - } - - if (visitor != null) { - out.println("
    "); - out.println("
    "); - out.println(" "); - out.println("
    "); - out.println(" или загрузить
    "); - out.println("
    "); - out.println(" "); - out.println("
    "); - out.println("
    "); - out.println("
    "); - } - - if (mids.size() > 0) { - int ad_mid = 0; - if (paramShow == null || paramShow.equals("top") || paramShow.equals("all")) { - int vuid = visitor != null ? visitor.getUID() : 0; - ad_mid = AdsQueries.getAdMID(sql, vuid); - if (ad_mid > 0 && mids.indexOf(ad_mid) == -1) { - mids.add(0, ad_mid); - AdsQueries.logAdMID(sql, vuid, ad_mid); - } else { - ad_mid = 0; - } - } - - PageTemplates.printMessages(out, sql, null, mids, visitor, visitor == null ? 2 : 3, ad_mid); - } - - if (mids.size() >= 20) { - String nextpage = "?before=" + mids.get(mids.size() - 1); - if (paramShow != null) { - nextpage += "&show=" + paramShow; - } - if (paramSearch != null) { - nextpage += "&search=" + URLEncoder.encode(paramSearch, "UTF-8"); - } - - out.println("

    Читать дальше →

    "); - } - - if (paramShow == null && paramBefore == 0) { - out.println(""); - } - - out.println("
    "); - - PageTemplates.pageFooter(request, out, visitor, true); - PageTemplates.pageEnd(out); - } - } -} diff --git a/src/main/java/com/juick/http/www/Login.java b/src/main/java/com/juick/http/www/Login.java deleted file mode 100644 index f12c7096..00000000 --- a/src/main/java/com/juick/http/www/Login.java +++ /dev/null @@ -1,246 +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.http.www; - -import org.springframework.jdbc.core.JdbcTemplate; - -import java.io.IOException; -import java.io.PrintWriter; -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * - * @author Ugnich Anton - */ -public class Login { - - protected void doGetLoginForm(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - if (visitor != null) { - Utils.sendTemporaryRedirect(response, "/"); - return; - } - - response.setContentType("text/html; charset=UTF-8"); - try (PrintWriter out = response.getWriter()) { - out.println(""); - out.println(""); - out.println(""); - out.println("Juick"); - out.println(""); - out.println(""); - out.println(""); - out.println(""); - - out.println(""); - - out.println(""); - - out.println("
    juick.com © 2008-2014   Контакты · Помощь
    "); - - out.println("
    "); - out.println(" Зарегистрироваться:"); - out.println(" "); - out.println(" "); - out.println("
    XMPP"); - out.println("
    Отправьте LOGIN на juick@juick.com
    "); - out.println("
    "); - out.println("
    "); - out.println("
    Уже зарегистрированы?"); - out.println("
    "); - out.println(""); - out.println(""); - out.println(""); - out.println("
    "); - out.println("
    "); - - out.println(""); - out.println(""); - } - } - - protected void doGetLogin(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - String hash = request.getQueryString(); - if (hash.length() > 32) { - response.sendError(400); - return; - } - - if (com.juick.server.UserQueries.getUIDbyHash(sql, hash) > 0) { - Cookie c = new Cookie("hash", hash); - c.setMaxAge(365 * 24 * 60 * 60); - response.addCookie(c); - response.sendRedirect("/"); - } else { - response.sendError(403); - } - } - - protected void doPostLogin(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - String username = request.getParameter("username"); - String password = request.getParameter("password"); - if (username == null || password == null || username.length() > 32 || password.isEmpty()) { - response.sendError(400); - return; - } - - int uid = com.juick.server.UserQueries.checkPassword(sql, username, password); - if (uid > 0) { - String hash = com.juick.server.UserQueries.getHashByUID(sql, uid); - Cookie c = new Cookie("hash", hash); - c.setMaxAge(365 * 24 * 60 * 60); - response.addCookie(c); - - String referer = request.getHeader("Referer"); - if (referer != null && referer.startsWith("http://juick.com/") && !referer.equals("http://juick.com/login")) { - response.sendRedirect(referer); - } else { - response.sendRedirect("/"); - } - } else { - response.sendError(403); - } - } - - protected void doGetLogout(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - if (visitor != null) { - sql.update("DELETE FROM logins WHERE user_id=?", visitor.getUID()); - } - - Cookie c = new Cookie("hash", "-"); - c.setDomain(".juick.com"); - c.setMaxAge(0); - response.addCookie(c); - - Cookie c2 = new Cookie("hash", "-"); - c2.setMaxAge(0); - response.addCookie(c2); - - response.sendRedirect("/"); - } -} diff --git a/src/main/java/com/juick/http/www/Main.java b/src/main/java/com/juick/http/www/Main.java deleted file mode 100644 index ebdda1c9..00000000 --- a/src/main/java/com/juick/http/www/Main.java +++ /dev/null @@ -1,310 +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.http.www; - -import com.juick.server.UserQueries; -import com.juick.xmpp.JID; -import com.juick.xmpp.Stream; -import com.juick.xmpp.StreamComponent; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.datasource.DriverManagerDataSource; -import ru.sape.Sape; - -import javax.servlet.ServletException; -import javax.servlet.annotation.MultipartConfig; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.net.Socket; -import java.net.URLEncoder; -import java.util.Properties; - -/** - * - * @author Ugnich Anton - */ -@WebServlet(name = "Main", urlPatterns = {"/"}) -@MultipartConfig(fileSizeThreshold = 1024 * 1024, maxRequestSize = 1024 * 1024 * 10) -public class Main extends HttpServlet implements Stream.StreamListener { - - JdbcTemplate sql; - JdbcTemplate sqlSearch; - String sqlSearchConnStr = "jdbc:mysql://127.0.0.1:9306?autoReconnect=true&useUnicode=yes&characterEncoding=utf8&maxAllowedPacket=512000"; - Stream xmpp; - Home home = new Home(); - Discover discover = new Discover(); - PM pm = new PM(); - Login login = new Login(); - Help help = new Help(); - User pagesUser = new User(); - UserThread pagesUserThread = new UserThread(); - NewMessage pagesNewMessage = new NewMessage(); - FacebookLogin loginFacebook = new FacebookLogin(); - VKontakteLogin loginVK = new VKontakteLogin(); - TwitterAuth twitterAuth; - SignUp signup = new SignUp(); - Settings settings = new Settings(); - RSS rss = new RSS(); - - @Override - public void init() throws ServletException { - - super.init(); - try { - Properties conf = new Properties(); - conf.load(getServletContext().getResourceAsStream("/WEB-INF/juick.conf")); - - DriverManagerDataSource dataSource = new DriverManagerDataSource(); - dataSource.setDriverClassName(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver")); - dataSource.setUrl(conf.getProperty("datasource_url")); - DriverManagerDataSource dataSourceSearch = new DriverManagerDataSource(); - dataSourceSearch.setDriverClassName(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver")); - dataSourceSearch.setUrl(sqlSearchConnStr); - sql = new JdbcTemplate(dataSource); - sqlSearch = new JdbcTemplate(dataSourceSearch); - - setupXmppComponent(conf.getProperty("xmpp_password")); - twitterAuth = new TwitterAuth(conf.getProperty("twitter_consumer_key"), - conf.getProperty("twitter_consumer_secret")); - PageTemplates.sape = new Sape(conf.getProperty("sape_user"), "juick.com", 2000, 3600); - } catch (Exception e) { - log(null, e); - } - } - - public void setupXmppComponent(final String password) { - Thread thr = new Thread(() -> { - try { - Socket socket = new Socket("localhost", 5347); - xmpp = new StreamComponent(new JID("", "www.juick.com", ""), socket.getInputStream(), socket.getOutputStream(), password); - xmpp.addListener(Main.this); - xmpp.startParsing(); - } catch (IOException e) { - log("xmpp exception", e); - } - }); - thr.start(); - } - - @Override - public void onStreamFail(Exception e) {log("XMPP STREAM FAIL:" + e);} - - @Override - public void onStreamReady() { - log("XMPP STREAM READY"); - } - - - /** - * Handles the HTTP GET method. - * @param request servlet request - * @param response servlet response - * @throws ServletException if a servlet-specific error occurs - * @throws IOException if an I/O error occurs - */ - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - if (request.getCharacterEncoding() == null) { - request.setCharacterEncoding("UTF-8"); - } - String uri = request.getRequestURI(); - - if (uri.equals("/")) { - String tag = request.getParameter("tag"); - if (tag != null) { - Utils.sendPermanentRedirect(response, "/tag/" + URLEncoder.encode(tag, "UTF-8")); - } else { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - home.doGet(sql, sqlSearch, request, response, visitor); - } - } else if (uri.equals("/post")) { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - if (visitor != null) { - pagesNewMessage.doGetNewMessage(sql, request, response, visitor); - } else { - Utils.sendTemporaryRedirect(response, "/login"); - } - } else if (uri.equals("/login")) { - if (request.getQueryString() == null) { - login.doGetLoginForm(sql, request, response); - } else { - login.doGetLogin(sql, request, response); - } - } else if (uri.startsWith("/pm/")) { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - if (visitor == null) { - Utils.sendTemporaryRedirect(response, "/login"); - } else { - switch (uri) { - case "/pm/inbox": - pm.doGetInbox(sql, request, response, visitor); - break; - case "/pm/sent": - pm.doGetSent(sql, request, response, visitor); - break; - default: - Errors.doGet404(sql, request, response); - break; - } - } - } else if (uri.startsWith("/rss/")) { - String uname = uri.substring(5); - int uid = UserQueries.getUIDbyName(sql, uname); - if (uid > 0) { - rss.doGet(sql, request, response, uid, uname); - } else { - response.sendError(404); - } - } else if (uri.equals("/logout")) { - login.doGetLogout(sql, request, response); - } else if (uri.equals("/settings")) { - settings.doGet(sql, request, response); - } else if (uri.equals("/_fblogin")) { - loginFacebook.doGet(sql, request, response); - } else if (uri.equals("/_vklogin")) { - loginVK.doGet(sql, request, response); - } else if (uri.startsWith("/_twitter")) { - twitterAuth.doGet(sql, request, response); - } else if (uri.equals("/signup")) { - signup.doGet(sql, request, response); - } else if (uri.equals("/help") || uri.equals("/help/")) { - help.doRedirectToHelpIndex(response); - } else if (uri.startsWith("/help/")) { - help.doGetHelp(sql, request, response); - } else if (uri.startsWith("/tag/")) { - discover.doGet(sql, sqlSearch, request, response); - } else if (uri.matches("^/\\d+$")) { - String strID = request.getRequestURI().substring(1); - int mid = 0; - try { - mid = Integer.parseInt(strID); - } catch (NumberFormatException e) { - } - if (mid > 0) { - com.juick.User author = com.juick.server.MessagesQueries.getMessageAuthor(sql, mid); - if (author != null) { - Utils.sendPermanentRedirect(response, "/" + author.getUName() + "/" + mid); - return; - } - } - Errors.doGet404(sql, request, response); - } else if (uri.matches("^/[^/]+$")) { - com.juick.User user = com.juick.server.UserQueries.getUserByName(sql, request.getRequestURI().substring(1)); - if (user != null) { - Utils.sendPermanentRedirect(response, "/" + user.getUName() + "/"); - } else { - Errors.doGet404(sql, request, response); - } - } else if (uri.matches("^/.+/.*")) { - String uriparts[] = uri.split("/"); - com.juick.User user = com.juick.server.UserQueries.getUserByName(sql, uriparts[1]); - if (user != null && user.getUName().equals(uriparts[1]) && !user.Banned) { - if (uriparts.length == 2) { // http://juick.com/username/ - pagesUser.doGetBlog(sql, sqlSearch, request, response, user); - } else if (uriparts[2].equals("tags")) { - pagesUser.doGetTags(sql, request, response, user); - } else if (uriparts[2].equals("friends")) { - pagesUser.doGetFriends(sql, request, response, user); - } else if (uriparts[2].equals("readers")) { - pagesUser.doGetReaders(sql, request, response, user); - } else { - int mid = 0; - try { - mid = Integer.parseInt(uriparts[2]); - } catch (NumberFormatException e) { - } - if (mid > 0) { - com.juick.User author = com.juick.server.MessagesQueries.getMessageAuthor(sql, mid); - if (author != null) { - if (!author.getUName().equals(user.getUName())) { - Utils.sendPermanentRedirect(response, "/" + author.getUName() + "/" + mid); - } else { - pagesUserThread.doGetThread(sql, request, response, mid); - } - } else { - Errors.doGet404(sql, request, response); - } - } else { - Errors.doGet404(sql, request, response); - } - } - } else if (user != null && !user.Banned) { - Utils.sendPermanentRedirect(response, "/" + user.getUName() + "/" + (uriparts.length > 2 ? uriparts[2] : "")); - } else { - Errors.doGet404(sql, request, response); - } - } else { - Errors.doGet404(sql, request, response); - } - } - - /** - * Handles the HTTP POST method. - * @param request servlet request - * @param response servlet response - * @throws ServletException if a servlet-specific error occurs - * @throws IOException if an I/O error occurs - */ - @Override - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - if (request.getCharacterEncoding() == null) { - request.setCharacterEncoding("UTF-8"); - } - - String uri = request.getRequestURI(); - if (uri.equals("/post")) { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - if (visitor != null && !visitor.Banned) { - pagesNewMessage.doPostMessage(sql, request, response, xmpp, visitor); - } else { - response.sendError(403); - } - } else if (uri.equals("/comment")) { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - if (visitor != null && !visitor.Banned) { - pagesNewMessage.doPostComment(sql, request, response, xmpp, visitor); - } else { - response.sendError(403); - } - } else if (uri.equals("/like")) { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - if (visitor != null && !visitor.Banned) { - pagesNewMessage.doPostRecomm(sql, request, response, xmpp, visitor); - } else { - response.sendError(403); - } - } else if (uri.equals("/pm/send")) { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - if (visitor != null && !visitor.Banned) { - pm.doPostPM(sql, request, response, xmpp, visitor); - } else { - response.sendError(403); - } - } else if (uri.equals("/login")) { - login.doPostLogin(sql, request, response); - } else if (uri.equals("/signup")) { - signup.doPost(sql, request, response); - } else if (uri.equals("/settings")) { - settings.doPost(sql, request, response); - } else { - response.sendError(405); - } - } -} diff --git a/src/main/java/com/juick/http/www/NewMessage.java b/src/main/java/com/juick/http/www/NewMessage.java deleted file mode 100644 index 642bd794..00000000 --- a/src/main/java/com/juick/http/www/NewMessage.java +++ /dev/null @@ -1,413 +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.http.www; - -import com.juick.Tag; -import com.juick.server.*; -import com.juick.xmpp.JID; -import com.juick.xmpp.Message; -import com.juick.xmpp.Stream; -import com.juick.xmpp.extensions.JuickMessage; -import com.juick.xmpp.extensions.JuickUser; -import com.juick.xmpp.extensions.Nickname; -import com.juick.xmpp.extensions.XOOB; -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; -import java.net.URL; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author Ugnich Anton - */ -public class NewMessage { - - protected void doGetNewMessage(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { - response.setContentType("text/html; charset=UTF-8"); - try (PrintWriter out = response.getWriter()) { - PageTemplates.pageHead(out, "Написать", "" - + "" - + "" - + ""); - PageTemplates.pageNavigation(out, visitor, null); - - out.println("
    "); - out.println("
    "); - out.println("

    Место: Отменить

    "); - out.println("

    Фото: (JPG, PNG, до 10Мб)

    "); - - String body = request.getParameter("body"); - if (body == null) { - body = ""; - } else { - if (body.length() > 4096) { - body = body.substring(0, 4096); - } - body = Utils.encodeHTML(body); - } - out.println("


    "); - - out.println("" + "" + "

    "); - out.println("
    "); - out.println("
    "); - out.println("

    Теги:

    "); - printUserTags(sql, out, visitor); - out.println("
    "); - - PageTemplates.pageFooter(request, out, visitor, false); - PageTemplates.pageEnd(out); - } - } - - void printUserTags(JdbcTemplate sql, PrintWriter out, com.juick.User visitor) { - List tags = TagQueries.getUserTagsAll(sql, visitor.getUID()); - - if (tags.isEmpty()) { - return; - } - - int min = tags.get(0).UsageCnt; - int max = tags.get(0).UsageCnt; - for (int i = 1; i < tags.size(); i++) { - int usagecnt = tags.get(i).UsageCnt; - if (usagecnt < min) { - min = usagecnt; - } - if (usagecnt > max) { - max = usagecnt; - } - } - max -= min; - - out.print("

    "); - for (int i = 0; i < tags.size(); i++) { - if (i > 0) { - out.print(" "); - } - String taglink = ""; - try { - taglink = "" + Utils.encodeHTML(tags.get(i).Name) + ""; - } catch (UnsupportedEncodingException e) { - } - int usagecnt = tags.get(i).UsageCnt; - if (usagecnt <= max / 5 + min) { - out.print("" + taglink + ""); - } else if (usagecnt <= max / 5 * 2 + min) { - out.print(taglink); - } else if (usagecnt <= max / 5 * 3 + min) { - out.print("" + taglink + ""); - } else if (usagecnt <= max / 5 * 4 + min) { - out.print("" + taglink + ""); - } else { - out.print("" + taglink + ""); - } - } - out.println("

    "); - } - - public void doPostMessage(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, Stream xmpp, com.juick.User visitor) throws ServletException, IOException { - String body = request.getParameter("body"); - if (body == null || body.length() < 1 || body.length() > 4096) { - response.sendError(400); - return; - } - body = body.replace("\r", ""); - - String tagsStr = request.getParameter("tags"); - List tags = new ArrayList(); - String tagsArr[] = new String[1]; - if (tagsStr != null && !tagsStr.isEmpty()) { - tagsArr = tagsStr.split("[ \\,]"); - for (int i = 0; i < tagsArr.length; i++) { - if (tagsArr[i].startsWith("*")) { - tagsArr[i] = tagsArr[i].substring(1); - } - if (tagsArr[i].length() > 64) { - tagsArr[i] = tagsArr[i].substring(0, 64); - } - } - tags = TagQueries.getTags(sql, tagsArr, true); - while (tags.size() > 5) { - tags.remove(5); - } - } - - String attachmentFName = null; - try { - attachmentFName = Utils.receiveMultiPartFile(request, "attach"); - } catch (Exception e) { - System.out.println("MULTIPART ERROR: " + e.toString()); - response.sendError(400); - return; - } - - String paramImg = request.getParameter("img"); - if (attachmentFName == null && paramImg != null && paramImg.length() > 10 ) { - try { - URL imgUrl = new URL(paramImg); - attachmentFName = Utils.downloadImage(imgUrl); - } catch (Exception e) { - System.out.println("DOWNLOAD ERROR: " + e.toString()); - response.sendError(500); - return; - } - } - - String attachmentType = attachmentFName != null ? attachmentFName.substring(attachmentFName.length() - 3) : null; - int mid = MessagesQueries.createMessage(sql, visitor.getUID(), body, attachmentType, tags); - SubscriptionsQueries.subscribeMessage(sql, mid, visitor.getUID()); - - Message xmsg = new Message(); - xmsg.from = new JID("juick", "juick.com", null); - xmsg.type = Message.Type.chat; - xmsg.thread = "juick-" + mid; - - JuickMessage jmsg = new JuickMessage(MessagesQueries.getMessage(sql, mid)); - xmsg.addChild(jmsg); - - Nickname nick = new Nickname(); - nick.Nickname = "@" + jmsg.getUser().getUName(); - xmsg.addChild(nick); - - if (attachmentFName != null) { - String fname = mid + "." + attachmentType; - String attachmentURL = "http://i.juick.com/photos-1024/" + fname; - - Runtime.getRuntime().exec("/var/www/juick.com/cgi/p-convert.sh /var/www/juick.com/i/tmp/" + attachmentFName + " " + fname); - - body = attachmentURL + "\n" + body; - XOOB xoob = new XOOB(); - xoob.URL = attachmentURL; - xmsg.addChild(xoob); - } - - String tagsStr2 = ""; - for (String tag : tagsArr) { - tagsStr2 += " *" + tag; - } - xmsg.body = "@" + jmsg.getUser().getUName() + ":" + tagsStr2 + "\n" + body + "\n\n#" + mid + " http://juick.com/" + mid; - - xmsg.to = new JID("juick", "s2s.juick.com", null); - xmpp.send(xmsg); - - xmsg.to.Host = "ws.juick.com"; - xmpp.send(xmsg); - - xmsg.to.Host = "push.juick.com"; - xmpp.send(xmsg); - - xmsg.to.Host = "crosspost.juick.com"; - xmsg.to.Username = "twitter"; - xmpp.send(xmsg); - xmsg.to.Username = "fb"; - xmpp.send(xmsg); - - xmsg.to.Host = "nologin.ru"; - xmsg.to.Username = "jubo"; - xmpp.send(xmsg); - - // - - response.setContentType("text/html; charset=UTF-8"); - try (PrintWriter out = response.getWriter()) { - PageTemplates.pageHead(out, "Сообщение опубликовано", null); - PageTemplates.pageNavigation(out, visitor, null); - PageTemplates.pageHomeColumn(out, sql, visitor); - - String hashtags = ""; - String tagscomma = ""; - for (int i = 0; i < tagsArr.length; i++) { - if (i > 0) { - hashtags += " "; - tagscomma += ","; - } - hashtags += "#" + tagsArr[i]; - tagscomma += tagsArr[i]; - } - - String url = URLEncoder.encode("http://juick.com/" + mid, "utf-8"); - String sharetwi = hashtags + " " + body; - if (sharetwi.length() > 115) { - sharetwi = sharetwi.substring(0, 114) + "…"; - } - sharetwi += " http://juick.com/" + mid; - String sharelj = URLEncoder.encode(body + "\n", "utf-8") + url; - - out.println("
    "); - out.println("

    Сообщение опубликовано

    "); - out.println("

    Поделитесь своим новым постом в социальных сетях:

    "); - if (CrosspostQueries.getTwitterTokens(sql, visitor.getUID()).isPresent()) { - out.println("

    Отправить в Twitter

    "); - } - out.println("

    Отправить в LiveJournal

    "); - out.println("

    Отправить в ВКонтакте

    "); - if (CrosspostQueries.getFacebookToken(sql, visitor.getUID()).isPresent()) { - out.println("

    Отправить в Facebook

    "); - } - out.println("

    Отправить в Google+

    "); - out.println("

    Ссылка на сообщение: http://juick.com/" + mid + "

    "); - out.println("
    "); - - PageTemplates.pageFooter(request, out, visitor, false); - PageTemplates.pageEnd(out); - } - } - - public void doPostComment(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, Stream xmpp, com.juick.User visitor) throws ServletException, IOException { - int mid = Utils.parseInt(request.getParameter("mid"), 0); - if (mid == 0) { - response.sendError(400); - return; - } - com.juick.Message msg = MessagesQueries.getMessage(sql, mid); - if (msg == null) { - response.sendError(404); - return; - } - - int rid = Utils.parseInt(request.getParameter("rid"), 0); - com.juick.Message reply = null; - if (rid > 0) { - reply = MessagesQueries.getReply(sql, mid, rid); - if (reply == null) { - response.sendError(404); - return; - } - } - - String body = request.getParameter("body"); - if (body == null || body.length() < 1 || body.length() > 4096) { - response.sendError(400); - return; - } - body = body.replace("\r", ""); - - if ((msg.ReadOnly && msg.getUser().getUID() != visitor.getUID()) || UserQueries.isInBLAny(sql, msg.getUser().getUID(), visitor.getUID()) || (reply != null && UserQueries.isInBLAny(sql, reply.getUser().getUID(), visitor.getUID()))) { - response.sendError(403); - return; - } - - String attachmentFName = null; - try { - attachmentFName = Utils.receiveMultiPartFile(request, "attach"); - } catch (Exception e) { - System.out.println("MULTIPART ERROR: " + e.toString()); - response.sendError(400); - return; - } - - String paramImg = request.getParameter("img"); - if (attachmentFName == null && paramImg != null && paramImg.length() > 10) { - try { - attachmentFName = Utils.downloadImage(new URL(paramImg)); - } catch (Exception e) { - System.out.println("DOWNLOAD ERROR: " + e.toString()); - response.sendError(500); - return; - } - } - - String attachmentType = attachmentFName != null ? attachmentFName.substring(attachmentFName.length() - 3) : null; - int ridnew = MessagesQueries.createReply(sql, mid, rid, visitor.getUID(), body, attachmentType); - SubscriptionsQueries.subscribeMessage(sql, mid, visitor.getUID()); - - Message xmsg = new Message(); - xmsg.from = new JID("juick", "juick.com", null); - xmsg.type = Message.Type.chat; - xmsg.thread = "juick-" + mid; - - JuickMessage jmsg = new JuickMessage(MessagesQueries.getReply(sql, mid, ridnew)); - xmsg.addChild(jmsg); - - String quote = reply != null ? reply.getText() : msg.getText(); - if (quote.length() >= 50) { - quote = quote.substring(0, 47) + "..."; - } - - Nickname nick = new Nickname(); - nick.Nickname = "@" + jmsg.getUser().getUName(); - xmsg.addChild(nick); - - if (attachmentFName != null) { - String fname = mid + "-" + ridnew + "." + attachmentType; - String attachmentURL = "http://i.juick.com/photos-1024/" + fname; - - Runtime.getRuntime().exec("/var/www/juick.com/cgi/p-convert.sh /var/www/juick.com/i/tmp/" + attachmentFName + " " + fname); - - body = attachmentURL + "\n" + body; - XOOB xoob = new XOOB(); - xoob.URL = attachmentURL; - xmsg.addChild(xoob); - } - - xmsg.body = "Reply by @" + jmsg.getUser().getUName() + ":\n>" + quote + "\n" + body + "\n\n#" + mid + "/" + ridnew + " http://juick.com/" + mid + "#" + ridnew; - - xmsg.to = new JID("juick", "s2s.juick.com", null); - xmpp.send(xmsg); - - xmsg.to.Host = "ws.juick.com"; - xmpp.send(xmsg); - - xmsg.to.Host = "push.juick.com"; - xmpp.send(xmsg); - - Utils.sendTemporaryRedirect(response, "/" + msg.getUser().getUName() + "/" + mid + "#" + ridnew); - } - - public void doPostRecomm(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, Stream xmpp, com.juick.User visitor) throws ServletException, IOException { - int mid = Utils.parseInt(request.getParameter("mid"), 0); - if (mid == 0) { - response.sendError(400); - return; - } - com.juick.Message msg = MessagesQueries.getMessage(sql, mid); - if (msg == null) { - response.sendError(404); - return; - } - if (msg.getUser().getUID() == visitor.getUID()) { - response.sendError(403); - return; - } - - boolean res = MessagesQueries.recommendMessage(sql, mid, visitor.getUID()); - - if (res) { - Message xmsg = new Message(); - xmsg.from = new JID("juick", "juick.com", null); - xmsg.to = new JID("recomm", "s2s.juick.com", null); - JuickMessage jmsg = new JuickMessage(); - jmsg.setMID(mid); - jmsg.setUser(new JuickUser(visitor)); - xmsg.addChild(jmsg); - xmpp.send(xmsg); - - Utils.replyJSON(request, response, "{\"status\":\"ok\"}"); - } else { - response.sendError(500); - } - } -} diff --git a/src/main/java/com/juick/http/www/PM.java b/src/main/java/com/juick/http/www/PM.java deleted file mode 100644 index 932d1baf..00000000 --- a/src/main/java/com/juick/http/www/PM.java +++ /dev/null @@ -1,224 +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.http.www; - -import com.juick.server.PMQueries; -import com.juick.server.UserQueries; -import com.juick.xmpp.JID; -import com.juick.xmpp.Message; -import com.juick.xmpp.Stream; -import com.juick.xmpp.extensions.JuickMessage; -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.List; - -/** - * - * @author Ugnich Anton - */ -public class PM { - - protected void doGetInbox(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { - /* - int paramBefore = 0; - String paramBeforeStr = request.getParameter("before"); - if (paramBeforeStr != null) { - try { - paramBefore = Integer.parseInt(paramBeforeStr); - } catch (NumberFormatException e) { - } - } - */ - - String title = "PM: Inbox"; - List msgs = PMQueries.getLastPMInbox(sql, visitor.getUID()); - - response.setContentType("text/html; charset=UTF-8"); - try (PrintWriter out = response.getWriter()) { - PageTemplates.pageHead(out, title, null); - PageTemplates.pageNavigation(out, visitor, null); - PageTemplates.pageHomeColumn(out, sql, visitor); - - out.println("
    "); - - if (!msgs.isEmpty()) { - out.println(""); - } - - /* - if (msgs.size() >= 20) { - String nextpage = "?before=" + msgs.get(msgs.size() - 1); - out.println("

    Читать дальше →

    "); - } - */ - - out.println("
    "); - - PageTemplates.pageFooter(request, out, visitor, false); - PageTemplates.pageEnd(out); - } - } - - protected void doGetSent(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { - /* - int paramBefore = 0; - String paramBeforeStr = request.getParameter("before"); - if (paramBeforeStr != null) { - try { - paramBefore = Integer.parseInt(paramBeforeStr); - } catch (NumberFormatException e) { - } - } - */ - - String title = "PM: Sent"; - List msgs = PMQueries.getLastPMSent(sql, visitor.getUID()); - - String uname = request.getParameter("uname"); - if (!UserQueries.checkUserNameValid(uname)) { - uname = ""; - } - - response.setContentType("text/html; charset=UTF-8"); - try (PrintWriter out = response.getWriter()) { - PageTemplates.pageHead(out, title, null); - PageTemplates.pageNavigation(out, visitor, null); - PageTemplates.pageHomeColumn(out, sql, visitor); - - out.println("
    "); - - out.println("
    "); - out.println("
    "); - out.println("
    To:
    "); - out.println("
    "); - out.println("
    "); - out.println("
    "); - out.println("
    "); - - if (!msgs.isEmpty()) { - out.println(""); - } - - /* - if (msgs.size() >= 20) { - String nextpage = "?before=" + msgs.get(msgs.size() - 1); - out.println("

    Читать дальше →

    "); - } - */ - - out.println("
    "); - - PageTemplates.pageFooter(request, out, visitor, false); - PageTemplates.pageEnd(out); - } - } - - public void doPostPM(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, Stream xmpp, com.juick.User visitor) throws ServletException, IOException { - String uname = request.getParameter("uname"); - if (uname.startsWith("@")) { - uname = uname.substring(1); - } - int uid = 0; - if (UserQueries.checkUserNameValid(uname)) { - uid = UserQueries.getUIDbyName(sql, uname); - } - - String body = request.getParameter("body"); - if (uid == 0 || body == null || body.length() < 1 || body.length() > 10240) { - response.sendError(400); - return; - } - - if (UserQueries.isInBLAny(sql, uid, visitor.getUID())) { - response.sendError(403); - return; - } - - if (PMQueries.createPM(sql, visitor.getUID(), uid, body)) { - Message msg = new Message(); - msg.from = new JID("juick", "juick.com", null); - msg.to = new JID(Integer.toString(uid), "push.juick.com", null); - JuickMessage jmsg = new JuickMessage(); - jmsg.setUser(visitor); - jmsg.setText(body); - msg.childs.add(jmsg); - xmpp.send(msg); - - msg.to.Host = "ws.juick.com"; - xmpp.send(msg); - - List jids = UserQueries.getJIDsbyUID(sql, uid); - for (String jid : jids) { - Message mm = new Message(); - mm.to = new JID(jid); - mm.type = Message.Type.chat; - if (PMQueries.havePMinRoster(sql, visitor.getUID(), jid)) { - mm.from = new JID(jmsg.getUser().getUName(), "juick.com", "Juick"); - mm.body = body; - } else { - mm.from = new JID("juick", "juick.com", "Juick"); - mm.body = "Private message from @" + jmsg.getUser().getUName() + ":\n" + body; - } - xmpp.send(mm); - } - - Utils.sendTemporaryRedirect(response, "/pm/sent"); - - } else { - response.sendError(500); - } - } -} diff --git a/src/main/java/com/juick/http/www/PageTemplates.java b/src/main/java/com/juick/http/www/PageTemplates.java deleted file mode 100644 index da5659e9..00000000 --- a/src/main/java/com/juick/http/www/PageTemplates.java +++ /dev/null @@ -1,479 +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.http.www; - -import com.juick.Message; -import com.juick.Tag; -import com.juick.server.MessagesQueries; -import com.juick.server.TagQueries; -import com.juick.server.UserQueries; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.util.StringUtils; -import ru.sape.Sape; - -import javax.servlet.http.HttpServletRequest; -import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -/** - * - * @author Ugnich Anton - */ -public class PageTemplates { - - public static Sape sape = null; - protected static final SimpleDateFormat sdfSQL = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - private static SimpleDateFormat sdfSimple = new SimpleDateFormat("d MMM"); - private static SimpleDateFormat sdfFull = new SimpleDateFormat("d MMM yyyy"); - private static String tagsHTML = null; - - public static void pageHead(PrintWriter out, String title, String headers) { - out.println(""); - out.print(""); - out.print(""); - out.println(""); - out.print(""); - out.print(""); - out.print(""); - if (headers != null) { - out.print(headers); - } - out.print("" + title + ""); - out.println(""); - out.println(""); - out.println(""); - out.println(""); - out.flush(); - out.println(""); - } - - public static void pageNavigation(PrintWriter out, com.juick.User visitor, String search) { - out.println("
    "); - out.println(" "); - out.print(" "); - out.print("
    "); - out.println("
    "); - if (visitor != null) { - out.print(" "); - out.print(" "); - } else { - out.println("

    Чтобы добавлять сообщения и комментарии, представьтесь.

    "); - } - out.println("
    "); - out.println("
    "); - } - - public static void pageHomeColumn(PrintWriter out, JdbcTemplate sql, com.juick.User visitor) { - pageHomeColumn(out, sql, visitor, false); - } - - public static void pageHomeColumn(PrintWriter out, JdbcTemplate sql, com.juick.User visitor, boolean showAdv) { - if (tagsHTML == null) { - tagsHTML = PageTemplates.formatPopularTags(sql, 80); - } - - out.println(""); - } - - public static String formatPopularTags(JdbcTemplate sql, int cnt) { - List popularTags = TagQueries.getPopularTags(sql).stream() - .map(t -> "" + Utils.encodeHTML(t) + "").collect(Collectors.toList()); - return StringUtils.collectionToDelimitedString(popularTags, " "); - } - - public static void pageFooter(HttpServletRequest request, PrintWriter out, com.juick.User visitor, boolean sapeon) { - out.println("
    "); - out.println(" "); - out.print("
    "); - out.print("Twitter"); - out.print("ВКонтакте"); - out.print("Facebook"); - out.println("
    "); - out.print("
    juick.com © 2008-2016"); - - String queryString = request.getQueryString(); - String requestURI = request.getRequestURI(); - if (sapeon && sape != null && (visitor == null || visitor.getUID() == 1) && queryString == null) { - String links = sape.getPageLinks(requestURI, request.getCookies()).render(); - if (links != null && !links.isEmpty()) { - out.print("
    Спонсоры: " + links); - } - } - - out.println("
    "); - out.println("
    "); - - if (visitor != null) { - out.println(""); - } - - out.println(""); - } - - public static void pageEnd(PrintWriter out) { - out.println(""); - } - - public static String formatTags(List tags) { - String ret = ""; - for (Tag tag : tags) { - String tagName = tag.Name.replaceAll("<", "<").replaceAll(">", ">"); - try { - ret += " *"; - } catch (UnsupportedEncodingException e) { - } - } - - return ret; - } - - public static String formatTags(List tags, com.juick.User user) { - String ret = ""; - for (String tag : tags) { - tag = tag.replaceAll("<", "<"); - tag = tag.replaceAll(">", ">"); - try { - ret += " *" + tag + ""; - } catch (UnsupportedEncodingException e) { - } - } - - return ret; - } - - public static String formatDate(int minutes, Date fulldate) { - if (minutes < 1) { - return "сейчас"; - } else if (minutes < 60) { - String unit; - int ld = minutes % 10; - if ((minutes < 10 || minutes > 20) && ld == 1) { - unit = "минуту"; - } else if ((minutes < 10 || minutes > 20) && ld > 1 && ld < 5) { - unit = "минуты"; - } else { - unit = "минут"; - } - return minutes + " " + unit + " назад"; - } else if (minutes < 1440) { - int hours = (minutes / 60); - String unit; - int ld = hours % 10; - if ((hours < 10 || hours > 20) && ld == 1) { - unit = "час"; - } else if ((hours < 10 || hours > 20) && ld > 1 && ld < 5) { - unit = "часа"; - } else { - unit = "часов"; - } - return hours + " " + unit + " назад"; - } else if (minutes < 20160) { - int days = (minutes / 1440); - String unit; - int ld = days % 10; - if ((days < 10 || days > 20) && ld == 1) { - unit = "день"; - } else if ((days < 10 || days > 20) && ld > 1 && ld < 5) { - unit = "дня"; - } else { - unit = "дней"; - } - return days + " " + unit + " назад"; - } else { - String ret = sdfFull.format(fulldate); - synchronized (sdfSQL) { - try { - Calendar c = Calendar.getInstance(); - int curyear = c.get(Calendar.YEAR); - c.setTime(fulldate); - if (c.get(Calendar.YEAR) == curyear) { - ret = sdfSimple.format(fulldate); - } else { - ret = sdfFull.format(fulldate); - } - } catch (Exception e) { - System.err.println("PARSE EXCEPTION: " + fulldate); - } - } - return ret; - } - } - - public static String formatJSLocalTime(Date ts) { - return ""; - } - - public static String formatReplies(int replies) { - int ld = replies % 10; - int lh = replies % 100; - if ((lh < 10 || lh > 20) && ld == 1) { - return replies + " ответ"; - } else if ((lh < 10 || lh > 20) && ld > 1 && ld < 5) { - return replies + " ответа"; - } else { - return replies + " ответов"; - } - } - private static Pattern regexLinks2 = Pattern.compile("((?<=\\s)|(?<=\\A))([\\[\\{]|<)((?:ht|f)tps?://(?:www\\.)?([^\\/\\s\\\"\\)\\!]+)/?(?:[^\\]\\}](?", ">"); - - // http://juick.com/last?page=2 - // http://juick.com/last?page=2 - msg = msg.replaceAll("((?<=\\s)|(?<=\\A))((?:ht|f)tps?://(?:www\\.)?([^\\/\\s\\n\\\"]+)/?[^\\s\\n\\\"]*)", "$1$2"); - - // (http://juick.com/last?page=2) - // (http://juick.com/last?page=2) - Matcher m = regexLinks2.matcher(msg); - StringBuffer sb = new StringBuffer(); - while (m.find()) { - String url = m.group(3).replace(" ", "%20").replaceAll("\\s+", ""); - m.appendReplacement(sb, "$1$2" + url + "$5"); - } - m.appendTail(sb); - msg = sb.toString(); - - return "
    " + msg + "
    "; - } - - public static String formatMessage(String msg) { - msg = msg.replaceAll("&", "&"); - msg = msg.replaceAll("<", "<"); - msg = msg.replaceAll(">", ">"); - - // -- - // — - msg = msg.replaceAll("((?<=\\s)|(?<=\\A))\\-\\-?((?=\\s)|(?=\\Z))", "$1—$2"); - - // http://juick.com/last?page=2 - // juick.com - msg = msg.replaceAll("((?<=\\s)|(?<=\\A))((?:ht|f)tps?://(?:www\\.)?([^\\/\\s\\n\\\"]+)/?[^\\s\\n\\\"]*)", "$1$3"); - - // [link text][http://juick.com/last?page=2] - // link text - msg = msg.replaceAll("\\[([^\\]]+)\\]\\[((?:ht|f)tps?://[^\\]]+)\\]", "$1"); - msg = msg.replaceAll("\\[([^\\]]+)\\]\\(((?:ht|f)tps?://[^\\)]+)\\)", "$1"); - - // #12345 - // #12345 - msg = msg.replaceAll("((?<=\\s)|(?<=\\A)|(?<=\\p{Punct}))#(\\d+)((?=\\s)|(?=\\Z)|(?=\\))|(?=\\.)|(?=\\,))", "$1#$2$3"); - - // #12345/65 - // #12345/65 - msg = msg.replaceAll("((?<=\\s)|(?<=\\A)|(?<=\\p{Punct}))#(\\d+)/(\\d+)((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1#$2/$3$4"); - - // *bold* - // bold - msg = msg.replaceAll("((?<=\\s)|(?<=\\A)|(?<=\\p{Punct}))\\*([^\\*\\n<>]+)\\*((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1$2$3"); - - // /italic/ - // italic - msg = msg.replaceAll("((?<=\\s)|(?<=\\A))/([^\\/\\n<>]+)/((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1$2$3"); - - // _underline_ - // underline - msg = msg.replaceAll("((?<=\\s)|(?<=\\A))_([^\\_\\n<>]+)_((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1$2$3"); - - // /12 - // /12 - msg = msg.replaceAll("((?<=\\s)|(?<=\\A))\\/(\\d+)((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1/$2$3"); - - // @username@jabber.org - // @username@jabber.org - msg = msg.replaceAll("((?<=\\s)|(?<=\\A))@([\\w\\-\\.]+@[\\w\\-\\.]+)((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1@$2$3"); - - // @username - // @username - msg = msg.replaceAll("((?<=\\s)|(?<=\\A))@([\\w\\-]{2,16})((?=\\s)|(?=\\Z)|(?=\\p{Punct}))", "$1@$2$3"); - - // (http://juick.com/last?page=2) - // (juick.com) - Matcher m = regexLinks2.matcher(msg); - StringBuffer sb = new StringBuffer(); - while (m.find()) { - String url = m.group(3).replace(" ", "%20").replaceAll("\\s+", ""); - m.appendReplacement(sb, "$1$2$4$5"); - } - m.appendTail(sb); - msg = sb.toString(); - - // > citate - msg = msg.replaceAll("(?:(?<=\\n)|(?<=\\A))> *(.*)?(\\n|(?=\\Z))", "$1"); - msg = msg.replaceAll("", "\n"); - - msg = msg.replaceAll("\n", "
    \n"); - return msg; - } - - public static void printMessages(PrintWriter out, JdbcTemplate sql, com.juick.User user, List mids, com.juick.User visitor, int YandexID, int ad_mid) { - List msgs = MessagesQueries.getMessages(sql, mids); - - for (int i = 0; i < msgs.size(); i++) { - com.juick.Message msg = msgs.get(i); - if (msg.getMID() == ad_mid) { - msgs.remove(i); - msgs.add(0, msg); - break; - } - } - - List blUIDs = new ArrayList(20); - if (visitor != null) { - for (Message msg : msgs) { - blUIDs.add(msg.getUser().getUID()); - } - blUIDs = UserQueries.checkBL(sql, visitor.getUID(), blUIDs); - } - - for (int i = 0; i < msgs.size(); i++) { - - com.juick.Message msg = msgs.get(i); - - List tags = MessagesQueries.getMessageTags(sql, msg.getMID()); - String tagsStr = formatTags(tags); - if (msg.ReadOnly) { - tagsStr += " *readonly"; - } - if (msg.Privacy < 0) { - tagsStr += " *friends"; - } - if (msg.getMID() == ad_mid) { - tagsStr += " *реклама"; - } - - String txt; - if (!msg.Tags.isEmpty() && msg.Tags.contains("code")) { - txt = formatMessageCode(msg.getText()); - } else { - txt = formatMessage(msg.getText()); - } - - out.println("
    "); - out.println(" "); - out.println("
    @" + msg.getUser().getUName() + ":" + tagsStr + "
    "); - out.println("
    "); - if (msg.AttachmentType != null) { - String fname = msg.getMID() + "." + msg.AttachmentType; - out.println("

    \"\"/

    "); - } - out.println("

    " + txt + "

    "); - if (msg.AttachmentType != null) { - out.println("
    "); - } - out.print(" "); - - out.print(" "); - out.print("
    "); - } - } -} diff --git a/src/main/java/com/juick/http/www/RSS.java b/src/main/java/com/juick/http/www/RSS.java deleted file mode 100644 index 349743b5..00000000 --- a/src/main/java/com/juick/http/www/RSS.java +++ /dev/null @@ -1,101 +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.http.www; - -import com.juick.Message; -import com.juick.server.MessagesQueries; -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; - -/** - * - * @author ugnich - */ -public class RSS { - - private static final SimpleDateFormat sdfRSS = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z"); - - protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, int uid, String uname) throws ServletException, IOException { - List mids = MessagesQueries.getUserBlog(sql, uid, 0, 0); - if (mids.isEmpty()) { - response.sendError(404); - return; - } - - List msgs = MessagesQueries.getMessages(sql, mids); - - response.setContentType("application/rss+xml; charset=UTF-8"); - try (PrintWriter out = response.getWriter()) { - out.println(""); - out.println(""); - out.println(""); - out.println(""); - out.println("" + uname + " - Juick"); - out.println("http://juick.com/" + uname + "/"); - out.println("The latest messages by @" + uname + " at Juick"); - out.println("http://i.juick.com/a/" + uid + ".png" + uname + " - Juickhttp://juick.com/" + uname + "/"); - - for (Message msg : msgs) { - out.println(""); - out.println("http://juick.com/" + msg.getUser().getUName() + "/" + msg.getMID() + ""); - out.println("http://juick.com/" + msg.getUser().getUName() + "/" + msg.getMID() + ""); - - out.print("<![CDATA[@" + msg.getUser().getUName() + ":"); - if (!msg.Tags.isEmpty()) { - for (int n = 0; n < msg.Tags.size(); n++) { - out.print(" *" + msg.Tags.get(n)); - } - } - out.println("]]>"); - out.println(""); - - Date date = msg.getDate(); - out.println("" + sdfRSS.format(date) + ""); - - - out.println("http://juick.com/" + msg.getUser().getUName() + "/" + msg.getMID() + ""); - if (!msg.Tags.isEmpty()) { - for (int n = 0; n < msg.Tags.size(); n++) { - out.println("" + msg.Tags.get(n) + ""); - } - } - if (msg.AttachmentType != null) { - if (msg.AttachmentType.equals("jpg")) { - out.println(""); - out.println(""); - } else if (msg.AttachmentType.equals("png")) { - out.println(""); - out.println(""); - } - } - out.println(""); - out.println(""); - } - - out.println(""); - } - } -} diff --git a/src/main/java/com/juick/http/www/Settings.java b/src/main/java/com/juick/http/www/Settings.java deleted file mode 100644 index 54ee0ee9..00000000 --- a/src/main/java/com/juick/http/www/Settings.java +++ /dev/null @@ -1,91 +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.http.www; - -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; - -/** - * - * @author Ugnich Anton - */ -public class Settings { - - protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - - response.setContentType("text/html; charset=UTF-8"); - try (PrintWriter out = response.getWriter()) { - PageTemplates.pageHead(out, "Логин", ""); - PageTemplates.pageNavigation(out, visitor, null); - - out.println("
    "); - out.println("
    "); - out.println("
    "); - out.println("
    "); - out.println("

    Имя пользователя:

    "); - out.println("

    Пароль:

    "); - out.println("

    "); - out.println("
    "); - out.println("
    "); - out.println("
    "); - out.println("
    "); // topwrapper - - PageTemplates.pageFooter(request, out, visitor, false); - PageTemplates.pageEnd(out); - } - } - - protected void doPost(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - String username = request.getParameter("username"); - String password = request.getParameter("password"); - if (username == null || password == null || username.length() > 32 || password.isEmpty()) { - response.sendError(400); - return; - } - - int uid = com.juick.server.UserQueries.checkPassword(sql, username, password); - if (uid > 0) { - String hash = com.juick.server.UserQueries.getHashByUID(sql, uid); - Cookie c = new Cookie("hash", hash); - c.setDomain(".juick.com"); - c.setMaxAge(365 * 24 * 60 * 60); - response.addCookie(c); - - - if (uid > 0) { - throw new IOException("Settings"); - } - - String referer = request.getHeader("Referer"); - if (referer != null && referer.startsWith("http://juick.com/") && !referer.equals("http://juick.com/login")) { - response.sendRedirect(referer); - } else { - response.sendRedirect("/"); - } - } else { - response.sendError(403); - } - } -} diff --git a/src/main/java/com/juick/http/www/SignUp.java b/src/main/java/com/juick/http/www/SignUp.java deleted file mode 100644 index 1ee23386..00000000 --- a/src/main/java/com/juick/http/www/SignUp.java +++ /dev/null @@ -1,258 +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.http.www; - -import com.juick.server.UserQueries; -import org.apache.commons.lang3.tuple.Pair; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.List; - -/** - * - * @author Ugnich Anton - */ -public class SignUp { - - protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - - String type = request.getParameter("type"); - String hash = request.getParameter("hash"); - if (type == null || type.isEmpty() || hash == null || hash.isEmpty() || hash.length() > 36 || !type.matches("^[a-zA-Z0-9\\-]+$") || !hash.matches("^[a-zA-Z0-9\\-]+$")) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return; - } - - String account = null; - if (type.equals("fb")) { - account = getFacebookNameByHash(sql, hash); - } else if (type.equals("vk")) { - account = getVKNameByHash(sql, hash); - } else if (type.equals("xmpp")) { - account = getJIDByHash(sql, hash); - } else if (type.equals("durov")) { - account = getTelegramNameByHash(sql, hash); - } - if (account == null) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return; - } - - response.setContentType("text/html; charset=UTF-8"); - try (PrintWriter out = response.getWriter()) { - PageTemplates.pageHead(out, "Новый пользователь", null); - PageTemplates.pageNavigation(out, visitor, null); - - out.println("
    "); - - out.print("

    "); - if (type.charAt(0) == 'f') { - out.print("\"Facebook\"/"); - } else if (type.charAt(0) == 'v') { - out.print("\"VKontakte\"/"); - } else if (type.charAt(0) == 'x') { - out.print("\"XMPP\"/"); - } else if (type.charAt(0) == 'd') { - out.print("\"Telegram\"/"); - } - out.println(account + "

    "); - - out.println("

    Связать с существующим аккаунтом Juick

    "); - out.println("
    "); - out.println(""); - out.println(""); - out.println(""); - if (visitor != null) { - out.println(""); - } else { - out.println("

    Имя пользователя:

    "); - out.println("

    Пароль:

    "); - out.println("

    "); - } - out.println("
    "); - - out.println("
    "); - - out.println("

    Создать новый аккаунт Juick

    "); - out.println("
    "); - out.println(""); - out.println(""); - out.println(""); - out.println("

    Имя пользователя:
    (От 2-х до 16-и латинских символов и/или цифр, дефис)

    "); - out.println("

    Пароль:
    (от 6-и до 32-х символов)

    "); - out.println("

    "); - out.println("
    "); - - out.println("
    "); - - PageTemplates.pageFooter(request, out, visitor, false); - PageTemplates.pageEnd(out); - } - } - - protected void doPost(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - int uid = 0; - - String type = request.getParameter("type"); - String hash = request.getParameter("hash"); - if (type == null || type.isEmpty() || hash == null || hash.isEmpty() || hash.length() > 36 || !type.matches("^[a-zA-Z0-9\\-]+$") || !hash.matches("^[a-zA-Z0-9\\-]+$")) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return; - } - - String action = request.getParameter("action"); - if (action.charAt(0) == 'l') { - - if (visitor == null) { - String username = request.getParameter("username"); - String password = request.getParameter("password"); - if (username == null || password == null || username.length() > 32 || password.isEmpty()) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return; - } - uid = com.juick.server.UserQueries.checkPassword(sql, username, password); - } else { - uid = visitor.getUID(); - } - - if (uid <= 0) { - response.sendError(HttpServletResponse.SC_FORBIDDEN); - return; - } - - if (!(type.charAt(0) == 'f' && setFacebookUser(sql, hash, uid)) - && !(type.charAt(0) == 'v' && setVKUser(sql, hash, uid)) - && !(type.charAt(0) == 'd' && setTelegramUser(sql, hash, uid)) - && !(type.charAt(0) == 'x' && setJIDUser(sql, hash, uid))) { - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - - } else { // Create new account - String username = request.getParameter("username"); - String password = request.getParameter("password"); - if (username == null || password == null || username.length() < 2 || username.length() > 16 || !username.matches("^[a-zA-Z0-9\\-]+$") || password.length() < 6 || password.length() > 32) { - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return; - } - - // CHECK USERNAME - - uid = UserQueries.createUser(sql, username, password); - if (uid <= 0) { - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - - if (!(type.charAt(0) == 'f' && setFacebookUser(sql, hash, uid)) - && !(type.charAt(0) == 'v' && setVKUser(sql, hash, uid)) - && !(type.charAt(0) == 'd' && setTelegramUser(sql, hash, uid)) - && !(type.charAt(0) == 'x' && setJIDUser(sql, hash, uid))) { - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - - int ref = 0; - String sRef = Utils.getCookie(request, "ref"); - if (sRef != null) { - try { - ref = Integer.parseInt(sRef); - } catch (Exception e) { - } - } - - if (ref > 0) { - setUserRef(sql, uid, ref); - } - - visitor = null; - } - - if (visitor == null) { - hash = com.juick.server.UserQueries.getHashByUID(sql, uid); - Cookie c = new Cookie("hash", hash); - c.setMaxAge(365 * 24 * 60 * 60); - response.addCookie(c); - } - - response.sendRedirect("/"); - } - - private boolean setUserRef(JdbcTemplate sql, int uid, int ref) { - return sql.update("INSERT INTO users_refs(user_id,ref) VALUES (?,?)", uid, ref) > 0; - } - - private String getFacebookNameByHash(JdbcTemplate sql, String hash) { - try { - return sql.queryForObject("SELECT fb_name,fb_link FROM facebook WHERE loginhash=?", String.class, hash); - } catch (EmptyResultDataAccessException e) { - return null; - } - } - private String getTelegramNameByHash(JdbcTemplate sql, String hash) { - try { - String name = sql.queryForObject("SELECT tg_name FROM telegram WHERE loginhash=?", String.class, hash); - return "" + name + ""; - } catch (EmptyResultDataAccessException e) { - return null; - } - } - - private boolean setFacebookUser(JdbcTemplate sql, String hash, int uid) { - return sql.update("UPDATE facebook SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; - } - - private String getVKNameByHash(JdbcTemplate sql, String hash) { - List> logins = sql.query("SELECT vk_name,vk_link FROM vk WHERE loginhash=?", - (rs, num) -> { - return Pair.of(rs.getString(1), rs.getString(2)); - }, hash); - if (logins.size() > 0) { - return "" + logins.get(0).getLeft() + ""; - } - return null; - } - - private boolean setVKUser(JdbcTemplate sql, String hash, int uid) { - return sql.update("UPDATE vk SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; - } - private boolean setTelegramUser(JdbcTemplate sql, String hash, int uid) { - return sql.update("UPDATE telegram SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; - } - - private String getJIDByHash(JdbcTemplate sql, String hash) { - try { - return sql.queryForObject("SELECT jid FROM jids WHERE loginhash=?", String.class, hash); - } catch (EmptyResultDataAccessException e) { - return null; - } - } - - private boolean setJIDUser(JdbcTemplate sql, String hash, int uid) { - return sql.update("UPDATE jids SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; - } -} diff --git a/src/main/java/com/juick/http/www/TwitterAuth.java b/src/main/java/com/juick/http/www/TwitterAuth.java deleted file mode 100644 index cc586740..00000000 --- a/src/main/java/com/juick/http/www/TwitterAuth.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.juick.http.www; - -import com.github.scribejava.apis.TwitterApi; -import com.github.scribejava.core.builder.ServiceBuilder; -import com.github.scribejava.core.model.OAuth1AccessToken; -import com.github.scribejava.core.model.OAuth1RequestToken; -import com.github.scribejava.core.model.OAuthRequest; -import com.github.scribejava.core.model.Verb; -import com.github.scribejava.core.oauth.OAuth10aService; -import com.juick.server.UserQueries; -import org.json.JSONObject; -import org.springframework.jdbc.core.JdbcTemplate; - -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 vt on 01.12.2015. - */ -public class TwitterAuth { - - private final static String VERIFY_URL = "https://api.twitter.com/1.1/account/verify_credentials.json"; - - private String consumerKey, consumerSecret; - - public TwitterAuth(String consumerKey, String consumerSecret) { - this.consumerKey = consumerKey; - this.consumerSecret = consumerSecret; - } - - protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - String hash = "", request_token = "", request_token_secret = ""; - 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 = UserQueries.getUserByHash(sql, hash); - if ( user == null || user.getUID() == 0) { - response.sendError(403); - return; - } - OAuth10aService oAuthService = new ServiceBuilder() - .apiKey(consumerKey) - .apiSecret(consumerSecret) - .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, VERIFY_URL, oAuthService); - oAuthService.signRequest(accessToken, oAuthRequest); - JSONObject jsonResponse = new JSONObject(oAuthRequest.send().getBody()); - String screenName = jsonResponse.getString("screen_name"); - if (UserQueries.linkTwitterAccount(sql, user, accessToken.getToken(), accessToken.getTokenSecret(), - screenName)) { - response.setStatus(HttpServletResponse.SC_FOUND); - response.setHeader("Location", "http://juick.com/settings"); - } else { - response.sendError(500); - } - } - } - } -} diff --git a/src/main/java/com/juick/http/www/User.java b/src/main/java/com/juick/http/www/User.java deleted file mode 100644 index b8441560..00000000 --- a/src/main/java/com/juick/http/www/User.java +++ /dev/null @@ -1,343 +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.http.www; - -import com.juick.Tag; -import com.juick.server.MessagesQueries; -import com.juick.server.TagQueries; -import com.juick.server.UserQueries; -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.List; - -/** - * - * @author Ugnich Anton - */ -public class User { - - protected void doGetBlog(JdbcTemplate sql, JdbcTemplate sqlSearch, HttpServletRequest request, HttpServletResponse response, com.juick.User user) throws ServletException, IOException { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - - List mids; - - String paramShow = request.getParameter("show"); - - com.juick.Tag paramTag = null; - String paramTagStr = request.getParameter("tag"); - if (paramTagStr != null) { - if (paramTagStr.length() < 64) { - paramTag = TagQueries.getTag(sql, paramTagStr, false); - } - if (paramTag == null) { - Errors.doGet404(sql, request, response); - return; - } else if (!paramTag.Name.equals(paramTagStr)) { - String url = "/" + user.getUName() + "/?tag=" + URLEncoder.encode(paramTag.Name, "UTF-8"); - Utils.sendPermanentRedirect(response, url); - return; - } - } - - int paramBefore = 0; - String paramBeforeStr = request.getParameter("before"); - if (paramBeforeStr != null) { - try { - paramBefore = Integer.parseInt(paramBeforeStr); - } catch (NumberFormatException e) { - } - } - - String paramSearch = request.getParameter("search"); - if (paramSearch != null && paramSearch.length() > 64) { - paramSearch = null; - } - - int privacy = 0; - if (visitor != null) { - if (user.getUID() == visitor.getUID() || visitor.getUID() == 1) { - privacy = -3; - } else if (UserQueries.isInWL(sql, user.getUID(), visitor.getUID())) { - privacy = -2; - } - } - - String title; - if (paramShow == null) { - if (paramTag != null) { - title = "Блог " + user.getUName() + ": *" + Utils.encodeHTML(paramTag.Name); - mids = MessagesQueries.getUserTag(sql, user.getUID(), paramTag.TID, privacy, paramBefore); - } else if (paramSearch != null) { - title = "Блог " + user.getUName() + ": " + Utils.encodeHTML(paramSearch); - mids = MessagesQueries.getUserSearch(sql, sqlSearch, user.getUID(), Utils.encodeSphinx(paramSearch), privacy, paramBefore); - } else { - title = "Блог " + user.getUName(); - mids = MessagesQueries.getUserBlog(sql, user.getUID(), privacy, paramBefore); - } - } else if (paramShow.equals("recomm")) { - title = "Рекомендации " + user.getUName(); - mids = MessagesQueries.getUserRecommendations(sql, user.getUID(), paramBefore); - } else if (paramShow.equals("photos")) { - title = "Фотографии " + user.getUName(); - mids = MessagesQueries.getUserPhotos(sql, user.getUID(), privacy, paramBefore); - } else { - Errors.doGet404(sql, request, response); - return; - } - - if (visitor == null) { - pageUserRefCookie(request, response, user.getUID()); - } - - response.setContentType("text/html; charset=UTF-8"); - try (PrintWriter out = response.getWriter()) { - String head = ""; - if (paramTag != null && TagQueries.getTagNoIndex(sql, paramTag.TID)) { - head += ""; - } else if (paramBefore > 0 || paramShow != null) { - head += ""; - } - PageTemplates.pageHead(out, title, head); - PageTemplates.pageNavigation(out, visitor, null); - pageUserColumn(out, sql, user, visitor); - - if (mids.size() > 0) { - out.println("
    "); - - if (paramTag != null) { - out.println("

    ← Все записи с тегом " + Utils.encodeHTML(paramTag.Name) + "

    "); - } - - PageTemplates.printMessages(out, sql, user, mids, visitor, visitor == null ? 4 : 5, 0); - - if (mids.size() >= 20) { - String nextpage = "?before=" + mids.get(mids.size() - 1); - if (paramShow != null) { - nextpage += "&show=" + paramShow; - } - if (paramTag != null) { - nextpage += "&tag=" + URLEncoder.encode(paramTag.Name, "UTF-8"); - } - if (paramSearch != null) { - nextpage += "&search=" + URLEncoder.encode(paramSearch, "UTF-8"); - } - out.println("

    Читать дальше →

    "); - } - - out.println("
    "); - } - - PageTemplates.pageFooter(request, out, visitor, true); - PageTemplates.pageEnd(out); - } - } - - protected void doGetTags(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User user) throws ServletException, IOException { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - - if (visitor == null) { - pageUserRefCookie(request, response, user.getUID()); - } - - response.setContentType("text/html; charset=UTF-8"); - try (PrintWriter out = response.getWriter()) { - String head = ""; - PageTemplates.pageHead(out, "Теги " + user.getUName(), head); - PageTemplates.pageNavigation(out, visitor, null); - pageUserColumn(out, sql, user, visitor); - - out.println("
    "); - out.println("

    " + pageUserTags(sql, user, visitor, 0) + "

    "); - out.println("
    "); - - PageTemplates.pageFooter(request, out, visitor, false); - PageTemplates.pageEnd(out); - } - } - - protected void doGetFriends(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User user) throws ServletException, IOException { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - - if (visitor == null) { - pageUserRefCookie(request, response, user.getUID()); - } - - response.setContentType("text/html; charset=UTF-8"); - try (PrintWriter out = response.getWriter()) { - String head = ""; - PageTemplates.pageHead(out, "Подписки " + user.getUName(), head); - PageTemplates.pageNavigation(out, visitor, null); - pageUserColumn(out, sql, user, visitor); - - out.println("
    "); - out.println(""); - - List friends = UserQueries.getUserFriends(sql, user.getUID()); - for (int i = 0; i < friends.size(); i++) { - if (i % 3 == 0 && i > 0) { - out.print(""); - } - out.print(""); - } - - out.println("
    " - + friends.get(i).getUName() + "
    "); - out.println("
    "); - - PageTemplates.pageFooter(request, out, visitor, false); - PageTemplates.pageEnd(out); - } - } - - protected void doGetReaders(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, com.juick.User user) throws ServletException, IOException { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - - if (visitor == null) { - pageUserRefCookie(request, response, user.getUID()); - } - - response.setContentType("text/html; charset=UTF-8"); - try (PrintWriter out = response.getWriter()) { - String head = ""; - PageTemplates.pageHead(out, "Читатели " + user.getUName(), head); - PageTemplates.pageNavigation(out, visitor, null); - pageUserColumn(out, sql, user, visitor); - - out.println("
    "); - out.println(""); - - List readers = UserQueries.getUserReaders(sql, user.getUID()); - for (int i = 0; i < readers.size(); i++) { - if (i % 3 == 0 && i > 0) { - out.print(""); - } - out.print(""); - } - - out.println("
    " - + readers.get(i).getUName() + "
    "); - out.println("
    "); - - PageTemplates.pageFooter(request, out, visitor, false); - PageTemplates.pageEnd(out); - } - } - - public static void pageUserRefCookie(HttpServletRequest request, HttpServletResponse response, int uid) { - String hReferer = request.getHeader("Referer"); - String ref = Utils.getCookie(request, "ref"); - - if (ref == null && (hReferer == null || !(hReferer.startsWith("http://juick.com/") || hReferer.startsWith("https://juick.com/")))) { - Cookie c = new Cookie("ref", Integer.toString(uid)); - c.setMaxAge(7 * 24 * 60 * 60); - c.setPath("/"); - response.addCookie(c); - } - } - - public static void pageUserColumn(PrintWriter out, JdbcTemplate sql, com.juick.User user, com.juick.User visitor) { - out.println(""); - } - - public static String pageUserTags(JdbcTemplate sql, com.juick.User user, com.juick.User visitor, int cnt) { - List tags = TagQueries.getUserTagsAll(sql, user.getUID()); - int maxUsageCnt = tags.stream().map(t -> t.UsageCnt).max(Integer::max).orElse(0); - String ret = ""; - int count = Math.min(tags.size(), cnt); - for (int i = 0; i < count; i++) { - String tag = Utils.encodeHTML(tags.get(i).Name); - try { - tag = "" + tag + ""; - } catch (UnsupportedEncodingException e) { - } - - if (tags.get(i).UsageCnt > maxUsageCnt / 3 * 2) { - ret += "" + tag + " "; - } else if (tags.get(i).UsageCnt > maxUsageCnt / 3) { - ret += "" + tag + " "; - } else { - ret += tag + " "; - } - } - return ret; - } -} diff --git a/src/main/java/com/juick/http/www/UserThread.java b/src/main/java/com/juick/http/www/UserThread.java deleted file mode 100644 index 638e3a3b..00000000 --- a/src/main/java/com/juick/http/www/UserThread.java +++ /dev/null @@ -1,364 +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.http.www; - -import com.juick.Message; -import com.juick.Tag; -import com.juick.server.MessagesQueries; -import com.juick.server.UserQueries; -import org.springframework.jdbc.core.JdbcTemplate; - -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.List; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * - * @author Ugnich Anton - */ -public class UserThread { - - protected void doGetThread(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response, int MID) throws ServletException, IOException { - com.juick.User visitor = Utils.getVisitorUser(sql, request, response); - - if (!MessagesQueries.canViewThread(sql, MID, visitor != null ? visitor.getUID() : 0)) { - response.sendError(403); - return; - } - - com.juick.Message msg = MessagesQueries.getMessage(sql, MID); - - boolean listview = false; - String paramView = request.getParameter("view"); - if (paramView != null) { - if (paramView.equals("list")) { - listview = true; - if (visitor != null) { - UserQueries.setUserOptionInt(sql, visitor.getUID(), "repliesview", 1); - } - } else if (paramView.equals("tree") && visitor != null) { - UserQueries.setUserOptionInt(sql, visitor.getUID(), "repliesview", 0); - } - } else if (visitor != null && UserQueries.getUserOptionInt(sql, visitor.getUID(), "repliesview", 0) == 1) { - listview = true; - } - - String title = msg.getUser().getUName() + ": " + msg.getTagsString(); - - if (visitor == null) { - User.pageUserRefCookie(request, response, msg.getUser().getUID()); - } - - response.setContentType("text/html; charset=UTF-8"); - PrintWriter out = response.getWriter(); - try { - String headers = ""; - if (paramView != null) { - headers += ""; - } - if (msg.Hidden) { - headers += ""; - } - PageTemplates.pageHead(out, title, headers); - PageTemplates.pageNavigation(out, visitor, null); - - out.println("
    "); - printMessage(out, sql, msg, visitor); - printReplies(out, sql, msg, visitor, listview); - out.println("
    "); - - PageTemplates.pageFooter(request, out, visitor, false); - - out.println(""); - - PageTemplates.pageEnd(out); - } finally { - out.close(); - } - } - - public static com.juick.Message printMessage(PrintWriter out, JdbcTemplate sql, com.juick.Message msg, com.juick.User visitor) { - msg.VisitorCanComment = visitor != null; - - List tags = MessagesQueries.getMessageTags(sql, msg.getMID()); - String tagsStr = PageTemplates.formatTags(tags); - if (msg.ReadOnly) { - tagsStr += " *readonly"; - msg.VisitorCanComment = false; - } - if (msg.Privacy < 0) { - tagsStr += " *friends"; - } - - String txt; - if (!msg.Tags.isEmpty() && msg.Tags.contains("code")) { - txt = PageTemplates.formatMessageCode(msg.getText()); - } else { - txt = PageTemplates.formatMessage(msg.getText()); - } - - if (!tags.isEmpty()) { - tagsStr = "" + tagsStr + ""; - } - - out.println("
      "); - out.println("
    • "); - out.println("
      \""
      "); - out.println("
      "); - out.println("
      "); - out.println(" "); - out.println("
      " + PageTemplates.formatJSLocalTime(msg.getDate()) + "
      "); - out.println("
      " + txt + "
      "); - - if (msg.AttachmentType != null) { - out.println("
      \"\"/
      "); - } - - boolean visitorInBL = false; - if (visitor != null) { - if (visitor.getUID() == msg.getUser().getUID()) { - msg.VisitorCanComment = true; - } else { - visitorInBL = UserQueries.isInBL(sql, msg.getUser().getUID(), visitor.getUID()); - if (visitorInBL) { - msg.VisitorCanComment = false; - } - } - } - - if (msg.VisitorCanComment) { - out.println("
      "); - out.println("
      "); - out.println("
      "); - } - - List recomm = MessagesQueries.getMessageRecommendations(sql, msg.getMID()); - if (!recomm.isEmpty()) { - out.print("
      Рекомендовали (" + recomm.size() + "): "); - for (int i = 0; i < recomm.size(); i++) { - if (i > 0) { - out.print(", "); - } - out.print("@" + recomm.get(i) + ""); - } - out.println("
      "); - } - out.println("
      "); - out.println("
    • "); - - out.println("
    • "); - out.println("
    "); - - return msg; - } - - public static void printReplies(PrintWriter out, JdbcTemplate sql, com.juick.Message msg, com.juick.User visitor, boolean listview) { - List replies = MessagesQueries.getReplies(sql, msg.getMID()); - - List blUIDs = new ArrayList(); - for (int i = 0; i < replies.size(); i++) { - com.juick.Message reply = replies.get(i); - if (reply.getUser().getUID() != msg.getUser().getUID() && !blUIDs.contains(reply.getUser().getUID())) { - blUIDs.add(reply.getUser().getUID()); - } - if (reply.ReplyTo > 0) { - boolean added = false; - for (int n = 0; n < replies.size(); n++) { - if (replies.get(n).getRID() == reply.ReplyTo) { - replies.get(n).childs.add(reply); - added = true; - break; - } - } - if (!added) { - reply.ReplyTo = 0; - } - } - } - - if (!replies.isEmpty()) { - if (visitor != null && msg.getUser().getUID() == visitor.getUID()) { - for (Message reply : replies) { - reply.VisitorCanComment = true; - } - } else if (visitor != null && msg.VisitorCanComment) { - blUIDs = UserQueries.checkBL(sql, visitor.getUID(), blUIDs); - for (Message reply : replies) { - reply.VisitorCanComment = reply.getUser().getUID() == visitor.getUID() || !blUIDs.contains(reply.getUser().getUID()); - } - } else { - for (Message reply : replies) { - reply.VisitorCanComment = false; - } - } - - boolean foldable = false; - if (replies.size() > 10) { - for (int i = 0; i < replies.size() - 1; i++) { - if (replies.get(i).getChildsCount() > 1) { - foldable = true; - break; - } - } - } - - out.println("
    "); - out.print("
    "); - if (listview) { - out.print("Показать деревом"); - } else { - if (foldable) { - out.print("li').show(); $('#replies .msg-comments').hide(); $('#unfoldall').hide(); return false\">Раскрыть все · "); - } - out.print("Показать списком"); - } - out.print("
    "); - out.println("

    Ответы (" + replies.size() + ")

    "); - out.println("
    "); - - out.println("
      "); - if (listview) { - printList(out, replies, visitor); - } else { - printTree(out, replies, visitor, 0, 0, false); - } - out.println("
    "); - - for (Message reply : replies) { - reply.cleanupChilds(); - } - replies.clear(); - } - } - - public static void printTree(PrintWriter out, List replies, com.juick.User visitor, int ReplyTo, int margin, boolean hidden) { - if (margin > 240) { - margin = 240; - } - - for (int i = 0; i < replies.size(); i++) { - com.juick.Message msg = replies.get(i); - if (msg.ReplyTo == ReplyTo) { - - out.print("
  • 0) { - out.print("margin-left: " + margin + "px;"); - } - if (hidden) { - out.print("display:none;"); - } - out.println("\">"); - if (!msg.getUser().Banned) { - out.println("
    \""
    "); - } else { - out.println("
    "); - } - out.println("
    "); - out.println("
    "); - if (!msg.getUser().Banned) { - out.println(" "); - } else { - out.println("
    [удалено]:
    "); - } - out.println(" "); - out.println("
    " + PageTemplates.formatMessage(msg.getText()) + "
    "); - if (msg.AttachmentType != null) { - out.println("
    \"\"/
    "); - } - if (msg.VisitorCanComment) { - out.println(" "); - out.println("
    "); - } else if (visitor == null) { - out.println(" "); - } - - int childs = msg.getChildsCount(); - if (ReplyTo == 0 && childs > 1 && replies.size() > 10) { - out.println(" "); - - } - out.println("
    "); - out.println("
  • "); - - if (ReplyTo == 0 && childs > 1 && replies.size() > 10) { - printTree(out, msg.childs, visitor, msg.getRID(), margin + 20, true); - } else if (childs > 0) { - printTree(out, msg.childs, visitor, msg.getRID(), margin + 20, hidden); - } - } - } - } - - public static void printList(PrintWriter out, List replies, com.juick.User visitor) { - for (Message msg : replies) { - out.print("
  • "); - if (!msg.getUser().Banned) { - out.println("
    \""
    "); - } else { - out.println("
    "); - } - out.println("
    "); - out.println("
    "); - if (!msg.getUser().Banned) { - out.println(" "); - } else { - out.println("
    [удалено]:
    "); - } - out.println(" "); - out.println("
    " + PageTemplates.formatMessage(msg.getText()) + "
    "); - if (msg.AttachmentType != null) { - out.println("
    \"\"/
    "); - } - out.print("
    /" + msg.getRID()); - if (msg.ReplyTo > 0) { - out.print(" в ответ на /" + msg.ReplyTo + ""); - } - if (msg.VisitorCanComment) { - out.println(" · Ответить
    "); - out.println("
    "); - } else if (visitor == null) { - out.println(" "); - } - out.println("
    "); - out.println("
  • "); - } - } -} diff --git a/src/main/java/com/juick/http/www/Utils.java b/src/main/java/com/juick/http/www/Utils.java deleted file mode 100644 index 44b085b1..00000000 --- a/src/main/java/com/juick/http/www/Utils.java +++ /dev/null @@ -1,242 +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.http.www; - -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.Part; -import java.io.*; -import java.net.URL; -import java.net.URLConnection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.UUID; - -/** - * - * @author Ugnich Anton - */ -public class Utils { - - public static String getCookie(HttpServletRequest request, String name) { - Cookie cookies[] = request.getCookies(); - if (cookies != null) { - for (int i = 0; i < cookies.length; i++) { - if (cookies[i].getName().equals(name)) { - return cookies[i].getValue(); - } - } - } - return null; - } - - public static String receiveMultiPartFile(HttpServletRequest request, String name) throws Exception { - String attachmentFName = null; - - Part filePart = request.getPart("attach"); - if (filePart != null) { - String partname = Utils.getPartFilename(filePart); - if (partname != null && partname.length() > 0) { - String attachmentType = partname.substring(partname.length() - 3).toLowerCase(); - if (attachmentType.equals("jpg") || attachmentType.equals("peg") || attachmentType.equals("png")) { - if (attachmentType.equals("peg")) { - attachmentType = "jpg"; - } - attachmentFName = UUID.randomUUID().toString() + "." + attachmentType; - filePart.write("/var/www/juick.com/i/tmp/" + attachmentFName); - } else { - throw new Exception("Wrong file type"); - } - } - } - - return attachmentFName; - } - - public static com.juick.User getVisitorUser(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) { - String hash = getCookie(request, "hash"); - if (hash != null) { - com.juick.User visitor = com.juick.server.UserQueries.getUserByHash(sql, hash); - if (response != null && visitor != null) { - response.setHeader("X-Username", visitor.getUName()); - } - return visitor; - } else { - return null; - } - } - - public static void sendTemporaryRedirect(HttpServletResponse response, String location) { - response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); - response.setHeader("Location", location); - } - - public static void sendPermanentRedirect(HttpServletResponse response, String location) { - response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); - response.setHeader("Location", location); - } - - public static String getPartFilename(Part part) { - for (String cd : part.getHeader("content-disposition").split(";")) { - if (cd.trim().startsWith("filename")) { - String filename = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", ""); - return filename.substring(filename.lastIndexOf('/') + 1).substring(filename.lastIndexOf('\\') + 1); // MSIE fix. - } - } - return null; - } - - public static void finishSQL(ResultSet rs, Statement stmt) { - if (rs != null) { - try { - rs.close(); - } catch (SQLException e) { - } - } - if (stmt != null) { - try { - stmt.close(); - } catch (SQLException e) { - } - } - } - - public static void replyJSON(HttpServletRequest request, HttpServletResponse response, String json) throws IOException { - response.setContentType("application/json; charset=UTF-8"); - response.setHeader("Access-Control-Allow-Origin", "*"); - - String callback = request.getParameter("callback"); - if (callback != null && (callback.length() > 64 || !callback.matches("[a-zA-Z0-9\\-\\_]+"))) { - callback = null; - } - - PrintWriter out = response.getWriter(); - try { - if (callback != null) { - out.print(callback + "("); - out.print(json); - out.print(")"); - } else { - out.print(json); - } - } finally { - out.close(); - } - } - - public static String convertArray2String(ArrayList mids) { - String q = ""; - for (int i = 0; i < mids.size(); i++) { - if (i > 0) { - q += ","; - } - q += mids.get(i); - } - return q; - } - - public static String encodeHTML(String str) { - return str.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("'", "'").replaceAll("\"", """).replaceAll("\n", " "); - } - - public static String encodeSphinx(String str) { - return str.replaceAll("@", "\\\\@"); - } - - public static int parseInt(String str, int def) { - int ret = def; - if (str != null) { - try { - ret = Integer.parseInt(str); - } catch (Exception e) { - } - } - return ret; - } - - public static String fetchURL(String url) { - try { - URLConnection c = new URL(url).openConnection(); - BufferedReader in = new BufferedReader(new InputStreamReader(c.getInputStream())); - String inputLine; - StringBuilder b = new StringBuilder(); - while ((inputLine = in.readLine()) != null) { - b.append(inputLine).append("\n"); - } - in.close(); - return b.toString(); - } catch (Exception e) { - System.err.println("fetchURL: "+e.toString()); - return null; - } - } - - public static String downloadImage(URL url) throws Exception { - String attachmentFName = null; - Exception ex = null; - - InputStream is = null; - FileOutputStream fos = null; - try { - URLConnection urlConn = url.openConnection(); - is = urlConn.getInputStream(); - String mime = urlConn.getContentType(); - - String attachmentType; - if (mime != null && mime.equals("image/jpeg")) { - attachmentType = "jpg"; - } else if (mime != null && mime.equals("image/png")) { - attachmentType = "png"; - } else { - throw new Exception("Wrong file type"); - } - - attachmentFName = UUID.randomUUID().toString() + "." + attachmentType; - fos = new FileOutputStream("/var/www/juick.com/i/tmp/" + attachmentFName); - byte[] buffer = new byte[10240]; - int len; - while ((len = is.read(buffer)) > 0) { - fos.write(buffer, 0, len); - } - } catch (Exception e) { - ex = e; - attachmentFName = null; - } finally { - try { - if (is != null) { - is.close(); - } - } finally { - if (fos != null) { - fos.close(); - } - } - } - - if (ex != null) { - throw ex; - } else { - return attachmentFName; - } - } -} diff --git a/src/main/java/com/juick/http/www/VKontakteLogin.java b/src/main/java/com/juick/http/www/VKontakteLogin.java deleted file mode 100644 index 8fad3b7a..00000000 --- a/src/main/java/com/juick/http/www/VKontakteLogin.java +++ /dev/null @@ -1,128 +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.http.www; - -import com.juick.server.UserQueries; -import org.json.JSONException; -import org.json.JSONObject; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.net.URLEncoder; -import java.util.UUID; - -/** - * - * @author Ugnich Anton - */ -public class VKontakteLogin { - - private static final String VK_APPID = "3544101"; - private static final String VK_SECRET = "z2afNI8jA5lIpZ2jsTm1"; - private static final String VK_REDIRECT = "http://juick.com/_vklogin"; - - protected void doGet(JdbcTemplate sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - String code = request.getParameter("code"); - if (code == null || code.equals("")) { - response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); - response.setHeader("Location", "https://oauth.vk.com/authorize?client_id=" + VK_APPID + "&redirect_uri=" + URLEncoder.encode(VK_REDIRECT, "utf-8") + "&scope=friends,wall,offline&response_type=code"); - return; - } - - - String tokenjson = Utils.fetchURL("https://oauth.vk.com/access_token?client_id=" + VK_APPID + "&redirect_uri=" + URLEncoder.encode(VK_REDIRECT, "utf-8") + "&client_secret=" + VK_SECRET + "&code=" + URLEncoder.encode(code, "utf-8")); - if (tokenjson == null || tokenjson.isEmpty()) { - System.err.println("VK TOKEN EMPTY"); - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - String token = null; - long vkID = 0; - try { - JSONObject json = new JSONObject(tokenjson); - token = json.getString("access_token"); - vkID = json.getLong("user_id"); - } catch (JSONException e) { - System.err.println("VK TOKEN EXCEPTION: " + e); - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - if (token == null || vkID == 0) { - System.err.println("VK TOKEN EMPTY: " + tokenjson); - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - - - - String graph = Utils.fetchURL("https://api.vk.com/method/users.get?uids=" + vkID + "&fields=screen_name&access_token=" + token); - if (graph == null || graph.isEmpty()) { - System.err.println("VK GRAPH ERROR"); - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - - try { - JSONObject json = new JSONObject(graph).getJSONArray("response").getJSONObject(0); - String vkName = json.getString("first_name") + " " + json.getString("last_name"); - String vkLink = json.getString("screen_name"); - - if (vkName == null || vkLink == null || vkName.isEmpty() || vkName.length() == 1 || vkLink.isEmpty()) { - throw new Exception(); - } - - int uid = getUIDbyVKID(sql, vkID); - if (uid > 0) { - Cookie c = new Cookie("hash", UserQueries.getHashByUID(sql, uid)); - c.setMaxAge(50 * 24 * 60 * 60); - response.addCookie(c); - response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); - response.setHeader("Location", "/"); - } else { - String loginhash = UUID.randomUUID().toString(); - if (!insertDB(sql, vkID, loginhash, token, vkName, vkLink)) { - throw new Exception(); - } - response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); - response.setHeader("Location", "/signup?type=vk&hash=" + loginhash); - } - } catch (Exception e) { - System.err.println("JSON ERROR: " + e); - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - return; - } - } - - private int getUIDbyVKID(JdbcTemplate sql, long vkID) { - try { - return sql.queryForObject("SELECT user_id FROM vk WHERE vk_id=? AND user_id IS NOT NULL", Integer.class, vkID); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - - private boolean insertDB(JdbcTemplate sql, long vkID, String loginhash, String token, String vkName, String vkLink) { - return sql.update("INSERT INTO vk(vk_id,loginhash,access_token,vk_name,vk_link) VALUES (?,?,?,?,?)", - vkID, loginhash, token, vkName, vkLink) > 0; - } -} diff --git a/src/main/java/com/juick/json/JSONSerializer.java b/src/main/java/com/juick/json/JSONSerializer.java deleted file mode 100644 index 142cacf0..00000000 --- a/src/main/java/com/juick/json/JSONSerializer.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package com.juick.json; - -import java.text.ParseException; -import java.util.Iterator; -import java.util.List; -import org.json.JSONObject; - -/** - * - * @author vt - * @param - */ -public abstract class JSONSerializer { - - /** - * - * @param json - * @return - */ - public abstract T deserialize(JSONObject json) throws ParseException; - - /** - * - * @param obj - * @return - */ - public abstract JSONObject serialize(T obj); - - /** - * - * @param objs - * @return - */ - public String serializeList(List objs) { - String json = "["; - - Iterator i = objs.iterator(); - while (i.hasNext()) { - T m = i.next(); - if (json.length() > 1) { - json += ","; - } - json += serialize(m).toString(); - } - - json += "]"; - return json; - } -} diff --git a/src/main/java/com/juick/json/MessageSerializer.java b/src/main/java/com/juick/json/MessageSerializer.java deleted file mode 100644 index e8402d51..00000000 --- a/src/main/java/com/juick/json/MessageSerializer.java +++ /dev/null @@ -1,138 +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.json; - -import com.juick.Message; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.TimeZone; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author Ugnich Anton - */ -public class MessageSerializer extends JSONSerializer { - - private final static Logger LOGGER = Logger.getLogger(MessageSerializer.class.getName()); - - UserSerializer userSerializer = new UserSerializer(); - PlaceSerializer placeSerializer = new PlaceSerializer(); - - final SimpleDateFormat df; - - public MessageSerializer() { - df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - df.setTimeZone(TimeZone.getTimeZone("UTC")); - } - - @Override - public Message deserialize(JSONObject json) throws JSONException, ParseException { - com.juick.Message jmsg = new com.juick.Message(); - jmsg.setMID(json.getInt("mid")); - if (json.has("rid")) { - jmsg.setRID(json.getInt("rid")); - } - if (json.has("replyto")) { - jmsg.ReplyTo = json.getInt("replyto"); - } - - jmsg.FriendsOnly = json.has("friendsonly"); - jmsg.ReadOnly = json.has("readonly"); - - jmsg.setText(json.getString("body").replace(""", "\"")); - jmsg.setUser(userSerializer.deserialize(json.getJSONObject("user"))); - - jmsg.setDate(df.parse(json.getString("timestamp"))); - - if (json.has("tags")) { - JSONArray tags = json.getJSONArray("tags"); - for (int n = 0; n < tags.length(); n++) { - jmsg.Tags.add(tags.getString(n).replace(""", "\"")); - } - } - - if (json.has("replies")) { - jmsg.Replies = json.getInt("replies"); - } - - if (json.has("photo")) { - jmsg.Photo = json.getJSONObject("photo").getString("small"); - } - - return jmsg; - } - - @Override - public JSONObject serialize(Message msg) { - JSONObject json = new JSONObject(); - - try { - if (msg.getMID() > 0) { - json.put("mid", msg.getMID()); - } - if (msg.getRID() > 0) { - json.put("rid", msg.getRID()); - } - if (msg.ReplyTo > 0) { - json.put("replyto", msg.ReplyTo); - } - if (msg.FriendsOnly) { - json.put("friendsonly", 1); - } - if (msg.ReadOnly) { - json.put("readonly", 1); - } - if (msg.getText() != null) { - json.put("body", msg.getText()); - } - if (msg.getDate() != null) { - json.put("timestamp", df.format(msg.getDate())); - } - if (msg.getUser() != null) { - json.put("user", userSerializer.serialize(msg.getUser())); - } - if (msg.Tags != null && msg.Tags.size() > 0) { - json.put("tags", new JSONArray(msg.Tags)); - } - if (msg.Replies > 0) { - json.put("replies", msg.Replies); - } - if (msg.Place != null) { - json.put("place", placeSerializer.serialize(msg.Place)); - } - if (msg.AttachmentType != null) { - String fname = msg.getMID() + (msg.getRID() > 0 ? "-" + msg.getRID() : "") + "." + msg.AttachmentType; - JSONObject photo = new JSONObject(); - photo.put("thumbnail", "http://i.juick.com/ps/" + fname); - photo.put("small", "http://i.juick.com/photos-512/" + fname); - photo.put("medium", "http://i.juick.com/photos-1024/" + fname); - json.put("photo", photo); - } - } catch (JSONException e) { - LOGGER.log(Level.SEVERE, "JSON Exception", e); - } - - return json; - } -} diff --git a/src/main/java/com/juick/json/PlaceSerializer.java b/src/main/java/com/juick/json/PlaceSerializer.java deleted file mode 100644 index f433f7f0..00000000 --- a/src/main/java/com/juick/json/PlaceSerializer.java +++ /dev/null @@ -1,73 +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.json; - -import com.juick.Place; -import org.json.JSONException; -import org.json.JSONObject; - -/** - * - * @author Ugnich Anton - */ -public class PlaceSerializer extends JSONSerializer { - - @Override - public Place deserialize(JSONObject json) throws JSONException { - Place jplace = new Place(); - - jplace.pid = json.getInt("pid"); - jplace.lat = json.getDouble("lat"); - jplace.lon = json.getDouble("lon"); - jplace.name = json.getString("name").replace(""", "\""); - if (json.has("users")) { - jplace.users = json.getInt("users"); - } - if (json.has("messages")) { - jplace.messages = json.getInt("messages"); - } - if (json.has("distance")) { - jplace.distance = json.getInt("distance"); - } - - return jplace; - } - - @Override - public JSONObject serialize(Place place) { - JSONObject json = new JSONObject(); - - try { - if (place.pid > 0) { - json.put("pid", place.pid); - } - if (place.lat >= -90 && place.lat <= 90) { - json.put("lat", place.lat); - } - if (place.lon >= -180 && place.lon <= 180) { - json.put("lon", place.lon); - } - if (place.name != null) { - json.put("name", place.name); - } - } catch (JSONException e) { - } - - return json; - } -} diff --git a/src/main/java/com/juick/json/UserSerializer.java b/src/main/java/com/juick/json/UserSerializer.java deleted file mode 100644 index 390f4bf6..00000000 --- a/src/main/java/com/juick/json/UserSerializer.java +++ /dev/null @@ -1,66 +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.json; - -import com.juick.User; -import org.json.JSONException; -import org.json.JSONObject; - -/** - * - * @author Ugnich Anton - */ -public class UserSerializer extends JSONSerializer { - - @Override - public User deserialize(JSONObject json) throws JSONException { - User juser = new User(); - juser.setUID(json.getInt("uid")); - juser.setUName(json.getString("uname")); - if (json.has("fullname")) { - juser.setFullName(json.getString("fullname")); - } - return juser; - } - - @Override - public JSONObject serialize(User user) { - JSONObject json = new JSONObject(); - - try { - if (user.getUID() > 0) { - json.put("uid", user.getUID()); - } - if (user.getUName() != null) { - json.put("uname", user.getUName()); - } - if (user.getFullName() != null) { - json.put("fullname", user.getFullName()); - } - if (user.getJID() != null) { - json.put("jid", user.getJID()); - } - if (user.getUnreadCount() > 0) { - json.put("unreadCount", user.getUnreadCount()); - } - } catch (JSONException e) { - } - - return json; - } -} diff --git a/src/main/java/com/juick/server/AdsQueries.java b/src/main/java/com/juick/server/AdsQueries.java deleted file mode 100644 index 06590817..00000000 --- a/src/main/java/com/juick/server/AdsQueries.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Juick - * Copyright (C) 2008-2011, 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; - -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; - -/** - * - * @author ugnich - */ -public class AdsQueries { - - public static int getAdMID(JdbcTemplate sql, int uid) { - if (uid > 0) { - try { - return sql.queryForObject("SELECT message_id FROM ads_messages " + - "WHERE message_id NOT IN (SELECT message_id FROM ads_messages_log WHERE user_id=? " + - "AND ts>UNIX_TIMESTAMP()-60*60*24 GROUP BY message_id HAVING COUNT(*)>2) ORDER BY RAND() LIMIT 1", - Integer.class, uid); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } else { - try { - return sql.queryForObject("SELECT message_id FROM ads_messages ORDER BY RAND() LIMIT 1", Integer.class); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - } - - public static void logAdMID(JdbcTemplate sql, int uid, int mid) { - sql.update("INSERT INTO ads_messages_log(user_id,message_id,ts) VALUES (?,?,UNIX_TIMESTAMP())", uid, mid); - } -} diff --git a/src/main/java/com/juick/server/CrosspostQueries.java b/src/main/java/com/juick/server/CrosspostQueries.java deleted file mode 100644 index 04898d0a..00000000 --- a/src/main/java/com/juick/server/CrosspostQueries.java +++ /dev/null @@ -1,62 +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; - -import org.apache.commons.lang3.tuple.Pair; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; - -import java.util.Optional; - -/** - * - * @author ugnich - */ -public class CrosspostQueries { - - public static Optional> getTwitterTokens(JdbcTemplate sql, int uid) { - try { - return sql.queryForObject("SELECT access_token,access_token_secret FROM twitter WHERE user_id=? AND crosspost=1", - (rs, num) -> { - return Optional.of(Pair.of(rs.getString(1), rs.getString(2))); - }, uid); - } catch (EmptyResultDataAccessException e) { - return Optional.empty(); - } - } - - public static Optional getFacebookToken(JdbcTemplate sql, int uid) { - try { - return Optional.of(sql.queryForObject("SELECT access_token FROM facebook WHERE user_id=? AND access_token IS NOT NULL " + - "AND crosspost=1", String.class, uid)); - } catch (EmptyResultDataAccessException e) { - return Optional.empty(); - } - } - - public static Optional> getVKTokens(JdbcTemplate sql, int uid) { - try { - return sql.queryForObject("SELECT vk_id,access_token FROM vk WHERE user_id=? AND crosspost=1", - (rs, num) -> { - return Optional.of(Pair.of(rs.getString(1), rs.getString(2))); - }, uid); - } catch (EmptyResultDataAccessException e) { - return Optional.empty(); - } - } -} diff --git a/src/main/java/com/juick/server/MessagesQueries.java b/src/main/java/com/juick/server/MessagesQueries.java deleted file mode 100644 index a0242543..00000000 --- a/src/main/java/com/juick/server/MessagesQueries.java +++ /dev/null @@ -1,663 +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; - -import com.juick.Message; -import com.juick.User; -import com.juick.server.helpers.PrivacyOpts; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.jdbc.core.ConnectionCallback; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.Statement; -import java.sql.Types; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.logging.Logger; - -/** - * - * @author Ugnich Anton - */ -public class MessagesQueries { - - private static final Logger LOGGER = Logger.getLogger(MessagesQueries.class.getName()); - - public static int createMessage(JdbcTemplate sql, int uid, String txt, String attachment, List tags) { - KeyHolder holder = new GeneratedKeyHolder(); - sql.update(con -> { - PreparedStatement stmt = con.prepareStatement("INSERT INTO messages(user_id,attach) VALUES (?,?)", - Statement.RETURN_GENERATED_KEYS); - stmt.setInt(1, uid); - if (attachment != null) { - stmt.setString(2, attachment); - } else { - stmt.setNull(2, Types.VARCHAR); - } - return stmt; - }, holder); - - int mid = holder.getKey().intValue(); - - - if (mid > 0) { - - String tagsNames = ""; - String tagsIDs = ""; - - for (int i = 0; i < tags.size(); i++) { - if (i > 0) { - tagsNames += " "; - tagsIDs += ","; - } - tagsNames += tags.get(i).Name; - tagsIDs += "(" + mid + "," + tags.get(i).TID + ")"; - } - if (tags.size() > 0) { - sql.execute("INSERT INTO messages_tags(message_id,tag_id) VALUES " + tagsIDs); - } - final String finalTagsNames = tagsNames; - sql.update(con -> { - PreparedStatement stmt = con.prepareStatement("INSERT INTO messages_txt(message_id,tags,txt) " + - "VALUES (?,?,?)", Statement.NO_GENERATED_KEYS); - stmt.setInt(1, mid); - if (finalTagsNames.isEmpty()) { - stmt.setNull(2, Types.VARCHAR); - } else { - stmt.setString(2, finalTagsNames); - } - stmt.setString(3, txt); - return stmt; - }); - } - - return mid; - } - - public static int createReply(JdbcTemplate sql, int mid, int rid, int uid, String txt, String attachment) { - int ridnew = getReplyIDIncrement(sql, mid); - - sql.update( con -> { - PreparedStatement stmt = con.prepareStatement("INSERT INTO replies(message_id,reply_id,user_id," + - "replyto,attach,txt) VALUES (?,?,?,?,?,?)", Statement.NO_GENERATED_KEYS); - stmt.setInt(1, mid); - stmt.setInt(2, ridnew); - stmt.setInt(3, uid); - stmt.setInt(4, rid); - if (attachment != null) { - stmt.setString(5, attachment); - } else { - stmt.setNull(5, Types.VARCHAR); - } - stmt.setString(6, txt); - return stmt; - }); - - if (ridnew > 0) { - sql.update("UPDATE messages SET replies=replies+1 WHERE message_id=?", mid); - } - - return ridnew; - } - - public static int getReplyIDIncrement(JdbcTemplate sql, int mid) { - return sql.execute((ConnectionCallback) conn -> { - conn.setAutoCommit(false); - try (PreparedStatement ps = conn.prepareStatement("UPDATE messages SET maxreplyid=maxreplyid+1 WHERE message_id=?")) { - ps.setInt(1, mid); - if (ps.executeUpdate() != 1) { - throw new IncorrectResultSizeDataAccessException("Cannot find a message to update: " + mid, 1, 0); - } - } - try (PreparedStatement ps = conn.prepareStatement("SELECT maxreplyid FROM messages WHERE message_id=?")) { - ps.setInt(1, mid); - try (ResultSet resultSet = ps.executeQuery()) { - if (resultSet.next()) { - int rv = resultSet.getInt(1); - conn.commit(); - return rv; - } else { - throw new IncorrectResultSizeDataAccessException("while getting getReplyIDIncrement, mid=" + mid, 1, 0); - } - } - } - }); - - } - - public static boolean recommendMessage(JdbcTemplate sql, int mid, int vuid) { - boolean res = sql.update("INSERT IGNORE INTO favorites(user_id,message_id) VALUES (" + vuid + "," + mid + ")") == 1; - if (res) { - sql.update("UPDATE messages SET likes=likes+1 WHERE message_id=?", mid); - } - return res; - } - - public static boolean canViewThread(JdbcTemplate sql, int mid, int uid) { - PrivacyOpts privacyOpts; - try { - privacyOpts = sql.queryForObject("SELECT user_id,privacy FROM messages WHERE messages.message_id=?", - (rs, rowNum) -> { - PrivacyOpts res = new PrivacyOpts(); - res.setUid(rs.getInt(1)); - res.setPrivacy(rs.getInt(2)); - return res; - }, mid); - } catch (EmptyResultDataAccessException e) { - return true; - } - return privacyOpts.getPrivacy() >= 0 - || uid == privacyOpts.getUid() - || ((privacyOpts.getPrivacy() == -1 || privacyOpts.getPrivacy() == -2) && uid > 0 - && UserQueries.isInWL(sql, privacyOpts.getUid(), uid)); - } - - public static boolean isReadOnly(JdbcTemplate sql, int mid) { - try { - return sql.queryForObject("SELECT readonly FROM messages WHERE message_id=?", new Object[]{mid}, Integer.class) == 1; - } catch (EmptyResultDataAccessException e) { - return false; - } - } - - public static boolean isSubscribed(JdbcTemplate sql, int uid, int mid) { - try { - return sql.queryForObject("SELECT 1 FROM subscr_messages WHERE suser_id=? AND message_id=?", new Object[]{uid, mid}, Integer.class) == 1; - } catch (EmptyResultDataAccessException e) { - return false; - } - } - - public static int getMessagePrivacy(JdbcTemplate sql, int mid) { - try { - return sql.queryForObject("SELECT privacy FROM messages WHERE message_id=?", new Object[]{mid}, Integer.class); - } catch (EmptyResultDataAccessException e) { - return -4; - } - } - - - public static com.juick.Message getMessage(JdbcTemplate sql, int mid) { - try { - return sql.queryForObject("SELECT messages.user_id,users.nick," - + "TIMESTAMPDIFF(MINUTE,messages.ts,NOW())," - + "messages.ts," - + "messages.readonly,messages.privacy,messages.replies," - + "messages.attach,messages.place_id,messages.lat," - + "messages.lon,messages.likes,messages.hidden," - + "txt.tags,txt.repliesby,txt.txt FROM messages " - + "INNER JOIN users ON messages.user_id=users.id " - + "INNER JOIN messages_txt AS txt " - + "ON messages.message_id=txt.message_id " - + "WHERE messages.message_id=?", new Object[]{mid}, - (rs, rowNum) -> { - Message msg = new Message(); - msg.setMID(mid); - msg.setUser(new User()); - msg.getUser().setUID(rs.getInt(1)); - msg.getUser().setUName(rs.getString(2)); - msg.TimeAgo = rs.getInt(3); - msg.setDate(rs.getTimestamp(4)); - msg.ReadOnly = rs.getBoolean(5); - msg.Privacy = rs.getInt(6); - msg.FriendsOnly = msg.Privacy < 0; - msg.Replies = rs.getInt(7); - msg.AttachmentType = rs.getString(8); - //if (rs.getInt(9) > 0) { - // msg.Place = PlacesQueries.getPlace(sql, rs.getInt(9)); - //} - if (rs.getDouble(10) != 0) { - msg.Place = new com.juick.Place(); - msg.Place.lat = rs.getDouble(10); - msg.Place.lon = rs.getDouble(11); - } - msg.Likes = rs.getInt(12); - msg.Hidden = rs.getBoolean(13); - msg.parseTags(rs.getString(14)); - msg.RepliesBy = rs.getString(15); - msg.setText(rs.getString(16)); - return msg; - }); - } catch (EmptyResultDataAccessException e) { - return null; - } - } - - public static com.juick.Message getReply(JdbcTemplate sql, int mid, int rid) { - try { - return sql.queryForObject("SELECT replies.user_id,users.nick," - + "replies.replyto,replies.ts," - + "replies.attach,replies.txt FROM replies INNER JOIN users " - + "ON replies.user_id=users.id " - + "WHERE replies.message_id=? AND replies.reply_id=?", - new Object[]{mid, rid}, (rs, num) -> { - Message msg = new Message(); - msg.setMID(mid); - msg.setRID(rid); - msg.setUser(new User()); - msg.getUser().setUID(rs.getInt(1)); - msg.getUser().setUName(rs.getString(2)); - msg.ReplyTo = rs.getInt(3); - msg.setDate(rs.getTimestamp(4)); - msg.AttachmentType = rs.getString(5); - msg.setText(rs.getString(6)); - return msg; - }); - } catch (EmptyResultDataAccessException e) { - return null; - } - } - - public static User getMessageAuthor(JdbcTemplate sql, int mid) { - try { - return sql.queryForObject("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.setUName(rs.getString(2)); - return res; - }); - } catch (EmptyResultDataAccessException e) { - return null; - } - } - - public static List getMessageTags(JdbcTemplate sql, int mid) { - return sql.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)", - new Object[]{mid}, (rs, num) -> { - com.juick.Tag t = new com.juick.Tag(); - t.TID = rs.getInt(1); - t.SynonymID = rs.getInt(2); - t.Name = rs.getString(3); - t.UsageCnt = rs.getInt(4); - return t; - }); - } - - public static List getMessageTagsIDs(JdbcTemplate sql, int mid) { - return sql.queryForList("SELECT tag_id FROM messages_tags WHERE message_id=?", new Object[] {mid}, Integer.class); - } - - public static List getMessageRecommendations(JdbcTemplate sql, int mid) { - return sql.queryForList("SELECT users.nick FROM favorites INNER JOIN users " + - "ON (favorites.message_id=? AND favorites.user_id=users.id)", - new Object[] {mid}, String.class); - } - - public static List getAll(JdbcTemplate sql, int visitor_uid, int before) { - if (visitor_uid > 1) { - if (before > 0) { - return sql.queryForList("SELECT message_id FROM messages WHERE message_id0 OR user_id=?) AND user_id NOT IN (SELECT bl_user_id FROM bl_users WHERE user_id=?)" + - " AND user_id NOT IN (SELECT id from users WHERE banned=1) ORDER BY message_id DESC LIMIT 20", - new Object[]{before, visitor_uid, visitor_uid}, Integer.class); - } else { - return sql.queryForList("SELECT message_id FROM messages WHERE hidden=0" + - " AND (privacy>0 OR user_id=?)" + - " AND user_id NOT IN (SELECT bl_user_id FROM bl_users WHERE user_id=?)" + - " AND user_id NOT IN (SELECT id from users WHERE banned=1) ORDER BY message_id DESC LIMIT 20", - new Object[]{visitor_uid, visitor_uid}, Integer.class); - } - } else { - if (before > 0) { - return sql.queryForList("SELECT message_id FROM messages WHERE message_id0 AND user_id NOT IN (SELECT id from users WHERE banned=1) " + - " ORDER BY message_id DESC LIMIT 20", - new Object[]{before}, Integer.class); - } else { - return sql.queryForList("SELECT message_id FROM messages WHERE hidden=0 AND privacy>0" + - " AND user_id NOT IN (SELECT id from users WHERE banned=1) ORDER BY message_id DESC LIMIT 20", - Integer.class); - } - } - } - - public static List getTag(JdbcTemplate sql, int tid, int visitor_uid, int before, int cnt) { - if (before > 0) { - return sql.queryForList("SELECT message_id FROM (tags INNER JOIN messages_tags " + - "ON ((tags.synonym_id=? OR tags.tag_id=?) AND tags.tag_id=messages_tags.tag_id)) " + - "INNER JOIN messages USING(message_id) WHERE messages.message_id0 OR messages.user_id=?) ORDER BY message_id DESC LIMIT ?", - new Object[]{tid, tid, before, visitor_uid, cnt}, Integer.class); - } else { - return sql.queryForList("SELECT message_id FROM (tags INNER JOIN messages_tags " + - "ON ((tags.synonym_id=? OR tags.tag_id=?) AND tags.tag_id=messages_tags.tag_id)) " + - "INNER JOIN messages USING(message_id) WHERE messages.privacy>0 OR messages.user_id=? " + - "ORDER BY message_id DESC LIMIT ?", - new Object[]{tid, tid, visitor_uid, cnt}, Integer.class); - } - } - - public static List getTags(JdbcTemplate sql, String tids, int visitor_uid, int before, int cnt) { - if (before > 0) { - return sql.queryForList("SELECT messages.message_id FROM messages_tags " + - "INNER JOIN messages USING(message_id) WHERE messages_tags.tag_id IN (" + tids + ") " + - "AND messages.message_id0 OR messages.user_id=?) " + - "ORDER BY messages.message_id DESC LIMIT ?", new Object[]{before, visitor_uid, cnt}, Integer.class); - } else { - return sql.queryForList("SELECT messages.message_id FROM messages_tags " + - "INNER JOIN messages USING(message_id) WHERE messages_tags.tag_id IN (" + tids + ") " + - "AND (messages.privacy>0 OR messages.user_id=?) " + - "ORDER BY messages.message_id DESC LIMIT ?", new Object[]{visitor_uid, cnt}, Integer.class); - } - } - - public static List getPlace(JdbcTemplate sql, int place_id, int visitor_uid, int before) { - if (before > 0) { - return sql.queryForList("SELECT message_id FROM messages WHERE place_id=? AND message_id0 OR user_id=?) ORDER BY message_id DESC LIMIT 20", - new Object[]{place_id, before, visitor_uid}, Integer.class); - } else { - return sql.queryForList("SELECT message_id FROM messages WHERE place_id=? AND (privacy>0 OR user_id=?) " + - "ORDER BY message_id DESC LIMIT 20", new Object[]{place_id, visitor_uid}, Integer.class); - } - } - - public static List getMyFeed(JdbcTemplate sql, int uid, int before) { - List mids; - if (before > 0) { - mids = sql.queryForList("SELECT message_id FROM messages " + - "INNER JOIN subscr_users ON (subscr_users.suser_id=? AND subscr_users.user_id=messages.user_id) " + - "WHERE message_id=0 OR (privacy>=-2 AND privacy<=-1 AND messages.user_id " + - "IN (SELECT user_id FROM wl_users WHERE wl_user_id=?))) ORDER BY message_id DESC LIMIT 20", - Integer.class, uid, before, uid); - } else { - mids = sql.queryForList("SELECT message_id FROM messages " + - "INNER JOIN subscr_users ON (subscr_users.suser_id=? " + - "AND subscr_users.user_id=messages.user_id) " + - "WHERE (privacy>=0 OR (privacy>=-2 AND privacy<=-1 AND messages.user_id " + - "IN (SELECT user_id FROM wl_users WHERE wl_user_id=?))) ORDER BY message_id DESC LIMIT 20", - Integer.class, uid, uid); - } - - - if (before > 0) { - mids.addAll(sql.queryForList("SELECT message_id FROM messages " + - "WHERE user_id=? AND message_id getPrivate(JdbcTemplate sql, int uid, int before) { - - if (before > 0) { - return sql.queryForList("SELECT message_id FROM messages WHERE user_id=? AND privacy<0 AND message_id getDiscussions(JdbcTemplate sql, int uid, int before) { - if (before > 0) { - return sql.queryForList("SELECT message_id FROM subscr_messages WHERE suser_id=? AND message_id getRecommended(JdbcTemplate sql, int uid, int before) { - if (before > 0) { - return sql.queryForList("SELECT message_id FROM favorites WHERE user_id " + - "IN (SELECT user_id FROM subscr_users WHERE suser_id=?) AND message_id getPopular(JdbcTemplate sql, int visitor_uid, int before) { - - if (before > 0) { - return sql.queryForList("SELECT message_id FROM messages WHERE message_id0 " + - "AND popular>0 AND user_id NOT IN (SELECT bl_user_id FROM bl_users WHERE user_id=?) " + - "ORDER BY message_id DESC LIMIT 20", Integer.class, before, visitor_uid); - } else { - return sql.queryForList("SELECT message_id FROM messages WHERE privacy>0 " + - "AND popular>0 AND user_id NOT IN (SELECT bl_user_id FROM bl_users WHERE user_id=?) " + - "ORDER BY message_id DESC LIMIT 20", Integer.class, visitor_uid); - } - } - - public static List getPhotos(JdbcTemplate sql, int visitor_uid, int before) { - if (before > 0) { - return sql.queryForList("SELECT message_id FROM messages WHERE message_id0 OR user_id=?) " + - "AND attach IS NOT NULL AND user_id NOT IN (SELECT id from users WHERE banned=1) " + - "AND user_id NOT IN (SELECT bl_user_id FROM bl_users WHERE user_id=?) " + - "ORDER BY message_id DESC LIMIT 20", Integer.class, before, visitor_uid, visitor_uid); - } else { - return sql.queryForList("SELECT message_id FROM messages WHERE (privacy>0 OR user_id=?) " + - "AND attach IS NOT NULL AND user_id NOT IN (SELECT id from users WHERE banned=1) " + - "AND user_id NOT IN (SELECT bl_user_id FROM bl_users WHERE user_id=?) " + - "ORDER BY message_id DESC LIMIT 20", Integer.class, visitor_uid, visitor_uid); - } - } - - public static List getSearch(JdbcTemplate sql, JdbcTemplate sqlSearch, String search, int before) { - List mids; - - if (before > 0) { - mids = sqlSearch.queryForList("SELECT id AS message_id FROM messages WHERE MATCH(?) AND id 0) { - return sql.queryForList("SELECT message_id FROM messages WHERE message_id " + - "IN (" + Utils.convertArrayInt2String(mids) + ") AND privacy>0 ORDER BY message_id DESC LIMIT 20", - Integer.class); - } - return mids; - } - - public static List getUserBlog(JdbcTemplate sql, int UID, int privacy, int before) { - if (before > 0) { - return sql.queryForList("SELECT message_id FROM messages WHERE user_id=? AND message_id=" + - privacy + " ORDER BY message_id DESC LIMIT 20", new Object[]{UID, before}, Integer.class); - } else { - return sql.queryForList("SELECT message_id FROM messages WHERE user_id=? AND privacy>=" + privacy + - " ORDER BY message_id DESC LIMIT 20", new Object[]{UID}, Integer.class); - } - } - - public static List getUserTag(JdbcTemplate sql, int UID, int TID, int privacy, int before) { - if (before > 0) { - return sql.queryForList("SELECT messages.message_id FROM messages_tags INNER JOIN messages " + - "USING(message_id) WHERE messages.user_id=? AND messages_tags.tag_id=? " + - "AND messages.message_id=" + privacy + - " ORDER BY messages.message_id DESC LIMIT 20", - new Object[] {UID, TID, before}, Integer.class); - } else { - return sql.queryForList("SELECT messages.message_id FROM messages_tags INNER JOIN messages " + - "USING(message_id) WHERE messages.user_id=? AND messages_tags.tag_id=? " + - "AND messages.privacy>=" + privacy + " ORDER BY messages.message_id DESC LIMIT 20", - new Object[]{UID, TID}, Integer.class); - } - } - - public static List getUserRecommendations(JdbcTemplate sql, int UID, int before) { - if (before > 0) { - return sql.queryForList("SELECT message_id FROM favorites " - + "WHERE user_id=? AND message_id getUserPhotos(JdbcTemplate sql, int UID, int privacy, int before) { - if (before > 0) { - return sql.queryForList("SELECT message_id FROM messages WHERE user_id=? AND message_id=" + - privacy + " AND attach IS NOT NULL ORDER BY message_id DESC LIMIT 20", - new Object[] {UID, before}, Integer.class); - } else { - return sql.queryForList("SELECT message_id FROM messages WHERE user_id=? AND privacy>=" + privacy + - " AND attach IS NOT NULL ORDER BY message_id DESC LIMIT 20", - new Object[] {UID}, Integer.class); - } - } - - public static List getUserSearch(JdbcTemplate sql, JdbcTemplate sqlSearch, int UID, String search, int privacy, int before) { - List mids; - - - if (before > 0) { - mids = sqlSearch.queryForList("SELECT id AS message_id FROM messages WHERE user_id=? AND MATCH(?) AND id 0) { - return sql.queryForList("SELECT message_id FROM messages WHERE message_id IN (" + - Utils.convertArrayInt2String(mids) + ") AND privacy>=" + privacy + " ORDER BY message_id DESC", - Integer.class); - } - return mids; - } - - public static List getMessages(JdbcTemplate sql, List mids) { - List msgs = new ArrayList<>(20); - - return sql.query("SELECT messages.message_id,messages.user_id,users.nick," - + "messages_txt.tags,messages.readonly,messages.privacy,messages_txt.txt," - + "TIMESTAMPDIFF(MINUTE,messages.ts,NOW())," - + "messages.ts,messages.replies," - + "messages_txt.repliesby,messages.attach,messages.lat," - + "messages.lon,messages.likes " - + "FROM (messages INNER JOIN messages_txt " - + "ON messages.message_id=messages_txt.message_id) " - + "INNER JOIN users ON messages.user_id=users.id " - + "WHERE messages.message_id " - + "IN (" + Utils.convertArrayInt2String(mids) + ") " - + "ORDER BY messages.message_id DESC", (rs, rowNum) -> { - com.juick.Message msg = new com.juick.Message(); - msg.setUser(new User()); - - msg.setMID(rs.getInt(1)); - msg.getUser().setUID(rs.getInt(2)); - msg.getUser().setUName(rs.getString(3)); - if (rs.getString(4) != null) { - msg.parseTags(rs.getString(4)); - } - msg.ReadOnly = rs.getInt(5) == 1; - msg.Privacy = rs.getInt(6); - msg.FriendsOnly = msg.Privacy < 0; - msg.setText(rs.getString(7)); - msg.TimeAgo = rs.getInt(8); - msg.setDate(rs.getTimestamp(9)); - msg.Replies = rs.getInt(10); - msg.RepliesBy = rs.getString(11); - msg.AttachmentType = rs.getString(12); - if (rs.getDouble(13) != 0) { - msg.Place = new com.juick.Place(); - msg.Place.lat = rs.getDouble(13); - msg.Place.lon = rs.getDouble(14); - } - msg.Likes = rs.getInt(15); - return msg; - }); - } - - public static List getReplies(JdbcTemplate sql, int mid) { - return sql.query("SELECT replies.reply_id,replies.replyto,replies.user_id,users.nick,users.banned,replies.txt," + - "TIMESTAMPDIFF(MINUTE,replies.ts,NOW()),replies.ts,replies.attach FROM replies INNER JOIN users " + - "ON replies.user_id=users.id WHERE replies.message_id=? ORDER BY replies.reply_id ASC", - new Object[]{mid}, (rs, num) -> { - com.juick.Message msg = new com.juick.Message(); - msg.setMID(mid); - msg.setRID(rs.getInt(1)); - msg.ReplyTo = rs.getInt(2); - msg.setUser(new User()); - msg.getUser().setUID(rs.getInt(3)); - msg.getUser().setUName(rs.getString(4)); - msg.getUser().Banned = rs.getBoolean(5); - msg.setText(rs.getString(6)); - msg.TimeAgo = rs.getInt(7); - msg.setDate(rs.getTimestamp(8)); - msg.AttachmentType = rs.getString(9); - return msg; - }); - } - - public static boolean setMessagePopular(JdbcTemplate sql, int mid, int popular) { - boolean ret; - switch (popular) { - case -2: - ret = sql.update("UPDATE messages SET hidden=1 WHERE message_id=?", mid) > 0; - break; - case -1: - ret = sql.update("UPDATE messages SET popular=0 WHERE message_id=?", mid) > 0; - break; - default: - ret = sql.update("UPDATE messages SET popular=? WHERE message_id=?", popular, mid) > 0; - break; - } - if (popular == -1) { - ret = sql.update("INSERT INTO top_ignore_messages VALUES (?)", mid) > 0; - } - return ret; - } - - public static boolean setMessagePrivacy(JdbcTemplate sql, int mid) { - return sql.update("UPDATE messages SET privacy=1 WHERE message_id=?", - mid) > 0; - } - public static boolean deleteMessage(JdbcTemplate sql, int uid, int mid) { - if (sql.update("DELETE FROM messages WHERE message_id=? AND user_id=?", mid, uid) > 0) { - sql.update("DELETE FROM messages_txt WHERE message_id=?", mid); - sql.update("DELETE FROM replies WHERE message_id=?", mid); - sql.update("DELETE FROM subscr_messages WHERE message_id=?", mid); - sql.update("DELETE FROM messages_tags WHERE message_id=?", mid); - return true; - } - return false; - } - -} diff --git a/src/main/java/com/juick/server/PMQueries.java b/src/main/java/com/juick/server/PMQueries.java deleted file mode 100644 index 74d5df3e..00000000 --- a/src/main/java/com/juick/server/PMQueries.java +++ /dev/null @@ -1,145 +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; - -import com.juick.User; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; - -import java.util.List; - -/** - * - * @author Ugnich Anton - */ -public class PMQueries { - - public static boolean createPM(JdbcTemplate sql, int uid_from, int uid_to, String body) { - boolean ret = sql.update("INSERT INTO pm(user_id,user_id_to,txt) VALUES (?,?,?)", - uid_from, uid_to, body) > 0; - - if (ret) { - sql.update("INSERT INTO pm_streams(user_id,user_id_to,lastmessage,unread) " - + "VALUES (?,?,NOW(),1) " - + "ON DUPLICATE KEY UPDATE lastmessage=NOW(),unread=unread+1", - uid_from, uid_to); - } - return ret; - } - - public static boolean addPMinRoster(JdbcTemplate sql, int uid, String jid) { - return sql.update("INSERT INTO pm_inroster(user_id,jid) VALUES (?,?)", - uid, jid) > 0; - } - - public static boolean removePMinRoster(JdbcTemplate sql, int uid, String jid) { - return sql.update("DELETE FROM pm_inroster WHERE user_id=? AND jid=?", uid, jid) > 0; - } - - public static boolean havePMinRoster(JdbcTemplate sql, int uid, String jid) { - List res = sql.queryForList("SELECT 1 FROM pm_inroster " - + "WHERE user_id=? AND jid=?", Integer.class, - uid, jid); - return res.size() > 0; - } - - public static String getLastView(JdbcTemplate sql, int uid_from, int uid_to) { - try { - return sql.queryForObject("SELECT lastview FROM pm_streams WHERE user_id=? AND user_id_to=?", String.class, - uid_from, uid_to); - } catch (EmptyResultDataAccessException e) { - return null; - } - } - - public static List getPMLastConversationsUsers(JdbcTemplate sql, int uid, int cnt) { - List qusers = sql.query("SELECT user_id,unread FROM pm_streams " - + "WHERE user_id_to=? " - + "ORDER BY unread DESC, lastmessage DESC LIMIT " + cnt, (rs, rowNum) -> { - com.juick.User u = new com.juick.User(); - u.setUID(rs.getInt(1)); - u.MessagesCount = rs.getInt(2); - return u; - }, uid); - if (!qusers.isEmpty()) { - UserQueries.fillUsersByID(sql, qusers); - } - - return qusers; - } - - public static boolean haveUserInArray(List arr, int uid) { - for (User user : arr) { - if (user.getUID() == uid) { - return true; - } - } - return false; - } - - public static List getPMMessages(JdbcTemplate sql, int uid, int uid_to) { - List msgs = sql.query("SELECT user_id,txt,ts FROM pm " - + "WHERE (user_id=? AND user_id_to=?) " - + "OR (user_id_to=? AND user_id=?) ORDER BY ts DESC LIMIT 20", - (rs, rowNum) -> { - com.juick.Message msg = new com.juick.Message(); - int uuid = rs.getInt(1); - msg.setUser(new User()); - msg.getUser().setUID(uuid); - msg.setText(rs.getString(2)); - msg.setDate(rs.getTimestamp(3)); - return msg; - }, uid, uid_to, uid, uid_to); - - sql.update("UPDATE pm_streams SET lastview=NOW(),unread=0 " - + "WHERE user_id_to=? AND user_id=?", uid, uid_to); - - return msgs; - } - - public static List getLastPMInbox(JdbcTemplate sql, int uid) { - return sql.query("SELECT pm.user_id,users.nick,pm.txt,TIMESTAMPDIFF(MINUTE,pm.ts,NOW()),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().setUName(rs.getString(2)); - msg.setText(rs.getString(3)); - msg.TimeAgo = rs.getInt(4); - msg.setDate(rs.getTimestamp(5)); - return msg; - }, uid); - } - - public static List getLastPMSent(JdbcTemplate sql, int uid) { - return sql.query("SELECT pm.user_id_to,users.nick,pm.txt,TIMESTAMPDIFF(MINUTE,pm.ts,NOW())," + - "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().setUName(rs.getString(2)); - msg.setText(rs.getString(3)); - msg.TimeAgo = rs.getInt(4); - msg.setDate(rs.getTimestamp(5)); - return msg; - }, uid); - } -} diff --git a/src/main/java/com/juick/server/PushQueries.java b/src/main/java/com/juick/server/PushQueries.java deleted file mode 100644 index 54528993..00000000 --- a/src/main/java/com/juick/server/PushQueries.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package com.juick.server; - -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.util.StringUtils; - -import java.util.List; -import java.util.Optional; - -/** - * - * @author ugnich - */ -public class PushQueries { - - public static Optional getAndroidRegID(JdbcTemplate sql, int uid) { - try { - return Optional.of(sql.queryForObject("SELECT regid FROM android WHERE user_id=?", String.class, uid)); - } catch (EmptyResultDataAccessException e) { - return Optional.empty(); - } - - } - - public static List getAndroidSubscribers(JdbcTemplate sql, int uid) { - return sql.queryForList("SELECT regid FROM android INNER JOIN subscr_users " + - "ON (subscr_users.user_id=? AND android.user_id=subscr_users.suser_id)", String.class, uid); - } - - public static List getAndroidTokens(JdbcTemplate sql, List uids) { - return sql.queryForList("SELECT regid FROM android INNER JOIN users " + - "ON (users.id=android.user_id) WHERE users.id IN (" + StringUtils.collectionToCommaDelimitedString(uids) + ")", String.class); - } - - public static Optional getWinPhoneURL(JdbcTemplate sql, int uid) { - try { - return Optional.of(sql.queryForObject("SELECT url FROM winphone WHERE user_id=?", String.class, uid)); - } catch (EmptyResultDataAccessException e) { - return Optional.empty(); - } - } - - public static List getWinPhoneSubscribers(JdbcTemplate sql, int uid) { - return sql.queryForList("SELECT url FROM winphone INNER JOIN subscr_users " + - "ON (subscr_users.user_id=? AND winphone.user_id=subscr_users.suser_id)", String.class, uid); - } - - public static List getWindowsTokens(JdbcTemplate sql, List uids) { - return sql.queryForList("SELECT url FROM winphone INNER JOIN users " + - "ON (users.id=winphone.user_id) WHERE users.id IN (" + StringUtils.collectionToCommaDelimitedString(uids) + ")", String.class); - } - - public static Optional getAPNSToken(JdbcTemplate sql, int uid) { - try { - return Optional.of(sql.queryForObject("SELECT token from ios WHERE user_id=?", String.class, uid)); - } catch (EmptyResultDataAccessException e) { - return Optional.empty(); - } - } - - public static List getAPNSSubscribers(JdbcTemplate sql, int uid) { - return sql.queryForList("SELECT token FROM ios INNER JOIN subscr_users " + - "ON (subscr_users.user_id=? AND ios.user_id=subscr_users.suser_id)", String.class, uid); - } - public static List getAPNSTokens(JdbcTemplate sql, List uids) { - return sql.queryForList("SELECT token FROM ios INNER JOIN users " + - "ON (users.id=ios.user_id) WHERE users.id IN (" + StringUtils.collectionToCommaDelimitedString(uids) + ")", String.class); - } - -} diff --git a/src/main/java/com/juick/server/SQLHelpers.java b/src/main/java/com/juick/server/SQLHelpers.java deleted file mode 100644 index 13ebe98a..00000000 --- a/src/main/java/com/juick/server/SQLHelpers.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package com.juick.server; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author ugnich - */ -public class SQLHelpers { - - private static final Logger logger = Logger.getLogger(SQLHelpers.class.getName()); - - public static int execute(Connection sql, String query) { - int ret = -1; - PreparedStatement stmt = null; - try { - stmt = sql.prepareStatement(query); - ret = stmt.executeUpdate(); - } catch (SQLException e) { - logger.log(Level.SEVERE, "sql exception", e); - } finally { - Utils.finishSQL(null, stmt); - } - return ret; - } - - public static int executeInt(Connection sql, String query, int param) { - int ret = -1; - PreparedStatement stmt = null; - try { - stmt = sql.prepareStatement(query); - stmt.setInt(1, param); - ret = stmt.executeUpdate(); - } catch (SQLException e) { - logger.log(Level.SEVERE, "sql exception", e); - } finally { - Utils.finishSQL(null, stmt); - } - return ret; - } - - public static int getInt(Connection sql, String query, int defvalue) { - int ret = defvalue; - PreparedStatement stmt = null; - ResultSet rs = null; - try { - stmt = sql.prepareStatement(query); - rs = stmt.executeQuery(); - if (rs.first()) { - ret = rs.getInt(1); - } - } catch (SQLException e) { - logger.log(Level.SEVERE, "sql exception", e); - } finally { - Utils.finishSQL(rs, stmt); - } - return ret; - } - - public static int getInt(Connection sql, String query, int param, int defvalue) { - int ret = defvalue; - PreparedStatement stmt = null; - ResultSet rs = null; - try { - stmt = sql.prepareStatement(query); - stmt.setInt(1, param); - rs = stmt.executeQuery(); - if (rs.first()) { - ret = rs.getInt(1); - } - } catch (SQLException e) { - logger.log(Level.SEVERE, "sql exception", e); - } finally { - Utils.finishSQL(rs, stmt); - } - return ret; - } - - public static int getInt(Connection sql, String query, String param, int defvalue) { - int ret = defvalue; - PreparedStatement stmt = null; - ResultSet rs = null; - try { - stmt = sql.prepareStatement(query); - stmt.setString(1, param); - rs = stmt.executeQuery(); - if (rs.first()) { - ret = rs.getInt(1); - } - } catch (SQLException e) { - logger.log(Level.SEVERE, "sql exception", e); - } finally { - Utils.finishSQL(rs, stmt); - } - return ret; - } - - public static String getString(Connection sql, String query, int param) { - String ret = null; - PreparedStatement stmt = null; - ResultSet rs = null; - try { - stmt = sql.prepareStatement(query); - stmt.setInt(1, param); - rs = stmt.executeQuery(); - if (rs.first()) { - ret = rs.getString(1); - } - } catch (SQLException e) { - logger.log(Level.SEVERE, "sql exception", e); - } finally { - Utils.finishSQL(rs, stmt); - } - return ret; - } - - public static String getString(Connection sql, String query, String param) { - String ret = null; - PreparedStatement stmt = null; - ResultSet rs = null; - try { - stmt = sql.prepareStatement(query); - stmt.setString(1, param); - rs = stmt.executeQuery(); - if (rs.first()) { - ret = rs.getString(1); - } - } catch (SQLException e) { - logger.log(Level.SEVERE, "sql exception", e); - } finally { - Utils.finishSQL(rs, stmt); - } - return ret; - } - - public static List getArrayInteger(Connection sql, String query, int param) { - List ret = new ArrayList<>(); - - PreparedStatement stmt = null; - ResultSet rs = null; - try { - stmt = sql.prepareStatement(query); - stmt.setInt(1, param); - rs = stmt.executeQuery(); - rs.beforeFirst(); - while (rs.next()) { - ret.add(rs.getInt(1)); - } - } catch (SQLException e) { - logger.log(Level.SEVERE, "sql exception", e); - } finally { - Utils.finishSQL(rs, stmt); - } - - return ret; - } -} diff --git a/src/main/java/com/juick/server/ShowQueries.java b/src/main/java/com/juick/server/ShowQueries.java deleted file mode 100644 index 3ddff412..00000000 --- a/src/main/java/com/juick/server/ShowQueries.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.juick.server; - -import com.juick.User; - -import java.util.List; -import org.springframework.jdbc.core.JdbcTemplate; - -/** - * Created by vt on 10/01/16. - */ -public class ShowQueries { - - public static List getRecommendedUsers(JdbcTemplate sql, User forUser) { - return sql.queryForList("SELECT users.nick FROM subscr_users INNER JOIN users " + - "ON subscr_users.user_id=users.id " + - "WHERE subscr_users.user_id NOT IN (SELECT user_id FROM subscr_users WHERE suser_id=?) " + - "AND subscr_users.suser_id IN (SELECT user_id FROM subscr_users WHERE suser_id=?) " + - "AND subscr_users.user_id NOT IN (SELECT bl_user_id FROM bl_users WHERE user_id=?) " + - "AND subscr_users.user_id!=? AND users.lastmessage>UNIX_TIMESTAMP()-259200 " + - "GROUP BY subscr_users.user_id ORDER BY count(*) DESC LIMIT 10", - String.class, new Object[] {forUser.getUID(), forUser.getUID(), forUser.getUID(), forUser.getUID()}); - } - - public static List getTopUsers(JdbcTemplate sql) { - return sql.queryForList("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", - String.class); - } -} diff --git a/src/main/java/com/juick/server/SubscriptionsQueries.java b/src/main/java/com/juick/server/SubscriptionsQueries.java deleted file mode 100644 index 7c30285b..00000000 --- a/src/main/java/com/juick/server/SubscriptionsQueries.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package com.juick.server; - -import com.juick.Tag; -import com.juick.User; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.util.StringUtils; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -/** - * - * @author ugnich - */ -public class SubscriptionsQueries { - - private static final Logger logger = Logger.getLogger(SubscriptionsQueries.class.getName()); - - public static List getJIDSubscribedToUser(JdbcTemplate sql, int uid, boolean friendsonly) { - if (friendsonly == false) { - return sql.queryForList("SELECT jids.jid FROM subscr_users INNER JOIN jids " + - "ON (subscr_users.user_id=? AND subscr_users.suser_id=jids.user_id) WHERE jids.active=1", - String.class, uid); - } else { - return sql.queryForList("SELECT jids.jid FROM subscr_users INNER JOIN jids " + - "ON (subscr_users.user_id=? AND subscr_users.suser_id=jids.user_id) WHERE jids.active=1 " + - "AND jids.user_id IN (SELECT wl_user_id FROM wl_users WHERE user_id=?)", String.class, uid, uid); - } - } - - public static List getSubscribedUsers(JdbcTemplate sql, int uid, int mid) { - List userids = UserQueries.getUserReaders(sql, uid); - Set set = new HashSet<>(); - set.addAll(userids.stream().map(User::getUID).collect(Collectors.toList())); - List tags = MessagesQueries.getMessageTagsIDs(sql, mid); - if (tags.size() > 0) { - List tagUsers = sql.queryForList("SELECT suser_id FROM subscr_tags " + - "WHERE tag_id IN (" + StringUtils.arrayToCommaDelimitedString(tags.toArray()) + ") AND suser_id!=" + uid, Integer.class); - set.addAll(tagUsers); - } - return UserQueries.getUsersByID(sql, new ArrayList<>(set)); - } - - public static List getUsersSubscribedToComments(JdbcTemplate sql, int mid, int ignore_uid) { - List userids = sql.queryForList("SELECT suser_id FROM subscr_messages WHERE message_id=? AND suser_id!=?", - new Object[] {mid, ignore_uid}, Integer.class); - if (userids.size() > 0) { - return UserQueries.getUsersByID(sql, userids); - } else { - return new ArrayList<>(); - } - } - - public static List getUsersSubscribedToUserRecommendations(JdbcTemplate sql, int uid, int mid, int muid) { - List tags = MessagesQueries.getMessageTagsIDs(sql, mid); - - String query = "SELECT suser_id FROM subscr_users WHERE user_id=" + uid; - query += " AND user_id NOT IN (SELECT user_id FROM bl_users WHERE bl_user_id=" + muid + ")"; - query += " AND user_id NOT IN (SELECT suser_id FROM subscr_users WHERE user_id=" + muid + ")"; - query += " AND user_id NOT IN (SELECT suser_id FROM subscr_messages WHERE message_id=" + mid + ")"; - query += " AND user_id NOT IN (SELECT user_id FROM favorites WHERE message_id=" + mid + ")"; - query += " AND user_id NOT IN (SELECT subscr_users.suser_id FROM subscr_users INNER JOIN favorites ON (favorites.message_id=" + mid + " AND subscr_users.user_id=favorites.user_id AND favorites.user_id!=" + uid + "))"; - if (!tags.isEmpty()) { - String tagsStr = Utils.convertArrayInt2String(tags); - query += " AND user_id NOT IN (SELECT suser_id FROM subscr_tags WHERE tag_id IN (" + tagsStr + "))"; - query += " AND user_id NOT IN (SELECT user_id FROM bl_tags WHERE tag_id IN (" + tagsStr + "))"; - } - List userids = sql.queryForList(query, Integer.class); - return UserQueries.getUsersByID(sql, userids); - } - - public static boolean subscribeMessage(JdbcTemplate sql, int mid, int vuid) { - return sql.update("INSERT IGNORE INTO subscr_messages(suser_id,message_id) VALUES (" + vuid + "," + mid + ")") == 1; - } - public static boolean unSubscribeMessage(JdbcTemplate sql, int mid, int vuid) { - return sql.update("DELETE FROM subscr_messages WHERE message_id=? AND suser_id=?", - mid, vuid) > 0; - } - public static boolean subscribeUser(JdbcTemplate sql, User user, User toUser) { - return sql.update("INSERT IGNORE INTO subscr_users(user_id,suser_id) VALUES (?,?)", - user.getUID(), toUser.getUID()) == 1; - } - public static boolean unSubscribeUser(JdbcTemplate sql, User user, User fromUser) { - return sql.update("DELETE FROM subscr_users WHERE suser_id=? AND user_id=?", - user.getUID(), fromUser.getUID()) > 0; - } - public static boolean subscribeTag(JdbcTemplate sql, User user, Tag toTag) { - return sql.update("INSERT IGNORE INTO subscr_tags(tag_id,suser_id) VALUES (?,?)", - toTag.TID, user.getUID()) == 1; - } - public static boolean unSubscribeTag(JdbcTemplate sql, User user, Tag toTag) { - return sql.update("DELETE FROM subscr_tags WHERE tag_id=? AND suser_id=?", - toTag.TID, user.getUID()) > 0; - } - -} diff --git a/src/main/java/com/juick/server/TagQueries.java b/src/main/java/com/juick/server/TagQueries.java deleted file mode 100644 index 2258a8ea..00000000 --- a/src/main/java/com/juick/server/TagQueries.java +++ /dev/null @@ -1,166 +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; - -import com.juick.Tag; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; - -import java.sql.PreparedStatement; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -/** - * - * @author Ugnich Anton - */ -public class TagQueries { - - public static com.juick.Tag getTag(JdbcTemplate sql, int tid) { - try { - return sql.queryForObject("SELECT synonym_id,name FROM tags WHERE tag_id=?", - new Object[]{tid}, (rs, num) -> { - Tag ret = new Tag(); - ret.TID = tid; - ret.SynonymID = rs.getInt(1); - ret.Name = rs.getString(2); - return ret; - }); - } catch (EmptyResultDataAccessException e) { - return null; - } - } - - public static com.juick.Tag getTag(JdbcTemplate sql, String tag, boolean autoCreate) { - Tag ret = null; - try { - ret = sql.queryForObject("SELECT tag_id,synonym_id,name FROM tags WHERE name=?", - new Object[]{tag}, (rs, rowNum) -> { - Tag ret1 = new Tag(); - ret1.TID = rs.getInt(1); - ret1.SynonymID = rs.getInt(2); - ret1.Name = rs.getString(3); - return ret1; - }); - } catch (EmptyResultDataAccessException e) { - // tag not found - } - if (ret == null && autoCreate) { - ret = new com.juick.Tag(); - ret.TID = createTag(sql, tag); - ret.Name = tag; - } - - return ret; - } - - public static List getTags(JdbcTemplate sql, String[] tags, boolean autoCreate) { - List ret = new ArrayList<>(); - - for (String tag : tags) { - if (!tag.isEmpty()) { - Tag t = getTag(sql, tag, autoCreate); - if (t != null) { - ret.add(t); - } - } - } - - return ret; - } - - public static boolean getTagNoIndex(JdbcTemplate sql, int tag_id) { - try { - return sql.queryForObject("SELECT noindex FROM tags WHERE tag_id=?", Integer.class, tag_id) == 1; - } catch (EmptyResultDataAccessException e) { - return false; - } - } - - public static int createTag(JdbcTemplate sql, String name) { - KeyHolder holder = new GeneratedKeyHolder(); - sql.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(); - } - - public static List getUserTagsAll(JdbcTemplate sql, int uid) { - return sql.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", - (rs, rowNum) -> { - Tag t = new Tag(); - t.Name = rs.getString(1); - t.UsageCnt = rs.getInt(2); - return t; - }, uid); - } - - public static List getUserBLTags(JdbcTemplate sql, int uid) { - return sql.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); - } - - public static List getPopularTags(JdbcTemplate sql) { - return sql.queryForList("SELECT name FROM tags WHERE top=1 ORDER BY name ASC", String.class); - } - public static List updateTags(JdbcTemplate sql, int mid, List newTags) { - List currentTags = MessagesQueries.getMessageTags(sql, mid); - newTags.stream().filter(currentTags::contains) - .forEach(t -> sql.update("DELETE FROM messages_tags WHERE message_id=? AND tag_id=?", mid, t.TID)); - newTags.stream().filter(t -> !currentTags.contains(t)) - .forEach(t -> sql.update("INSERT INTO messages_tags(message_id,tag_id) VALUES (?,?)", mid, t.TID)); - return MessagesQueries.getMessageTags(sql, mid); - } - - public static List fromString(JdbcTemplate sql, String txt, boolean tagsOnly) { - String patternString = tagsOnly ? "^(?:(?:\\*[^ \\r\\n\\t]+)|\\s)+$" : "^\\*([^ \\r\\n\\t]+)\\s+([\\s\\S]+)"; - Pattern tagsPattern = Pattern.compile(patternString); - if (tagsPattern.matcher(txt).matches()) { - Pattern tagPattern = Pattern.compile("\\*([^ \\r\\n\\t]+)"); - Matcher tagMatcher = tagPattern.matcher(txt); - List tags = new ArrayList<>(); - // TODO: process readonly, private, friends, public - while (tagMatcher.find()) { - for (int i = 1; i <= tagMatcher.groupCount(); i++) { - tags.add(getTag(sql, tagMatcher.group(i), true)); - } - } - return tags; - } - return Collections.emptyList(); - } - public static String toString(List tags) { - return tags.stream().map(t -> " *" + t.Name) - .collect(Collectors.joining()); - } -} diff --git a/src/main/java/com/juick/server/UserQueries.java b/src/main/java/com/juick/server/UserQueries.java deleted file mode 100644 index db380d7a..00000000 --- a/src/main/java/com/juick/server/UserQueries.java +++ /dev/null @@ -1,440 +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; - -import com.juick.User; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.util.StringUtils; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.*; - -/** - * - * @author Ugnich Anton - */ -public class UserQueries { - - static final String ABCDEF = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - public static class UserMapper implements RowMapper { - @Override - public User mapRow(ResultSet rs, int rowNum) throws SQLException { - User user = new User(); - user.setUID(rs.getInt(1)); - user.setUName(rs.getString(2)); - user.Banned = rs.getBoolean(3); - return user; - } - } - - public static String getSignUpHashByJID(JdbcTemplate sql, String jid) { - String hash = sql.queryForObject("SELECT loginhash FROM jids WHERE jid=? AND user_id IS NULL", - String.class, jid); - if (hash == null) { - hash = UUID.randomUUID().toString(); - sql.update("INSERT INTO jids(jid,loginhash) VALUES (?,?)", jid, hash); - } - return hash; - } - public static String getSignUpHashByTelegramID(JdbcTemplate sql, Long telegramId, String username) { - try { - return sql.queryForObject("SELECT loginhash FROM telegram WHERE tg_id=? AND user_id IS NULL", - String.class, telegramId); - } catch (EmptyResultDataAccessException e) { - String hash = UUID.randomUUID().toString(); - sql.update("INSERT INTO telegram(tg_id, loginhash, tg_name) VALUES (?, ?, ?)", telegramId, hash, username); - return hash; - } - } - - public static int createUser(JdbcTemplate sql, String username, String password) { - KeyHolder holder = new GeneratedKeyHolder(); - sql.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); - - int uid = holder.getKey().intValue(); - - sql.update("INSERT INTO useroptions(user_id) VALUES (?)", uid); - sql.update("INSERT INTO subscr_users(user_id,suser_id) VALUES (2,?)", uid); - - return uid; - } - - public static Optional getUserByUID(JdbcTemplate sql, int uid) { - try { - return Optional.of(sql.queryForObject("SELECT id, nick,banned FROM users WHERE id=?", - new UserMapper(), uid)); - } catch (EmptyResultDataAccessException e) { - return Optional.empty(); - } - } - - public static User getUserByName(JdbcTemplate sql, String username) { - try { - return sql.queryForObject("SELECT id,nick,banned FROM users WHERE nick=?", - new UserMapper(), - username); - } catch (EmptyResultDataAccessException e) { - return null; - } - } - - public static User getUserByJID(JdbcTemplate sql, String jid) { - try { - return sql.queryForObject("SELECT id,nick,banned FROM users WHERE id=(SELECT user_id FROM jids WHERE jid=?)", - new UserMapper(), jid); - } catch (EmptyResultDataAccessException e) { - return null; - } - } - - public static List getUsersByName(JdbcTemplate sql, List unames) { - if (!unames.isEmpty()) { - return sql.query("SELECT id,nick,banned FROM users WHERE nick IN (" + Utils.convertArrayString2String(unames) + ")", - new UserMapper()); - } - return Collections.emptyList(); - } - - public static List getUsersByID(JdbcTemplate sql, List uids) { - if (!uids.isEmpty()) { - return sql.query("SELECT id,nick,banned FROM users WHERE id IN (" + Utils.convertArrayInt2String(uids) + ")", - new UserMapper()); - } - return Collections.emptyList(); - } - - public static boolean fillUsersByID(JdbcTemplate sql, List users) { - boolean ret = false; - - String uids = ""; - final int usersSize = users.size(); - for (int i = 0; i < usersSize; i++) { - if (i > 0) { - uids += ","; - } - uids += users.get(i).getUID(); - } - - sql.query("SELECT id,nick,banned FROM users WHERE id IN (" + uids + ")", - (rs, num) -> { - User u = new User(); - u.setUID(rs.getInt(1)); - u.setUName(rs.getString(2)); - return u; - }); - - return true; - } - - public static List getUsersByJID(JdbcTemplate sql, List jids) { - return sql.query("SELECT users.id,users.nick,jids.jid FROM users " - + "INNER JOIN jids ON jids.user_id=users.id " - + "WHERE jids.jid IN (" + Utils.convertArrayString2String(jids) + ")", - (rs, rowNum) -> { - com.juick.User user = new com.juick.User(); - user.setUID(rs.getInt(1)); - user.setUName(rs.getString(2)); - user.setJID(rs.getString(3)); - return user; - }); - } - - public static List getJIDsbyUID(JdbcTemplate sql, int uid) { - return sql.queryForList("SELECT jid FROM jids WHERE user_id=? AND active=1", String.class, uid); - } - - public static int getUIDbyJID(JdbcTemplate sql, String jid) { - return sql.queryForObject("SELECT user_id FROM jids WHERE jid=?", Integer.class, jid); - } - - public static int getUIDbyName(JdbcTemplate sql, String uname) { - try { - return sql.queryForObject("SELECT id FROM users WHERE nick=?", Integer.class, uname); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - - public static int getUIDbyHash(JdbcTemplate sql, String hash) { - try { - return sql.queryForObject("SELECT user_id FROM logins WHERE hash=?", Integer.class, hash); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - - public static com.juick.User getUserByHash(JdbcTemplate sql, String hash) { - try { - User user = sql.queryForObject("SELECT logins.user_id,users.nick, users.banned FROM logins " + - "INNER JOIN users ON logins.user_id=users.id WHERE logins.hash=?", - new UserMapper(), hash); - user.setAuthHash(hash); - return user; - } catch (EmptyResultDataAccessException e) { - return new User(); - } - } - - public static String getHashByUID(JdbcTemplate sql, int uid) { - try { - return sql.queryForObject("SELECT hash FROM logins WHERE user_id=?", String.class, uid); - } catch (EmptyResultDataAccessException e){ - String hash = generateHash(16); - sql.update(con -> { - PreparedStatement stmt = con.prepareStatement("INSERT INTO logins(user_id,hash) VALUES (?,?)"); - stmt.setInt(1, uid); - stmt.setString(2, hash); - return stmt; - }); - return hash; - } - } - - public static String generateHash(int len) { - Random rnd = new Random(); - StringBuilder sb = new StringBuilder(len); - for (int i = 0; i < len; i++) { - sb.append(ABCDEF.charAt(rnd.nextInt(ABCDEF.length()))); - } - return sb.toString(); - } - - public static boolean checkUserNameValid(String uname) { - return uname != null && uname.length() >= 2 && uname.length() <= 16 && uname.matches("[a-zA-Z0-9\\-]+"); - } - - public static int checkPassword(JdbcTemplate sql, String username, String password) { - try { - String realPassword = sql.queryForObject("SELECT passw FROM users WHERE nick=?", String.class, username); - if (realPassword.equals(password)) { - User user = UserQueries.getUserByName(sql, username); - if (user != null) { - return user.getUID(); - } else { - return -1; - } - } else { - return -1; - } - } catch (EmptyResultDataAccessException e) { - return -1; - } - } - - public static int getUserOptionInt(JdbcTemplate sql, int uid, String option, int defaultValue) { - try { - return sql.queryForObject("SELECT " + option + " FROM useroptions WHERE user_id=?", Integer.class, uid); - } catch (EmptyResultDataAccessException e) { - return defaultValue; - } - } - - public static void setUserOptionInt(JdbcTemplate sql, int uid, String option, int value) { - sql.update("UPDATE useroptions SET " + option + "=? WHERE user_id=?", value, uid); - } - - public static boolean getCanMedia(JdbcTemplate sql, int uid) { - try { - int res = sql.queryForObject("SELECT users.lastphoto-UNIX_TIMESTAMP() FROM users WHERE id=?", - Integer.class, uid); - return res < 3600; - } catch (EmptyResultDataAccessException e) { - return false; - } - } - - public static boolean isInWL(JdbcTemplate sql, int uid, int check) { - try { - return sql.queryForObject("SELECT 1 FROM wl_users WHERE user_id=? AND wl_user_id=?", - Integer.class, uid, check) == 1; - } catch (EmptyResultDataAccessException e) { - return false; - } - } - - public static boolean isInBL(JdbcTemplate sql, int uid, int check) { - try { - return sql.queryForObject("SELECT 1 FROM bl_users WHERE user_id=? AND bl_user_id=?", - Integer.class, uid, check) == 1; - } catch (EmptyResultDataAccessException e) { - return false; - } - } - - public static boolean isInBLAny(JdbcTemplate sql, int uid, int uid2) { - try { - return sql.queryForObject("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) == 1; - } catch (EmptyResultDataAccessException e) { - return false; - } - } - - public static List checkBL(JdbcTemplate sql, int visitor, List uids) { - if (!uids.isEmpty()) { - return sql.queryForList("SELECT user_id FROM bl_users WHERE bl_user_id=? and user_id IN (" + - StringUtils.collectionToCommaDelimitedString(uids) + ")", Integer.class, visitor); - } else { - return new ArrayList<>(); - } - } - - public static boolean isSubscribed(JdbcTemplate sql, int uid, int check) { - try { - return sql.queryForObject("SELECT 1 FROM subscr_users WHERE suser_id=? AND user_id=?", - Integer.class, uid, check) == 1; - } catch (EmptyResultDataAccessException e) { - return false; - } - } - - public static List getUserRead(JdbcTemplate sql, int uid) { - return sql.queryForList("SELECT user_id FROM subscr_users WHERE suser_id=?", Integer.class, uid); - } - - public static List getUserReadLeastPopular(JdbcTemplate sql, int uid, int cnt) { - return sql.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.setUName(rs.getString(2)); - return u; - }, uid, cnt); - } - - public static List getUserReaders(JdbcTemplate sql, int uid) { - return sql.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.setUName(rs.getString(2)); - return u; - }, uid); - } - - public static List getUserFriends(JdbcTemplate sql, int uid) { - return sql.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.setUName(rs.getString(2)); - return u; - }, uid, uid); - } - - public static List getUserBLUsers(JdbcTemplate sql, int uid) { - return sql.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.setUName(rs.getString(2)); - return u; - }, uid); - } - - public static boolean linkTwitterAccount(JdbcTemplate sql, User user, String accessToken, - String accessTokenSecret, String screenName) { - if (sql.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) { - return sql.update("INSERT INTO subscr_users(user_id,suser_id,jid) " + - "VALUES (?,1741,'juick\\@twitter.juick.com')", user.getUID()) > 0; - } - return false; - - } - - public static int getStatsIRead(JdbcTemplate sql, int uid) { - try { - return sql.queryForObject("SELECT COUNT(*) FROM subscr_users WHERE suser_id=?", Integer.class, uid); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - - public static int getStatsMyReaders(JdbcTemplate sql, int uid) { - try { - return sql.queryForObject("SELECT COUNT(*) FROM subscr_users WHERE user_id=?", Integer.class, uid); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - - public static int getStatsMessages(JdbcTemplate sql, int uid) { - try { - return sql.queryForObject("SELECT COUNT(*) FROM messages WHERE user_id=?", Integer.class, uid); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - - public static int getStatsReplies(JdbcTemplate sql, int uid) { - try { - return sql.queryForObject("SELECT COUNT(*) FROM replies WHERE user_id=?", Integer.class, uid); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - - public enum ActiveStatus { - Inactive, - Active - } - - public static boolean setActiveStatusForJID(JdbcTemplate sql, String JID, ActiveStatus jidStatus) { - User user = getUserByJID(sql, JID); - if (user != null) { - return sql.update(con -> { - PreparedStatement preparedStatement = con.prepareStatement( - "UPDATE jids SET active=? WHERE user_id=? AND jid=?"); - int newStatus = jidStatus == ActiveStatus.Active ? 1 : 0; - preparedStatement.setInt(1, newStatus); - preparedStatement.setInt(2, user.getUID()); - preparedStatement.setString(3, JID); - return preparedStatement; - - }) >= 0; - } - return false; - } -} diff --git a/src/main/java/com/juick/server/Utils.java b/src/main/java/com/juick/server/Utils.java deleted file mode 100644 index 1968150e..00000000 --- a/src/main/java/com/juick/server/Utils.java +++ /dev/null @@ -1,86 +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; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.List; - -/** - * - * @author Ugnich Anton - */ -public class Utils { - - public static String convertArrayInt2String(List mids) { - String q = ""; - for (int i = 0; i < mids.size(); i++) { - if (i > 0) { - q += ","; - } - q += mids.get(i); - } - return q; - } - - public static String convertArrayString2String(List unames) { - String q = ""; - for (int i = 0; i < unames.size(); i++) { - if (i > 0) { - q += ","; - } - q += "\"" + unames.get(i) + "\""; - } - return q; - } - - public static String buildQueryArray(String query1, int length, String query2) { - String ret = query1; - for (int i = 0; i < length; i++) { - if (i > 0) { - ret += ","; - } - ret += "?"; - } - ret += query2; - return ret; - } - - public static void stmtSetStringArray(PreparedStatement stmt, int offset, String strs[]) throws SQLException { - for (int i = 0; i < strs.length; i++) { - stmt.setString(offset + i, strs[i]); - } - } - - public static void finishSQL(ResultSet rs, Statement stmt) { - if (rs != null) { - try { - rs.close(); - } catch (SQLException e) { - } - } - if (stmt != null) { - try { - stmt.close(); - } catch (SQLException e) { - } - } - } -} diff --git a/src/main/java/com/juick/server/helpers/PrivacyOpts.java b/src/main/java/com/juick/server/helpers/PrivacyOpts.java deleted file mode 100644 index 66cf9410..00000000 --- a/src/main/java/com/juick/server/helpers/PrivacyOpts.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.juick.server.helpers; - -/** - * Created by vt on 16/01/16. - */ -public class PrivacyOpts { - private int uid; - private int privacy; - - public PrivacyOpts() { - - } - - public int getUid() { - return uid; - } - - public void setUid(int uid) { - this.uid = uid; - } - - public int getPrivacy() { - return privacy; - } - - public void setPrivacy(int privacy) { - this.privacy = privacy; - } -} diff --git a/src/main/java/com/juick/server/protocol/JuickProtocol.java b/src/main/java/com/juick/server/protocol/JuickProtocol.java deleted file mode 100644 index 1a2e5333..00000000 --- a/src/main/java/com/juick/server/protocol/JuickProtocol.java +++ /dev/null @@ -1,375 +0,0 @@ -package com.juick.server.protocol; - -import com.juick.*; -import com.juick.json.MessageSerializer; -import com.juick.server.*; -import com.juick.server.protocol.annotation.UserCommand; -import com.juick.xmpp.extensions.JuickMessage; -import org.springframework.jdbc.core.JdbcTemplate; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -/** - * Created by oxpa on 22.03.16. - */ - -public class JuickProtocol { - MessageSerializer json = new MessageSerializer(); - JdbcTemplate sql; - String baseUri; - - public JuickProtocol(JdbcTemplate sql, String baseUri) { - this.sql = sql; - this.baseUri = baseUri; - } - - /** - * find command by pattern and invoke - * @param user who send command - * @param userInput given by user - * @return command result - * @throws InvocationTargetException - * @throws IllegalAccessException - * @throws NoSuchMethodException - */ - public ProtocolReply getReply(User user, String userInput) throws InvocationTargetException, - IllegalAccessException, NoSuchMethodException { - Optional cmd = Arrays.asList(getClass().getDeclaredMethods()).stream() - .filter(m -> m.isAnnotationPresent(UserCommand.class)) - .filter(m -> Pattern.compile(m.getAnnotation(UserCommand.class).pattern(), - m.getAnnotation(UserCommand.class).patternFlags()).matcher(userInput).matches()) - .findFirst(); - if (!cmd.isPresent()) { - // default command - post as new message - return postMessage(user, userInput); - } else { - Matcher matcher = Pattern.compile(cmd.get().getAnnotation(UserCommand.class).pattern(), - cmd.get().getAnnotation(UserCommand.class).patternFlags()).matcher(userInput); - List groups = new ArrayList<>(); - while (matcher.find()) { - for (int i = 1; i <= matcher.groupCount(); i++) { - groups.add(matcher.group(i)); - } - } - return (ProtocolReply) getClass().getMethod(cmd.get().getName(), User.class, String[].class) - .invoke(this, user, groups.toArray(new String[groups.size()])); - } - } - - public ProtocolReply postMessage(User user, String input) { - List tags = TagQueries.fromString(sql, input, false); - String body = input.substring(TagQueries.toString(tags).length()); - int mid = MessagesQueries.createMessage(sql, user.getUID(), body, null, tags); - //app.events().publishEvent(new JuickMessageEvent(app.messages().getMessage(mid))); - return new ProtocolReply("New message posted.\n#" + mid + " " + baseUri + mid, - Optional.of(json.serializeList(Collections.singletonList(MessagesQueries.getMessage(sql, mid))))); - } - - @UserCommand(pattern = "^#(\\++)$", help = "#+ - Show last Juick messages (#++ - second page, ...)") - public ProtocolReply commandLast(User user, String... arguments) { - // number of + is the page count - int page = arguments[0].length(); - List mids = MessagesQueries.getAll(sql, user.getUID(), page); - List messages = MessagesQueries.getMessages(sql, mids); - // TODO: message toString - return new ProtocolReply("Last messages: \n" + String.join("\n", messages.stream().map(Object::toString) - .collect(Collectors.toList())), Optional.of(json.serializeList(messages))); - } - - @UserCommand(pattern = "^\\s*bl\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "BL - Show your blacklist") - public ProtocolReply commandBL(User user_from, String... arguments) { - List blusers; - List bltags; - - blusers = UserQueries.getUserBLUsers(sql, user_from.getUID()); - bltags = TagQueries.getUserBLTags(sql, user_from.getUID()); - - - String txt = ""; - 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.getUName() + "\n"; - } - } - if (txt.isEmpty()) { - txt = "You don't have any users or tags in your blacklist."; - } - return new ProtocolReply(txt, Optional.empty()); - } - - @UserCommand(pattern = "^\\@([^\\s\\n\\+]+)(\\+?)$", - help = "@username+ - Show user's info and last 10 messages (@username++ - second page, ..)") - public ProtocolReply commandUser(User user, String... arguments) { - User blogUser = UserQueries.getUserByName(sql, arguments[0]); - int page = arguments[1].length(); - if (blogUser.getUID() > 0) { - List mids = MessagesQueries.getUserBlog(sql, blogUser.getUID(), 0, page); - List messages = MessagesQueries.getMessages(sql, mids); - return new ProtocolReply(String.format("Last messages from @%s:\n%s", arguments[0], - String.join("\n", messages.stream() - .map(Object::toString).collect(Collectors.toList()))), - Optional.of(json.serializeList(messages))); - } - return new ProtocolReply("User not found", Optional.empty()); - } - - @UserCommand(pattern = "^\\s*d\\s*\\#([0-9]+)\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "D #12345 - delete the message") - public ProtocolReply commandDel(User user, String... args) { - try { - int mid = Integer.parseInt(args[0]); - if (MessagesQueries.deleteMessage(sql, user.getUID(), mid)) { - return new ProtocolReply(String.format("Message %s deleted", mid), Optional.empty()); - } - } catch (NumberFormatException e) { - return new ProtocolReply("Error", Optional.empty()); - } - return new ProtocolReply("Error", Optional.empty()); - } - - @UserCommand(pattern = "^\\s*login\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "LOGIN - log in to Juick website") - public ProtocolReply commandLogin(User user, String... arguments) { - return new ProtocolReply(baseUri + "?" + UserQueries.getHashByUID(sql, user.getUID()), - Optional.empty()); - } - - @UserCommand(pattern = "^(#+)$", help = "# - Show last messages from your feed (## - second page, ...)") - public ProtocolReply commandMyFeed(User user, String... arguments) { - // number of # is the page count - int page = arguments[0].length(); - List mids = MessagesQueries.getMyFeed(sql, user.getUID(), page); - List messages = MessagesQueries.getMessages(sql, mids); - // TODO: add instructions for empty feed - return new ProtocolReply("Your feed: \n" + String.join("\n", - messages.stream().map(Object::toString).collect(Collectors.toList())), - Optional.of(json.serializeList(messages))); - } - - @UserCommand(pattern = "^\\s*(on|off)\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "ON/OFF - Enable/disable subscriptions delivery") - public ProtocolReply commandOnOff(User user, String[] input) { - UserQueries.ActiveStatus newStatus; - String retValUpdated; - if (input[0].toLowerCase().equals("on")) { - newStatus = UserQueries.ActiveStatus.Active; - retValUpdated = "Notifications are activated for " + user.getJID(); - } else { - newStatus = UserQueries.ActiveStatus.Inactive; - retValUpdated = "Notifications are disabled for " + user.getJID(); - } - - if (UserQueries.setActiveStatusForJID(sql, user.getJID(), newStatus)) { - return new ProtocolReply(retValUpdated, Optional.empty()); - } else { - return new ProtocolReply(String.format("Subscriptions status for %s was not changed", user.getJID()), - Optional.empty()); - } - } - - @UserCommand(pattern = "^\\s*ping\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "PING - returns you a PONG") - public ProtocolReply commandPing(User user, String[] input) { - return new ProtocolReply("PONG", Optional.empty()); - } - - @UserCommand(pattern = "^\\@(\\S+)\\s+([\\s\\S]+)$", help = "@username message - send PM to username") - public ProtocolReply commandPM(User user_from, String... arguments) { - String user_to = arguments[0]; - String body = arguments[1]; - int ret = 0; - - int uid_to = 0; - String jid_to = null; - boolean haveInRoster = false; - - if (user_to.indexOf('@') > 0) { - uid_to = UserQueries.getUIDbyJID(sql, user_to); - } else { - uid_to = UserQueries.getUIDbyName(sql, user_to); - } - - if (uid_to > 0) { - if (!UserQueries.isInBLAny(sql, uid_to, user_from.getUID())) { - if (PMQueries.createPM(sql, user_from.getUID(), uid_to, body)) { - //jid_to = UserQueries.getJIDsbyUID(sql, uid_to); - if (jid_to != null) { - haveInRoster = PMQueries.havePMinRoster(sql, user_from.getUID(), jid_to); - } - ret = 200; - } else { - ret = 500; - } - } else { - ret = 403; - } - } else { - ret = 404; - } - - - if (ret == 200) { - JuickMessage jmsg = new JuickMessage(); - jmsg.setUser(user_from); - jmsg.setText(body); - // TODO: add PM payload - //app.events().publishEvent(new JuickMessageEvent(jmsg)); - /* TODO: move to XMPP component - if (jid_to != null) { - Message mm = new Message(); - mm.to = new JID(jid_to); - mm.type = Message.Type.chat; - if (haveInRoster) { - mm.from = new JID(user_from.getUName(), getDomain(), "Juick"); - mm.body = body; - } else { - mm.from = new JID("juick", getDomain(), "Juick"); - mm.body = "Private message from @" + user_from.getUName() + ":\n" + body; - } - return Collections.singletonList(mm); - } - */ - } - if (ret == 200) { - return new ProtocolReply("Private message sent", Optional.empty()); - } else { - return new ProtocolReply("Error " + ret, Optional.empty()); - } - } - - @UserCommand(pattern = "^#(\\d+)(\\+?)$", help = "#1234 - Show message (#1234+ - message with replies)") - public ProtocolReply commandShow(User user, String... arguments) { - boolean showReplies = arguments[1].length() > 0; - int mid; - try { - mid = Integer.parseInt(arguments[0]); - } catch (NumberFormatException e) { - return new ProtocolReply("Error", Optional.empty()); - } - Message msg = MessagesQueries.getMessage(sql, mid); - if (showReplies) { - List replies = MessagesQueries.getReplies(sql, mid); - replies.add(0, msg); - return new ProtocolReply(String.join("\n", - replies.stream().map(Object::toString).collect(Collectors.toList())), - Optional.of(json.serializeList(replies))); - } - return new ProtocolReply(msg.toString(), Optional.of(json.serializeList(Collections.singletonList(msg)))); - } - @UserCommand(pattern = "^(#|\\.)(\\d+)((\\.|\\-|\\/)(\\d+))?\\s([\\s\\S]+)", - help = "#1234 *tag *tag2 - edit tags\n#1234 text - reply to message") - public ProtocolReply EditOrReply(User user, String... args) { - int mid; - try { - mid = Integer.parseInt(args[1]); - } catch (NumberFormatException e) { - return new ProtocolReply("Error", Optional.empty()); - } - int rid; - try { - rid = Integer.parseInt(args[4]); - } catch (NumberFormatException e) { - rid = 0; - } - String txt = args[5]; - List messageTags = TagQueries.fromString(sql, txt, true); - if (messageTags.size() > 0) { - if (user.getUID() != MessagesQueries.getMessageAuthor(sql, mid).getUID()) { - return new ProtocolReply("It is not your message", Optional.empty()); - } - TagQueries.updateTags(sql, mid, messageTags); - return new ProtocolReply("Tags are updated", Optional.empty()); - } else { - int newrid = MessagesQueries.createReply(sql, mid, rid, user.getUID(), txt, null); - return new ProtocolReply("Reply posted.\n#" + mid + "/" + newrid + " " - + baseUri + mid + "/" + newrid, - Optional.of(json.serializeList(Collections.singletonList(MessagesQueries.getReply(sql, mid, newrid))))); - } - } - - @UserCommand(pattern = "^(s|u)\\s+#(\\d+)$", help = "S #1234 - subscribe to comments", - patternFlags = Pattern.CASE_INSENSITIVE) - public ProtocolReply commandSubscribeMessage(User user, String... args) { - boolean subscribe = args[0].equalsIgnoreCase("s"); - int mid; - try { - mid = Integer.parseInt(args[1]); - } catch (NumberFormatException e) { - return new ProtocolReply("Error", Optional.empty()); - } - if (subscribe) { - if (SubscriptionsQueries.subscribeMessage(sql, mid, user.getUID())) { - return new ProtocolReply("Subscribed", Optional.empty()); - } - } else { - if (SubscriptionsQueries.unSubscribeMessage(sql, mid, user.getUID())) { - return new ProtocolReply("Unsubscribed from #" + mid, Optional.empty()); - } - return new ProtocolReply("You was not subscribed to #" + mid, Optional.empty()); - } - return new ProtocolReply("Error", Optional.empty()); - } - @UserCommand(pattern = "^(s|u)\\s+\\@(\\S+)$", help = "S @user - subscribe to user's posts", - patternFlags = Pattern.CASE_INSENSITIVE) - public ProtocolReply commandSubscribeUser(User user, String... args) { - boolean subscribe = args[0].equalsIgnoreCase("s"); - User toUser = UserQueries.getUserByName(sql, args[1]); - if (toUser.getUID() > 0) { - if (subscribe) { - if (SubscriptionsQueries.subscribeUser(sql, user, toUser)) { - return new ProtocolReply("Subscribed", Optional.empty()); - // TODO: notification - // TODO: already subscribed case - } - } else { - if (SubscriptionsQueries.unSubscribeUser(sql, user, toUser)) { - return new ProtocolReply("Unsubscribed from @" + toUser.getUName(), Optional.empty()); - } - return new ProtocolReply("You was not subscribed to @" + toUser.getUName(), Optional.empty()); - } - } - return new ProtocolReply("Error", Optional.empty()); - } - @UserCommand(pattern = "^(s|u)\\s+\\*(\\S+)$", help = "S *tag - subscribe to tag" + - "\nU *tag - unsubscribe from tag", patternFlags = Pattern.CASE_INSENSITIVE) - public ProtocolReply commandSubscribeTag(User user, String... args) { - boolean subscribe = args[0].equalsIgnoreCase("s"); - Tag tag = TagQueries.getTag(sql, args[1], true); - if (subscribe) { - if (SubscriptionsQueries.subscribeTag(sql, user, tag)) { - return new ProtocolReply("Subscribed", Optional.empty()); - } - } else { - if (SubscriptionsQueries.unSubscribeTag(sql, user, tag)) { - return new ProtocolReply("Unsubscribed from " + tag.Name, Optional.empty()); - } - return new ProtocolReply("You was not subscribed to " + tag.Name, Optional.empty()); - } - return new ProtocolReply("Error", Optional.empty()); - } - - @UserCommand(pattern = "^\\s*help\\s*$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "HELP - returns this help message") - public ProtocolReply commandHelp(User user, String[] input) { - List commandsHelp = Arrays.asList(getClass().getDeclaredMethods()).stream() - .filter(m -> m.isAnnotationPresent(UserCommand.class)) - .map(m -> m.getAnnotation(UserCommand.class).help()) - .collect(Collectors.toList()); - return new ProtocolReply(String.join("\n", commandsHelp), Optional.empty()); - } -} diff --git a/src/main/java/com/juick/server/protocol/ProtocolReply.java b/src/main/java/com/juick/server/protocol/ProtocolReply.java deleted file mode 100644 index d9d36a5d..00000000 --- a/src/main/java/com/juick/server/protocol/ProtocolReply.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.juick.server.protocol; - -import java.util.Optional; - -/** - * Created by vitalyster on 08.04.2016. - */ -public class ProtocolReply { - - private Optional json; - private String description; - - public ProtocolReply(String text, Optional json) { - this.description = text; - this.json = json; - } - public String getDescription() { - return description; - } - public Optional getJson() { - return json; - } -} diff --git a/src/main/java/com/juick/server/protocol/annotation/UserCommand.java b/src/main/java/com/juick/server/protocol/annotation/UserCommand.java deleted file mode 100644 index af7c4924..00000000 --- a/src/main/java/com/juick/server/protocol/annotation/UserCommand.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.juick.server.protocol.annotation; - -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 ""; - - /** - * - * @return pattern flags - */ - int patternFlags() default 0; - - /** - * - * @return a string used in HELP command output. Basically, only 1 string - */ - String help() default ""; -} diff --git a/src/main/java/com/juick/xmpp/extensions/JuickMessage.java b/src/main/java/com/juick/xmpp/extensions/JuickMessage.java deleted file mode 100644 index 885b2375..00000000 --- a/src/main/java/com/juick/xmpp/extensions/JuickMessage.java +++ /dev/null @@ -1,184 +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.xmpp.extensions; - -import com.juick.xmpp.utils.XmlUtils; -import com.juick.xmpp.*; -import java.io.IOException; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.TimeZone; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -/** - * - * @author Ugnich Anton - */ -public class JuickMessage extends com.juick.Message implements StanzaChild { - - public final static String XMLNS = "http://juick.com/message"; - public final static String TagName = "juick"; - private SimpleDateFormat df; - - public JuickMessage() { - df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - df.setTimeZone(TimeZone.getTimeZone("UTC")); - } - - public JuickMessage(com.juick.Message msg) { - super(msg); - df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - df.setTimeZone(TimeZone.getTimeZone("UTC")); - } - - @Override - public String getXMLNS() { - return XMLNS; - } - - @Override - public JuickMessage parse(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException { - JuickMessage jmsg = new JuickMessage(); - - final String sMID = parser.getAttributeValue(null, "mid"); - if (sMID != null) { - jmsg.setMID(Integer.parseInt(sMID)); - } - final String sRID = parser.getAttributeValue(null, "rid"); - if (sRID != null) { - jmsg.setRID(Integer.parseInt(sRID)); - } - final String sReplyTo = parser.getAttributeValue(null, "replyto"); - if (sReplyTo != null) { - jmsg.ReplyTo = Integer.parseInt(sReplyTo); - } - final String sPrivacy = parser.getAttributeValue(null, "privacy"); - if (sPrivacy != null) { - jmsg.Privacy = Integer.parseInt(sPrivacy); - } - final String sFriendsOnly = parser.getAttributeValue(null, "friendsonly"); - if (sFriendsOnly != null) { - jmsg.FriendsOnly = true; - } - final String sReadOnly = parser.getAttributeValue(null, "readonly"); - if (sReadOnly != null) { - jmsg.ReadOnly = true; - } - String ts = parser.getAttributeValue(null, "timestamp"); - if (ts != null) { - jmsg.setDate(df.parse(ts)); - } - jmsg.AttachmentType = parser.getAttributeValue(null, "attach"); - - while (parser.next() == XmlPullParser.START_TAG) { - final String tag = parser.getName(); - final String xmlns = parser.getNamespace(); - if (tag.equals("body")) { - jmsg.setText(XmlUtils.getTagText(parser)); - } else if (tag.equals(JuickUser.TagName) && xmlns != null && xmlns.equals(JuickUser.XMLNS)) { - jmsg.setUser(new JuickUser().parse(parser)); - } else if (tag.equals("tag")) { - jmsg.Tags.add(XmlUtils.getTagText(parser)); - } else { - XmlUtils.skip(parser); - } - } - return jmsg; - } - - @Override - public String toString() { - String ret = ""; - - ret = "<" + TagName + " xmlns=\"" + XMLNS + "\""; - if (getMID() > 0) { - ret += " mid=\"" + getMID() + "\""; - } - if (getRID() > 0) { - ret += " rid=\"" + getRID() + "\""; - } - if (ReplyTo > 0) { - ret += " replyto=\"" + ReplyTo + "\""; - } - ret += " privacy=\"" + Privacy + "\""; - if (FriendsOnly) { - ret += " friendsonly=\"1\""; - } - if (ReadOnly) { - ret += " readonly=\"1\""; - } - if (getDate() != null) { - ret += " ts=\"" + df.format(getDate()) + "\""; - } - if (AttachmentType != null) { - ret += " attach=\"" + AttachmentType + "\""; - } - ret += ">"; - if (getUser() != null) { - ret += JuickUser.toString(getUser()); - } - if (getText() != null) { - ret += "" + XmlUtils.escape(getText()) + ""; - } - if (!Tags.isEmpty()) { - for (int i = 0; i < Tags.size(); i++) { - ret += "" + XmlUtils.escape(Tags.get(i)) + ""; - } - } - ret += ""; - - return ret; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof JuickMessage)) { - return false; - } - JuickMessage jmsg = (JuickMessage) obj; - return (this.getMID() == jmsg.getMID() && this.getRID() == jmsg.getRID()); - } - - @Override - public int compareTo(Object obj) throws ClassCastException { - if (!(obj instanceof JuickMessage)) { - throw new ClassCastException(); - } - JuickMessage jmsg = (JuickMessage) obj; - - if (this.getMID() != jmsg.getMID()) { - if (this.getMID() > jmsg.getMID()) { - return -1; - } else { - return 1; - } - } - - if (this.getRID() != jmsg.getRID()) { - if (this.getRID() < jmsg.getRID()) { - return -1; - } else { - return 1; - } - } - - return 0; - } -} diff --git a/src/main/java/com/juick/xmpp/extensions/JuickUser.java b/src/main/java/com/juick/xmpp/extensions/JuickUser.java deleted file mode 100644 index edc6749a..00000000 --- a/src/main/java/com/juick/xmpp/extensions/JuickUser.java +++ /dev/null @@ -1,75 +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.xmpp.extensions; - -import com.juick.xmpp.utils.XmlUtils; -import com.juick.xmpp.*; -import java.io.IOException; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -/** - * - * @author Ugnich Anton - */ -public class JuickUser extends com.juick.User implements StanzaChild { - - public final static String XMLNS = "http://juick.com/user"; - public final static String TagName = "user"; - - public JuickUser() { - } - - public JuickUser(com.juick.User user) { - super(user); - } - - @Override - public String getXMLNS() { - return XMLNS; - } - - @Override - public JuickUser parse(final XmlPullParser parser) throws XmlPullParserException, IOException { - JuickUser juser = new JuickUser(); - String strUID = parser.getAttributeValue(null, "uid"); - if (strUID != null) { - juser.setUID(Integer.parseInt(strUID)); - } - juser.setUName(parser.getAttributeValue(null, "uname")); - XmlUtils.skip(parser); - return juser; - } - - public static String toString(com.juick.User user) { - String str = "<" + TagName + " xmlns='" + XMLNS + "'"; - if (user.getUID() > 0) { - str += " uid='" + user.getUID() + "'"; - } - if (user.getUName() != null && user.getUName().length() > 0) { - str += " uname='" + XmlUtils.escape(user.getUName()) + "'"; - } - str += "/>"; - return str; - } - - @Override - public String toString() { - return toString(this); - } -} diff --git a/src/main/java/com/juick/xmpp/s2s/CacheEntry.java b/src/main/java/com/juick/xmpp/s2s/CacheEntry.java deleted file mode 100644 index 7cdb18ab..00000000 --- a/src/main/java/com/juick/xmpp/s2s/CacheEntry.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.juick.xmpp.s2s; - -/** - * - * @author ugnich - */ -public class CacheEntry { - - public String hostname; - public long tsCreated; - public long tsUpdated; - public String xml; - - public CacheEntry(String hostname, String xml) { - this.hostname = hostname; - this.tsCreated = this.tsUpdated = System.currentTimeMillis(); - this.xml = xml; - } -} diff --git a/src/main/java/com/juick/xmpp/s2s/CleaningUp.java b/src/main/java/com/juick/xmpp/s2s/CleaningUp.java deleted file mode 100644 index 14d97ed8..00000000 --- a/src/main/java/com/juick/xmpp/s2s/CleaningUp.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.juick.xmpp.s2s; - -import java.io.FileNotFoundException; -import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; -import java.util.Iterator; - -/** - * - * @author ugnich - */ -public class CleaningUp implements Runnable { - XMPPComponent xmpp; - - public CleaningUp(XMPPComponent xmpp) { - this.xmpp = xmpp; - } - - @Override - public void run() { - while (true) { - try { - PrintWriter statsFile = new PrintWriter(xmpp.STATSFILE, "UTF-8"); - statsFile.write("

    Threads: " + Thread.activeCount() + "

    "); - statsFile.write("

    Out (" + xmpp.outConnections.size() + ")

    "); - - long now = System.currentTimeMillis(); - - synchronized (xmpp.outConnections) { - for (Iterator i = xmpp.outConnections.iterator(); i.hasNext();) { - ConnectionOut c = i.next(); - int inactive = (int) ((double) (now - c.tsLocalData) / 1000.0); - if (inactive > 900) { - c.closeConnection(); - i.remove(); - } else { - statsFile.write(" "); - statsFile.write(" \n"); - statsFile.write(" \n"); - statsFile.write(" \n"); - statsFile.write(" \n"); - statsFile.write(" \n"); - statsFile.write(" "); - } - } - } - - statsFile.write("
    tosidinactiveout packetsout bytes
    " + c.to + "" + c.streamID + "" + inactive + "" + c.packetsLocal + "" + c.bytesLocal + "

    In (" + xmpp.inConnections.size() + ")

    "); - - synchronized (xmpp.inConnections) { - for (Iterator i = xmpp.inConnections.iterator(); i.hasNext();) { - ConnectionIn c = i.next(); - int inactive = (int) ((double) (now - c.tsRemoteData) / 1000.0); - if (inactive > 900) { - c.closeConnection(); - i.remove(); - } else { - statsFile.write(" "); - if (c.from.isEmpty()) { - statsFile.write(" \n"); - } else if (c.from.size() == 1) { - statsFile.write(" \n"); - } else { - String out = " \n"); - } - statsFile.write(" \n"); - statsFile.write(" \n"); - statsFile.write(" \n"); - statsFile.write(" "); - } - } - } - - statsFile.write("
    fromsidinactivein packets
     " + c.from.get(0) + ""; - for (int n = 0; n < c.from.size(); n++) { - if (n > 0) { - out += "
    "; - } - out += c.from.get(n); - } - statsFile.write(out + "
    " + c.streamID + "" + inactive + "" + c.packetsRemote + "

    Cache (" + xmpp.outCache.size() + ")

    "); - - synchronized (xmpp.outCache) { - for (Iterator i = xmpp.outCache.iterator(); i.hasNext();) { - CacheEntry c = i.next(); - int inactive = (int) ((double) (now - c.tsCreated) / 1000.0); - if (inactive > 600) { - i.remove(); - } else { - statsFile.write(""); - } - } - } - - statsFile.write("
    hostlivesize
    " + c.hostname + "" + inactive + "" + c.xml.length() + "
    "); - statsFile.close(); - - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - } - } catch (FileNotFoundException e) { - } catch (UnsupportedEncodingException e) { - } - } - } -} diff --git a/src/main/java/com/juick/xmpp/s2s/Connection.java b/src/main/java/com/juick/xmpp/s2s/Connection.java deleted file mode 100644 index eae6efaa..00000000 --- a/src/main/java/com/juick/xmpp/s2s/Connection.java +++ /dev/null @@ -1,148 +0,0 @@ -package com.juick.xmpp.s2s; - -import org.xmlpull.mxp1.MXParser; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; -import java.io.*; -import java.net.Socket; -import java.security.*; -import java.security.cert.CertificateException; -import java.util.UUID; -import java.util.logging.Logger; - -/** - * - * @author ugnich - */ -public class Connection { - - protected static final Logger LOGGER = Logger.getLogger(Connection.class.getName()); - - public String streamID; - public long tsCreated = 0; - public long tsLocalData = 0; - public long bytesLocal = 0; - public long packetsLocal = 0; - XMPPComponent xmpp; - 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_STREAM = "http://etherx.jabber.org/streams"; - XmlPullParser parser = new MXParser(); - OutputStreamWriter writer; - private boolean secured = false; - SSLContext sc; - 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 null; - } - } - }; - - - public Connection(XMPPComponent xmpp) throws XmlPullParserException, KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException, KeyManagementException { - this.xmpp = xmpp; - tsCreated = System.currentTimeMillis(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - KeyStore ks = KeyStore.getInstance("JKS"); - try (InputStream ksIs = new FileInputStream(xmpp.keystore)) { - ks.load(ksIs, xmpp.keystorePassword.toCharArray()); - } - - KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory - .getDefaultAlgorithm()); - kmf.init(ks, xmpp.keystorePassword.toCharArray()); - sc = SSLContext.getInstance("TLSv1.2"); - - sc.init(kmf.getKeyManagers(), trustAllCerts, new SecureRandom()); - - } - - 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.fine(tag); - } - - public void sendStanza(String xml) throws IOException { - if (streamID != null) { - LOGGER.fine("OUT: " + xml + "\n"); - } - writer.write(xml); - writer.flush(); - tsLocalData = System.currentTimeMillis(); - bytesLocal += xml.length(); - packetsLocal++; - } - - void closeConnection() { - if (streamID != null) { - LOGGER.info(String.format("CLOSING STREAM %s", streamID)); - } - - try { - writer.write(""); - } catch (Exception e) { - } - - try { - writer.close(); - } catch (Exception e) { - } - - try { - socket.close(); - } catch (Exception e) { - } - } - - static String generateDialbackKey(String to, String from, String id) throws Exception { - Mac hmacSha256 = Mac.getInstance("hmacSHA256"); - - SecretKeySpec secret_key = new SecretKeySpec("$UppPerSeCCret4".getBytes(), "SHA-256"); - hmacSha256.init(secret_key); - byte key[] = hmacSha256.doFinal((to + " " + from + " " + id).getBytes()); - - StringBuilder hexkey = new StringBuilder(); - for (int i = 0; i < key.length; i++) { - hexkey.append(Integer.toHexString(0xFF & key[i])); - } - - return hexkey.toString(); - } - - public boolean isSecured() { - return secured; - } - - public void setSecured(boolean secured) { - this.secured = secured; - } - - public void restartParser() throws XmlPullParserException, IOException { - parser = new MXParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new InputStreamReader(socket.getInputStream())); - writer = new OutputStreamWriter(socket.getOutputStream()); - streamID = UUID.randomUUID().toString(); - } -} diff --git a/src/main/java/com/juick/xmpp/s2s/ConnectionIn.java b/src/main/java/com/juick/xmpp/s2s/ConnectionIn.java deleted file mode 100644 index 345caea9..00000000 --- a/src/main/java/com/juick/xmpp/s2s/ConnectionIn.java +++ /dev/null @@ -1,222 +0,0 @@ -package com.juick.xmpp.s2s; - -import com.juick.xmpp.Iq; -import com.juick.xmpp.JID; -import com.juick.xmpp.Message; -import com.juick.xmpp.Presence; -import com.juick.xmpp.utils.XmlUtils; -import org.xmlpull.v1.XmlPullParser; - -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLSocket; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.net.Socket; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author ugnich - */ -public class ConnectionIn extends Connection implements Runnable { - - private static final Logger LOGGER = Logger.getLogger(ConnectionIn.class.getName()); - - final public List from = new ArrayList<>(); - public long tsRemoteData = 0; - public long packetsRemote = 0; - JuickBot bot; - - public ConnectionIn(XMPPComponent xmpp, JuickBot bot, Socket socket) throws Exception { - super(xmpp); - this.bot = bot; - this.socket = socket; - streamID = UUID.randomUUID().toString(); - restartParser(); - } - - @Override - public void run() { - LOGGER.info("STREAM FROM ? " + streamID + " START"); - 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("STREAM FROM ? " + streamID + " INVALID FIRST PACKET"); - } - boolean xmppversionnew = parser.getAttributeValue(null, "version") != null; - - sendOpenStream(parser.getAttributeValue(null, "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.info("STREAM FROM " + dfrom + " TO " + to + " " + streamID + " ASKING FOR DIALBACK"); - if (dfrom.endsWith(xmpp.HOSTNAME) && (dfrom.equals(xmpp.HOSTNAME) || dfrom.endsWith("." + xmpp.HOSTNAME))) { - break; - } - if (to != null && to.equals(xmpp.HOSTNAME)) { - String dbKey = XmlUtils.getTagText(parser); - updateTsRemoteData(); - - ConnectionOut c = xmpp.getConnectionOut(dfrom, false); - if (c != null) { - c.sendDialbackVerify(streamID, dbKey); - } else { - c = new ConnectionOut(xmpp, dfrom, streamID, dbKey); - xmpp.executorService.submit(c); - } - } else { - throw new HostUnknownException("STREAM FROM " + dfrom + " " + streamID + " INVALID TO " + to); - } - } 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(); - boolean valid = false; - if (vfrom != null && vto != null && vid != null && vkey != null) { - String vkey2 = generateDialbackKey(vfrom, vto, vid); - valid = vkey.equals(vkey2); - } - if (valid) { - sendStanza(""); - LOGGER.info("STREAM FROM " + vfrom + " " + streamID + " DIALBACK VERIFY VALID"); - } else { - sendStanza(""); - LOGGER.warning("STREAM FROM " + vfrom + " " + streamID + " DIALBACK VERIFY INVALID"); - } - } else if (tag.equals("presence") && checkFromTo(parser)) { - Presence p = Presence.parse(parser, null); - if (p != null && (p.type == null || !p.type.equals(Presence.Type.error))) { - bot.incomingPresence(p); - } - } else if (tag.equals("message") && checkFromTo(parser)) { - updateTsRemoteData(); - Message msg = Message.parse(parser, xmpp.childParsers); - if (msg != null && (msg.type == null || !msg.type.equals(Message.Type.error))) { - LOGGER.info("STREAM " + streamID + ": " + msg.toString()); - if (!bot.incomingMessage(msg)) { - xmpp.router.send(msg.toString()); - } - } - } else if (tag.equals("iq") && checkFromTo(parser)) { - updateTsRemoteData(); - String type = parser.getAttributeValue(null, "type"); - String xml = XmlUtils.parseToString(parser, true); - if (type == null || !type.equals(Iq.Type.error)) { - LOGGER.info("STREAM " + streamID + ": " + xml); - xmpp.router.send(xml); - } - } else if (!isSecured() && tag.equals("starttls")) { - LOGGER.info("STREAM " + streamID + " SECURING"); - sendStanza(""); - try { - socket = sc.getSocketFactory().createSocket(socket, socket.getInetAddress().getHostAddress(), - socket.getPort(), true); - ((SSLSocket) socket).setUseClientMode(false); - ((SSLSocket) socket).startHandshake(); - setSecured(true); - LOGGER.info("STREAM " + streamID + " SECURED"); - restartParser(); - } catch (SSLException sex) { - LOGGER.warning("STREAM " + streamID + " SSL ERROR"); - sendStanza(""); - xmpp.removeConnectionIn(this); - closeConnection(); - } - } else if (isSecured() && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) { - sendOpenStream(null, true); - } else { - LOGGER.info("STREAM " + streamID + ": " + XmlUtils.parseToString(parser, true)); - } - } - LOGGER.warning("STREAM " + streamID + " FINISHED"); - xmpp.removeConnectionIn(this); - closeConnection(); - } catch (EOFException ex) { - LOGGER.info(String.format("STREAM %s CLOSED (dirty)", streamID)); - xmpp.removeConnectionIn(this); - closeConnection(); - } catch (HostUnknownException e) { - LOGGER.warning(e.getMessage()); - } catch (Exception e) { - LOGGER.log(Level.WARNING, "STREAM " + streamID + " ERROR", e); - xmpp.removeConnectionIn(this); - closeConnection(); - } - } - - void updateTsRemoteData() { - tsRemoteData = System.currentTimeMillis(); - } - - void sendOpenStream(String from, boolean xmppversionnew) throws IOException { - String openStream = ""; - if (xmppversionnew) { - openStream += ""; - if (!isSecured() && !xmpp.brokenSSLhosts.contains(from)) { - openStream += ""; - } - openStream += ""; - } - sendStanza(openStream); - } - - public void sendDialbackResult(String sfrom, String type) { - try { - sendStanza(""); - if (type.equals("valid")) { - from.add(sfrom); - LOGGER.info("STREAM FROM " + sfrom + " " + streamID + " READY"); - } - } catch (IOException e) { - LOGGER.warning("STREAM FROM " + sfrom + " " + streamID + " ERROR: " + e.toString()); - } - } - - boolean checkFromTo(XmlPullParser parser) throws Exception { - String cfrom = parser.getAttributeValue(null, "from"); - String cto = parser.getAttributeValue(null, "to"); - if (cfrom != null && cto != null && !cfrom.isEmpty() && !cto.isEmpty()) { - JID jidto = new JID(cto); - if (jidto.Host != null && jidto.Username != null && jidto.Host.equals(xmpp.HOSTNAME) && jidto.Username.matches("^[a-zA-Z0-9\\-]{2,16}$")) { - JID jidfrom = new JID(cfrom); - int size = from.size(); - for (int i = 0; i < size; i++) { - if (from.get(i).equals(jidfrom.Host)) { - return true; - } - } - } - } - return false; - } - class HostUnknownException extends Exception { - public HostUnknownException(String message) { - super(message); - } - } -} diff --git a/src/main/java/com/juick/xmpp/s2s/ConnectionOut.java b/src/main/java/com/juick/xmpp/s2s/ConnectionOut.java deleted file mode 100644 index e604a5b4..00000000 --- a/src/main/java/com/juick/xmpp/s2s/ConnectionOut.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.juick.xmpp.s2s; - -import com.juick.xmpp.extensions.StreamFeatures; -import com.juick.xmpp.utils.XmlUtils; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLSocket; -import java.io.EOFException; -import java.io.IOException; -import java.net.Socket; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.util.UUID; -import java.util.logging.Level; - -/** - * @author ugnich - */ -public class ConnectionOut extends Connection implements Runnable { - - public boolean streamReady = false; - public String to; - String checkSID = null; - String dbKey = null; - - public ConnectionOut(XMPPComponent xmpp, String hostname) throws CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException, XmlPullParserException, KeyManagementException, KeyStoreException, IOException { - super(xmpp); - to = hostname; - } - - public ConnectionOut(XMPPComponent xmpp, String hostname, String checkSID, String dbKey) throws Exception { - super(xmpp); - to = hostname; - this.checkSID = checkSID; - this.dbKey = dbKey; - streamID = UUID.randomUUID().toString(); - } - - void sendOpenStream() throws IOException { - sendStanza(""); - } - - void processDialback() throws Exception { - if (checkSID != null) { - sendDialbackVerify(checkSID, dbKey); - } - sendStanza("" + - generateDialbackKey(to, xmpp.HOSTNAME, streamID) + ""); - } - - @Override - public void run() { - LOGGER.info("STREAM TO " + to + " START"); - try { - socket = new Socket(); - socket.connect(DNSQueries.getServerAddress(to)); - restartParser(); - - 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.info("STREAM TO " + to + " " + streamID + " OPEN"); - xmpp.addConnectionOut(ConnectionOut.this); - boolean xmppversionnew = parser.getAttributeValue(null, "version") != null; - if (!xmppversionnew) { - processDialback(); - } - - while (parser.next() != XmlPullParser.END_DOCUMENT) { - if (parser.getEventType() != XmlPullParser.START_TAG) { - continue; - } - logParser(); - - 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; - LOGGER.info("STREAM TO " + to + " " + streamID + " READY"); - - String cache = xmpp.getFromCache(to); - if (cache != null) { - LOGGER.info("STREAM TO " + to + " " + streamID + " SENDING CACHE"); - sendStanza(cache); - } - - } else { - LOGGER.info("STREAM TO " + to + " " + streamID + " DIALBACK FAIL"); - } - 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"); - if (from != null && from.equals(to) && sid != null && !sid.isEmpty() && type != null) { - ConnectionIn c = xmpp.getConnectionIn(sid); - if (c != null) { - c.sendDialbackResult(from, type); - } - } - XmlUtils.skip(parser); - } else if (tag.equals("features") && parser.getNamespace().equals(NS_STREAM)) { - StreamFeatures features = StreamFeatures.parse(parser); - if (!isSecured() && features.STARTTLS >= 0 && !xmpp.brokenSSLhosts.contains(to)) { - System.out.println("STREAM TO " + to + " " + streamID + " SECURING"); - sendStanza(""); - } else { - processDialback(); - } - } else if (tag.equals("proceed") && parser.getNamespace().equals(NS_TLS)) { - try { - socket = sc.getSocketFactory().createSocket(socket, socket.getInetAddress().getHostAddress(), - socket.getPort(), true); - ((SSLSocket) socket).startHandshake(); - setSecured(true); - System.out.println("STREAM " + streamID + " SECURED"); - restartParser(); - sendOpenStream(); - } catch (SSLException sex) { - LOGGER.log(Level.SEVERE, String.format("s2s ssl error: %s %s", to, streamID), sex); - sendStanza(""); - xmpp.removeConnectionOut(this); - closeConnection(); - } - } else if (isSecured() && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) { - streamID = parser.getAttributeValue(null, "id"); - } else { - LOGGER.info("STREAM TO " + to + " " + streamID + ": " + XmlUtils.parseToString(parser, true)); - } - } - - LOGGER.warning("STREAM TO " + to + " " + streamID + " FINISHED"); - xmpp.removeConnectionOut(ConnectionOut.this); - closeConnection(); - } catch (EOFException eofex) { - LOGGER.info(String.format("STREAM %s %s CLOSED (dirty)", to, streamID)); - xmpp.removeConnectionOut(ConnectionOut.this); - closeConnection(); - } catch (Exception e) { - LOGGER.log(Level.SEVERE, String.format("s2s out exception: %s %s", to, streamID), e); - xmpp.removeConnectionOut(ConnectionOut.this); - closeConnection(); - } - } - - public void sendDialbackVerify(String sid, String key) { - try { - sendStanza("" + key + ""); - } catch (IOException e) { - LOGGER.log(Level.WARNING, "STREAM TO " + to + " " + streamID + " ERROR", e); - } - } -} diff --git a/src/main/java/com/juick/xmpp/s2s/DNSQueries.java b/src/main/java/com/juick/xmpp/s2s/DNSQueries.java deleted file mode 100644 index e4c5f085..00000000 --- a/src/main/java/com/juick/xmpp/s2s/DNSQueries.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.juick.xmpp.s2s; - -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -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 Random rand = new Random(); - - public static InetSocketAddress getServerAddress(String hostname) throws UnknownHostException { - - String host = hostname; - int port = 5269; - - try { - Hashtable env = new Hashtable(5); - env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory"); - 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()); - try { - String srv[] = att.get(i).toString().split(" "); - port = Integer.parseInt(srv[2]); - host = srv[3]; - } catch (Exception e) { - } - } - - ctx.close(); - } catch (NamingException e) { - } - - return new InetSocketAddress(host, port); - } -} diff --git a/src/main/java/com/juick/xmpp/s2s/JuickBot.java b/src/main/java/com/juick/xmpp/s2s/JuickBot.java deleted file mode 100644 index f0b71689..00000000 --- a/src/main/java/com/juick/xmpp/s2s/JuickBot.java +++ /dev/null @@ -1,378 +0,0 @@ -package com.juick.xmpp.s2s; - -import com.juick.User; -import com.juick.server.PMQueries; -import com.juick.server.TagQueries; -import com.juick.server.UserQueries; -import com.juick.xmpp.JID; -import com.juick.xmpp.Message; -import com.juick.xmpp.Presence; -import com.juick.xmpp.extensions.Error; -import com.juick.xmpp.extensions.JuickMessage; - -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * - * @author ugnich - */ -public class JuickBot { - XMPPComponent xmpp; - public JuickBot(XMPPComponent xmpp, JID JuickJID) { - this.xmpp = xmpp; - this.JuickJID = JuickJID; - } - - public final JID JuickJID; - private static final String HELPTEXT = - "@username text - Send private message\n" - + "*tagname Blah-blah-blah - Post a message with tag 'tagname'\n" - + "#1234 Blah-blah-blah - Answer to message #1234\n" - + "#1234/5 Blah - Answer to reply #1234/5\n" - + "! #1234 - Recommend post\n" - + "\n" - + "# - Show last messages from your feed (## - second page, ...)\n" - + "@ - Show recomendations and popular personal blogs\n" - + "* - Show your tags\n" - + "#1234 - Show message\n" - + "#1234+ - Show message with replies\n" - + "@username - Show user's info\n" - + "@username+ - Show user's info and last 10 messages\n" - + "@username *tag - User's messages with this tag\n" - + "*tag - Show last 10 messages with this tag\n" - + "? blah - Search posts for 'blah'\n" - + "? @username blah - Searching among user\'s posts for 'blah'\n" - + "D #123 - Delete message\n" - + "D #123/45 - Delete reply\n" - + "DL - Delete last message/reply\n" - + "S - Show your subscriptions\n" - + "S #123 - Subscribe to message replies\n" - + "S @username - Subscribe to user's blog\n" - + "U #123 - Unsubscribe from comments\n" - + "U @username - Unsubscribe from user's blog\n" - + "BL - Show your blacklist\n" - + "BL @username - Add/delete user to/from your blacklist\n" - + "BL *tag - Add/delete tag to/from your blacklist\n" - + "ON / OFF - Enable/disable subscriptions delivery\n" - + "PING - Pong\n" - + "\n" - + "Read more: http://juick.com/help/"; - - public boolean incomingPresence(Presence p) throws Exception { - final String username = p.to.Username.toLowerCase(); - final boolean toJuick = username.equals("juick"); - - if (p.type == null) { - Presence reply = new Presence(); - reply.from = new JID(p.to.Username, p.to.Host, null); - reply.to = new JID(p.from.Username, p.from.Host, null); - reply.type = Presence.Type.unsubscribe; - xmpp.sendOut(reply); - return true; - } else if (p.type.equals(Presence.Type.probe)) { - int uid_to = 0; - if (!toJuick) { - uid_to = UserQueries.getUIDbyName(xmpp.sql, username); - } - - if (toJuick || uid_to > 0) { - Presence reply = new Presence(); - reply.from = p.to; - reply.from.Resource = "Juick"; - reply.to = p.from; - reply.priority = 10; - xmpp.sendOut(reply); - } else { - Presence reply = new Presence(p.to, p.from, Presence.Type.error); - reply.id = p.id; - reply.addChild(new Error(Error.Type.cancel, "item-not-found")); - xmpp.sendOut(reply); - return true; - } - return true; - } else if (p.type.equals(Presence.Type.subscribe)) { - boolean canSubscribe = false; - if (toJuick) { - canSubscribe = true; - } else { - int uid_to = UserQueries.getUIDbyName(xmpp.sql, username); - if (uid_to > 0) { - PMQueries.addPMinRoster(xmpp.sql, uid_to, p.from.Bare()); - canSubscribe = true; - } - } - - if (canSubscribe) { - Presence reply = new Presence(p.to, p.from, Presence.Type.subscribed); - xmpp.sendOut(reply); - - reply.from.Resource = "Juick"; - reply.priority = 10; - reply.type = null; - xmpp.sendOut(reply); - - return true; - } else { - Presence reply = new Presence(p.to, p.from, Presence.Type.error); - reply.id = p.id; - reply.addChild(new Error(Error.Type.cancel, "item-not-found")); - xmpp.sendOut(reply); - return true; - } - } else if (p.type.equals(Presence.Type.unsubscribe)) { - if (!toJuick) { - int uid_to = UserQueries.getUIDbyName(xmpp.sql, username); - if (uid_to > 0) { - PMQueries.removePMinRoster(xmpp.sql, uid_to, p.from.Bare()); - } - } - - Presence reply = new Presence(p.to, p.from, Presence.Type.unsubscribed); - xmpp.sendOut(reply); - } - - return false; - } - - public boolean incomingMessage(Message msg) throws Exception { - if (msg.body == null || msg.body.isEmpty()) { - return true; - } - - String username = msg.to.Username.toLowerCase(); - - User user_from = null; - String signuphash = ""; - user_from = UserQueries.getUserByJID(xmpp.sql, msg.from.Bare()); - if (user_from == null) { - signuphash = UserQueries.getSignUpHashByJID(xmpp.sql, msg.from.Bare()); - } - - if (user_from == null) { - Message reply = new Message(msg.to, msg.from, Message.Type.chat); - if (username.equals("juick")) { - reply.body = "Для того, чтобы начать пользоваться сервисом, пожалуйста пройдите быструю регистрацию: 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."; - } else { - reply.body = "Внимание, системное сообщение!\nВаш JabberID не обнаружен в списке доверенных. Для того, чтобы отправить сообщение пользователю " + username + "@juick.com, пожалуйста зарегистрируйте свой JabberID в системе: http://juick.com/signup?type=xmpp&hash=" + signuphash + "\nЕсли у вас уже есть учетная запись на Juick, вы сможете присоединить этот JabberID к ней.\n\nWarning, system message!\nYour JabberID is not found in our server's white list. To send a message to " + username + "@juick.com, 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."; - } - xmpp.sendOut(reply); - return true; - } - - if (username.equals("juick")) { - return incomingMessageJuick(user_from, msg); - } - - int uid_to = UserQueries.getUIDbyName(xmpp.sql, username); - - if (uid_to == 0) { - Message reply = new Message(msg.to, msg.from, Message.Type.error); - reply.id = msg.id; - reply.addChild(new Error(Error.Type.cancel, "item-not-found")); - xmpp.sendOut(reply); - return true; - } - - boolean success = false; - if (!UserQueries.isInBLAny(xmpp.sql, uid_to, user_from.getUID())) { - success = PMQueries.createPM(xmpp.sql, user_from.getUID(), uid_to, msg.body); - } - - if (success) { - Message m = new Message(); - m.from = new JID("juick", "juick.com", null); - m.to = new JID(Integer.toString(uid_to), "push.juick.com", null); - JuickMessage jmsg = new JuickMessage(); - jmsg.setUser(user_from); - jmsg.setText(msg.body); - m.childs.add(jmsg); - xmpp.router.send(m.toString()); - - m.to.Host = "ws.juick.com"; - xmpp.router.send(m.toString()); - - List jids; - boolean inroster = false; - jids = UserQueries.getJIDsbyUID(xmpp.sql, uid_to); - for (String jid : jids) { - Message mm = new Message(); - mm.to = new JID(jid); - mm.type = Message.Type.chat; - inroster = PMQueries.havePMinRoster(xmpp.sql, user_from.getUID(), jid); - if (inroster) { - mm.from = new JID(jmsg.getUser().getUName(), "juick.com", "Juick"); - mm.body = msg.body; - } else { - mm.from = new JID("juick", "juick.com", "Juick"); - mm.body = "Private message from @" + jmsg.getUser().getUName() + ":\n" + msg.body; - } - xmpp.sendOut(mm); - } - } else { - Message reply = new Message(msg.to, msg.from, Message.Type.error); - reply.id = msg.id; - reply.addChild(new Error(Error.Type.cancel, "not-allowed")); - xmpp.sendOut(reply); - } - - return true; - } - private static Pattern regexPM = Pattern.compile("^\\@(\\S+)\\s+([\\s\\S]+)$"); - - public boolean incomingMessageJuick(User user_from, Message msg) throws Exception { - String command = msg.body.trim(); - int commandlen = command.length(); - - // COMPATIBILITY - if (commandlen > 7 && command.substring(0, 3).equalsIgnoreCase("PM ")) { - command = command.substring(3).trim(); - commandlen = command.length(); - } - - if (commandlen == 4) { - if (command.equalsIgnoreCase("PING")) { - commandPing(msg); - return true; - } else if (command.equalsIgnoreCase("HELP")) { - commandHelp(msg); - return true; - } - } else if (commandlen == 5 && command.equalsIgnoreCase("LOGIN")) { - commandLogin(msg, user_from); - return true; - } else if (command.charAt(0) == '@') { - Matcher matchPM = regexPM.matcher(command); - if (matchPM.find()) { - String user_to = matchPM.group(1); - String msgtxt = matchPM.group(2); - commandPM(msg, user_from, user_to, msgtxt); - return true; - } - } else if (commandlen == 2 && command.equalsIgnoreCase("BL")) { - commandBLShow(msg, user_from); - return true; - } - - return false; - } - - private void commandPing(Message m) throws Exception { - Presence p = new Presence(JuickJID, m.from); - p.priority = 10; - xmpp.sendOut(p); - - Message reply = new Message(JuickJID, m.from, Message.Type.chat); - reply.body = "PONG"; - xmpp.sendOut(reply); - } - - private void commandHelp(Message m) throws Exception { - Message reply = new Message(JuickJID, m.from, Message.Type.chat); - reply.body = HELPTEXT; - xmpp.sendOut(reply); - } - - private void commandLogin(Message m, User user_from) throws Exception { - Message reply = new Message(JuickJID, m.from, Message.Type.chat); - reply.body = "http://juick.com/login?" + UserQueries.getHashByUID(xmpp.sql, user_from.getUID()); - xmpp.sendOut(reply); - } - - private void commandPM(Message m, User user_from, String user_to, String body) throws Exception { - int ret = 0; - - int uid_to = 0; - List jids_to = null; - boolean haveInRoster = false; - - if (user_to.indexOf('@') > 0) { - uid_to = UserQueries.getUIDbyJID(xmpp.sql, user_to); - } else { - uid_to = UserQueries.getUIDbyName(xmpp.sql, user_to); - } - - if (uid_to > 0) { - if (!UserQueries.isInBLAny(xmpp.sql, uid_to, user_from.getUID())) { - if (PMQueries.createPM(xmpp.sql, user_from.getUID(), uid_to, body)) { - jids_to = UserQueries.getJIDsbyUID(xmpp.sql, uid_to); - ret = 200; - } else { - ret = 500; - } - } else { - ret = 403; - } - } else { - ret = 404; - } - - if (ret == 200) { - Message msg = new Message(); - msg.from = new JID("juick", "juick.com", null); - msg.to = new JID(Integer.toString(uid_to), "push.juick.com", null); - JuickMessage jmsg = new JuickMessage(); - jmsg.setUser(user_from); - jmsg.setText(body); - msg.childs.add(jmsg); - xmpp.router.send(msg.toString()); - - msg.to.Host = "ws.juick.com"; - xmpp.router.send(msg.toString()); - - for (String jid : jids_to) { - Message mm = new Message(); - mm.to = new JID(jid); - mm.type = Message.Type.chat; - haveInRoster = PMQueries.havePMinRoster(xmpp.sql, user_from.getUID(), jid); - if (haveInRoster) { - mm.from = new JID(user_from.getUName(), "juick.com", "Juick"); - mm.body = body; - } else { - mm.from = new JID("juick", "juick.com", "Juick"); - mm.body = "Private message from @" + user_from.getUName() + ":\n" + body; - } - xmpp.sendOut(mm); - } - } - - Message reply = new Message(m.to, m.from); - if (ret == 200) { - reply.type = m.type; - reply.body = "Private message sent"; - } else { - reply.type = Message.Type.error; - reply.body = "Error " + ret; - } - xmpp.sendOut(reply); - } - - private void commandBLShow(Message m, User user_from) throws Exception { - List blusers = UserQueries.getUserBLUsers(xmpp.sql, user_from.getUID()); - List bltags = TagQueries.getUserBLTags(xmpp.sql, user_from.getUID()); - - String txt = ""; - 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.getUName() + "\n"; - } - } - if (txt.isEmpty()) { - txt = "You don't have any users or tags in your blacklist."; - } - - Message reply = new Message(JuickJID, m.from, Message.Type.chat); - reply.body = txt; - xmpp.sendOut(reply); - } -} diff --git a/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java b/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java deleted file mode 100644 index d0b231e2..00000000 --- a/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java +++ /dev/null @@ -1,443 +0,0 @@ -package com.juick.xmpp.s2s; - -import com.juick.User; -import com.juick.server.MessagesQueries; -import com.juick.server.SubscriptionsQueries; -import com.juick.server.UserQueries; -import com.juick.xmpp.*; -import com.juick.xmpp.extensions.JuickMessage; -import com.juick.xmpp.extensions.Nickname; -import com.juick.xmpp.extensions.XOOB; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.datasource.DriverManagerDataSource; -import org.xmlpull.v1.XmlPullParserException; - -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.util.*; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author ugnich - */ -public class XMPPComponent implements ServletContextListener, Stream.StreamListener, - Message.MessageListener, Iq.IqListener, Presence.PresenceListener { - - private static final Logger logger = Logger.getLogger(XMPPComponent.class.getName()); - - public final ExecutorService executorService = Executors.newCachedThreadPool(); - StreamComponent router; - JuickBot bot; - - public String HOSTNAME, componentName; - public String STATSFILE = null; - public String keystore; - public String keystorePassword; - public List brokenSSLhosts; - final List inConnections = Collections.synchronizedList(new ArrayList<>()); - final List outConnections = Collections.synchronizedList(new ArrayList<>()); - final List outCache = Collections.synchronizedList(new ArrayList<>()); - JdbcTemplate sql; - final public HashMap childParsers = new HashMap<>(); - - public void addConnectionIn(ConnectionIn c) { - synchronized (inConnections) { - inConnections.add(c); - } - } - - public void addConnectionOut(ConnectionOut c) { - synchronized (outConnections) { - outConnections.add(c); - } - } - - public void removeConnectionIn(ConnectionIn c) { - synchronized (inConnections) { - inConnections.remove(c); - } - } - - public void removeConnectionOut(ConnectionOut c) { - synchronized (outConnections) { - outConnections.remove(c); - } - } - - public String getFromCache(String hostname) { - CacheEntry ret = null; - synchronized (outCache) { - for (Iterator i = outCache.iterator(); i.hasNext();) { - CacheEntry c = i.next(); - if (c.hostname != null && c.hostname.equals(hostname)) { - ret = c; - i.remove(); - break; - } - } - } - return (ret != null) ? ret.xml : null; - } - - public ConnectionOut getConnectionOut(String hostname, boolean needReady) { - synchronized (outConnections) { - for (ConnectionOut c : outConnections) { - if (c.to != null && c.to.equals(hostname) && (!needReady || c.streamReady)) { - return c; - } - } - } - return null; - } - - public ConnectionIn getConnectionIn(String streamID) { - synchronized (inConnections) { - for (ConnectionIn c : inConnections) { - if (c.streamID != null && c.streamID.equals(streamID)) { - return c; - } - } - } - return null; - } - - public void sendOut(Stanza s) { - sendOut(s.to.Host, s.toString()); - } - - public void sendOut(String hostname, String xml) { - boolean haveAnyConn = false; - - ConnectionOut connOut = null; - synchronized (outConnections) { - for (ConnectionOut c : outConnections) { - if (c.to != null && c.to.equals(hostname)) { - if (c.streamReady) { - connOut = c; - break; - } else { - haveAnyConn = true; - break; - } - } - } - } - if (connOut != null) { - try { - connOut.sendStanza(xml); - } catch (IOException e) { - logger.warning("STREAM TO " + connOut.to + " " + connOut.streamID + " ERROR: " + e.toString()); - } - return; - } - - boolean haveCache = false; - synchronized (outCache) { - for (CacheEntry c : outCache) { - if (c.hostname != null && c.hostname.equals(hostname)) { - c.xml += xml; - c.tsUpdated = System.currentTimeMillis(); - haveCache = true; - break; - } - } - if (!haveCache) { - outCache.add(new CacheEntry(hostname, xml)); - } - } - - if (!haveAnyConn) { - try { - ConnectionOut connectionOut = new ConnectionOut(this, hostname); - executorService.submit(connectionOut); - } catch (CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | XmlPullParserException | KeyStoreException | KeyManagementException | IOException e) { - logger.log(Level.SEVERE, "s2s out error", e); - } - } - } - - @Override - public void contextInitialized(ServletContextEvent sce) { - - logger.info("component initialized"); - Properties conf = new Properties(); - try { - conf.load(sce.getServletContext().getResourceAsStream("/WEB-INF/juick.conf")); - HOSTNAME = conf.getProperty("hostname"); - componentName = conf.getProperty("componentname"); - JID Jid = new JID(conf.getProperty("xmppbot_jid")); - STATSFILE = conf.getProperty("statsfile"); - keystore = conf.getProperty("keystore"); - keystorePassword = conf.getProperty("keystore_password"); - brokenSSLhosts = Arrays.asList(conf.getProperty("broken_ssl_hosts", "").split(",")); - DriverManagerDataSource dataSource = new DriverManagerDataSource(); - dataSource.setDriverClassName(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver")); - dataSource.setUrl(conf.getProperty("datasource_url")); - sql = new JdbcTemplate(dataSource); - bot = new JuickBot(this, Jid); - - childParsers.put(JuickMessage.XMLNS, new JuickMessage()); - - executorService.submit(() -> { - Socket routerSocket = null; - try { - routerSocket = new Socket("localhost", 5347); - router = new StreamComponent(new JID("s2s"), routerSocket.getInputStream(), routerSocket.getOutputStream(), conf.getProperty("xmpp_password")); - router.addChildParser(new JuickMessage()); - router.addListener((Stream.StreamListener) this); - router.addListener((Message.MessageListener) this); - router.addListener((Iq.IqListener) this); - router.startParsing(); - } catch (IOException e) { - logger.log(Level.SEVERE, "router error", e); - } - }); - executorService.submit(() -> { - final ServerSocket listener = new ServerSocket(5269); - logger.info("s2s listener ready"); - while (true) { - try { - Socket socket = listener.accept(); - ConnectionIn client = new ConnectionIn(this, bot, socket); - addConnectionIn(client); - executorService.submit(client); - } catch (Exception e) { - logger.log(Level.SEVERE, "s2s error", e); - } - } - }); - executorService.submit(new CleaningUp(this)); - - } catch (Exception e) { - logger.log(Level.SEVERE, "XMPPComponent error", e); - } - } - - - - @Override - public void contextDestroyed(ServletContextEvent sce) { - synchronized (outConnections) { - for (Iterator i = outConnections.iterator(); i.hasNext();) { - ConnectionOut c = i.next(); - c.closeConnection(); - i.remove(); - } - } - - synchronized (inConnections) { - for (Iterator i = inConnections.iterator(); i.hasNext();) { - ConnectionIn c = i.next(); - c.closeConnection(); - i.remove(); - } - } - - try { - closeRouterConnection(); - } catch (IOException e) { - logger.log(Level.WARNING, "router warning", e); - } - executorService.shutdown(); - logger.info("component destroyed"); - } - public void closeRouterConnection() throws IOException { - router.logoff(); - } - - public void sendJuickMessage(JuickMessage jmsg) { - List jids = new ArrayList<>(); - - if (jmsg.FriendsOnly) { - jids = SubscriptionsQueries.getJIDSubscribedToUser(sql, jmsg.getUser().getUID(), jmsg.FriendsOnly); - } else { - List users = SubscriptionsQueries.getSubscribedUsers(sql, jmsg.getUser().getUID(), jmsg.getMID()); - for (User user : users) { - for (String jid : UserQueries.getJIDsbyUID(sql, user.getUID())) { - jids.add(jid); - } - } - } - - String txt = "@" + jmsg.getUser().getUName() + ":" + jmsg.getTagsString() + "\n"; - String attachment = jmsg.getAttachmentURL(); - if (attachment != null) { - txt += attachment + "\n"; - } - txt += jmsg.getText() + "\n\n"; - txt += "#" + jmsg.getMID() + " http://juick.com/" + jmsg.getMID(); - - Nickname nick = new Nickname(); - nick.Nickname = "@" + jmsg.getUser().getUName(); - - com.juick.xmpp.Message msg = new com.juick.xmpp.Message(); - msg.from = bot.JuickJID; - msg.body = txt; - msg.type = Message.Type.chat; - msg.thread = "juick-" + jmsg.getMID(); - msg.addChild(jmsg); - msg.addChild(nick); - if (attachment != null) { - XOOB oob = new XOOB(); - oob.URL = attachment; - msg.addChild(oob); - } - - for (String jid : jids) { - msg.to = new JID(jid); - sendOut(msg); - } - } - - public void sendJuickComment(JuickMessage jmsg) { - List users; - String replyQuote; - String replyTo; - - users = SubscriptionsQueries.getUsersSubscribedToComments(sql, jmsg.getMID(), jmsg.getUser().getUID()); - com.juick.Message replyMessage = jmsg.ReplyTo > 0 ? MessagesQueries.getReply(sql, jmsg.getMID(), jmsg.ReplyTo) - : MessagesQueries.getMessage(sql, jmsg.getMID()); - replyTo = replyMessage.getUser().getUName(); - replyQuote = getReplyQuote(replyMessage); - - String txt = "Reply by @" + jmsg.getUser().getUName() + ":\n" + replyQuote + "\n@" + replyTo + " "; - String attachment = jmsg.getAttachmentURL(); - if (attachment != null) { - txt += attachment + "\n"; - } - txt += jmsg.getText() + "\n\n" + "#" + jmsg.getMID() + "/" + jmsg.getRID() + " http://juick.com/" + jmsg.getMID() + "#" + jmsg.getRID(); - - com.juick.xmpp.Message msg = new com.juick.xmpp.Message(); - msg.from = bot.JuickJID; - msg.body = txt; - msg.type = Message.Type.chat; - msg.addChild(jmsg); - for (User user : users) { - for (String jid : UserQueries.getJIDsbyUID(sql, user.getUID())) { - msg.to = new JID(jid); - sendOut(msg); - } - } - } - - private String getReplyQuote(com.juick.Message q) { - String quote = q.getText(); - if (quote.length() > 50) { - quote = ">" + quote.substring(0, 47).replace('\n', ' ') + "...\n"; - } else if (quote.length() > 0) { - quote = ">" + quote.replace('\n', ' ') + "\n"; - } - return quote; - } - - public void sendJuickRecommendation(JuickMessage recomm) { - List users; - JuickMessage jmsg; - jmsg = new JuickMessage(MessagesQueries.getMessage(sql, recomm.getMID())); - users = SubscriptionsQueries.getUsersSubscribedToUserRecommendations(sql, - recomm.getUser().getUID(), recomm.getMID(), jmsg.getUser().getUID()); - - String txt = "Recommended by @" + recomm.getUser().getUName() + ":\n"; - txt += "@" + jmsg.getUser().getUName() + ":" + jmsg.getTagsString() + "\n"; - String attachment = jmsg.getAttachmentURL(); - if (attachment != null) { - txt += attachment + "\n"; - } - txt += jmsg.getText() + "\n\n"; - txt += "#" + jmsg.getMID(); - if (jmsg.Replies > 0) { - if (jmsg.Replies % 10 == 1 && jmsg.Replies % 100 != 11) { - txt += " (" + jmsg.Replies + " reply)"; - } else { - txt += " (" + jmsg.Replies + " replies)"; - } - } - txt += " http://juick.com/" + jmsg.getMID(); - - Nickname nick = new Nickname(); - nick.Nickname = "@" + jmsg.getUser().getUName(); - - com.juick.xmpp.Message msg = new com.juick.xmpp.Message(); - msg.from = bot.JuickJID; - msg.body = txt; - msg.type = Message.Type.chat; - msg.thread = "juick-" + jmsg.getMID(); - msg.addChild(jmsg); - msg.addChild(nick); - if (attachment != null) { - XOOB oob = new XOOB(); - oob.URL = attachment; - msg.addChild(oob); - } - - for (User user : users) { - for (String jid : UserQueries.getJIDsbyUID(sql, user.getUID())) { - msg.to = new JID(jid); - sendOut(msg); - } - } - } - - @Override - public boolean onIq(Iq iq) { - JID jid = iq.to; - if (!jid.Host.equals(componentName)) { - logger.info("STREAM ROUTER (IQ): " + iq.toString()); - sendOut(iq); - } - return false; - } - - @Override - public void onMessage(Message xmsg) { - logger.info("STREAM ROUTER (PROCESS): " + xmsg.toString()); - JuickMessage jmsg = (JuickMessage) xmsg.getChild(JuickMessage.XMLNS); - JID jid = xmsg.to; - if (jid.Host.equals(componentName)) { - if (jmsg != null) { - if (jid.Username != null && jid.Username.equals("recomm")) { - sendJuickRecommendation(jmsg); - } else { - if (jmsg.getRID() > 0) { - sendJuickComment(jmsg); - } else if (jmsg.getMID() > 0) { - sendJuickMessage(jmsg); - } - } - } - } else { - sendOut(xmsg); - } - } - - @Override - public void onPresence(Presence presence) { - JID jid = presence.to; - if (!jid.Host.equals(componentName)) { - logger.info("STREAM ROUTER (PRESENCE): " + presence.toString()); - sendOut(presence); - } - } - - @Override - public void onStreamReady() { - logger.info("STREAM ROUTER (READY)"); - } - - @Override - public void onStreamFail(Exception ex) { - logger.log(Level.SEVERE, "STREAM ROUTER (FAIL)", ex); - } -} diff --git a/src/main/java/ru/sape/Sape.java b/src/main/java/ru/sape/Sape.java deleted file mode 100644 index c00054ae..00000000 --- a/src/main/java/ru/sape/Sape.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * http://code.google.com/p/javasape/ - */ -package ru.sape; - -import javax.servlet.http.Cookie; - -public class Sape { - - private final String sapeUser; - private final SapeConnection sapePageLinkConnection; - - public Sape(String sapeUser, String host, int socketTimeout, int cacheLifeTime) { - this.sapeUser = sapeUser; - - this.sapePageLinkConnection = new SapeConnection( - "/code.php?user=" + sapeUser + "&host=" + host, - "SAPE_Client PHP", socketTimeout, cacheLifeTime); - } - public boolean debug = false; - - public SapePageLinks getPageLinks(String requestUri, Cookie[] cookies) { - return new SapePageLinks(sapePageLinkConnection, sapeUser, requestUri, cookies, debug); - } -} diff --git a/src/main/java/ru/sape/SapeConnection.java b/src/main/java/ru/sape/SapeConnection.java deleted file mode 100644 index 8c794b08..00000000 --- a/src/main/java/ru/sape/SapeConnection.java +++ /dev/null @@ -1,107 +0,0 @@ -package ru.sape; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringWriter; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class SapeConnection { - - private final String version = "1.0.3"; - private final List serverList = Arrays.asList("dispenser-01.sape.ru", "dispenser-02.sape.ru"); - private final String dispenserPath; - private final String userAgent; - private final int socketTimeout; - private final int cacheLifeTime; - - public SapeConnection(String dispenserPath, String userAgent, int socketTimeout, int cacheLifeTime) { - this.dispenserPath = dispenserPath; - this.userAgent = userAgent; - this.socketTimeout = socketTimeout; - this.cacheLifeTime = cacheLifeTime; - } - - protected String fetchRemoteFile(String host, String path) throws IOException { - Reader r = null; - - try { - HttpURLConnection connection = (HttpURLConnection) ((new URL(("http://" + host + path)).openConnection())); - - if (socketTimeout > 0) { - connection.setConnectTimeout(socketTimeout); - connection.setReadTimeout(socketTimeout); - } - - connection.addRequestProperty("User-Agent", userAgent + ' ' + version); - - connection.setDoOutput(true); - connection.setDoInput(true); - connection.setUseCaches(false); - connection.setRequestMethod("GET"); - connection.connect(); - - r = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8")); - - StringWriter sw = new StringWriter(); - - int b; - - while ((b = r.read()) != -1) { - sw.write(b); - } - - return sw.toString(); - } finally { - if (r != null) { - r.close(); - } - } - } - Map cached; - long cacheUpdated; - - @SuppressWarnings("unchecked") - public Map getData() { - if (cacheLifeTime <= (System.currentTimeMillis() - cacheUpdated) / 1000) { - for (String server : serverList) { - String data; - - try { - data = fetchRemoteFile(server, dispenserPath + "&charset=UTF-8"); - } catch (IOException e1) { - continue; - } - - if (data.startsWith("FATAL ERROR:")) { - System.err.println("Sape responded with error: " + data); - - continue; - } - - try { - cached = (Map) new SerializedPhpParser(data).parse(); - } catch (Exception e) { - System.err.println("Can't parse Sape data: " + e); - continue; - } - - cacheUpdated = System.currentTimeMillis(); - - return cached; - } - - System.err.println("Unable to fetch Sape data"); - - return new HashMap(); - } - - return cached; - } -} diff --git a/src/main/java/ru/sape/SapePageLinks.java b/src/main/java/ru/sape/SapePageLinks.java deleted file mode 100644 index 498aeac0..00000000 --- a/src/main/java/ru/sape/SapePageLinks.java +++ /dev/null @@ -1,95 +0,0 @@ -package ru.sape; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import javax.servlet.http.Cookie; - -public class SapePageLinks { - - private boolean showCode; - - public SapePageLinks(SapeConnection sapeConnection, String sapeUser, String requestUri, Cookie[] cookies) { - this(sapeConnection, sapeUser, requestUri, cookies, false); - } - - @SuppressWarnings("unchecked") - public SapePageLinks(SapeConnection sapeConnection, String sapeUser, String requestUri, Cookie[] cookies, boolean showCode) { - if (sapeUser.equals(getCookieValue(cookies, "sape_cookie"))) { - showCode = true; - } - - Map data = sapeConnection.getData(); - - if (data.containsKey("__sape_delimiter__")) { - linkDelimiter = (String) data.get("__sape_delimiter__"); - } - - if (data.containsKey(requestUri)) { - pageLinks = new ArrayList(((Map) data.get(requestUri)).values()); - } - - if (data.containsKey("__sape_new_url__")) { - if (showCode) { - Object newUrl = data.get("__sape_new_url__"); - - if (newUrl instanceof Map) { - pageLinks = new ArrayList(((Map) newUrl).values()); - } else { - pageLinks = new ArrayList(Arrays.asList((String) newUrl)); - } - } - } - - this.showCode = showCode; - } - private String linkDelimiter = "."; - private List pageLinks = new ArrayList(); - - public String render() { - return render(-1); - } - - public String render(int count) { - StringBuilder s = new StringBuilder(); - - if (count < 0) { - count = pageLinks.size(); - } - - for (Iterator i = pageLinks.iterator(); i.hasNext() && count > 0; count--) { - if (s.length() > 0) { - s.append(linkDelimiter); - } - - String l = i.next(); - - s.append(l); - - i.remove(); - } - - if (showCode) { - s.insert(0, ""); - s.append(""); - } - - return s.toString(); - } - - private static String getCookieValue(Cookie[] cookies, String name) { - if (cookies == null) { - return null; - } - - for (Cookie cookie : cookies) { - if (cookie.getName().equals(name)) { - return cookie.getValue(); - } - } - - return null; - } -} diff --git a/src/main/java/ru/sape/SerializedPhpParser.java b/src/main/java/ru/sape/SerializedPhpParser.java deleted file mode 100644 index a24551b9..00000000 --- a/src/main/java/ru/sape/SerializedPhpParser.java +++ /dev/null @@ -1,221 +0,0 @@ -/* -Copyright (c) 2007 Zsolt Szász - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package ru.sape; - -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.regex.Pattern; - -/** - * Deserializes a serialized PHP data structure into corresponding Java objects. It supports - * the integer, float, boolean, string primitives that are mapped to their Java - * equivalent, plus arrays that are parsed into Map instances and objects - * that are represented by {@link SerializedPhpParser.PhpObject} instances. - *

    - * Example of use: - *

    - *      String input = "O:8:"TypeName":1:{s:3:"foo";s:3:"bar";}";
    - *      SerializedPhpParser serializedPhpParser = new SerializedPhpParser(input);
    - *      Object result = serializedPhpParser.parse();
    - * 
    - * - * The result object will be a PhpObject with the name "TypeName" and - * the attribute "foo" = "bar". - */ -class SerializedPhpParser { - - private final String input; - private int index; - private boolean assumeUTF8 = true; - private Pattern acceptedAttributeNameRegex = null; - - public SerializedPhpParser(String input) { - this.input = input; - } - - public Object parse() { - char type = input.charAt(index); - switch (type) { - case 'i': - index += 2; - return parseInt(); - case 'd': - index += 2; - return parseFloat(); - case 'b': - index += 2; - return parseBoolean(); - case 's': - index += 2; - return parseString(); - case 'a': - index += 2; - return parseArray(); - case 'O': - index += 2; - return parseObject(); - case 'N': - index += 2; - return NULL; - default: - throw new IllegalStateException("Encountered unknown type [" + type + "], str=" + input.substring(index)); - } - } - - private Object parseObject() { - PhpObject phpObject = new PhpObject(); - int strLen = readLength(); - phpObject.name = input.substring(index, index + strLen); - index = index + strLen + 2; - int attrLen = readLength(); - for (int i = 0; i < attrLen; i++) { - Object key = parse(); - Object value = parse(); - if (isAcceptedAttribute(key)) { - phpObject.attributes.put(key, value); - } - } - index++; - return phpObject; - } - - private Map parseArray() { - int arrayLen = readLength(); - Map result = new LinkedHashMap(); - for (int i = 0; i < arrayLen; i++) { - Object key = parse(); - Object value = parse(); - if (isAcceptedAttribute(key)) { - result.put(key, value); - } - } - index++; - return result; - } - - private boolean isAcceptedAttribute(Object key) { - if (acceptedAttributeNameRegex == null) { - return true; - } - if (!(key instanceof String)) { - return true; - } - return acceptedAttributeNameRegex.matcher((String) key).matches(); - } - - private int readLength() { - int delimiter = input.indexOf(':', index); - int arrayLen = Integer.valueOf(input.substring(index, delimiter)); - index = delimiter + 2; - return arrayLen; - } - - /** - * Assumes strings are utf8 encoded - * - * @return - */ - private String parseString() { - int strLen = readLength(); - - int utfStrLen = 0; - int byteCount = 0; - while (byteCount != strLen) { - char ch = input.charAt(index + utfStrLen++); - - /* - if (ch == '\'') { - utfStrLen -= 1; - break; - } - */ - - if (assumeUTF8) { - if ((ch >= 0x0001) && (ch <= 0x007F)) { - byteCount++; - } else if (ch > 0x07FF) { - byteCount += 3; - } else { - byteCount += 2; - } - } else { - byteCount++; - } - } - String value = input.substring(index, index + utfStrLen); - index = index + utfStrLen + 2; - return value; - } - - private Boolean parseBoolean() { - int delimiter = input.indexOf(';', index); - String value = input.substring(index, delimiter); - if (value.equals("1")) { - value = "true"; - } else if (value.equals("0")) { - value = "false"; - } - index = delimiter + 1; - return Boolean.valueOf(value); - } - - private Double parseFloat() { - int delimiter = input.indexOf(';', index); - String value = input.substring(index, delimiter); - index = delimiter + 1; - return Double.valueOf(value); - } - - private Integer parseInt() { - int delimiter = input.indexOf(';', index); - String value = input.substring(index, delimiter); - index = delimiter + 1; - return Integer.valueOf(value); - } - - public void setAcceptedAttributeNameRegex(String acceptedAttributeNameRegex) { - this.acceptedAttributeNameRegex = Pattern.compile(acceptedAttributeNameRegex); - } - public static final Object NULL = new Object() { - - @Override - public String toString() { - return "NULL"; - } - }; - - /** - * Represents an object that has a name and a map of attributes - */ - public static class PhpObject { - - public String name; - public Map attributes = new HashMap(); - - @Override - public String toString() { - return "\"" + name + "\" : " + attributes.toString(); - } - } -} diff --git a/src/main/webapp/WEB-INF/juick.conf.example b/src/main/webapp/WEB-INF/juick.conf.example deleted file mode 100644 index c0812b61..00000000 --- a/src/main/webapp/WEB-INF/juick.conf.example +++ /dev/null @@ -1,5 +0,0 @@ -mysql_username=username -xmpp_password=secret -sape_user=usertoken -wns_application_sip=ms-app://x-1-11-1-1111111111-... -wns_client_secret=secret \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/logging.properties.example b/src/main/webapp/WEB-INF/logging.properties.example deleted file mode 100644 index 7bdaa72e..00000000 --- a/src/main/webapp/WEB-INF/logging.properties.example +++ /dev/null @@ -1,5 +0,0 @@ -handlers = java.util.logging.FileHandler -java.util.logging.FileHandler.pattern = www.log -java.util.logging.FileHandler.limit = 1000000 -java.util.logging.FileHandler.count = 5 -java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index b4fbc03d..00000000 --- a/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - Main - com.juick.http.www.Main - 1 - - - Main - / - - - default - /scripts.js - - - default - /style.css - - - js - application/javascript;charset=UTF-8 - - - css - text/css;charset=UTF-8 - - - - 30 - - - - APNS/GCM/MPNS module - PushComponent - com.juick.PushComponent - - - Crossposting module - CrosspostComponent - com.juick.CrosspostComponent - - - XMPP module - XMPPComponent - com.juick.xmpp.s2s.XMPPComponent - - diff --git a/src/main/webapp/favicon.png b/src/main/webapp/favicon.png deleted file mode 100644 index bc7161e2..00000000 Binary files a/src/main/webapp/favicon.png and /dev/null differ diff --git a/src/main/webapp/logo.png b/src/main/webapp/logo.png deleted file mode 100644 index 933f6099..00000000 Binary files a/src/main/webapp/logo.png and /dev/null differ diff --git a/src/main/webapp/scripts.js b/src/main/webapp/scripts.js deleted file mode 100644 index 32427e1e..00000000 --- a/src/main/webapp/scripts.js +++ /dev/null @@ -1,684 +0,0 @@ -var ws=null; -var pageTitle; - -function initWS() { - if(typeof(pageMID)!="undefined" && pageMID>0) { - var url; - var protocolPrefix = (window.location.protocol === 'https:') ? 'wss:' : 'ws:'; - if(typeof(juickDebug)!="undefined") { - url= protocolPrefix + "//ws.juick.com/_replies"; - } else { - url= protocolPrefix + "//ws.juick.com/"+pageMID; - } - if(typeof(hash)!="undefined" && hash) { - url+="?hash="+hash; - } - - ws = new WebSocket(url); - ws.onopen = function() { - console.log('online'); - if($('#wsthread').length==0) { - var d=$('
    '); - d.on('click',onclickNextReply); - d.appendTo("body"); - pageTitle=document.title; - } - }; - ws.onclose = function() { - console.log('offline'); - ws=null; - setTimeout(function() { - initWS(); - },2000); - }; - ws.onmessage = function(msg) { - if(msg.data==' ') { - ws.send(' '); - } else { - try { - var jsonMsg=$.parseJSON(msg.data); - console.log('data: '+msg.data); - wsIncomingReply(jsonMsg); - } catch(err) { - console.log(err); - } - } - }; - setInterval(wsSendKeepAlive, 90000); - } -} - -function wsSendKeepAlive() { - if(ws) { - ws.send(' '); - } -} - -function wsShutdown() { - if(ws) { - ws.onclose=function(){}; - ws.close(); - } -} - -function wsIncomingReply(msg) { - var p; - if(msg.replyto>0) { - p=$('#'+msg.replyto); - if(p.length==0) { - p=null; - } - } - - var li=$('
  • '); - li.html('
    '+ - '
    '+ - '
    '+ - ''+ - ''+ - '
    '+msg.body+'
    '+ - ''+ - ''+ - '
    '); - - if(p) { - li.css('margin-left',parseInt(p.css('margin-left'))+20+'px'); - p.after(li); - } else { - $('#replies').append(li); - } - - updateRepliesCounter(); -} - -function onclickNewReply(e) { - var li=$(e); - li.removeClass('reply-new'); - li.off('click'); - li.off('mouseover'); - updateRepliesCounter(); -} - -function onclickNextReply() { - var li=$('#replies>li.reply-new:first'); - if(li.length) { - li.removeClass('reply-new'); - li.off('click'); - li.get(0).scrollIntoView(); - updateRepliesCounter(); - } -} - -function updateRepliesCounter() { - var replies=$('#replies>li.reply-new').length; - if(replies>0) { - $('#wsthread').text(replies).css('display','block'); - document.title='['+replies+'] '+pageTitle; - } else { - $('#wsthread').css('display','none'); - document.title=pageTitle; - } -} - -/******************************************************************************/ -/******************************************************************************/ -/******************************************************************************/ - -function postformListener(formEl,ev) { - if(ev.ctrlKey && (ev.keyCode==10 || ev.keyCode==13)) { - if(!formEl.onsubmit || formEl.onsubmit()) { - formEl.submit(); - } - } -} - -function unfoldPostForm() { - if(window.location.pathname==="/" && window.location.hash==="#post") { - $('#newmessage>div').css('display','block'); - $('#newmessage textarea').css('min-height','70px'); - $('#newmessage textarea')[0].focus(); - } -} - -function onsubmitNewMessage() { - if($('#newmessage textarea').val().length==0) { - openDialog('

    Пожалуйста, введите текст сообщения

    '); - return false; - } - return true; -} - -function showMoreReplies(id) { - $('#'+id+' .msg-comments').hide(); - - var replies=$('#replies>li'); - var flagshow=0; - for(var i=0; i'); - c.before(''); - c.append('
    '); - } - // $('#replies #'+rid+' .msg-links').hide(); - $('#replies #'+rid+' .msg-comment').show(); - $('#replies #'+rid+' textarea')[0].focus(); - $('#replies #'+rid+' textarea').autoResize({ - extraSpace: 0, - minHeight: 1 - }); - return false; -} - -function showCommentFooter(e) { - var a=$(e).closest("article"); - if(a.find("footer.comm").length==0) { - a.append('
    '); - a.find('textarea').autoResize({ - extraSpace: 0, - minHeight: 1 - }); - } - a.find('textarea')[0].focus(); - return false; -} - -function attachCommentPhoto(div) { - if($(div).children().length===0) { - var inp=$(''); - inp.on('change',function() { - $(this).parent().attr('class','attach-photo-active'); - }); - inp.trigger('click'); - $(div).append(inp); - } else { - $(div).empty(); - $(div).attr('class','attach-photo'); - } -} - -function attachMessagePhoto(div) { - var f=$(div).closest('form'); - if(f.find('input:file').length===0) { - var inp=$(''); - inp.on('change',function() { - $(div).text("загрузить (✓)"); - }); - f.append(inp); - inp.trigger('click'); - } else { - f.find('input:file').remove(); - $(div).text("загрузить"); - } -} - -function unfoldReply() { - if((0+window.location.hash.substring(1))>0) { - var el=$(window.location.hash); - while(el.is(":hidden")) { - el=el.prev(); - } - showMoreReplies(el.attr('id')); - window.location.replace(window.location.hash); - } -} - -function showMessageLinksDialog(mid,rid) { - var hlink=window.location.protocol+"//juick.com/"+mid; - var mlink="#"+mid; - if(rid>0) { - hlink+="#"+rid; - mlink+="/"+rid; - } - var hlinkenc=encodeURIComponent(hlink); - - var html="
    Ссылка на сообщение:"; - html+="
    "+hlink+"
    "; - html+="Номер сообщения:"; - html+="
    "+mlink+"
    "; - html+="Поделиться:
      "; - html+="
    • "; - html+="
    • "; - html+="
    • "; - html+="
    • "; - html+="
    "; - - openDialog(html); -} - -function showPhotoDialog(fname) { - var width=$(window).width(); - var height=$(window).height()*0.9; - if(width<640) { - return true; - } else if(width<1280) { - openDialog(""); - $('#dialogw img').css('max-height',height+'px'); - return false; - } else { - openDialog(""); - $('#dialogw img').css('max-height',height+'px'); - return false; - } -} - -function openDialog(html) { - var dhtml="
    "; - dhtml+=html; - dhtml+="
    "; - $('body').append(dhtml); -} - -function closeDialog() { - $('#dialogb').remove(); - $('#dialogt').remove(); -} - -function openSocialWindow(a) { - var w=window.open(a.href,'juickshare','width=640,height=400'); - if(window.focus) w.focus(); - return false; -} - -function checkUsername() { - var uname=$('#username').val(); - $.ajax('//api.juick.com/users?uname='+uname).done(function() { - $('#username').css('background','#FFCCCC'); - }).fail(function() { - $('#username').css('background','#CCFFCC'); - }); -} - -/******************************************************************************/ - -function openDialogLogin() { - var html='

    Пожалуйста, представьтесь:' - +'Facebook ' - +'ВКонтакте

    ' - +'

    Уже зарегистрированы?

    ' - +'
    ' - +'
    ' - +'
    ' - +'' - +'
    '; - openDialog(html); - return false; -} - -/******************************************************************************/ - -function likeMessage(e,mid) { - if (confirm("Are you sure?")) { - $.ajax({ - url: '//juick.com/like?mid='+mid, - type: 'POST' - }).done(function() { - $(e).closest("article").append("

    OK!

    "); - }).fail(function() { - $(e).closest("article").append("

    Ошибка

    "); - }); - } - return false; -} - -/******************************************************************************/ - -function setPopular(e,mid,popular) { - $.ajax('//api.juick.com/messages/set_popular?mid='+mid+'&popular='+popular+'&hash='+hash).done(function() { - var a=$(e).closest("article"); - a.append("

    OK!

    "); - }); - return false; -} - -function setPrivacy(e,mid) { - $.ajax('//api.juick.com/messages/set_privacy?mid='+mid+'&hash='+hash).done(function() { - var a=$(e).closest("article"); - a.append("

    OK!

    "); - }); - return false; -} - -/******************************************************************************/ - -function readerLinkReplace(e) { - var a=$(e); - a.attr('href','/_out?lid='+a.data('lid')); -} - -/******************************************************************************/ - -jQuery.fn.selectText = function(){ - var d = document; - if (d.body.createTextRange) { - var range = d.body.createTextRange(); - range.moveToElementText(this[0]); - range.select(); - } else if (window.getSelection) { - var selection = window.getSelection(); - var range = d.createRange(); - range.selectNodeContents(this[0]); - selection.removeAllRanges(); - selection.addRange(range); - } -}; - -/* - * jQuery.fn.autoResize 1.14 - */ - -(function($){ - - var uid = 'ar' + +new Date, - - defaults = autoResize.defaults = { - onResize: function(){}, - onBeforeResize: function(){ - return 123 - }, - onAfterResize: function(){ - return 555 - }, - animate: { - duration: 200, - complete: function(){} - }, - extraSpace: 50, - minHeight: 'original', - maxHeight: 500, - minWidth: 'original', - maxWidth: 500 - }; - - autoResize.cloneCSSProperties = [ - 'lineHeight', 'textDecoration', 'letterSpacing', - 'fontSize', 'fontFamily', 'fontStyle', 'fontWeight', - 'textTransform', 'textAlign', 'direction', 'wordSpacing', 'fontSizeAdjust', - 'paddingTop', 'paddingLeft', 'paddingBottom', 'paddingRight', 'width' - ]; - - autoResize.cloneCSSValues = { - position: 'absolute', - top: -9999, - left: -9999, - opacity: 0, - overflow: 'hidden' - }; - - autoResize.resizableFilterSelector = [ - 'textarea:not(textarea.' + uid + ')', - 'input:not(input[type])', - 'input[type=text]', - 'input[type=password]', - 'input[type=email]', - 'input[type=url]' - ].join(','); - - autoResize.AutoResizer = AutoResizer; - - $.fn.autoResize = autoResize; - - function autoResize(config) { - this.filter(autoResize.resizableFilterSelector).each(function(){ - new AutoResizer( $(this), config ); - }); - return this; - } - - function AutoResizer(el, config) { - - if (el.data('AutoResizer')) { - el.data('AutoResizer').destroy(); - } - - config = this.config = $.extend({}, autoResize.defaults, config); - this.el = el; - - this.nodeName = el[0].nodeName.toLowerCase(); - - this.originalHeight = el.height(); - this.previousScrollTop = null; - - this.value = el.val(); - - if (config.maxWidth === 'original') config.maxWidth = el.width(); - if (config.minWidth === 'original') config.minWidth = el.width(); - if (config.maxHeight === 'original') config.maxHeight = el.height(); - if (config.minHeight === 'original') config.minHeight = el.height(); - - if (this.nodeName === 'textarea') { - el.css({ - resize: 'none', - overflowY: 'hidden' - }); - } - - el.data('AutoResizer', this); - - // Make sure onAfterResize is called upon animation completion - config.animate.complete = (function(f){ - return function() { - config.onAfterResize.call(el); - return f.apply(this, arguments); - }; - }(config.animate.complete)); - - this.bind(); - - } - - AutoResizer.prototype = { - - bind: function() { - - var check = $.proxy(function(){ - this.check(); - return true; - }, this); - - this.unbind(); - - this.el - .bind('keyup.autoResize', check) - //.bind('keydown.autoResize', check) - .bind('change.autoResize', check) - .bind('paste.autoResize', function() { - setTimeout(function() { - check(); - }, 0); - }); - - if (!this.el.is(':hidden')) { - this.check(null, true); - } - - }, - - unbind: function() { - this.el.unbind('.autoResize'); - }, - - createClone: function() { - - var el = this.el, - clone = this.nodeName === 'textarea' ? el.clone() : $(''); - - this.clone = clone; - - $.each(autoResize.cloneCSSProperties, function(i, p){ - clone[0].style[p] = el.css(p); - }); - - clone - .removeAttr('name') - .removeAttr('id') - .addClass(uid) - .attr('tabIndex', -1) - .css(autoResize.cloneCSSValues); - - if (this.nodeName === 'textarea') { - clone.height('auto'); - } else { - clone.width('auto').css({ - whiteSpace: 'nowrap' - }); - } - - }, - - check: function(e, immediate) { - - if (!this.clone) { - this.createClone(); - this.injectClone(); - } - - var config = this.config, - clone = this.clone, - el = this.el, - value = el.val(); - - // Do nothing if value hasn't changed - if (value === this.prevValue) { - return true; - } - this.prevValue = value; - - if (this.nodeName === 'input') { - - clone.text(value); - - // Calculate new width + whether to change - var cloneWidth = clone.width(), - newWidth = (cloneWidth + config.extraSpace) >= config.minWidth ? - cloneWidth + config.extraSpace : config.minWidth, - currentWidth = el.width(); - - newWidth = Math.min(newWidth, config.maxWidth); - - if ( - (newWidth < currentWidth && newWidth >= config.minWidth) || - (newWidth >= config.minWidth && newWidth <= config.maxWidth) - ) { - - config.onBeforeResize.call(el); - config.onResize.call(el); - - el.scrollLeft(0); - - if (config.animate && !immediate) { - el.stop(1,1).animate({ - width: newWidth - }, config.animate); - } else { - el.width(newWidth); - config.onAfterResize.call(el); - } - - } - - return; - - } - - // TEXTAREA - - clone.width(el.width()).height(0).val(value).scrollTop(10000); - - var scrollTop = clone[0].scrollTop; - - // Don't do anything if scrollTop hasen't changed: - if (this.previousScrollTop === scrollTop) { - return; - } - - this.previousScrollTop = scrollTop; - - if (scrollTop + config.extraSpace >= config.maxHeight) { - el.css('overflowY', ''); - scrollTop = config.maxHeight; - immediate = true; - } else if (scrollTop <= config.minHeight) { - scrollTop = config.minHeight; - } else { - el.css('overflowY', 'hidden'); - scrollTop += config.extraSpace; - } - - config.onBeforeResize.call(el); - config.onResize.call(el); - - // Either animate or directly apply height: - if (config.animate && !immediate) { - el.stop(1,1).animate({ - height: scrollTop - }, config.animate); - } else { - el.height(scrollTop); - config.onAfterResize.call(el); - } - - }, - - destroy: function() { - this.unbind(); - this.el.removeData('AutoResizer'); - this.clone.remove(); - delete this.el; - delete this.clone; - }, - - injectClone: function() { - ( - autoResize.cloneContainer || - (autoResize.cloneContainer = $('').appendTo('body')) - ).append(this.clone); - } - - }; - -})(jQuery); - -/******************************************************************************/ - -$(document).ready(function() { - $('textarea').autoResize({ - extraSpace: 0, - minHeight: 1 - }); - - $('textarea.reply').click(function () { - $(this).addClass("narrow"); - $(this).after('
    '); - $(this).parent().after(''); - $(this).off('click'); - }); - - $('textarea.replypm').click(function () { - $(this).addClass("narrowpm"); - $(this).parent().after(''); - $(this).off('click'); - }); - - unfoldPostForm(); - unfoldReply(); - $(window).bind('hashchange',unfoldPostForm); - $(window).bind('hashchange',unfoldReply); - - $(window).on('pagehide',wsShutdown); -}); diff --git a/src/main/webapp/style.css b/src/main/webapp/style.css deleted file mode 100644 index 5548b79a..00000000 --- a/src/main/webapp/style.css +++ /dev/null @@ -1,263 +0,0 @@ -html,body,div,h1,h2,ul,li,p,form,input,textarea,pre { margin: 0; padding: 0; } -html,input,textarea { font-family: sans-serif; font-size: 12pt; } -html { background: #EEEEE5; color: #000; } -body { width: 1024px; margin: 0 auto; } -h1,h2 { font-weight: normal; } -ul { list-style-type: none; } -a { text-decoration: none; color: #069; } -img,hr { border: none; } -hr { height: 1px; background: #CCC; margin: 10px 0; } -pre { white-space: pre-wrap; white-space: -moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; word-wrap: break-word; } -span.u { text-decoration: underline; } - -#content { width: 728px; margin: 15px 0 0 286px; } -#topwrapper { position: relative; clear: both; } - -/********/ - -body>header { width: 1024px; } -body>header a { color: #000; border-bottom: 1px dotted #666; font-size: 13pt; } - -#logo { float: left; width: 110px; height: 36px; margin: 7px 25px 0 20px; } -#logo a { display: block; width: 110px; height: 36px; text-indent: 100%; white-space: nowrap; overflow: hidden; border: 0; -background: url("//i.juick.com/logo.png") no-repeat; } -@media screen and (-webkit-min-device-pixel-ratio: 2), - (min-resolution: 192dpi) { - #logo a { - background: url("//i.juick.com/logo_2x.png") no-repeat; - background-size: cover; - } -} -nav#global { float: left; } -nav#global li { display: inline-block; margin: 14px 12px 0 0; } -#search { float: right; margin: 12px 20px 12px 0; } -#search input { background: #FFF; border: 1px solid #DDDDD5; padding: 4px; } - -#headdiv { clear: both; margin: 0 0 5px 0; padding: 0 20px; background: #DDDDD5; border-top: 1px solid #D5D5D0; border-bottom: 1px solid #D5D5D0; position: relative; } -#headdiv li { display: inline-block; margin: 12px 12px 12px 0; } -nav#actions { top: 0; right: 8px; position: absolute; } - -body>header nav li:after { display: inline-block; content: "/"; margin-left: 12px; color: #AAA; } -body>header nav li:last-child:after { display: none; } - -body>header p { color: #000; font-size: 13pt; margin: 12px 0; text-align: center; } - -/********/ - -#content>p, #content>h1, #content>h2 { margin: 1em 0; } - -#newmessage { background: #E5E5E0; padding: 15px; margin-bottom: 20px; } -#newmessage textarea { border: 1px solid #CCC; padding: 4px; width: 688px; resize: vertical; min-height: 14pt; height: 14pt; margin: 0 0 5px 0; } -#newmessage input { border: 1px solid #CCC; padding: 2px 4px; margin: 5px 0; } -#newmessage>div { display: none; } -#newmessage .img { width: 500px; } -#newmessage .tags { width: 500px; } -#newmessage .subm { width: 150px; background: #EEEEE5; } - -article { margin: 10px 0 20px 58px; background: #FFF; padding: 12px 13px; } -article>aside { margin: -12px 0 0 -71px; width: 48px; height: 48px; float: left; } -article>aside img { width: 48px; height: 48px; } -article>header.u { overflow: hidden; display: inline-block; width: 460px; } -article>header.t { width: 140px; text-align: right; float: right; } -article time { color: #999; font-size: 10pt; border-bottom: 1px dotted #999; } -article p { margin: 10px 0 15px 0; } -article p.i { text-align: center; } -article p.ir { float: right; margin-left: 10px; margin-bottom: 10px; } -article p.ir a { cursor: -webkit-zoom-in; cursor: -moz-zoom-in; } -article p.ir img { max-width: 200px; max-height: 200px; } -article .irbr { clear: right; } -article>nav.l { display: inline-block; font-size: 10pt; } -article>nav.l a { color: #888; border-bottom: 1px dotted #AAA; margin-right: 15px; } -article>nav.s { display: inline-block; text-align: right; float: right; } -article>nav.s a { font-weight: bold; color: #222; } -article a.likes { padding-left: 20px; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAAXNSR0IArs4c6QAAAMRJREFUGBmtwbsuQ2EAAOCPX6LlKSRGD2E0YCKRGDqLQReDSVhIjI1r49bRdiZJn0GIQcPSxFO4HPHLOe1J2rpMvo//MaMl1batZNSWtlTLrK4JL5qqTqQO1aTqqppeTcrtexRklkTRskzw4Eju1rmOIIqCjrp7uRsNhQWLCmfu5C48Kxk05MmV3JR3xwatiaZ1rYg29Zr34VSPHdG6wpw3iaDPnmhDpiKVKPtmV7Sq4tOlET86EEUNw34RJK4Ffxgzrs8XpvA41+ECiwcAAAAASUVORK5CYII=") no-repeat 0px 1px; } -article a.replies { margin-left: 18px; padding-left: 20px; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAANdJREFUOMvN069OgmEUBvAfSCUxNicFKTQ3bkCLyRvwDrgHZ5Q7MFlobAY3G9UrsOi0iIGNBIGgG00o53PvCMAHBJ7thPecPc/5+3IoKKKDIeZrbIi74CiFQBs3eMTbmmRnuMUID5nzG90cFXcxyEqHU3zlEBigkQoUor9N8Recf4Gdpr8XgV+Uc/DK+Ekdz/hAcwNyE594Sp0NTJYOZobriB/FjUwjNkE9m36GKq5wHO9zXOICtch4j1f0Q2QlSnjBGL10dXlQxXvSVmWbTZ3E/5ijtRxcAIj4MflVC0WJAAAAAElFTkSuQmCC") no-repeat 0px 1px; } -article footer.comm { margin: 13px 0 0 0; } -article textarea { width: 530px; padding: 2px; resize: vertical; vertical-align: top; min-height: 12pt; height: 12pt; border: 0; } -article input { width: 50px; margin-left: 6px; vertical-align: top; border: 1px solid #CCC; background: #EEE; color: #999; } - -#yandex_ad_728 { width: 728px; height: 90px; margin: 20px 0; padding: 15px 0; background: #FFF; } -.adslot1 { display: inline-block; width: 320px; height: 90px; } - -@media (min-width:500px) {.adslot1 {width: 468px; height: 60px;}} -@media (min-width:800px) {.adslot1 {width: 728px; height: 90px; margin: 10px 0 20px 0; }} - - -#geomap { width: 700px; height: 300px; margin-top: 1em; overflow: hidden; } - -.msg { margin: 10px 0 20px 0; } -.ads { padding: 13px 10px 5px 10px; margin: 8px 0 16px 58px; background: #FFF; } -.msgthread { margin-bottom: 0; } -.msg-avatar { float: left; width: 48px; height: 48px; } -.msg-avatar img { width: 48px; height: 48px; vertical-align: top; } -.msg-cont { background: #FFF; margin-left: 58px; padding: 12px 15px; width: 640px; } -.msg-menu { float: right; width: 16px; height: 16px; } -.msg-menu>a { display: block; width: 16px; height: 16px; vertical-align: top; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAAXNSR0IArs4c6QAAALRQTFRFAAAAldX/ncT/ksj/ltL/nMb/lMn/mcz/l9H/lcr/mZmZmcz/m83/mJiYmZmZmcz/mM7/mpqamsr/l8v/m83/mJiYmM7/l8v/mZmZmcz/mpqamMv/mcz/mM3/ms3/msv/mMv/mMz/mcz/msz/mc3/mMz/mcz/msz/mc3/mcv/mc3/mcv/mcz/mcz/mcz/mcz/mZmZmZmZmcz/mcz/mcz/mcz/mcz/mcz/mcz/mcz/mcz/mZmZiZsGCAAAADx0Uk5TAAwNDhESExQWGC0tLi8yMjQ1NTY4OTk7PDxESktNUVNUd3h5eoGCg4mKmMvMzc7P29zd3uLj7O3u8vT2+A5wCAAAAJFJREFUGBkFwQlCgkAAAMDRxEySkryyzDxSAc+gkvX//2oGAAB42J5COG46AJ+/+SiKxvl1DsyqJyCtZnBXxgDxdxPrHQDZCvvUNIFkalAgtCSXHr1LIqpRt+ifu91zn6hG8YLh7TbEoMAyg8kEsgWaZQzwWDbgvXoG0p83YH7Nx+32a/73AdDZHEIovu4BAMA/t6QMuyHliCkAAAAASUVORK5CYII=") no-repeat; } -.msg-header { overflow: hidden; } -.msg-ts { font-size: small; vertical-align: top; margin: 5px 0; } -.msg-ts, .msg-ts>a { color: #999; } -.msg-place { font-size: small; } -.msg-place>a { color: #999; } -.msg-txt { overflow: hidden; margin: 10px 0 12px 0; } -.msg-media { text-align: center; } -.msg-links { font-size: small; color: #999; margin: 5px 0 0 0; } -.msg-comments { overflow: hidden; font-size: small; color: #AAA; text-indent: 10px; margin-top: 10px; } -.msg-comment { margin: 5px 0; } -.ta-wrapper { display: inline-block; border: 1px solid #DDD; } -.msg-comment textarea { width: 634px; padding: 2px; resize: vertical; vertical-align: top; min-height: 12pt; height: 12pt; border: 0; } -.msg-comment .narrow { width: 554px; } -.msg-comment .narrowpm { width: 580px; } -.attach-photo { display: inline-block; padding: 2px 4px; cursor: pointer; width: 16px; height: 13px; overflow: hidden; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAANCAQAAAAKsiavAAAAAXNSR0IArs4c6QAAAKRJREFUGNNjZICC6Q5M+xlQwD/HzAMMDEwwLnM9AxqAiDDO/M+AFzCh8B4zhrDwsfAxBjHcggkhm/CY2SDlHYQ5TZD5EoMMmgmMhSnvZnrPfDLzyQyvrPf/CzGs+L2TgYFhBoM0gzTjDAaGX7uwuQG/I1ldGRj+pzM8YXjMmMrAwOGK6cibfy2z3sMdeYpBBTMcHjMU/9zJzsjo8r8DIk1yQGEBAFzpL+AuTCqZAAAAAElFTkSuQmCC") no-repeat 3px 4px; } -.attach-photo-active { display: inline-block; padding: 2px 4px; cursor: pointer; width: 16px; height: 13px; overflow: hidden; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAANCAMAAACXZR4WAAAAAXNSR0IArs4c6QAAAEVQTFRFAAAAAJUAAJ0AAJIAAJkAAJYAAJwAAJcAAJoAAJgAAJkAAJoAAJoAAJgAAJkAAJoAAJkAAJkAAJkAAJkAAJkAAJkAAJkA9z3GXQAAABZ0Uk5TAAwNDg8REkBHSktRU1RVv9jZ2+Lj5OeV7PgAAABSSURBVAhbdcjBFkAgFADRIUlE9Or9/6daUC0cs5pzAXD65niKFSJQv/aChHHcrg4yA9jcILCW4tkbDGRVwfzDhs+yEBqcFsCmBiqHmULSDr0P3JdgDbuscEckAAAAAElFTkSuQmCC") no-repeat 3px 4px; } -.msg-comment input { width: 50px; margin-left: 6px; vertical-align: top; border: 1px solid #CCC; background: #EEE; color: #999; } -.msg-recomms { margin-top: 10px; overflow: hidden; font-size: small; color: #AAA; text-indent: 10px; } -.reply-new .msg-cont { border-right: 5px solid #0C0; } -q:before, q:after { content: "";} -q { border-left: 1px dashed #CCC; margin: 10px 0 10px 10px; padding-left: 10px; display: block; color: #666; } - -#mtoolbar { width: 670px; margin-left: 58px; background: #E5E5DD; border-top: 1px solid #CCC; } -#mtoolbar ul, #mtoolbar a { padding: 5px; } -#mtoolbar li { display: inline; } -#mtoolbar div { display: inline-block; width: 16px; height: 16px; background: url(//static.juick.com/toolbar-icons.png) no-repeat; vertical-align: middle; margin: 5px; } - -.newmessage { width: 695px; padding: 2px; resize: vertical; border: 1px solid #DDD; } /* textarea */ - -.users { width: 100%; margin: 10px 0; } /* table */ -.users td { width: 33%; padding: 6px 0; overflow: hidden; } /* table */ -.users img { width: 32px; height: 32px; vertical-align: middle; margin-right: 6px; } /* table */ - -.title2 { padding: 10px 20px; margin: 20px 0; background: #DDDDD0; } -.title2-right { float: right; line-height: 24px; } -#content .title2 h2 { font-size: x-large; margin: 0; } - -.page { text-align: center; padding: 5px; background: #E5E5DD; } - -/* signup form */ -.signup-h1>img { vertical-align: middle; margin-right: 10px; } -.signup-h1 { margin: 20px 0 10px 0; font-size: x-large; } -.signup-h2 { font-size: large; margin: 10px 0 5px 0; } -.signup-hr { margin: 20px 0; } - -/********/ - -#readerlinks li { margin: 15px 0; } -#readerlinks img { vertical-align: top; margin: 1px 7px 0 0; } -#readerlinks a { color: #000; border-bottom: 1px dotted #666; } -#readerlinks a:visited { color: #999; } - -/********/ - -.newpm { margin: 20px 60px 30px 60px; } -.newpm textarea { width: 100%; resize: vertical; } -.newpm-send input { width: 100px; } - -/********/ - -#column { width: 240px; padding-top: 10px; overflow: hidden; float: left; margin-left: 10px; } -#column ul, #column p, #column hr { margin: 10px 0; } -#column li { margin: 6px 0; } -#column .margtop { margin-top: 15px; } -#column p { font-size: 10pt; line-height: 140%; } -#column .tags { text-align: justify; } -#column .inp { width: 222px; padding: 3px; border: 1px solid #CCC; border-radius: 3px; background: #F5F5E9; } -#ctitle { font-size: 14pt; } -#ctitle img { vertical-align: middle; margin-right: 5px; } -#ctoolbar { margin: 10px 0; padding: 5px; line-height: 0; background: #E5E5DD; } -#ctoolbar li { display: inline; } -#ctoolbar a { padding: 5px 10px;} -#ctoolbar div { display: inline-block; width: 16px; height: 16px; background: url(//static.juick.com/toolbar-icons.png) no-repeat; vertical-align: middle; margin: 5px 0; } -#ustats li { margin: 3px 0; font-size: 10pt; } -#column table.iread { width: 100%; } -#column table.iread td { text-align: center; } -#column table.iread img { width: 48px; height: 48px; } - -/********/ - -#dialogb { position: fixed; top: 0; left: 0; width: 100%; height: 100%; opacity: 0.6; background: #000; z-index: 10; } -#dialogt { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 10; } -#dialogt td { vertical-align: middle; text-align: center; } -#dialogw { position: relative; display: inline-block; text-align: left; z-index: 11; } -#dialogc { position: absolute; top: -15px; right: -15px; width: 30px; height: 30px; z-index: 12; cursor: pointer; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAQAAACROWYpAAAEEElEQVQ4y32Va2yTVRzGf23fd10vUNqxsY1dGIyba7BsTgiwhcRkgxHkFkJAuQzIggY0IXGYkKA4DeAQMQTIiIB+MyRAJn7hAxJRJKIRiJgQIhESZxYFEuja9f74oe1aoPh/vrx5z3nO/3qeA0+aBSvW+UaT2VBUb+91HXY3FDUUNZnzDaxYsfBcs2DDpHjqqC1ln07YU79t0oGX+mY1V3dWv1/VWVrqphgTW+EDLBjYS91tZZ/5v+34d1/sijIWv/bw2IXFy2urfKVu7BjP0q0YOCaMWVlzfF7wdJY2mLybzH4/+mpq3cxKrwcHBtanvTqrfK/X97+avCvFUn2xtjDBNJpDn8SGU1Ls+oXVTTUeL8587xYMHKN97fWnlikqnU/UDmWJWUwL/ZKQpPOvTZvg8eLI0W3Y8cyo+3hB4p50MmYLUgC24EdRSTq6rLIaD/Z06SyYuMdWvjnv4TfS+URhahpHYlLwR16gHDcmVrBSPNrXOv2LTimUrMoE3BiaO5Jz2dCmiDNIkGBRcCAuffde1WR8FGMDKy5n5cp5f34pHYqlt/tDkrQ7SpDgxNBASurLrHRFpPsXmUEFbgyw4fFM7FgQvi7NznirHQorTZ8cGkhJUm80vVIylJA0ppU6xlAEBiXj/EvWSI+TuewWhdP0xylJOhPPVeJGVNqxbdQ0xlIMprusoXnzW9LtRH5xFoUjmfHIpxL8Oiod3ed6kXE4wHRW+Od0dku/J57s7P1UmvxBNP//2ah0+CCNVOICk8qyOS3bpAeJfOpgSpISypUujZ+GpXd6HC8zPkM2Z9Vtiv0jZRvlGRrM5Lo4k/vWSHZUHsWlGW+YI+QKs7lh/Z1L0tuZLbPDuVzTpTsVT6+0haXB31hNUzbsMjMwZdWBz6XBuDOY7efWiG3kYuyOZmO6FJLOnWM5mYIZlDDd3lHVPXAt18/C2DAsBf8u2cECpqdbZcNDndHi27S5T5I2DD+POjccT0qHz9JJS3ZIrLioIGBbNu7dI6claVdB72uHI0np8lW6WUIgO55WivFRT6ttvffDQ/2SdCuyatiZR2wLXwlL0uVf6WEdrdRnL4YFEzfl+Gm3dXn2bun/67YkRZI3oscjxyM/Rx7FJenBwP6L7KWLdvy5K5kRA6qZSYety9nDiYM//HFLeXbvzsmr5kl66KKDmeSJQUaG8FJDgHbW0e3YzwnPmVe+33lz582Oy+P7OcF+ullHOwFqeEKGMgKIl2r8tLCUjWxnl7HH6KXX2GPdxXY2spQW/FTzlACOSC8eyplEgBYWsoI1rGUta1jBQloIMIlyCkhv9r2w48JLObVMwU+ARhoJ4GcKtZTjxZXL9Vm6BSsGJnYcOHFl4MSBHRMDK5b/e69yxzyNAvYf9TCL+HAwka4AAAAASUVORK5CYII="); } - -.dialoglogin { width: 300px; background: #EEEEE5; padding: 25px; } -#signfb,#signvk { display: block; width: 100%; height: 32px; line-height: 32px; text-indent: 37px; text-decoration: none; overflow: hidden; margin: 10px 0; } -#signfb { color: #FFF; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAADNQTFRFO1edX3ewl6bLnKrOoK3QrrrYvMXe2N7r3OLu3+Tv5urz7O/29vf6+Pn7+vv9/Pz9////ykQjsQAAAEZJREFUOMtjYBgFuAATO68ADxdOaUYuATDAqYBbAL8CFgECCjiBcqz4XMiPz3oQEKCtAgEkwEdIAQchBWyEFDAPkDdHsAIAhZkIwz/VK/UAAAAASUVORK5CYII=") no-repeat #3A569C; } -#signvk { margin-bottom: 30px; color: #FFF; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAHJQTFRFbY+zbo+zbpCzb5C0cpO1c5O2dZW3dpa4e5m6gJ29gZ69lq/In7bNo7jPrcDUs8XXvs3dv87dy9fkztnlz9rm0Nrm093o1N7o1+Dq3OTt3ubu4Ofv5Orw7fH27vL28PP38vX49Pb5+vv8+/z9/Pz9////2jSYlQAAAG5JREFUOMvtkEcOgDAMBE3vvXdIyP+/iMMRKfYHmMtcRtE6AD8f1Is8pyKgAs0RGYO2HSWqMQaoBHVRgYsS3AsrtyFlrqgdJlCLb95gxQO6IkZCqL+KCjz0TQU5ejOf2a3aJXPF7BOB2PvMhp8PDzGRFgEe7xvEAAAAAElFTkSuQmCC") no-repeat #6d8fb3; } -.dialoglogin form { margin-top: 7px; } -.signinput,.signsubmit { border: 1px solid #CCC; margin: 3px 0; padding: 3px; } -.signinput { width: 292px; } -.signsubmit { width: 70px; } - -.dialogshare { padding: 20px; background: #EEEEE5; border: 1px solid #999; min-width: 300px; overflow: auto; } -.dialogl { padding: 5px; margin: 3px 0 20px; border: 1px solid #DDD; background: #F5F5E9; } -.dialogshare li { float: left; margin: 5px 10px 0 0; } -.dialogshare a { display: block; width: 32px; height: 32px; background-image: url(//static.juick.com/sharesocial.png); } - -.dialogtxt { background: #EEEEE5; padding: 20px; } - -/********/ - -#wsthread { position: fixed; bottom: 20px; right: 20px; background: #CCC; cursor: pointer; padding: 5px 10px; display: none; } - -/********/ - -#footer { clear: both; font-size: 10pt; padding: 10px 0; color: #999; width: 1004px; margin: 0 auto 20px 0; } -#footer-social { float: left; } -#footer-social a { display: inline-block; width: 32px; height: 32px; text-indent: 100%; white-space: nowrap; overflow: hidden; border: 0; margin: 0 15px 0 0; } -#footer-left { margin-left: 286px; margin-right: 350px; } -#footer-right { float: right; } - -/******************************************************************************/ - -.sharenew { display: inline-block; padding: 0 12px 0 37px; min-height: 32px; line-height: 32px; min-width: 200px; color: #FFF; } - -.ico32-twi { background: #55acee url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAM9QTFRFVazuVqzuV6zuWK3uWq7uXK/uXrDuYLHvY7PvZLPvZrTvZ7Tva7bwbLfwbbjwbrjwcbnwc7rxdbvxer7xfL/xf8Dyg8LyhcPyhsTyh8TzisXzi8bzjcfzjsfzksn0qNT1rNb2udz3v+D4weD4weH4w+H5w+L5yuT5yuX5y+X5zuf60ej51er61uv62uz62+374O/75fL76PP86vT87PX87vb87/f98Pf88Pf98fj99Pn99vr99/v++Pv9+vz9+/z9/P39/f39/f7+/v7+////bJnt2AAAAKlJREFUGBntwUdWAkEABNAaQJIOkpMSJCcDKEEJ2lTd/0wyD1bdbFiw43/g5kIJHJXf/2b1u2fY/IWPQO1botiBraBFNQqE2zIy3L/CFl9Tg6dUbiyjAx+2WE8kl29zBfZwZD5+xYDRwTQEW2RCUkfkiwdHerilTpiHK9zXCTWKwOWVVjSUZPiTxVnJJimRu6oH1/1DpbURjfRZDOGMx+7WkNRXI4Gba/gHx4AiZYtLseAAAAAASUVORK5CYII=") no-repeat; } -.ico32-vk { background: #6d8fb3 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAHJQTFRFbY+zbo+zbpCzb5C0cpO1c5O2dZW3dpa4e5m6gJ29gZ69lq/In7bNo7jPrcDUs8XXvs3dv87dy9fkztnlz9rm0Nrm093o1N7o1+Dq3OTt3ubu4Ofv5Orw7fH27vL28PP38vX49Pb5+vv8+/z9/Pz9////2jSYlQAAAG5JREFUOMvtkEcOgDAMBE3vvXdIyP+/iMMRKfYHmMtcRtE6AD8f1Is8pyKgAs0RGYO2HSWqMQaoBHVRgYsS3AsrtyFlrqgdJlCLb95gxQO6IkZCqL+KCjz0TQU5ejOf2a3aJXPF7BOB2PvMhp8PDzGRFgEe7xvEAAAAAElFTkSuQmCC") no-repeat; } -.ico32-fb { background: #3b579d url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAADNQTFRFO1edX3ewl6bLnKrOoK3QrrrYvMXe2N7r3OLu3+Tv5urz7O/29vf6+Pn7+vv9/Pz9////ykQjsQAAAEZJREFUOMtjYBgFuAATO68ADxdOaUYuATDAqYBbAL8CFgECCjiBcqz4XMiPz3oQEKCtAgEkwEdIAQchBWyEFDAPkDdHsAIAhZkIwz/VK/UAAAAASUVORK5CYII=") no-repeat; } -.ico32-lj { background: #888888 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAlJQTFRFAAAAAAAAAFVVADNmACRtAEBgADNmADdtADBgADljADNmADFiADFjADVpADJjADRpADRoADFnADNmADJkADVlADJnADRnADJlADJnADJlADRnADNmADJnADNmADJmADRmADNlADRmADNnADNmADJmADRmADNmADRmADJmADNlADNmADNmADJmADNnADNlADNnADNmADNlADNmADNmADNmBTRmADNmADNmADNmWGh4ADNmBDZnK1J6LWCURmiIRmmLc4mfADNmKlyPADNmGkNsLF+SKFiIR3qtL1Z+ATRnDT9xE0Z5Gk2BN2ueOVh1UIO2DkFzE0Z5JVeLK1J5M2WYPlp1T3ehJEpuADNmK16QPG+iQHOmRFxze35/ADNmLV+SjIeCMVl/TmJ4BThqBzZnR3mtBjpsWo7AZ4KeFENxH0h0bJ7SADNmAzZpHz9sAzdqCzppEkBvF0RyO1+EP3OmBDZodKbafWR+ATRoDjlpQ3epADNmATNlDDdoQWOGaJvOBDVmBDZoQWWKRnmrUoS3AzZqBzdoWFZ4cKTVc4mgADNmBjdmCzhoVom5W47CADNmADNnAzRnBjVoWlh5opSHADNmATRlATRmCDlqcaXYh7rtADNmWYy/XI/CXpLFYZTHY5bJZZjLZpjLZpnMb2B9cmB8hbjrhp2zibvvjL7ylMj6l8r9l8v9mY6Fmcz/qneIrba+snqKuaKLwKeMxMbIx8jJy8vLzMzM1LOQ1dXV1d3m2YqR4LmS4uju7sGW78KV8cSX+fn5+smY/cqZ/f39/8yZQ9icRAAAAJt0Uk5TAAEDBQcICg4QEhkaHyIkJyw0QUJESE9RUlZeX2Fpa2x5e4GHiYqMmZ2rr7S2uL/Cw8nNzs/R19/g4OPj4+fn5+fo6Onp6erq6+zs7Ozs7Ozt7e3t7e3t7u/v7+/v7/Dw8PHx8vLy8/Pz9PT09fX19vb29vb29/f3+Pj4+fn5+fn6+vr6+vv7+/v7/Pz8/Pz9/f39/f3+/v7+/v7uuPuRAAABVElEQVQ4y83QU3sDURCA4U2dOrVtpbZt27Ztt2lTY6tTnxpb2/pf3dxPcp3vdt5n5jyHIIQpKSOXZS91/nPN4p6FvckuFr+5Hrn/wpmvbHBnwnMWt+1qdPD563LAhgECu9qLufGn37fDRmQIgm5Oh//DXflKaFkyVxkCDqT3w23ByCpFnUQFyQBAsTr8vjeyM46iqIM6e1FAaKGmlmkc00qLPtIMOmKOMjDeDTymRRapAQBxx6INjDd9zijqNDpXHhBy6Qk7GA+H0SuOSp0lAKFG5mGMM6toMTZrAT3DhByiRcgaLUqQDgBE2DXrGG9V+KX6FgazoRXSAfHbGMe+P378eXiCP67ETcQ45Y8uf0YMFAaoeSqHB66RAggY1u1JnzzwilRBQDDdlrJvaPDTrwsDQlLfFaUtfp9PmBL8YqhYcesRsiQEJGscgZwIgYlp2xJC0z8XJH2ZPhmkKgAAAABJRU5ErkJggg==") no-repeat; } -.ico32-gp { background: #dc4a38 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAAXNSR0IArs4c6QAAAa9JREFUSMftlTFoU1EUhr/3EguZFFzSIVVehyq03dxEJy3iIiJCi4Nr6eQmLoKggg46CaKj6FBB0OIgXcRJiQiKiFIf2KEJVsSCTWlN+Bx8eaQtaa5Dt57tP+/+/33n3P/cCzuxPeFh7/nRFdvxxtFw8m4f+tojJl62qT4xsRBOj511yT0Zuqjq5P/8/Lj6LEd9LqoLRt0Z8QY8Dqy2QbTGI6CfSrjAADDYgd8BUAgXWABG3Z/jH8Ai81lJE97pJfAYiJnK8RBwJWplaISTvZpYcEZteh7AQ9a90fH1ut96n0Ofl0zVOT/Y8mZurapVa65atert7iUQrUXXooQS9xkm5m6WbpCS8osmKSn1EEdcVfVWpweCSsgXH7Ch6rSlrQSKm9vICY4xxF5qVNjFGcqORQ0AXrLUa98x55x30oPG4LCfVDubtjX9tE2/tEcpm81XasNiCL1oTT23Idvvb7USYuURypmZO4+1xnv+8DNE4N8UHt20Zh9Po+WwHrxVGx5fl7tg3YHunHVXhYO8IEFmeE7KCglnKTMRfQ63T8kpZ/1uy2W/+sBTxjuPzLbHX6Ju9rcCQGEmAAAAAElFTkSuQmCC") no-repeat; } - -/******************************************************************************/ - -@media screen and (max-width: 850px) { - body { text-size-adjust: 100%; -webkit-text-size-adjust: 100%;} - body,body>header,#topwrapper,#content,#footer,#mtoolbar { width: auto; min-width: 310px; margin: 0 auto; } - - body>header { margin-bottom: 15px; } - body>header a { font-size: 12pt; } - #logo { display: none; } - nav#global { margin-left: 10px; } - nav#global li { margin-right: 10px; } - #search { float: none; display: inline-block; margin: 10px 10px; } - #headdiv { padding: 0 10px; } - #headdiv li { margin-right: 10px; } - nav#actions { position: relative; right: auto; } - #newmessage .img, #newmessage .tags { width: 100%; } - #column { float: none; padding-top: 0; width: auto; margin: 0 10px; } - - article { margin-left: 0; overflow: auto; } - article>aside { margin: 0 10px 0 0; width: 40px; height: 40px; } - article>aside img { width: 40px; height: 40px; } - article>header.u { margin: 0; width: auto; display: block; } - article>header.t { float: none; text-align: left; margin: 0; width: auto; display: block; } - article p { margin: 10px 0 8px 0; } - article>nav.l { display: block; float: left; width: 80%; line-height: 15pt; } - article>nav.s { display: block; } - article textarea { width: 205px; } - article footer { float: left; } - - #content textarea { width: 100%; } - - .msg,.msg-cont { width: auto; min-width: 280px; } - .msg-cont,.ads { margin-left: 0; } - .msg-avatar { margin: 10px 10px 0 10px; width: 40px; height: 40px; } - .msg-avatar img { width: 40px; height: 40px; } - .msg-comment textarea { width: 100%; } - .msg-txt { margin: 8px 0 0 0; } - .msg-media { overflow: auto; } - - .title2 h2 { font-size: large; } - - #footer { margin: 0 10px; } - #footer div { float: none; margin: 10px 0; } -} -- cgit v1.2.3