diff options
Diffstat (limited to 'juick-server/src')
248 files changed, 0 insertions, 25734 deletions
diff --git a/juick-server/src/docs/index.adoc b/juick-server/src/docs/index.adoc deleted file mode 100644 index 4d492510..00000000 --- a/juick-server/src/docs/index.adoc +++ /dev/null @@ -1,3 +0,0 @@ -include::{src}/overview.adoc[] -include::{src}/paths.adoc[] -include::{src}/definitions.adoc[]
\ No newline at end of file diff --git a/juick-server/src/main/assets/embed.js b/juick-server/src/main/assets/embed.js deleted file mode 100644 index d4cbab8e..00000000 --- a/juick-server/src/main/assets/embed.js +++ /dev/null @@ -1,334 +0,0 @@ - -function insertAfter(newNode, referenceNode) { - referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); -} - -function setContent(containerNode, ...newNodes) { - removeAllFrom(containerNode); - newNodes.forEach(n => containerNode.appendChild(n)); - return containerNode; -} - -function removeAllFrom(fromNode) { - for (let c; c = fromNode.lastChild; ) { fromNode.removeChild(c); } -} - -function htmlEscape(html) { - let textarea = document.createElement('textarea'); - textarea.textContent = html; - return textarea.innerHTML; -} - -// rules :: [{pr: number, re: RegExp, with: string}] -// rules :: [{pr: number, re: RegExp, with: Function}] -// rules :: [{pr: number, re: RegExp, brackets: true, with: [string, string]}] -// rules :: [{pr: number, re: RegExp, brackets: true, with: [string, string, Function]}] -function formatText(txt, rules) { - let idCounter = 0; - function nextId() { return idCounter++; } - function ft(txt, rules) { - let matches = rules.map(r => { r.re.lastIndex = 0; return [r, r.re.exec(txt)]; }) - .filter(([,m]) => m !== null) - .sort(([r1,m1],[r2,m2]) => (r1.pr - r2.pr) || (m1.index - m2.index)); - if (matches && matches.length > 0) { - let [rule, match] = matches[0]; - let subsequentRules = rules.filter(r => r.pr >= rule.pr); - let idStr = `<>(${nextId()})<>`; - let outerStr = txt.substring(0, match.index) + idStr + txt.substring(rule.re.lastIndex); - let innerStr = (rule.brackets) - ? (() => { let [l ,r ,f] = rule.with; return l + ft((f ? f(match[1]) : match[1]), subsequentRules) + r; })() - : match[0].replace(rule.re, rule.with); - return ft(outerStr, subsequentRules).replace(idStr, innerStr); - } - return txt; - } - return ft(htmlEscape(txt), rules); // idStr above relies on the fact the text is escaped -} - -function fixWwwLink(url) { - return url.replace(/^(?!([a-z]+:)?\/\/)/i, '//'); -} - -function makeNewNode(embedType, aNode, reResult) { - const withClasses = el => { - if (embedType.className) { - el.classList.add(...embedType.className.split(' ')); - } - return el; - }; - return embedType.makeNode(aNode, reResult, withClasses(document.createElement('div'))); -} - -function makeIframe(src, w, h, scrolling='no') { - let iframe = document.createElement('iframe'); - iframe.style.width = w; - iframe.style.height = h; - iframe.frameBorder = 0; - iframe.scrolling = scrolling; - iframe.setAttribute('allowFullScreen', ''); - iframe.src = src; - iframe.innerHTML = 'Cannot show iframes.'; - return iframe; -} - -function makeResizableToRatio(element, ratio) { - element.dataset['ratio'] = ratio; - makeResizable(element, w => w * element.dataset['ratio']); -} - -// calcHeight :: Number -> Number -- calculate element height for a given width -function makeResizable(element, calcHeight) { - const setHeight = el => { - if (document.body.contains(el) && (el.offsetWidth > 0)) { - el.style.height = (calcHeight(el.offsetWidth)).toFixed(2) + 'px'; - } - }; - window.addEventListener('resize', () => setHeight(element)); - setHeight(element); -} - -function extractDomain(url) { - const domainRe = /^(?:https?:\/\/)?(?:[^@\/\n]+@)?(?:www\.)?([^:\/\n]+)/i; - return domainRe.exec(url)[1]; -} - -function urlReplace(match, p1, p2, p3) { - let isBrackets = (p1 !== undefined); - return (isBrackets) - ? `<a href="${fixWwwLink(p2 || p3)}">${p1}</a>` - : `<a href="${fixWwwLink(match)}">${extractDomain(match)}</a>`; -} - -function urlReplaceInCode(match, p1, p2, p3) { - let isBrackets = (p1 !== undefined); - return (isBrackets) - ? `<a href="${fixWwwLink(p2 || p3)}">${match}</a>` - : `<a href="${fixWwwLink(match)}">${match}</a>`; -} - -function messageReplyReplace(messageId) { - return function(match, mid, rid) { - let replyPart = (rid && rid != '0') ? '#' + rid : ''; - return `<a href="/${mid || messageId}${replyPart}">${match}</a>`; - }; -} - -/** - * Given "txt" message in unescaped plaintext with Juick markup, this function - * returns escaped formatted HTML string. - * - * @param {string} txt - * @param {string} messageId - current message id - * @param {boolean} isCode - * @returns {string} - */ -function juickFormat(txt, messageId, isCode) { - const urlRe = /(?:\[([^\]\[]+)\](?:\[([^\]]+)\]|\(((?:[a-z]+:\/\/|www\.|ftp\.)(?:\([-\w+*&@#/%=~|$?!:;,.]*\)|[-\w+*&@#/%=~|$?!:;,.])*(?:\([-\w+*&@#/%=~|$?!:;,.]*\)|[\w+*&@#/%=~|$]))\))|\b(?:[a-z]+:\/\/|www\.|ftp\.)(?:\([-\w+*&@#/%=~|$?!:;,.]*\)|[-\w+*&@#/%=~|$?!:;,.])*(?:\([-\w+*&@#/%=~|$?!:;,.]*\)|[\w+*&@#/%=~|$]))/gi; - const bqReplace = m => m.replace(/^(?:>|>)\s?/gmi, ''); - return (isCode) - ? formatText(txt, [ - { pr: 1, re: urlRe, with: urlReplaceInCode }, - { pr: 1, re: /\B(?:#(\d+))?(?:\/(\d+))?\b/g, with: messageReplyReplace(messageId) }, - { pr: 1, re: /\B@([\w-]+)\b/gi, with: '<a href="/$1">@$1</a>' }, - ]) - : formatText(txt, [ - { pr: 0, re: /((?:^(?:>|>)\s?[\s\S]+?$\n?)+)/gmi, brackets: true, with: ['<q>', '</q>', bqReplace] }, - { pr: 1, re: urlRe, with: urlReplace }, - { pr: 1, re: /\B(?:#(\d+))?(?:\/(\d+))?\b/g, with: messageReplyReplace(messageId) }, - { pr: 1, re: /\B@([\w-]+)\b/gi, with: '<a href="/$1">@$1</a>' }, - { pr: 2, re: /\B\*([^\n]+?)\*((?=\s)|(?=$)|(?=[!\"#$%&'*+,\-./:;<=>?@[\]^_`{|}~()]+))/g, brackets: true, with: ['<b>', '</b>'] }, - { pr: 2, re: /\B\/([^\n]+?)\/((?=\s)|(?=$)|(?=[!\"#$%&'*+,\-./:;<=>?@[\]^_`{|}~()]+))/g, brackets: true, with: ['<i>', '</i>'] }, - { pr: 2, re: /\b\_([^\n]+?)\_((?=\s)|(?=$)|(?=[!\"#$%&'*+,\-./:;<=>?@[\]^_`{|}~()]+))/g, brackets: true, with: ['<span class="u">', '</span>'] }, - { pr: 3, re: /\n/g, with: '<br/>' }, - ]); -} - -function getEmbeddableLinkTypes() { - return [ - { - name: 'Jpeg and png images', - id: 'embed_jpeg_and_png_images', - className: 'picture compact', - ctsDefault: false, - re: /\.(jpe?g|png|svg)(:[a-zA-Z]+)?(?:\?[\w&;\?=]*)?$/i, - makeNode: function(aNode, reResult, div) { - div.innerHTML = `<a href="${aNode.href}"><img src="${aNode.href}"></a>`; - return div; - } - }, - { - name: 'Gif images', - id: 'embed_gif_images', - className: 'picture compact', - ctsDefault: true, - re: /\.gif(:[a-zA-Z]+)?(?:\?[\w&;\?=]*)?$/i, - makeNode: function(aNode, reResult, div) { - div.innerHTML = `<a href="${aNode.href}"><img src="${aNode.href}"></a>`; - return div; - } - }, - { - name: 'Video (webm, mp4, ogv)', - id: 'embed_webm_and_mp4_videos', - className: 'video compact', - ctsDefault: false, - re: /\.(webm|mp4|m4v|ogv)(?:\?[\w&;\?=]*)?$/i, - makeNode: function(aNode, reResult, div) { - div.innerHTML = `<video src="${aNode.href}" title="${aNode.href}" controls></video>`; - return div; - } - }, - { - name: 'Audio (mp3, ogg, weba, opus, m4a, oga, wav)', - id: 'embed_sound_files', - className: 'audio singleColumn', - ctsDefault: false, - re: /\.(mp3|ogg|weba|opus|m4a|oga|wav)(?:\?[\w&;\?=]*)?$/i, - makeNode: function(aNode, reResult, div) { - div.innerHTML = `<audio src="${aNode.href}" title="${aNode.href}" controls></audio>`; - return div; - } - }, - { - name: 'YouTube videos (and playlists)', - id: 'embed_youtube_videos', - className: 'youtube resizableV singleColumn', - ctsDefault: false, - re: /^(?:https?:)?\/\/(?:www\.|m\.|gaming\.)?(?:youtu(?:(?:\.be\/|be\.com\/(?:v|embed)\/)([-\w]+)|be\.com\/watch)((?:(?:\?|&(?:amp;)?)(?:\w+=[-\.\w]*[-\w]))*)|youtube\.com\/playlist\?list=([-\w]*)(&(amp;)?[-\w\?=]*)?)/i, - makeNode: function(aNode, reResult, div) { - let [url, v, args, plist] = reResult; - let iframeUrl; - if (plist) { - iframeUrl = '//www.youtube-nocookie.com/embed/videoseries?list=' + plist; - } else { - let pp = {}; args.replace(/^\?/, '') - .split('&') - .map(s => s.split('=')) - .forEach(z => pp[z[0]] = z[1]); - let embedArgs = { rel: '0' }; - if (pp.t) { - const tre = /^(?:(\d+)|(?:(\d+)h)?(?:(\d+)m)?(\d+)s|(?:(\d+)h)?(\d+)m|(\d+)h)$/i; - let [, t, h, m, s, h1, m1, h2] = tre.exec(pp.t); - embedArgs['start'] = (+t) || ((+(h || h1 || h2 || 0))*60*60 + (+(m || m1 || 0))*60 + (+(s || 0))); - } - if (pp.list) { - embedArgs['list'] = pp.list; - } - v = v || pp.v; - let argsStr = Object.keys(embedArgs) - .map(k => `${k}=${embedArgs[k]}`) - .join('&'); - iframeUrl = `//www.youtube-nocookie.com/embed/${v}?${argsStr}`; - } - let iframe = makeIframe(iframeUrl, '100%', '360px'); - iframe.onload = () => makeResizableToRatio(iframe, 9.0 / 16.0); - return setContent(div, iframe); - } - }, - { - name: 'Vimeo videos', - id: 'embed_vimeo_videos', - className: 'vimeo resizableV', - ctsDefault: false, - re: /^(?:https?:)?\/\/(?:www\.)?(?:player\.)?vimeo\.com\/(?:video\/|album\/[\d]+\/video\/)?([\d]+)/i, - makeNode: function(aNode, reResult, div) { - let iframe = makeIframe('//player.vimeo.com/video/' + reResult[1], '100%', '360px'); - iframe.onload = () => makeResizableToRatio(iframe, 9.0 / 16.0); - return setContent(div, iframe); - } - } - ]; -} - -function embedLink(aNode, linkTypes, container, afterNode) { - let anyEmbed = false; - let linkId = (aNode.href.replace(/^https?:/i, '').replace(/\'/gi,'')); - let sameEmbed = container.querySelector(`*[data-linkid='${linkId}']`); // do not embed the same thing twice - if (sameEmbed === null) { - anyEmbed = [].some.call(linkTypes, function(linkType) { - let reResult = linkType.re.exec(aNode.href); - if (reResult) { - if (linkType.match && (linkType.match(aNode, reResult) === false)) { return false; } - let newNode = makeNewNode(linkType, aNode, reResult); - if (!newNode) { return false; } - newNode.setAttribute('data-linkid', linkId); - if (afterNode) { - insertAfter(newNode, afterNode); - } else { - container.appendChild(newNode); - } - aNode.classList.add('embedLink'); - return true; - } - }); - } - return anyEmbed; -} - -function embedLinks(aNodes, container) { - let anyEmbed = false; - let embeddableLinkTypes = getEmbeddableLinkTypes(); - Array.from(aNodes).forEach(aNode => { - let isEmbedded = embedLink(aNode, embeddableLinkTypes, container); - anyEmbed = anyEmbed || isEmbedded; - }); - return anyEmbed; -} - -/** - * Embed all the links inside element "x" that match to "allLinksSelector". - * All the embedded media is placed inside "div.embedContainer". - * "div.embedContainer" is inserted before an element matched by "beforeNodeSelector" - * if not present. Existing container is used otherwise. - * - * @param {Element} x - * @param {string} beforeNodeSelector - * @param {string} allLinksSelector - */ -export function embedLinksToX(x, beforeNodeSelector, allLinksSelector) { - let isCtsPost = false; - let allLinks = x.querySelectorAll(allLinksSelector); - - let existingContainer = x.querySelector('div.embedContainer'); - if (existingContainer) { - embedLinks(allLinks, existingContainer); - } else { - let embedContainer = document.createElement('div'); - embedContainer.className = 'embedContainer'; - - let anyEmbed = embedLinks(allLinks, embedContainer); - if (anyEmbed) { - let beforeNode = x.querySelector(beforeNodeSelector); - x.insertBefore(embedContainer, beforeNode); - } - } -} - -function embedLinksToArticles() { - let beforeNodeSelector = 'nav.l'; - let allLinksSelector = 'p:not(.ir) a, pre a'; - Array.from(document.querySelectorAll('#content article')).forEach(article => { - embedLinksToX(article, beforeNodeSelector, allLinksSelector); - }); -} - -function embedLinksToPost() { - let beforeNodeSelector = '.msg-txt + *'; - let allLinksSelector = '.msg-txt a'; - Array.from(document.querySelectorAll('#content .msg-cont')).forEach(msg => { - embedLinksToX(msg, beforeNodeSelector, allLinksSelector); - }); -} - -/** - * Embed all the links in all messages/replies on the page. - */ -export function embedAll() { - if (document.querySelector('#content article[data-mid]')) { - embedLinksToArticles(); - } else { - embedLinksToPost(); - } -} - -export const format = juickFormat; diff --git a/juick-server/src/main/assets/logo.png b/juick-server/src/main/assets/logo.png Binary files differdeleted file mode 100644 index 4e0f6d56..00000000 --- a/juick-server/src/main/assets/logo.png +++ /dev/null diff --git a/juick-server/src/main/assets/logo@2x.png b/juick-server/src/main/assets/logo@2x.png Binary files differdeleted file mode 100644 index 6febeaf9..00000000 --- a/juick-server/src/main/assets/logo@2x.png +++ /dev/null diff --git a/juick-server/src/main/assets/scripts.js b/juick-server/src/main/assets/scripts.js deleted file mode 100644 index 9ee0639e..00000000 --- a/juick-server/src/main/assets/scripts.js +++ /dev/null @@ -1,935 +0,0 @@ -require('element-closest'); -require('classlist.js'); -require('url-polyfill'); -require('formdata-polyfill'); -import { embedLinksToX, embedAll, format } from './embed'; - -if (!('remove' in Element.prototype)) { // Firefox <23 - Element.prototype.remove = function() { - if (this.parentNode) { - this.parentNode.removeChild(this); - } - }; -} - -NodeList.prototype.forEach = Array.prototype.forEach; -HTMLCollection.prototype.forEach = Array.prototype.forEach; - -NodeList.prototype.filter = Array.prototype.filter; -HTMLCollection.prototype.filter = Array.prototype.filter; - -Element.prototype.selectText = function() { - let d = document; - if (d.body.createTextRange) { - let range = d.body.createTextRange(); - range.moveToElementText(this); - range.select(); - } else if (window.getSelection) { - let selection = window.getSelection(); - let rangeSel = d.createRange(); - rangeSel.selectNodeContents(this); - selection.removeAllRanges(); - selection.addRange(rangeSel); - } -}; - -function autosize(el) { - let offset = (!window.opera) - ? (el.offsetHeight - el.clientHeight) - : (el.offsetHeight + parseInt(window.getComputedStyle(el, null).getPropertyValue('border-top-width'))); - - let resize = function(el) { - el.style.height = 'auto'; - el.style.height = (el.scrollHeight + offset) + 'px'; - }; - - if (el.addEventListener) { - el.addEventListener('input', () => resize(el)); - } else if (el.attachEvent) { - el.attachEvent('onkeyup', () => resize(el)); - } -} - -function evilIcon(name) { - return `<div class="icon icon--${name}"><svg class="icon__cnt"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#${name}-icon"></use></svg></div>`; -} - -/* eslint-disable only-ascii/only-ascii */ -const translations = { - 'en': { - 'message.inReplyTo': 'in reply to', - 'message.reply': 'Reply', - 'message.likeThisMessage?': 'Recommend this message?', - 'postForm.pleaseInputMessageText': 'Please input message text', - 'postForm.upload': 'Upload', - 'postForm.newMessage': 'New message...', - 'postForm.imageLink': 'Link to image', - 'postForm.imageFormats': 'JPG/PNG, up to 10 MB', - 'postForm.or': 'or', - 'postForm.tags': 'Tags (space separated)', - 'postForm.submit': 'Send', - 'comment.writeComment': 'Write a comment...', - 'shareDialog.linkToMessage': 'Link to message', - 'shareDialog.messageNumber': 'Message number', - 'shareDialog.share': 'Share', - 'loginDialog.pleaseIntroduceYourself': 'Please introduce yourself', - 'loginDialog.registeredAlready': 'Registered already?', - 'loginDialog.username': 'Username', - 'loginDialog.password': 'Password', - 'loginDialog.facebook': 'Login with Facebook', - 'loginDialog.vk': 'Login with VK', - 'loginDialog.email': 'Registration', - 'error.error': 'Error' - }, - 'ru': { - 'message.inReplyTo': 'в ответ на', - 'message.reply': 'Ответить', - 'message.likeThisMessage?': 'Рекомендовать это сообщение?', - 'postForm.pleaseInputMessageText': 'Пожалуйста, введите текст сообщения', - 'postForm.upload': 'загрузить', - 'postForm.newMessage': 'Новое сообщение...', - 'postForm.imageLink': 'Ссылка на изображение', - 'postForm.imageFormats': 'JPG/PNG, до 10Мб', - 'postForm.or': 'или', - 'postForm.tags': 'Теги (через пробел)', - 'postForm.submit': 'Отправить', - 'comment.writeComment': 'Написать комментарий...', - 'shareDialog.linkToMessage': 'Ссылка на сообщение', - 'shareDialog.messageNumber': 'Номер сообщения', - 'shareDialog.share': 'Поделиться', - 'loginDialog.pleaseIntroduceYourself': 'Пожалуйста, представьтесь', - 'loginDialog.registeredAlready': 'Уже зарегистрированы?', - 'loginDialog.username': 'Имя пользователя', - 'loginDialog.password': 'Пароль', - 'loginDialog.facebook': 'Войти через Facebook', - 'loginDialog.vk': 'Войти через ВКонтакте', - 'loginDialog.email': 'Регистрация', - 'error.error': 'Ошибка' - } -}; -/* eslint-enable only-ascii/only-ascii */ - -function getLang() { - return (window.navigator.languages && window.navigator.languages[0]) - || window.navigator.userLanguage - || window.navigator.language; -} -function i18n(key, lang = undefined) { - const fallbackLang = 'ru'; - lang = lang || getLang().split('-')[0]; - return (translations[lang] && translations[lang][key]) - || translations[fallbackLang][key] - || key; -} - -var es, pageTitle; - -function initES() { - if (!('EventSource' in window)) { - return; - } - let url = '/api/events'; - let hash = document.getElementById('body').getAttribute('data-hash'); - if (hash) { - url += '?hash=' + hash; - } - - es = new EventSource(url); - es.onopen = function() { - console.log('online'); - if (!document.querySelector('#wsthread')) { - var d = document.createElement('div'); - d.id = 'wsthread'; - d.addEventListener('click', nextReply); - document.querySelector('body').appendChild(d); - pageTitle = document.title; - } - }; - es.addEventListener('msg', msg => { - try { - var jsonMsg = JSON.parse(msg.data); - console.log('data: ' + msg.data); - if (jsonMsg.service) { - return; - } - wsIncomingReply(jsonMsg); - } catch (err) { - console.log(err); - } - }); -} - -function wsIncomingReply(msg) { - let content = document.getElementById('content'); - if (!content) { return; } - let pageMID = content.getAttribute('data-mid'); - if (!pageMID || pageMID != msg.mid) { return; } - let msgNum = '/' + msg.rid; - if (msg.replyto > 0) { - msgNum += ` ${i18n('message.inReplyTo')} <a href="#${msg.replyto}">/${msg.replyto}</a>`; - } - let photoDiv = (msg.attach == null) ? '' : ` - <div class="msg-media"><a href="//i.juick.com/p/${msg.mid}-${msg.rid}.${msg.attach}"> - <img src="//i.juick.com/photos-512/${msg.mid}-${msg.rid}.${msg.attach}"/></a> - </div>`; - let msgContHtml = ` - <div class="msg-cont"> - <div class="msg-header"> - <a href="/${msg.user.uname}/">${msg.user.uname}</a>: - <div class="msg-avatar"> - <a href="/${msg.user.uname}/"><img src="//i.juick.com/a/${msg.user.uid}.png" alt="${msg.user.uname}"/></a> - </div> - <div class="msg-ts"> - <a href="/m/${msg.mid}#${msg.rid}" title="${msg.timestamp}GMT">${msg.timestamp}</a> - </div> - </div> - <div class="msg-txt">${format(msg.body, msg.mid, false)}</div>${photoDiv} - <div class="msg-links">${msgNum} · <a class="msg-reply-link" href="#">${i18n('message.reply')}</a></div> - <div class="msg-comment-target msg-comment-hidden"></div> - </div>`; - - let li = document.createElement('li'); - li.setAttribute('class', 'msg reply-new'); - li.setAttribute('id', msg.rid); - li.innerHTML = msgContHtml; - li.addEventListener('click', newReply); - li.addEventListener('mouseover', newReply); - li.querySelector('a.msg-reply-link').addEventListener('click', function(e) { - showCommentForm(msg.mid, msg.rid); - e.preventDefault(); - }); - - embedLinksToX(li.querySelector('.msg-cont'), '.msg-links', '.msg-txt a'); - - document.getElementById('replies').appendChild(li); - - updateRepliesCounter(); -} - -function newReply(e) { - var li = e.target; - li.classList.remove('reply-new'); - li.removeEventListener('click', e); - li.removeEventListener('mouseover', e); - updateRepliesCounter(); -} - -function nextReply() { - var li = document.querySelector('#replies>li.reply-new'); - if (li) { - li.classList.remove('reply-new'); - li.removeEventListener('click', this); - li.children[0].scrollIntoView(); - updateRepliesCounter(); - } -} - -function updateRepliesCounter() { - var replies = document.querySelectorAll('#replies>li.reply-new').length; - var wsthread = document.getElementById('wsthread'); - if (replies) { - wsthread.textContent = replies; - wsthread.style.display = 'block'; - document.title = '[' + replies + '] ' + pageTitle; - } else { - wsthread.style.display = 'none'; - document.title = pageTitle; - } -} - -/******************************************************************************/ -/******************************************************************************/ -/******************************************************************************/ - -function postformListener(formEl, ev) { - if (ev.ctrlKey && (ev.keyCode == 10 || ev.keyCode == 13)) { - let form = formEl.closest('form'); - if (!form.onsubmit || form.onsubmit()) { - form.querySelector('input[type="submit"]').click(); - } - } -} -function closeDialogListener(ev) { - ev = ev || window.event; - if (ev.keyCode == 27) { - closeDialog(); - } -} - -function newMessage(evt) { - document.querySelectorAll('#newmessage .dialogtxt').forEach(t => { - t.remove(); - }); - if (document.querySelector('#newmessage textarea').value.length == 0 - && document.querySelector('#newmessage .img').value.length == 0 - && !document.querySelector('#newmessage input[type="file"]')) { - document.querySelector('#newmessage').insertAdjacentHTML('afterbegin', `<p class="dialogtxt">${i18n('postForm.pleaseInputMessageText')}</p>`); - evt.preventDefault(); - } -} - -function handleErrors(response) { - if (!response.ok) { - throw Error(response.statusText); - } - return response; -} - -function showCommentForm(mid, rid) { - let reply = document.getElementById(rid); - let formTarget = reply.querySelector('div.msg-cont .msg-comment-target'); - if (formTarget) { - let formHtml = ` - <form> - <input type="hidden" name="mid" value="${mid}"> - <input type="hidden" name="rid" value="${rid}"> - <div class="msg-comment"> - <div class="ta-wrapper"> - <textarea name="body" rows="1" class="reply" placeholder="${i18n('comment.writeComment')}"></textarea> - <div class="attach-photo">${evilIcon('ei-camera')}</div> - </div> - <input type="submit" value="OK"> - </div> - </form>`; - formTarget.insertAdjacentHTML('afterend', formHtml); - formTarget.remove(); - - let form = reply.querySelector('form'); - let submitButton = form.querySelector('input[type="submit"]'); - - let attachButton = form.querySelector('.msg-comment .attach-photo'); - attachButton.addEventListener('click', e => attachCommentPhoto(e.target)); - - let textarea = form.querySelector('.msg-comment textarea'); - textarea.addEventListener('keypress', e => postformListener(e.target, e)); - autosize(textarea); - - let validateMessage = () => { - let len = textarea.value.length; - if (len > 4096) { return 'Message is too long'; } - return ''; - }; - form.addEventListener('submit', e => { - let validationResult = validateMessage(); - if (validationResult) { - e.preventDefault(); - alert(validationResult); - return false; - } - submitButton.disabled = true; - let formData = new FormData(form); - fetch('/api/comment' + '?hash=' + document.getElementById('body').getAttribute('data-hash'), { - method: 'POST', - body: formData, - credentials: 'omit' - }).then(handleErrors) - .then(response => { - if (response.ok) { - response.json().then(result => { - if (result.newMessage) { - window.location.href = new URL(`${mid}#${result.newMessage.rid}`, window.location.href); - } else { - alert(result.text); - } - window.location.reload(true); - }); - } - }).catch(error => { - alert(error.message); - }); - e.preventDefault(); - }); - } - reply.querySelector('.msg-comment textarea').focus(); -} - -function attachInput() { - let inp = document.createElement('input'); - inp.setAttribute('type', 'file'); - inp.setAttribute('name', 'attach'); - inp.setAttribute('accept', 'image/jpeg,image/png'); - inp.style.visibility = 'hidden'; - return inp; -} - -function attachCommentPhoto(div) { - let input = div.querySelector('input'); - if (input) { - input.remove(); - div.classList.remove('attach-photo-active'); - } else { - let newInput = attachInput(); - newInput.addEventListener('change', function() { - div.classList.add('attach-photo-active'); - }); - newInput.click(); - div.appendChild(newInput); - } -} - -function attachMessagePhoto(div) { - var f = div.closest('form'), - finput = f.querySelector('input[type="file"]'); - if (!finput) { - var inp = attachInput(); - inp.style.float = 'left'; - inp.style.width = 0; - inp.style.height = 0; - inp.addEventListener('change', function() { - div.textContent = i18n('postForm.upload') + ' (✓)'; - }); - f.appendChild(inp); - inp.click(); - } else { - finput.remove(); - div.textContent = i18n('postForm.upload'); - } -} - -function showMessageLinksDialog(mid, rid) { - let hlink = window.location.protocol + '//juick.com/' + mid; - let mlink = '#' + mid; - if (rid > 0) { - hlink += '#' + rid; - mlink += '/' + rid; - } - let hlinkenc = encodeURIComponent(hlink); - let html = ` - <div class="dialogshare"> - ${i18n('shareDialog.linkToMessage')}: <div onclick="this.selectText()" class="dialogl">${hlink}</div> - ${i18n('shareDialog.messageNumber')}: <div onclick="this.selectText()" class="dialogl">${mlink}</div> - ${i18n('shareDialog.share')}: - <ul> - <li><a href="https://www.facebook.com/sharer/sharer.php?u=${hlinkenc}" onclick="return openSocialWindow(this)">${evilIcon('ei-sc-facebook')}</a></li> - <li><a href="https://twitter.com/intent/tweet?url=${hlinkenc}" onclick="return openSocialWindow(this)">${evilIcon('ei-sc-twitter')}</a></li> - <li><a href="https://vk.com/share.php?url=${hlinkenc}" onclick="return openSocialWindow(this)">${evilIcon('ei-sc-vk')}</a></li> - </ul> - </div>`; - - openDialog(html); -} - -function showPhotoDialog(fname) { - let width = window.innerWidth; - let height = window.innerHeight; - let minDimension = (width < height) ? width : height; - if (minDimension < 640) { - return true; // no dialog, open the link - } else if (minDimension < 1280) { - openDialog(`<a href="//i.juick.com/p/${fname}"><img src="//i.juick.com/photos-1024/${fname}"/></a>`, true); - return false; - } else { - openDialog(`<a href="//i.juick.com/p/${fname}"><img src="//i.juick.com/p/${fname}"/></a>`, true); - return false; - } -} - -function openPostDialog() { - let newmessageTemplate = ` - <form id="newmessage" action="/post" method="post" enctype="multipart/form-data"> - <textarea name="body" placeholder="${i18n('postForm.newMessage')}"></textarea> - <div> - <input class="img" name="img" placeholder="${i18n('postForm.imageLink')} (${i18n('postForm.imageFormats')})"/> - ${i18n('postForm.or')} <a href="#">${i18n('postForm.upload')}</a><br/> - <input id="tags_input" class="tags" name="tags" placeholder="${i18n('postForm.tags')}"/><br/> - <input type="submit" class="subm" value="${i18n('postForm.submit')}"/> - </div> - </form> - `; - return openDialog(newmessageTemplate); -} - -function openDialog(html, image) { - var dialogHtml = ` - <div id="dialogt"> - <div id="dialogb"></div> - <div id="dialogw"> - <div id="dialog_header"> - <div id="dialogc">${evilIcon('ei-close')}</div> - </div> - ${html} - </div> - </div>`; - let body = document.querySelector('body'); - body.classList.add('dialog-opened'); - body.insertAdjacentHTML('afterbegin', dialogHtml); - if (image) { - let header = document.querySelector('#dialog_header'); - header.classList.add('header_image'); - } - document.addEventListener('keydown', closeDialogListener); - document.querySelector('#dialogb').addEventListener('click', closeDialog); - document.querySelector('#dialogc').addEventListener('click', closeDialog); -} - -function closeDialog() { - let draft = document.querySelector('#newmessage textarea'); - if (draft) { - window.draft = draft.value; - } - document.querySelector('body').classList.remove('dialog-opened'); - document.querySelector('#dialogb').remove(); - document.querySelector('#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 = document.querySelector('#username').textContent, - style = document.querySelector('#username').style; - fetch('/api/users?uname=' + uname) - .then(handleErrors) - .then(function() { - style.background = '#FFCCCC'; - }) - .catch(function() { - style.background = '#CCFFCC'; - }); -} - -/******************************************************************************/ - -function openDialogLogin() { - let html = ` - <div class="dialoglogin"> - <p>${i18n('loginDialog.pleaseIntroduceYourself')}:</p> - <a href="mailto:juick@juick.com?subject=LOGIN" id="signemail">${evilIcon('ei-envelope')}${i18n('loginDialog.email')}</a> - <a href="/_fblogin" id="signfb">${evilIcon('ei-sc-facebook')}${i18n('loginDialog.facebook')}</a> - <a href="/_vklogin" id="signvk">${evilIcon('ei-sc-vk')}${i18n('loginDialog.vk')}</a> - <p>${i18n('loginDialog.registeredAlready')}</p> - <form action="/login" method="POST"> - <input class="signinput" type="text" name="username" placeholder="${i18n('loginDialog.username')}"/><br/> - <input class="signinput" type="password" name="password" placeholder="${i18n('loginDialog.password')}"/><br/> - <input class="signsubmit" type="submit" value="OK"/> - </form> - </div>`; - openDialog(html); - return false; -} - -/******************************************************************************/ - -function resultMessage(str) { - var result = document.createElement('p'); - result.textContent = str; - return result; -} - -function likeMessage(e, mid) { - if (confirm(i18n('message.likeThisMessage?'))) { - fetch('/api/like?mid=' + mid - + '&hash=' + document.getElementById('body').getAttribute('data-hash'), { - method: 'POST', - credentials: 'omit' - }) - .then(handleErrors) - .then(function(response) { - if (response.ok) { - e.closest('article').appendChild(resultMessage('OK!')); - } - }) - .catch(function() { - e.closest('article').appendChild(resultMessage(i18n('error.error'))); - }); - } - return false; -} - -function subscribeMessage(e, mid) { - fetch('/api/subscribe?mid=' + mid - + '&hash=' + document.getElementById('body').getAttribute('data-hash'), { - method: 'POST', - credentials: 'omit' - }) - .then(handleErrors) - .then(function(response) { - if (response.ok) { - window.location.reload(true); - } else { - alert('Something went wrong :('); - } - }) - .catch(error => { - alert(error.message); - }); - return false; -} - -/******************************************************************************/ - -function setPopular(e, mid, popular) { - fetch('/api/messages/set_popular?mid=' + mid - + '&popular=' + popular - + '&hash=' + document.getElementById('body').getAttribute('data-hash'), { - credentials: 'same-origin' - }) - .then(handleErrors) - .then(function() { - e.closest('article').append(resultMessage('OK!')); - }); - return false; -} - -function setPrivacy(e, mid) { - fetch('/api/messages/set_privacy?mid=' + mid - + '&hash=' + document.getElementById('body').getAttribute('data-hash'), { - credentials: 'same-origin' - }) - .then(handleErrors) - .then(function() { - e.closest('article').append(resultMessage('OK!')); - }); - return false; -} - -function getTags() { - fetch('/api/tags?hash=' + document.getElementById('body').getAttribute('data-hash'), { - credentials: 'omit' - }) - .then(handleErrors) - .then(response => { - return response.json(); - }) - .then(json => { - let tags = json.map(t => t.tag); - let input = document.getElementById('tags_input'); - }); - return false; -} - -function addTag(tag) { - document.forms['postmsg'].body.value = '*' + tag + ' ' + document.forms['postmsg'].body.value; - return false; -} - -var users = {}; - -function fetchUserUri(dataUri, callback) { - if (users[dataUri]) { - callback(users[dataUri]); - } else { - let data = new FormData(); - data.append('uri', dataUri); - fetch('/u/', { - method: 'POST', - body: data - }).then(handleErrors) - .then(response => { - return response.json(); - }) - .then(json => { - users[dataUri] = json; - callback(json); - }); - } -} - -/******************************************************************************/ - -function ready(fn) { - if (document.readyState != 'loading') { - fn(); - } else { - document.addEventListener('DOMContentLoaded', fn); - } -} - -ready(function() { - document.querySelectorAll('textarea').forEach((ta) => { - autosize(ta); - }); - - var insertPMButtons = function(e) { - e.target.classList.add('narrowpm'); - e.target.parentNode.insertAdjacentHTML('afterend', '<input type="submit" value="OK"/>'); - e.target.removeEventListener('click', insertPMButtons); - e.preventDefault(); - }; - document.querySelectorAll('textarea.replypm').forEach(function(e) { - e.addEventListener('click', insertPMButtons); - e.addEventListener('keypress', function(e) { - postformListener(e.target, e); - }); - }); - document.querySelectorAll('#postmsg textarea').forEach(function(e) { - e.addEventListener('keypress', function(e) { - postformListener(e.target, e); - }); - }); - - var content = document.getElementById('content'); - if (content) { - var pageMID = content.getAttribute('data-mid'); - if (pageMID > 0) { - document.querySelectorAll('li.msg').forEach(li => { - let showReplyFormBtn = li.querySelector('.a-thread-comment'); - if (showReplyFormBtn) { - showReplyFormBtn.addEventListener('click', function(e) { - showCommentForm(pageMID, li.id); - e.preventDefault(); - }); - } - }); - let opMessage = document.querySelector('.msgthread'); - if (opMessage) { - let replyTextarea = opMessage.querySelector('textarea.reply'); - if (replyTextarea) { - replyTextarea.addEventListener('focus', e => showCommentForm(pageMID, 0)); - replyTextarea.addEventListener('keypress', e => postformListener(e.target, e)); - if (!window.location.hash) { - replyTextarea.focus(); - } - } - } - } - } - - var postmsg = document.getElementById('postmsg'); - if (postmsg) { - document.querySelectorAll('a').filter(t => t.href.indexOf('?') >= 0).forEach(t => { - t.addEventListener('click', e => { - let params = new URLSearchParams(t.href.slice(t.href.indexOf('?') + 1)); - if (params.has('tag')) { - addTag(params.get('tag')); - e.preventDefault(); - } - }); - }); - postmsg.addEventListener('submit', e => { - let formData = new FormData(postmsg); - fetch('/api/post' + '?hash=' + document.getElementById('body').getAttribute('data-hash'), { - method: 'POST', - body: formData, - credentials: 'omit' - }).then(handleErrors) - .then(response => { - if (response.ok) { - response.json().then(result => { - if (result.newMessage) { - window.location = new URL(`/m/${result.newMessage.mid}`, window.location.href); - } else { - alert(result.text); - } - }); - } else { - alert('Something went wrong :('); - } - }).catch(error => { - alert(error.message); - }); - e.preventDefault(); - }); - } - document.querySelectorAll('.pmmsg').forEach(pmmsg => { - pmmsg.addEventListener('submit', e => { - let formData = new FormData(pmmsg); - fetch('/api/pm' + '?hash=' + document.getElementById('body').getAttribute('data-hash'), { - method: 'POST', - body: formData, - credentials: 'omit' - }).then(handleErrors) - .then(response => { - if (response.ok) { - response.json().then(result => { - if (result.to) { - window.location = new URL('/pm/sent', window.location.href); - } else { - alert('Something went wrong :('); - } - }); - } else { - alert('Something went wrong :('); - } - }).catch(error => { - alert(error.message); - }); - e.preventDefault(); - }); - }); - - document.querySelectorAll('.msg-menu').forEach(function(el) { - el.addEventListener('click', function(e) { - var reply = e.target.closest('li'); - var rid = reply ? parseInt(reply.id) : 0; - var message = e.target.closest('section'); - var mid = message.getAttribute('data-mid') || e.target.closest('article').getAttribute('data-mid'); - showMessageLinksDialog(mid, rid); - e.preventDefault(); - }); - }); - document.querySelectorAll('.l .a-privacy').forEach(function(e) { - e.addEventListener('click', function(e) { - setPrivacy( - e.target, - e.target.closest('article').getAttribute('data-mid')); - e.preventDefault(); - }); - }); - document.querySelectorAll('.ir a[data-fname], .msg-media a[data-fname]').forEach(function(el) { - el.addEventListener('click', function(e) { - let fname = e.target.closest('[data-fname]').getAttribute('data-fname'); - if (!showPhotoDialog(fname)) { - e.preventDefault(); - } - }); - }); - document.querySelectorAll('.social a').forEach(function(e) { - e.addEventListener('click', function(e) { - openSocialWindow(e.target); - e.preventDefault(); - }); - }); - var username = document.getElementById('username'); - if (username) { - username.addEventListener('blur', function() { - checkUsername(); - }); - } - - document.querySelectorAll('.l .a-like').forEach(function(e) { - e.addEventListener('click', function(e) { - likeMessage( - e.target, - e.target.closest('article').getAttribute('data-mid')); - e.preventDefault(); - }); - }); - document.querySelectorAll('.l .a-sub').forEach(function(e) { - e.addEventListener('click', function(e) { - subscribeMessage( - e.target, - document.getElementById('content').getAttribute('data-mid')); - e.preventDefault(); - }); - }); - document.querySelectorAll('.a-login').forEach(function(el) { - el.addEventListener('click', function(e) { - openDialogLogin(); - e.preventDefault(); - }); - }); - var unfoldall = document.getElementById('unfoldall'); - if (unfoldall) { - unfoldall.addEventListener('click', function(e) { - document.querySelectorAll('#replies>li').forEach(function(e) { - e.style.display = 'block'; - }); - document.querySelectorAll('#replies .msg-comments').forEach(function(e) { - e.style.display = 'none'; - }); - e.preventDefault(); - }); - } - document.querySelectorAll('article').forEach(function(article) { - if (Array.prototype.some.call( - article.querySelectorAll('.msg-tags a'), - function(a) { - return a.textContent === 'NSFW'; - } - )) { - article.classList.add('nsfw'); - } - }); - document.querySelectorAll('[data-uri]').forEach(el => { - let dataUri = el.getAttribute('data-uri'); - if (dataUri) { - setTimeout(() => fetchUserUri(dataUri, user => { - let header = el.closest('.msg-header'); - header.querySelectorAll('.a-username').forEach(a => { - a.setAttribute('href', user.uri); - let img = a.querySelector('img'); - if (img && user.avatar) { - img.setAttribute('src', user.avatar); - img.setAttribute('alt', user.uname); - } - let textNode = a.childNodes[0]; - if (textNode.nodeType === Node.TEXT_NODE && textNode.nodeValue.trim().length > 0) { - let uname = document.createTextNode(user.uname); - a.replaceChild(uname, a.firstChild); - } - }); - }), 100); - } - }); - document.querySelectorAll('[data-user-uri]').forEach(el => { - let dataUri = el.getAttribute('href'); - if (dataUri) { - setTimeout(() => fetchUserUri(dataUri, user => { - let textNode = el.childNodes[0]; - if (textNode.nodeType === Node.TEXT_NODE && textNode.nodeValue.trim().length > 0) { - let uname = document.createTextNode(`@${user.uname}`); - el.replaceChild(uname, el.firstChild); - } - }), 100); - } - }); - initES(); - - embedAll(); - var elSelector = 'header', - elClassHidden = 'header--hidden', - elClassBackground = 'header--background', - throttleTimeout = 500, - element = document.querySelector(elSelector); - - if (element) { - - var dHeight = 0, - wHeight = 0, - wScrollCurrent = 0, - wScrollBefore = 0, - wScrollDiff = 0, - - throttle = function(delay, fn) { - var last, deferTimer; - return function() { - var context = this, args = arguments, now = +new Date; - if (last && now < last + delay) { - clearTimeout(deferTimer); - deferTimer = setTimeout( - function() { - last = now; - fn.apply(context, args); - }, - delay); - } else { - last = now; - fn.apply(context, args); - } - }; - }; - - window.addEventListener('scroll', throttle(throttleTimeout, function() { - dHeight = document.body.offsetHeight; - wHeight = window.innerHeight; - wScrollCurrent = window.pageYOffset; - wScrollDiff = wScrollBefore - wScrollCurrent; - - if (wScrollCurrent <= 0) { - // scrolled to the very top; element sticks to the top - element.classList.remove(elClassHidden); - element.classList.remove(elClassBackground); - } else if (wScrollDiff > 0 && element.classList.contains(elClassHidden)) { - // scrolled up; element slides in - element.classList.remove(elClassHidden); - element.classList.add(elClassBackground); - } else if (wScrollDiff < 0) { - // scrolled down - if (wScrollCurrent + wHeight >= dHeight && element.classList.contains(elClassHidden)) { - // scrolled to the very bottom; element slides in - element.classList.remove(elClassHidden); - element.classList.add(elClassBackground); - } else { - // scrolled down; element slides out - element.classList.add(elClassHidden); - } - } - - wScrollBefore = wScrollCurrent; - })); - } -}); diff --git a/juick-server/src/main/assets/style.css b/juick-server/src/main/assets/style.css deleted file mode 100644 index 02c59aad..00000000 --- a/juick-server/src/main/assets/style.css +++ /dev/null @@ -1,956 +0,0 @@ -/* #region generic */ - -html, -body, -div, -h1, -h2, -ul, -li, -p, -form, -input, -textarea, -pre { - margin: 0; - padding: 0; -} -textarea { - overflow: auto; -} -html, -input, -textarea { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; - font-size: 12pt; - -webkit-font-smoothing: subpixel-antialiased; -} -h1, -h2 { - font-weight: normal; -} -ul { - list-style-type: none; -} -a { - color: #069; - text-decoration: none; -} -img, -hr { - border: none; -} -hr { - background: #CCC; - height: 1px; - margin: 10px 0; -} -pre { - background: #1e2028; - color: #41b645; - overflow-x: auto; - padding: 6px 20px; - white-space: pre; -} -pre::selection { - background: #41b645; - color: #1e2028; -} -pre::-moz-selection { - background: #41b645; - color: #1e2028; -} -.u { - text-decoration: underline; -} - -/* #endregion */ - -/* #region overall layout */ - -html { - background: #f8f8f8; - color: #222; -} -#wrapper { - margin: 0 auto; - width: 1000px; - margin-top: 52px; -} -#column { - float: left; - margin-left: 10px; - overflow: hidden; - padding-top: 10px; - width: 240px; -} -#content { - float: right; - margin: 12px 0 0 0; - width: 728px; -} -#minimal_content { - margin: 0 auto; - min-width: 310px; - width: auto; -} -*::selection { - background: #006699; - color: #fff; -} -body > header { - position: fixed; - top: 0; - width: 100%; - z-index: 10; - transition-duration: 0.5s; - transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); - transition-property: transform; -} -@supports (backdrop-filter: blur(10px)) { - body > header--background { - background: rgba(255, 255, 255, 0.8); - backdrop-filter: blur(10px); - } -} -#header_wrapper { - margin: 0 auto; - width: 1000px; - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - padding: 4px; -} -.header--background { - box-shadow: 0 0 3px rgba(0, 0, 0, 0.28); - background: #fff; -} -.header--hidden { - transform: translateY(-100%); -} -#footer { - clear: both; - color: #999; - font-size: 10pt; - margin: 40px; - padding: 10px 0; -} - -@media screen and (max-width: 850px) { - body { - text-size-adjust: 100%; - } - body, - #wrapper, - #topwrapper, - #content, - #footer { - float: none; - margin: 0 auto; - min-width: 310px; - width: auto; - } - #wrapper { - margin-top: 50px; - } - body > header { - margin-bottom: 15px; - } - #column { - float: none; - margin: 0 10px; - padding-top: 0; - width: auto; - } -} - -/* #endregion */ - -/* #region header internals */ - -#logo { - height: 36px; - width: 110px; -} -#logo a { - background: url("logo@2x.png") no-repeat; - background-size: cover; - border: 0; - display: block; - height: 36px; - overflow: hidden; - text-indent: 100%; - white-space: nowrap; - width: 110px; -} -#global { - display: flex; -} -#global a { - color: #888; - display: inline-block; - font-size: 13pt; - padding: 14px 6px; -} -#global li { - display: inline-block; -} -#ctitle a { - padding: 14px; -} -#global li:hover, -#ctitle a:hover, -.l a:hover { - background-color: #fff; - box-shadow: 0 0 3px rgba(0, 0, 0, 0.16); - cursor: pointer; - transition: box-shadow 0.2s ease-in; -} -#search input { - background: #FFF; - border: 1px solid #ccc; - outline: none !important; - padding: 4px; - -webkit-appearance: none; - border-radius: 0; -} - -/* #endregion */ - -/* #region left column internals */ - -.toolbar { - border-top: 1px solid #CCC; -} - -#column ul, -#column p, -#column hr { - margin: 10px 0; -} -#column li > a { - display: block; - height: 100%; - padding: 6px; -} -#column li > a:hover { - background-color: #fff; - box-shadow: 0 0 3px rgba(0, 0, 0, 0.16); - transition: background-color 0.2s ease-in; -} -#column .margtop { - margin-top: 15px; -} - -#column .tags { - background: #fff; - box-shadow: 0 0 3px rgba(0, 0, 0, 0.16); - line-height: 140%; - padding: 6px; - text-align: justify; -} -#column .inp { - background: #fff; - border: 1px solid #ddddd5; - outline: none !important; - padding: 4px; - width: 222px; -} -#column .tags h4 { - background: #eee; - border: 1px solid #eee; - color: #888; - display: block; - text-align: center; -} -#ctitle { - font-size: 14pt; -} -#ctitle img { - margin-right: 5px; - vertical-align: middle; - max-width: 48px; - max-height: 48px; -} -#ustats li { - font-size: 10pt; - margin: 3px 0; -} -#column table.iread { - width: 100%; -} -#column table.iread td { - text-align: center; -} -#column table.iread img { - height: 48px; - width: 48px; -} - -/* #endregion */ - -/* #region main content */ -#content > p, -#content > h1, -#content > h2, -#minimal_content > p, -#minimal_content > h1, -#minimal_content > h2 { - margin: 1em 0; -} -.page { - background: #eee; - padding: 6px; - text-align: center; -} - -.page a { - color: #888; -} - -/* #endregion */ - -/* #region article, message internals */ - -article { - background: #fff; - box-shadow: 0 0 3px rgba(0, 0, 0, 0.16); - line-height: 140%; - margin-bottom: 10px; - padding: 20px; -} -article time { - color: #999; - font-size: 10pt; -} -article p { - clear: left; - margin: 5px 0 15px 0; - word-wrap: break-word; - overflow-wrap: break-word; -} -article .ir { - text-align: center; -} -article .ir a { - cursor: zoom-in; - display: block; -} -article .ir img { - max-width: 100%; -} -article > nav.l, -.msg-cont > nav.l { - border-top: 1px solid #eee; - display: flex; - justify-content: space-around; - font-size: 10pt; -} -article > nav.l a, -.msg-cont > nav.l a { - color: #888; - margin-right: 15px; -} -article .likes { - padding-left: 20px; -} -article .replies { - margin-left: 18px; -} -article .tags { - margin-top: 3px; -} -.msg-tags { - margin-top: 12px; - min-height: 1px; -} -article .tags > a, -.badge, -.msg-tags > a { - background: #eee; - border: 1px solid #eee; - color: #888; - display: inline-block; - font-size: 10pt; - margin-bottom: 5px; - margin-right: 5px; - padding: 0 10px; -} -.l .msg-button { - align-items: center; - display: flex; - flex-basis: 0; - flex-direction: column; - flex-grow: 1; - padding-top: 12px; -} -.l .msg-button-icon { - font-weight: bold; -} -.msgthread { - margin-bottom: 0; -} -.msg-avatar { - float: left; - max-height: 48px; - margin-right: 10px; - max-width: 48px; -} -.msg-avatar img { - max-height: 48px; - vertical-align: top; - max-width: 48px; -} -.msg-cont { - background: #FFF; - box-shadow: 0 0 3px rgba(0, 0, 0, 0.16); - line-height: 140%; - margin-bottom: 12px; - padding: 20px; - width: 640px; -} -.reply-new .msg-cont { - border-right: 5px solid #0C0; -} -.msg-ts { - font-size: small; - vertical-align: top; -} -.msg-ts, -.msg-ts > a { - color: #999; -} -.msg-txt { - clear: both; - margin: 0 0 12px; - padding-top: 10px; - word-wrap: break-word; - overflow-wrap: break-word; -} -.msg-media { - text-align: center; -} -.msg-links { - color: #999; - font-size: small; - margin: 5px 0 0 0; -} -.msg-comments { - color: #AAA; - font-size: small; - margin-top: 10px; - overflow: hidden; - text-indent: 10px; -} -.ta-wrapper { - border: 1px solid #DDD; - display: flex; - flex-grow: 1; -} -.msg-comment { - display: flex; - width: 100%; - margin-top: 10px; -} -.msg-comment-hidden { - display: none; -} -.msg-comment textarea { - border: 0; - flex-grow: 1; - outline: none !important; - padding: 4px; - resize: vertical; - vertical-align: top; -} -.attach-photo { - cursor: pointer; -} -.attach-photo-active { - color: green; -} -.msg-comment input { - align-self: flex-start; - background: #EEE; - border: 1px solid #CCC; - color: #999; - margin: 0 0 0 6px; - position: -webkit-sticky; - position: sticky; - top: 70px; - vertical-align: top; - width: 50px; -} -.msg-recomms { - color: #AAA; - font-size: small; - margin-top: 10px; - overflow: hidden; - text-indent: 10px; -} -#replies .msg-txt, -#private-messages .msg-txt { - margin: 0; -} -.title2 { - background: #fff; - margin: 20px 0; - padding: 10px 20px; - width: 640px; -} -.title2-right { - float: right; - line-height: 24px; -} -#content .title2 h2 { - font-size: x-large; - margin: 0; -} - -@media screen and (max-width: 850px) { - #header_wrapper { - width: auto; - } - #global { - justify-content: space-around; - flex-grow: 1; - } - #search { - padding: 4px; - } - article { - overflow: auto; - } - article p { - margin: 10px 0 8px 0; - } - .msg, - .msg-cont { - min-width: 280px; - width: auto; - } - .msg-cont { - margin: 8px 0; - } - .msg-media { - overflow: auto; - } - .title2 h2 { - font-size: large; - } - .msg-comment { - flex-direction: column; - } - .msg-comment input { - align-self: flex-end; - margin: 6px 0 0 0; - width: 100px; - } -} - -@media screen and (max-width: 480px) { - #wrapper { - margin-top: 104px; - } - #search { - display: none; - } - #global a { - padding: 14px 2px; - font-size: 11pt; - } - .msg-cont > nav.l, - article > nav.l { - font-size: 9pt; - } - .msg-txt { - padding-top: 5px; - } - .title2 { - font-size: 11pt; - width: auto; - } - #content .title2 h2 { - font-size: 11pt; - } - .title2-right { - line-height: initial; - } -} - -/* #endregion */ - -/* #region user-generated texts */ - -q:before, -q:after { - content: ""; -} -q, -blockquote { - border-left: 3px solid #CCC; - color: #666; - display: block; - margin: 10px 0 10px 10px; - padding-left: 10px; -} - -/* #endregion */ - -/* #region new message form internals */ - -#newmessage { - background: #E5E5E0; - margin-bottom: 20px; - padding: 15px; -} -#newmessage textarea { - border: 1px solid #CCC; - box-sizing: border-box; - margin: 0 0 5px 0; - margin-top: 20px; - max-height: 6em; - min-width: 280px; - padding: 4px; - width: 100%; -} -#newmessage input { - border: 1px solid #CCC; - margin: 5px 0; - padding: 2px 4px; -} -#newmessage .img { - width: 500px; -} -#newmessage .tags { - width: 500px; -} -#newmessage .subm { - background: #EEEEE5; - width: 150px; -} -@media screen and (max-width: 850px) { - #newmessage .img, - #newmessage .tags { - width: 100%; - } -} - -/* #endregion */ - -/* #region user lists */ - -.users { - margin: 10px 0; - width: 100%; - display: flex; - flex-wrap: wrap; -} -.users > span { - overflow: hidden; - padding: 6px 0; - width: 200px; -} -.users img { - height: 32px; - margin-right: 6px; - vertical-align: middle; - width: 32px; -} - -/* #endregion */ - -/* #region signup form */ - -.signup-h1 > img { - margin-right: 10px; - vertical-align: middle; -} -.signup-h1 { - font-size: x-large; - margin: 20px 0 10px 0; -} -.signup-h2 { - font-size: large; - margin: 10px 0 5px 0; -} -.signup-hr { - margin: 20px 0; -} - -/* #endregion */ - -/* #region PM */ - -.newpm { - margin: 20px 60px 30px 60px; -} -.newpm textarea { - resize: vertical; - width: 100%; -} -.newpm-send input { - width: 100px; -} - -/* #endregion */ - -/* #region popup dialog (lightbox) */ - -#dialogb { - background: #222; - height: 100%; - left: 0; - opacity: 0.6; - position: fixed; - top: 0; - width: 100%; - z-index: 10; -} -#dialogt { - height: 100%; - left: 0; - position: fixed; - top: 0; - width: 100%; - z-index: 10; - display: flex; - align-items: center; - justify-content: center; -} -#dialogw { - z-index: 11; - max-width: 96%; - max-height: calc(100% - 100px); - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} -#dialogw a { - display: block; -} -#dialogw img { - max-height: 100%; - max-height: 90vh; - max-width: 100%; -} -#dialog_header { - width: 100%; - height: 44px; - position: fixed; - display: flex; - flex-direction: row-reverse; - align-items: center; -} -.header_image { - background: rgba(0, 0, 0, 0.28); -} -#dialogc { - cursor: pointer; - color: #ccc; - padding-right: 6px; -} -.dialoglogin { - background: #fff; - padding: 25px; - width: 300px; -} -.dialog-opened { - overflow: hidden; -} -#signemail, -#signfb, -#signvk { - display: block; - line-height: 32px; - margin: 10px 0; - text-decoration: none; - width: 100%; -} -#signvk { - margin-bottom: 30px; -} -.dialoglogin form { - margin-top: 7px; -} -.signinput, -.signsubmit { - border: 1px solid #CCC; - margin: 3px 0; - padding: 3px; -} -.signinput { - width: 292px; -} -.signsubmit { - width: 70px; -} -.dialogshare { - background: #fff; - min-width: 300px; - overflow: auto; - padding: 20px; -} -.dialogl { - background: #fff; - border: 1px solid #DDD; - margin: 3px 0 20px; - padding: 5px; -} -.dialogshare li { - float: left; - margin: 5px 10px 0 0; -} -.dialogshare a { - display: block; -} -.dialogtxt { - background: #fff; - padding: 20px; -} - -@media screen and (max-width: 480px) { - .dialog-opened { - position: fixed; - width: 100%; - } -} - -/* #endregion */ - -/* #region misc */ - -#wsthread { - background: #CCC; - bottom: 20px; - cursor: pointer; - display: none; - padding: 5px 10px; - position: fixed; - right: 20px; -} -.sharenew { - display: inline-block; - line-height: 32px; - min-height: 32px; - min-width: 200px; - padding: 0 12px 0 37px; -} -.icon { - margin-top: -2px; - vertical-align: middle; -} -.icon--ei-link { - margin-top: -1px; -} -.icon--ei-comment { - margin-top: -5px; -} -.newmessage { - /* textarea on the /post page */ - border: 1px solid #DDD; - padding: 2px; - resize: vertical; - width: 100%; -} - -/* #endregion */ - -/* #region footer internals */ - -#footer-social { - float: left; -} -#footer-social a { - border: 0; - display: inline-block; -} -#footer-left { - margin-left: 286px; - margin-right: 350px; -} -#footer-right { - float: right; -} - -@media screen and (max-width: 850px) { - #footer { - margin: 0 10px; - } - #footer div { - float: none; - margin: 10px 0; - } -} - -/* #endregion */ - -/* #region settings */ - -fieldset { - border: 1px dotted #ccc; - margin-top: 25px; -} - -/* #endregion */ - -/* #region embeds */ - -.embedContainer { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: center; - padding: 0; - margin: 30px -3px 15px -3px; -} -.embedContainer > * { - box-sizing: border-box; - flex-grow: 1; - margin: 3px; - min-width: 49%; -} -.embedContainer > .compact { - flex-grow: 0; -} -.embedContainer .picture img { - display: block; -} -.embedContainer img, -.embedContainer video { - max-width: 100%; - max-height: 80vh; -} -.embedContainer > .audio, -.embedContainer > .youtube { - min-width: 90%; -} -.embedContainer audio { - width: 100%; -} -.embedContainer iframe { - overflow: hidden; - resize: vertical; - display: block; -} - -/* #endregion */ - -/* #region nsfw */ - -article.nsfw .embedContainer img, -article.nsfw .embedContainer video, -article.nsfw .embedContainer iframe, -article.nsfw .ir img { - opacity: 0.1; -} -article.nsfw .embedContainer img:hover, -article.nsfw .embedContainer video:hover, -article.nsfw .embedContainer iframe:hover, -article.nsfw .ir img:hover { - opacity: 1; -} - -/* #endregion */ diff --git a/juick-server/src/main/java/com/cliqset/xrd/Alias.java b/juick-server/src/main/java/com/cliqset/xrd/Alias.java deleted file mode 100644 index 49e4052b..00000000 --- a/juick-server/src/main/java/com/cliqset/xrd/Alias.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - Copyright 2010 Cliqset Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package com.cliqset.xrd; - -import java.net.URI; -import java.util.HashMap; -import java.util.Map; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAnyAttribute; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.XmlValue; -import javax.xml.namespace.QName; - -@XmlType(name="Alias", namespace=XRDConstants.XRD_NAMESPACE) -@XmlAccessorType(XmlAccessType.FIELD) -public class Alias { - - @XmlAnyAttribute() - private Map<QName, Object> unknownAttributes; - - @XmlValue - private URI value; - - public void setValue(URI value) { - this.value = value; - } - - public URI getValue() { - return value; - } - - public void setUnknownAttributes(Map<QName, Object> unknownAttributes) { - this.unknownAttributes = unknownAttributes; - } - - public Map<QName, Object> getUnknownAttributes() { - if (null == this.unknownAttributes) { - this.unknownAttributes = new HashMap<QName, Object>(); - } - return unknownAttributes; - } - - public boolean hasUnknownAttributes() { - return !(null == this.unknownAttributes || this.unknownAttributes.size() < 1); - } -} diff --git a/juick-server/src/main/java/com/cliqset/xrd/Expires.java b/juick-server/src/main/java/com/cliqset/xrd/Expires.java deleted file mode 100644 index b4bcdd24..00000000 --- a/juick-server/src/main/java/com/cliqset/xrd/Expires.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - Copyright 2010 Cliqset Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package com.cliqset.xrd; - -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import javax.xml.namespace.QName; -import javax.xml.bind.annotation.*; - -@XmlType(name="Expires", namespace=XRDConstants.XRD_NAMESPACE) -@XmlAccessorType(XmlAccessType.FIELD) -public class Expires { - - @XmlAnyAttribute - private Map<QName, Object> unknownAttributes; - - @XmlValue - private Date value; - - public void setValue(Date value) { - this.value = value; - } - - public Date getValue() { - return value; - } - - public void setUnknownAttributes(Map<QName, Object> unknownAttributes) { - this.unknownAttributes = unknownAttributes; - } - - public Map<QName, Object> getUnknownAttributes() { - if (null == this.unknownAttributes) { - this.unknownAttributes = new HashMap<QName, Object>(); - } - return unknownAttributes; - } - - public boolean hasUnknownAttributes() { - return !(null == this.unknownAttributes || this.unknownAttributes.size() < 1); - } -} diff --git a/juick-server/src/main/java/com/cliqset/xrd/Link.java b/juick-server/src/main/java/com/cliqset/xrd/Link.java deleted file mode 100644 index ec8522f0..00000000 --- a/juick-server/src/main/java/com/cliqset/xrd/Link.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - Copyright 2010 Cliqset Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package com.cliqset.xrd; - -import java.net.URI; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.xml.namespace.QName; -import javax.xml.bind.annotation.XmlAnyElement; -import javax.xml.bind.annotation.XmlAnyAttribute; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; - -import org.w3c.dom.Element; - -@XmlType(name="Link", namespace=XRDConstants.XRD_NAMESPACE) -@XmlAccessorType(XmlAccessType.FIELD) -public class Link { - - @XmlAttribute(name="rel") - private URI rel; - - @XmlAttribute(name="type") - private String type; - - @XmlAttribute(name="href") - private URI href; - - @XmlAttribute(name="template") - private String template; - - @XmlElement(name="Title") - private List<Title> titles; - - @XmlElement(name="Property") - private List<Property> properties; - - @XmlAnyElement - private List<Element> unknownElements; - - @XmlAnyAttribute - private Map<QName,Object> unknownAttributes; - - private URI processedTemplate; - - public void setRel(URI rel) { - this.rel = rel; - } - - public URI getRel() { - return rel; - } - - public void setType(String type) { - this.type = type; - } - - public String getType() { - return type; - } - - public void setHref(URI href) { - this.href = href; - } - - public URI getHref() { - return href; - } - - public void setTemplate(String template) { - this.template = template; - } - - public String getTemplate() { - return template; - } - - public void setTitles(List<Title> titles) { - this.titles = titles; - } - - public List<Title> getTitles() { - return titles; - } - - public void setProperties(List<Property> properties) { - this.properties = properties; - } - - public List<Property> getProperties() { - return properties; - } - - public void setUnknownElements(List<Element> unknownElements) { - this.unknownElements = unknownElements; - } - - public List<Element> getUnknownElements() { - return unknownElements; - } - - public void setUnknownAttributes(Map<QName,Object> unknownAttributes) { - this.unknownAttributes = unknownAttributes; - } - - public Map<QName,Object> getUnknownAttributes() { - if (null == this.unknownAttributes) { - this.unknownAttributes = new HashMap<QName, Object>(); - } - return unknownAttributes; - } - - public boolean hasTemplate() { - return null != this.template; - } - - public boolean hasHref() { - return null != this.href; - } - - public boolean hasUnknownAttributes() { - return !(null == this.unknownAttributes || this.unknownAttributes.size() < 1); - } - - public void setProcessedTemplate(URI processedTemplate) { - this.processedTemplate = processedTemplate; - } - - public URI getProcessedTemplate() { - return processedTemplate; - } -} diff --git a/juick-server/src/main/java/com/cliqset/xrd/Property.java b/juick-server/src/main/java/com/cliqset/xrd/Property.java deleted file mode 100644 index 35c7d0cc..00000000 --- a/juick-server/src/main/java/com/cliqset/xrd/Property.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - Copyright 2010 Cliqset Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package com.cliqset.xrd; - -import java.net.URI; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAnyAttribute; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.XmlValue; -import javax.xml.namespace.QName; - -import java.util.HashMap; -import java.util.Map; - -@XmlType(name="Property", namespace=XRDConstants.XRD_NAMESPACE) -@XmlAccessorType(XmlAccessType.FIELD) -public class Property { - - @XmlAttribute(name="type", required=true) - private URI type; - - @XmlAnyAttribute() - private Map<QName, Object> unknownAttributes; - - @XmlValue() - private URI value; - - public void setType(URI type) { - this.type = type; - } - - public URI getType() { - return type; - } - - public void setValue(URI value) { - this.value = value; - } - - public URI getValue() { - return value; - } - - public void setUnknownAttributes(Map<QName, Object> unknownAttributes) { - this.unknownAttributes = unknownAttributes; - } - - public Map<QName, Object> getUnknownAttributes() { - if (null == this.unknownAttributes) { - this.unknownAttributes = new HashMap<QName, Object>(); - } - return unknownAttributes; - } - - public boolean hasUnknownAttributes() { - return !(null == this.unknownAttributes || this.unknownAttributes.size() < 1); - } -} diff --git a/juick-server/src/main/java/com/cliqset/xrd/Signature.java b/juick-server/src/main/java/com/cliqset/xrd/Signature.java deleted file mode 100644 index f52f9218..00000000 --- a/juick-server/src/main/java/com/cliqset/xrd/Signature.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - Copyright 2010 Cliqset Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package com.cliqset.xrd; - -public class Signature { - -} diff --git a/juick-server/src/main/java/com/cliqset/xrd/Subject.java b/juick-server/src/main/java/com/cliqset/xrd/Subject.java deleted file mode 100644 index f6815317..00000000 --- a/juick-server/src/main/java/com/cliqset/xrd/Subject.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - Copyright 2010 Cliqset Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package com.cliqset.xrd; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAnyAttribute; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.XmlValue; -import javax.xml.namespace.QName; - -import java.net.URI; -import java.util.HashMap; -import java.util.Map; - -@XmlType(name="Subject", namespace=XRDConstants.XRD_NAMESPACE) -@XmlAccessorType(XmlAccessType.FIELD) -public class Subject { - - @XmlAnyAttribute() - private Map<QName, Object> unknownAttributes; - - @XmlValue - private URI value; - - public void setValue(URI value) { - this.value = value; - } - - public URI getValue() { - return value; - } - - public void setUnknownAttributes(Map<QName, Object> unknownAttributes) { - this.unknownAttributes = unknownAttributes; - } - - public Map<QName, Object> getUnknownAttributes() { - if (null == this.unknownAttributes) { - this.unknownAttributes = new HashMap<QName, Object>(); - } - return unknownAttributes; - } - - public boolean hasUnknownAttributes() { - return !(null == this.unknownAttributes || this.unknownAttributes.size() < 1); - } -} diff --git a/juick-server/src/main/java/com/cliqset/xrd/Title.java b/juick-server/src/main/java/com/cliqset/xrd/Title.java deleted file mode 100644 index 7d6597bd..00000000 --- a/juick-server/src/main/java/com/cliqset/xrd/Title.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - Copyright 2010 Cliqset Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package com.cliqset.xrd; - -import java.util.HashMap; -import java.util.Map; - -import javax.xml.bind.annotation.*; -import javax.xml.namespace.QName; - -@XmlType(name="Title", namespace=XRDConstants.XRD_NAMESPACE) -@XmlAccessorType(XmlAccessType.FIELD) -public class Title { - - @XmlAttribute(name="lang", namespace=XRDConstants.XML_NAMESPACE) - private String lang; - - @XmlAnyAttribute() - private Map<QName, Object> unknownAttributes; - - @XmlValue - private String value; - - public void setValue(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - - public void setLang(String lang) { - this.lang = lang; - } - - public String getLang() { - return lang; - } - - public void setUnknownAttributes(Map<QName, Object> unknownAttributes) { - this.unknownAttributes = unknownAttributes; - } - - public Map<QName, Object> getUnknownAttributes() { - if (null == this.unknownAttributes) { - this.unknownAttributes = new HashMap<QName, Object>(); - } - return unknownAttributes; - } - - public boolean hasUnknownAttributes() { - return !(null == this.unknownAttributes || this.unknownAttributes.size() < 1); - } -} diff --git a/juick-server/src/main/java/com/cliqset/xrd/XRD.java b/juick-server/src/main/java/com/cliqset/xrd/XRD.java deleted file mode 100644 index 393e977b..00000000 --- a/juick-server/src/main/java/com/cliqset/xrd/XRD.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - Copyright 2010 Cliqset Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package com.cliqset.xrd; - -import javax.xml.namespace.QName; -import javax.xml.bind.annotation.*; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; - -import org.w3c.dom.Element; - -import java.io.InputStream; -import java.util.List; -import java.util.Map; - -@XmlRootElement(name="XRD", namespace=XRDConstants.XRD_NAMESPACE) -@XmlAccessorType(XmlAccessType.FIELD) -public class XRD { - - @XmlAttribute(name="id", namespace=XRDConstants.XML_NAMESPACE) - private String id; - - @XmlAnyAttribute - private Map<QName, Object> unknownAttributes; - - @XmlElement(name="Expires", namespace=XRDConstants.XRD_NAMESPACE) - private Expires expires; - - @XmlElement(name="Subject", namespace=XRDConstants.XRD_NAMESPACE) - private Subject subject; - - @XmlElement(name="Alias", namespace=XRDConstants.XRD_NAMESPACE) - private List<Alias> aliases; - - @XmlElement(name="Property", namespace=XRDConstants.XRD_NAMESPACE) - private List<Property> properties; - - @XmlElement(name="Link", namespace=XRDConstants.XRD_NAMESPACE) - private List<Link> links; - - @XmlElement(name="Signature", namespace=XRDConstants.XML_SIG_NAMESPACE) - private List<Signature> signatures; - - @XmlAnyElement - private List<Element> unknownElements; - - public void setExpires(Expires expires) { - this.expires = expires; - } - - public Expires getExpires() { - return expires; - } - - public void setSubject(Subject subject) { - this.subject = subject; - } - - public Subject getSubject() { - return subject; - } - - public void setAliases(List<Alias> aliases) { - this.aliases = aliases; - } - - public List<Alias> getAliases() { - return aliases; - } - - public void setProperties(List<Property> properties) { - this.properties = properties; - } - - public List<Property> getProperties() { - return properties; - } - - public void setLinks(List<Link> links) { - this.links = links; - } - - public List<Link> getLinks() { - return links; - } - - public void setSignatures(List<Signature> signatures) { - this.signatures = signatures; - } - - public List<Signature> getSignatures() { - return signatures; - } - - public void setId(String id) { - this.id = id; - } - - public String getId() { - return id; - } - - public void setUnknownAttributes(Map<QName, Object> unknownAttributes) { - this.unknownAttributes = unknownAttributes; - } - - public Map<QName, Object> getUnknownAttributes() { - return unknownAttributes; - } - - public void setUnknownElements(List<Element> unknownElements) { - this.unknownElements = unknownElements; - } - - public List<Element> getUnknownElements() { - return unknownElements; - } - - public boolean hasId() { - return null != this.id; - } - - public boolean hasExpires() { - return null != this.expires; - } - - public boolean hasSubject() { - return null != this.subject; - } - - public boolean hasAliases() { - return null != this.aliases; - } - - public boolean hasProperties() { - return null != this.properties; - } - - public boolean hasLinks() { - return null != this.links; - } - - public static XRD fromStream(InputStream stream) throws XRDException { - JAXBContext context; - try { - context = JAXBContext.newInstance(XRD.class); - return (XRD)context.createUnmarshaller().unmarshal(stream); - } catch (JAXBException e) { - throw new XRDException("Unable to deserialize stream into XRD", e); - } - } -} diff --git a/juick-server/src/main/java/com/cliqset/xrd/XRDConstants.java b/juick-server/src/main/java/com/cliqset/xrd/XRDConstants.java deleted file mode 100644 index 39e3c584..00000000 --- a/juick-server/src/main/java/com/cliqset/xrd/XRDConstants.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - Copyright 2010 Cliqset Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package com.cliqset.xrd; - -public class XRDConstants { - - public static final String XRD_NAMESPACE = "http://docs.oasis-open.org/ns/xri/xrd-1.0"; - public static final String XML_SIG_NAMESPACE = ""; - public static final String XML_NAMESPACE = ""; - - public static final String XRD_MEDIA_TYPE = "application/xrd+xml"; -} diff --git a/juick-server/src/main/java/com/cliqset/xrd/XRDException.java b/juick-server/src/main/java/com/cliqset/xrd/XRDException.java deleted file mode 100644 index da1e6849..00000000 --- a/juick-server/src/main/java/com/cliqset/xrd/XRDException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - Copyright 2010 Cliqset Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package com.cliqset.xrd; - -@SuppressWarnings("serial") -public class XRDException extends Exception { - - public XRDException() {} - - public XRDException(String message) { - super(message); - } - - public XRDException(Throwable cause) { - super(cause); - } - - public XRDException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/juick-server/src/main/java/com/cliqset/xrd/package-info.java b/juick-server/src/main/java/com/cliqset/xrd/package-info.java deleted file mode 100644 index bd8f0146..00000000 --- a/juick-server/src/main/java/com/cliqset/xrd/package-info.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -@XmlSchema( - namespace=XRD_NAMESPACE, - elementFormDefault = XmlNsForm.QUALIFIED, - xmlns={ - @XmlNs(prefix= StringUtils.EMPTY, namespaceURI=XRD_NAMESPACE) - } -) -package com.cliqset.xrd; - -import org.apache.commons.lang3.StringUtils; - -import javax.xml.bind.annotation.XmlNs; -import javax.xml.bind.annotation.XmlNsForm; -import javax.xml.bind.annotation.XmlSchema; - -import static com.cliqset.xrd.XRDConstants.XRD_NAMESPACE;
\ No newline at end of file diff --git a/juick-server/src/main/java/com/juick/ApiServer.java b/juick-server/src/main/java/com/juick/ApiServer.java deleted file mode 100644 index fb2d9701..00000000 --- a/juick-server/src/main/java/com/juick/ApiServer.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.juick; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration; -import org.springframework.context.annotation.ComponentScan; - -@SpringBootApplication -@EnableAutoConfiguration(exclude = { MailSenderAutoConfiguration.class }) -@ComponentScan(basePackages = {"com.juick.server", "com.juick.service"}) -public class ApiServer { - public static void main(String[] args) { - SpringApplication.run(ApiServer.class, args); - } -} diff --git a/juick-server/src/main/java/com/juick/server/ActivityPubManager.java b/juick-server/src/main/java/com/juick/server/ActivityPubManager.java deleted file mode 100644 index 4601f7d1..00000000 --- a/juick-server/src/main/java/com/juick/server/ActivityPubManager.java +++ /dev/null @@ -1,331 +0,0 @@ -package com.juick.server; - -import com.juick.Message; -import com.juick.User; -import com.juick.formatters.PlainTextFormatter; -import com.juick.server.api.activity.model.Context; -import com.juick.server.api.activity.model.activities.Accept; -import com.juick.server.api.activity.model.activities.Announce; -import com.juick.server.api.activity.model.activities.Create; -import com.juick.server.api.activity.model.activities.Delete; -import com.juick.server.api.activity.model.objects.Hashtag; -import com.juick.server.api.activity.model.objects.Image; -import com.juick.server.api.activity.model.objects.Mention; -import com.juick.server.api.activity.model.objects.Note; -import com.juick.server.api.activity.model.objects.Person; -import com.juick.server.util.HttpUtils; -import com.juick.service.SocialService; -import com.juick.service.UserService; -import com.juick.service.activities.*; -import com.juick.service.component.*; -import com.juick.util.MessageUtils; -import com.mitchellbosecke.pebble.PebbleEngine; -import com.mitchellbosecke.pebble.template.PebbleTemplate; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.net.URI; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -@Component -public class ActivityPubManager implements ActivityListener, NotificationListener { - private static final Logger logger = LoggerFactory.getLogger(ActivityPubManager.class); - @Inject - private SignatureManager signatureManager; - @Inject - private SocialService socialService; - @Inject - private UserService userService; - @Inject - private PebbleEngine pebbleEngine; - @Value("${ap_base_uri:http://localhost:8080/}") - private String baseUri; - @Value("${service_user:juick}") - private String serviceUsername; - - private User serviceUser; - - @PostConstruct - public void init() { - serviceUser = userService.getUserByName(serviceUsername); - } - - @Override - public void processFollowEvent(@Nonnull FollowEvent followEvent) { - String acct = (String)followEvent.getRequest().getObject(); - logger.info("received follower request to {}", acct); - User followedUser = socialService.getUserByAccountUri(acct); - if (!followedUser.isAnonymous()) { - // automatically accept follower requests - Person me = (Person) signatureManager.getContext(URI.create(acct)).get(); - Person follower = (Person) signatureManager.getContext(URI.create(followEvent.getRequest().getActor())).get(); - Accept accept = new Accept(); - accept.setActor(me.getId()); - accept.setObject(followEvent.getRequest()); - try { - signatureManager.post(me, follower, accept); - socialService.addFollower(followedUser, follower.getId()); - logger.info("Follower added for {}", followedUser.getName()); - } catch (IOException e) { - logger.info("activitypub exception", e); - } - } - } - - @Override - public void undoFollowEvent(UndoFollowEvent event) { - String actor = event.getActor(); - String me = event.getObject(); - logger.info("{} stopping to follow {}", actor, me); - User followedUser = socialService.getUserByAccountUri(me); - if (!followedUser.isAnonymous()) { - socialService.removeFollower(followedUser, actor); - } - } - - @Override - public void deleteUserEvent(DeleteUserEvent event) { - String acct = event.getUserUri(); - logger.info("Deleting {} from followers", acct); - socialService.removeAccount(acct); - } - - @Override - public void deleteMessageEvent(DeleteMessageEvent event) { - Message msg = event.getMessage(); - User user = msg.getUser(); - String userUri = personUri(user); - Note note = makeNote(msg); - Person me = (Person) signatureManager.getContext(URI.create(userUri)).get(); - socialService.getFollowers(user).forEach(acct -> { - Person follower = (Person) signatureManager.getContext(URI.create(acct)).get(); - Delete delete = new Delete(); - delete.setId(note.getId()); - delete.setActor(me.getId()); - delete.setPublished(note.getPublished()); - delete.setObject(note); - try { - logger.info("Deletion to follower {}", follower.getId()); - signatureManager.post(me, follower, delete); - } catch (IOException e) { - logger.warn("activitypub exception", e); - } - }); - } - - @Override - public void processMessageEvent(MessageEvent messageEvent) { - Message msg = messageEvent.getMessage(); - if (MessageUtils.isPM(msg)) { - return; - } - User user = msg.getUser(); - String userUri = personUri(user); - Note note = makeNote(msg); - Person me = (Person) signatureManager.getContext(URI.create(userUri)).get(); - Set<String> subscribers = new HashSet<>(socialService.getFollowers(user)); - if (MessageUtils.isReply(msg) && msg.getTo().getUri().toASCIIString().length() > 0) { - String replier = msg.getTo().getUri().toASCIIString(); - subscribers.add(replier); - List<String> cc = new ArrayList<>(note.getCc()); - cc.add(replier); - note.setCc(cc); - } - subscribers.forEach(acct -> { - Optional<Context> context = signatureManager.getContext(URI.create(acct)); - if (context.isPresent()) { - Person follower = (Person)context.get(); - Create create = new Create(); - create.setId(note.getId()); - create.setActor(me.getId()); - create.setPublished(note.getPublished()); - create.setObject(note); - try { - logger.info("Posting to subscriber {}", follower.getId()); - signatureManager.post(me, follower, create); - } catch (IOException e) { - logger.warn("activitypub exception", e); - } - } - }); - } - - public String inboxUri() { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath("/api/inbox").toUriString(); - } - - public String outboxUri(User user) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/u/%s/blog/toc", user.getName())).toUriString(); - } - - public String personUri(User user) { - if (user.getUri().toString().length() > 0) { - return user.getUri().toASCIIString(); - } - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/u/%s", user.getName())).toUriString(); - } - public String personWebUri(User user) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/%s/", user.getName())).toUriString(); - } - - public String followersUri(User user) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/u/%s/followers/toc", user.getName())).toUriString(); - } - - public String followingUri(User user) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/u/%s/following/toc", user.getName())).toUriString(); - } - public String messageUri(Message msg) { - return messageUri(msg.getMid(), msg.getRid()); - } - public String messageUri(int mid, int rid) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - uri.replacePath(String.format("/n/%d-%d", mid, rid)); - return uri.toUriString(); - } - public String tagUri(com.juick.Tag tag) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/t/%s", tag.getName())).toUriString(); - } - - public Note makeNote(Message msg) { - Note note = new Note(); - note.setId(messageUri(msg)); - note.setUrl(PlainTextFormatter.formatUrl(msg)); - note.setAttributedTo(personUri(msg.getUser())); - if (MessageUtils.isReply(msg)) { - if (msg.getReplyToUri().toASCIIString().length() > 0) { - note.setInReplyTo(msg.getReplyToUri().toASCIIString()); - } else { - note.setInReplyTo(messageUri(msg.getMid(), msg.getReplyto())); - } - } - if (MessageUtils.isPM(msg)) { - note.setTo(Collections.singletonList(personUri(msg.getTo()))); - } else { - note.setTo(Collections.singletonList("https://www.w3.org/ns/activitystreams#Public")); - note.setCc(Collections.singletonList(followersUri(msg.getUser()))); - } - note.setPublished(msg.getTimestamp()); - if (StringUtils.isNotBlank(msg.getAttachmentType())) { - Image attachment = new Image(); - attachment.setId(msg.getAttachment().getMedium().getUrl()); - attachment.setUrl(msg.getAttachment().getMedium().getUrl()); - attachment.setMediaType(HttpUtils.mediaType(msg.getAttachmentType())); - note.setAttachment(Collections.singletonList(attachment)); - } - note.setTags(msg.getTags().stream().map(t -> { - Hashtag hashtag = new Hashtag(); - hashtag.setId(tagUri(t)); - hashtag.setName(t.getName()); - return hashtag; - }).collect(Collectors.toList())); - if (msg.getReplyToUri() != null && msg.getReplyToUri().toASCIIString().length() > 0) { - Optional<Context> noteContext = signatureManager.getContext(msg.getReplyToUri()); - if (noteContext.isPresent()) { - Note activity = (Note) noteContext.get(); - Optional<Context> personContext = signatureManager.getContext(URI.create(activity.getAttributedTo())); - if (personContext.isPresent()) { - Person person = (Person) personContext.get(); - note.getTags().add(new Mention(person.getUrl(), person.getPreferredUsername())); - msg.getTo().setName(person.getPreferredUsername()); - note.setInReplyTo(activity.getInReplyTo()); - } - } - } else if (MessageUtils.isReply(msg)) { - note.getTags().add(new Mention(personWebUri(msg.getTo()), msg.getTo().getName())); - } - MessageUtils.getGlobalMentions(msg).forEach(m -> { - // @user@server.tld -> user@server.tld - Optional<Context> personContext = signatureManager.discoverPerson(m.substring(1)); - if (personContext.isPresent()) { - Person person = (Person) personContext.get(); - note.getTags().add(new Mention(person.getUrl(), person.getPreferredUsername())); - List<String> cc = new ArrayList<>(note.getCc()); - cc.add(person.getUrl()); - note.setCc(cc); - } - }); - if (msg.isHtml()) { - note.setContent(msg.getText()); - } else { - PebbleTemplate noteTemplate = pebbleEngine.getTemplate("layouts/note"); - Map<String, Object> context = new HashMap<>(); - context.put("msg", msg); - context.put("baseUri", baseUri); - try { - Writer writer = new StringWriter(); - noteTemplate.evaluate(writer, context); - note.setContent(writer.toString()); - } catch (IOException e) { - logger.warn("template not rendered, falling back"); - note.setContent(MessageUtils.formatMessage(StringUtils.defaultString(msg.getText()))); - } - } - return note; - } - - @Override - public void processSubscribeEvent(SubscribeEvent subscribeEvent) { - - } - - @Override - public void processLikeEvent(LikeEvent likeEvent) { - - } - - @Override - public void processPingEvent(PingEvent pingEvent) { - - } - - @Override - public void processMessageReadEvent(MessageReadEvent messageReadEvent) { - - } - - @Override - public void processTopEvent(TopEvent topEvent) { - Message message = topEvent.getMessage(); - Note note = makeNote(message); - Announce announce = new Announce(); - announce.setId(note.getId() + "#top"); - announce.setActor(personUri(serviceUser)); - announce.setObject(note); - Person me = (Person) signatureManager.getContext(URI.create(announce.getActor())).get(); - socialService.getFollowers(serviceUser).forEach(acct -> { - Person follower = (Person) signatureManager.getContext(URI.create(acct)).get(); - try { - logger.info("Announcing top: {}", message.getMid()); - signatureManager.post(me, follower, announce); - } catch (IOException e) { - logger.warn("activitypub exception", e); - } - }); - } -} diff --git a/juick-server/src/main/java/com/juick/server/CommandsManager.java b/juick-server/src/main/java/com/juick/server/CommandsManager.java deleted file mode 100644 index 82143482..00000000 --- a/juick-server/src/main/java/com/juick/server/CommandsManager.java +++ /dev/null @@ -1,540 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server; - -import com.juick.Message; -import com.juick.Tag; -import com.juick.User; -import com.juick.formatters.PlainTextFormatter; -import com.juick.service.activities.DeleteMessageEvent; -import com.juick.service.component.*; -import com.juick.model.CommandResult; -import com.juick.model.TagStats; -import com.juick.server.helpers.annotation.UserCommand; -import com.juick.server.util.HttpUtils; -import com.juick.service.*; -import com.juick.util.MessageUtils; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.apache.commons.lang3.reflect.MethodUtils; -import org.apache.commons.lang3.tuple.Pair; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Component; - -import javax.annotation.Nonnull; -import javax.inject.Inject; -import java.lang.reflect.Method; -import java.net.URI; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -/** - * - * @author ugnich - */ -@Component -public class CommandsManager { - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - @Inject - private TagService tagService; - @Inject - private PMQueriesService pmQueriesService; - @Inject - private ShowQueriesService showQueriesService; - @Inject - private PrivacyQueriesService privacyQueriesService; - @Inject - private SubscriptionService subscriptionService; - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String imgDir; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - @Inject - private ImagesService imagesService; - - public CommandResult processCommand(User user, String data, @Nonnull URI attachment) throws Exception { - if (!user.isAnonymous()) { - userService.updateLastSeen(user); - } - String strippedData = StringUtils.stripStart(data, null); - if (strippedData.startsWith("?OTR")) { - return CommandResult.fromString("?OTR Error: we are not using OTR"); - } - String input = MessageUtils.stripNonSafeUrls(strippedData); - Optional<Method> cmd = MethodUtils.getMethodsListWithAnnotation(getClass(), UserCommand.class).stream() - .filter(m -> Pattern.compile(m.getAnnotation(UserCommand.class).pattern(), - m.getAnnotation(UserCommand.class).patternFlags()).matcher(input).matches()) - .findFirst(); - if (cmd.isPresent()) { - Matcher matcher = Pattern.compile(cmd.get().getAnnotation(UserCommand.class).pattern(), - cmd.get().getAnnotation(UserCommand.class).patternFlags()).matcher(input); - List<String> groups = new ArrayList<>(); - while (matcher.find()) { - for (int i = 1; i <= matcher.groupCount(); i++) { - groups.add(matcher.group(i)); - } - } - CommandResult commandResult = (CommandResult) getClass().getMethod(cmd.get().getName(), User.class, URI.class, String[].class) - .invoke(this, user, attachment, groups.toArray(new String[groups.size()])); - if (StringUtils.isNotEmpty(commandResult.getText())) { - return commandResult; - } - } - Pair<String, List<Tag>> tags = tagService.fromString(input); - if (tags.getRight().size() > 5) { - return CommandResult.fromString("Sorry, 5 tags maximum."); - } - // new message - String body = tags.getLeft().trim(); - boolean haveAttachment = StringUtils.isNotEmpty(attachment.toString()); - String attachmentFName = null; - String attachmentType = null; - if (haveAttachment) { - attachmentFName = attachment.getScheme().equals("juick") ? attachment.getHost() - : HttpUtils.downloadImage(attachment.toURL(), tmpDir).getHost(); - attachmentType = attachmentFName.substring(attachmentFName.length() - 3); - } - int mid = messagesService.createMessage(user.getUid(), body, attachmentType, tags.getRight()); - if (haveAttachment) { - String fname = String.format("%d.%s", mid, attachmentType); - imagesService.saveImageWithPreviews(attachmentFName, fname); - } - Message msg = messagesService.getMessage(mid); - subscriptionService.subscribeMessage(msg, user); - - applicationEventPublisher.publishEvent(new MessageReadEvent(this, user, msg)); - applicationEventPublisher.publishEvent(new MessageEvent(this, msg, subscriptionService.getSubscribedUsers(msg.getUser().getUid(), msg))); - return CommandResult.build(msg, "New message posted.\n#" + msg.getMid() + " https://juick.com/m/" + msg.getMid(), String.format("[New message](%s) posted", PlainTextFormatter.formatUrl(msg))); - } - - @UserCommand(pattern = "^ping$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "PING - returns you a PONG") - public CommandResult commandPing(User user, URI attachment, String[] input) { - applicationEventPublisher.publishEvent(new PingEvent(this, user)); - return CommandResult.fromString("PONG"); - } - - @UserCommand(pattern = "^help$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "HELP - returns this help message") - public CommandResult commandHelp(User user, URI attachment, String[] input) { - return CommandResult.fromString(Arrays.stream(getClass().getDeclaredMethods()) - .filter(m -> m.isAnnotationPresent(UserCommand.class)) - .map(m -> m.getAnnotation(UserCommand.class).help()) - .collect(Collectors.joining("\n"))); - } - - @UserCommand(pattern = "^login$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "LOGIN - log in to Juick website") - public CommandResult commandLogin(User user_from, URI attachment, String[] input) { - return CommandResult.fromString("http://juick.com/login?hash=" + userService.getHashByUID(user_from.getUid())); - } - @UserCommand(pattern = "^\\@(\\S+)\\s+([\\s\\S]+)$", help = "@username message - send PM to username") - public CommandResult commandPM(User user_from, URI attachment, String... arguments) { - String body = arguments[1]; - - User user_to = userService.getUserByName(arguments[0]); - - if (!user_to.isAnonymous()) { - if (!userService.isInBLAny(user_to.getUid(), user_from.getUid())) { - if (pmQueriesService.createPM(user_from.getUid(), user_to.getUid(), body)) { - com.juick.Message jmsg = new com.juick.Message(); - jmsg.setUser(user_from); - jmsg.setTo(user_to); - jmsg.setText(body); - applicationEventPublisher.publishEvent(new MessageEvent(this, jmsg, Collections.singletonList(user_to))); - return CommandResult.fromString("Private message sent"); - } - } - } - return CommandResult.fromString("Error"); - } - @UserCommand(pattern = "^bl$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "BL - Show your blacklist") - public CommandResult commandBLShow(User user_from, URI attachment, String... arguments) { - List<User> blusers = userService.getUserBLUsers(user_from.getUid()); - List<String> bltags = tagService.getUserBLTags(user_from.getUid()); - - String txt = StringUtils.EMPTY; - if (bltags.size() > 0) { - for (String bltag : bltags) { - txt += "*" + bltag + "\n"; - } - - if (blusers.size() > 0) { - txt += "\n"; - } - } - if (blusers.size() > 0) { - for (User bluser : blusers) { - txt += "@" + bluser.getName() + "\n"; - } - } - if (txt.isEmpty()) { - txt = "You don't have any users or tags in your blacklist."; - } - - return CommandResult.fromString(txt); - } - - @UserCommand(pattern = "^#\\+$", help = "#+ - Show last Juick messages") - public CommandResult commandLast(User user_from, URI attachment, String... arguments) { - return CommandResult.fromString("Last messages:\n" - + printMessages(user_from, messagesService.getAll(user_from.getUid(), 0), true)); - } - - @UserCommand(pattern = "@", help = "@ - Show recommendations and popular personal blogs") - public CommandResult commandUsers(User user_from, URI attachment, String... arguments) { - StringBuilder msg = new StringBuilder(); - msg.append("Recommended blogs"); - List<String> recommendedUsers = showQueriesService.getRecommendedUsers(user_from); - if (recommendedUsers.size() > 0) { - for (String user : recommendedUsers) { - msg.append("\n@").append(user); - } - } else { - msg.append("\nNo recommendations now. Subscribe to more blogs. ;)"); - } - msg.append("\n\nTop 10 personal blogs:"); - List<String> topUsers = showQueriesService.getTopUsers(); - if (topUsers.size() > 0) { - for (String user : topUsers) { - msg.append("\n@").append(user); - } - } else { - msg.append("\nNo top users. Empty DB? ;)"); - } - return CommandResult.fromString(msg.toString()); - } - @UserCommand(pattern = "^bl\\s+@([^\\s\\n\\+]+)", patternFlags = Pattern.CASE_INSENSITIVE, - help = "BL @username - add @username to your blacklist") - public CommandResult blacklistUser(User user_from, URI attachment, String... arguments) { - User blUser = userService.getUserByName(arguments[0]); - if (!blUser.isAnonymous()) { - PrivacyQueriesService.PrivacyResult result = privacyQueriesService.blacklistUser(user_from, blUser); - if (result == PrivacyQueriesService.PrivacyResult.Added) { - return CommandResult.fromString("User added to your blacklist"); - } else { - return CommandResult.fromString("User removed from your blacklist"); - } - } - return CommandResult.fromString("User not found"); - } - @UserCommand(pattern = "^bl\\s\\*(\\S+)$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "BL *tag - add *tag to your blacklist") - public CommandResult blacklistTag(User user_from, URI attachment, String... arguments) { - if (!user_from.isAnonymous()) { - Tag tag = tagService.getTag(arguments[0], false); - if (tag != null) { - PrivacyQueriesService.PrivacyResult result = privacyQueriesService.blacklistTag(user_from, tag); - if (result == PrivacyQueriesService.PrivacyResult.Added) { - return CommandResult.fromString("Tag added to your blacklist"); - } else { - return CommandResult.fromString("Tag removed from your blacklist"); - } - } - } - return CommandResult.fromString("Tag not found"); - } - @UserCommand(pattern = "\\*", help = "* - Show your tags") - public CommandResult commandTags(User currentUser, URI attachment, String... args) { - List<TagStats> tags = tagService.getUserTagStats(currentUser.getUid()); - String msg = "Your tags: (tag - messages)\n" + - tags.stream() - .map(t -> String.format("\n*%s - %d", t.getTag().getName(), t.getUsageCount())).collect(Collectors.joining()); - return CommandResult.fromString(msg); - } - @UserCommand(pattern = "S", help = "S - Show your subscriptions", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandSubscriptions(User currentUser, URI attachment, String... args) { - List<User> friends = userService.getUserFriends(currentUser.getUid()); - List<String> tags = subscriptionService.getSubscribedTags(currentUser); - String msg = friends.size() > 0 ? "You are subscribed to users:" + friends.stream().map(u -> "\n@" + u.getName()) - .collect(Collectors.joining()) - : "You are not subscribed to any user."; - msg += tags.size() > 0 ? "\nYou are subscribed to tags:" + tags.stream().map(t -> "\n*" + t) - .collect(Collectors.joining()) - : "\nYou are not subscribed to any tag."; - return CommandResult.fromString(msg); - } - @UserCommand(pattern = "!", help = "! - Show your favorite messages") - public CommandResult commandFavorites(User currentUser, URI attachment, String... args) { - List<Integer> mids = messagesService.getUserRecommendations(currentUser.getUid(), 0); - if (mids.size() > 0) { - return CommandResult.fromString("Favorite messages: \n" + printMessages(currentUser, mids, false)); - } - return CommandResult.fromString("No favorite messages, try to \"like\" something ;)"); - } - @UserCommand(pattern = "^\\!\\s+#(\\d+)", help = "! #12345 - recommend message") - public CommandResult commandRecommend(User user, URI attachment, String... arguments) { - int mid = NumberUtils.toInt(arguments[0], 0); - if (mid > 0) { - com.juick.Message msg = messagesService.getMessage(mid); - if (msg != null) { - if (msg.getUser() == user) { - return CommandResult.fromString("You can't recommend your own messages."); - } - MessagesService.RecommendStatus status = messagesService.recommendMessage(mid, user.getUid()); - switch (status) { - case Added: - applicationEventPublisher.publishEvent(new LikeEvent(this, user, msg, - subscriptionService.getUsersSubscribedToUserRecommendations( - user.getUid(), msg))); - return CommandResult.fromString("Message is added to your recommendations"); - case Deleted: - return CommandResult.fromString("Message deleted from your recommendations."); - } - } - return CommandResult.fromString("Message not found"); - } - return CommandResult.fromString("Message not found"); - } - // TODO: target notification - @UserCommand(pattern = "^(s|u)\\s+\\@(\\S+)$", help = "S @username - subscribe to user" + - "\nU @username - unsubscribe from user", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandSubscribeUser(User user, URI attachment, String... args) { - boolean subscribe = args[0].equalsIgnoreCase("s"); - User toUser = userService.getUserByName(args[1]); - if (toUser.isAnonymous()) { - return CommandResult.fromString("User not found"); - } - if (subscribe) { - if (subscriptionService.subscribeUser(user, toUser)) { - // TODO: already subscribed case - applicationEventPublisher.publishEvent(new SubscribeEvent(this, user, toUser)); - return CommandResult.fromString("Subscribed to @" + toUser.getName()); - } - } else { - if (subscriptionService.unSubscribeUser(user, toUser)) { - return CommandResult.fromString("Unsubscribed from @" + toUser.getName()); - } - return CommandResult.fromString("You were not subscribed to @" + toUser.getName()); - } - return CommandResult.fromString("Error"); - } - @UserCommand(pattern = "^(s|u)\\s+\\*(\\S+)$", help = "S *tag - subscribe to tag" + - "\nU *tag - unsubscribe from tag", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandSubscribeTag(User user, URI attachment, String... args) { - boolean subscribe = args[0].equalsIgnoreCase("s"); - Tag tag = tagService.getTag(args[1], true); - if (subscribe) { - if (subscriptionService.subscribeTag(user, tag)) { - return CommandResult.fromString("Subscribed"); - } - } else { - if (subscriptionService.unSubscribeTag(user, tag)) { - return CommandResult.fromString("Unsubscribed from " + tag.getName()); - } - return CommandResult.fromString("You were not subscribed to " + tag.getName()); - } - return CommandResult.fromString("Error"); - } - @UserCommand(pattern = "^(s|u)\\s+#(\\d+)$", help = "S #1234 - subscribe to comments" + - "\nU #1234 - unsubscribe from comments", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandSubscribeMessage(User user, URI attachment, String... args) { - boolean subscribe = args[0].equalsIgnoreCase("s"); - int mid = NumberUtils.toInt(args[1], 0); - Message msg = messagesService.getMessage(mid); - if (msg != null) { - if (subscribe) { - if (subscriptionService.subscribeMessage(msg, user)) { - applicationEventPublisher.publishEvent( - new MessageReadEvent(this, user, msg)); - return CommandResult.fromString("Subscribed"); - } - } else { - if (subscriptionService.unSubscribeMessage(mid, user.getUid())) { - return CommandResult.fromString("Unsubscribed from #" + mid); - } - return CommandResult.fromString("You were not subscribed to #" + mid); - } - } - return CommandResult.fromString("Error"); - } - @UserCommand(pattern = "^(on|off)$", patternFlags = Pattern.CASE_INSENSITIVE, - help = "ON/OFF - Enable/disable subscriptions delivery") - public CommandResult commandOnOff(User user, URI attachment, String[] input) { - UserService.ActiveStatus newStatus; - String retValUpdated; - if (input[0].toLowerCase().equals("on")) { - newStatus = UserService.ActiveStatus.Active; - retValUpdated = "XMPP notifications are activated"; - } else { - newStatus = UserService.ActiveStatus.Inactive; - retValUpdated = "XMPP notifications are disabled"; - } - if (userService.getAllJIDs(user).stream().allMatch(jid -> userService.setActiveStatusForJID(jid, newStatus))) { - return CommandResult.fromString(retValUpdated); - } - return CommandResult.fromString("Error"); - } - @UserCommand(pattern = "^\\@([^\\s\\n\\+]+)(\\+?)$", - help = "@username+ - Show user's info and last 20 messages") - public CommandResult commandUser(User user, URI attachment, String... arguments) { - User blogUser = userService.getUserByName(arguments[0]); - int page = arguments[1].length(); - if (!blogUser.isAnonymous()) { - List<Integer> mids = messagesService.getUserBlog(blogUser.getUid(), 0, 0); - return CommandResult.fromString(String.format("Last messages from @%s:\n%s", arguments[0], - printMessages(user, mids, false))); - } - return CommandResult.fromString("User not found"); - } - @UserCommand(pattern = "^#(\\d+)(\\+?)$", help = "#1234 - Show message (#1234+ - message with replies)") - public CommandResult commandShow(User user, URI attachment, String... arguments) { - boolean showReplies = arguments[1].length() > 0; - int mid = NumberUtils.toInt(arguments[0], 0); - if (mid == 0) { - return CommandResult.fromString("Error"); - } - com.juick.Message msg = messagesService.getMessage(mid); - if (msg != null) { - if (showReplies) { - List<com.juick.Message> replies = messagesService.getReplies(user, mid); - applicationEventPublisher.publishEvent( - new MessageReadEvent(this, user, msg)); - replies.add(0, msg); - return CommandResult.fromString(String.join("\n", - replies.stream().map(PlainTextFormatter::formatPostSummary).collect(Collectors.toList()))); - } - return CommandResult.fromString(PlainTextFormatter.formatPost(msg)); - } - return CommandResult.fromString("Message not found"); - } - @UserCommand(pattern = "^#(\\d+)\\/(\\d+)$", help = "#1234/5 - Show reply") - public CommandResult commandShowReply(User user, URI attachment, String... arguments) { - int mid = NumberUtils.toInt(arguments[0], 0); - int rid = NumberUtils.toInt(arguments[1], 0); - com.juick.Message reply = messagesService.getReply(mid, rid); - if (reply != null) { - return CommandResult.fromString(PlainTextFormatter.formatPost(reply)); - } - return CommandResult.fromString("Reply not found"); - } - @UserCommand(pattern = "^\\*(\\S+)(\\+?)$", help = "*tag - Show last messages with tag") - public CommandResult commandShowTag(User user, URI attachment, String... arguments) { - if (StringUtils.isNotEmpty(attachment.toString())) { - // new message with tag - return CommandResult.fromString(StringUtils.EMPTY); - } - Tag tag = tagService.getTag(arguments[0], false); - if (tag != null) { - // TODO: synonyms - List<Integer> mids = messagesService.getTag(tag.TID, user.getUid(), 0, 10); - return CommandResult.fromString("Last messages with *" + tag.getName() + ":\n" + printMessages(user, mids, true)); - } - return CommandResult.fromString("Tag not found"); - } - @UserCommand(pattern = "^D #(\\d+)$", help = "D #1234 - Delete post", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandDeletePost(User user, URI attachment, String... args) { - int mid = Integer.valueOf(args[0]); - Message message = messagesService.getMessage(mid); - if (message != null && messagesService.deleteMessage(user.getUid(), mid)) { - applicationEventPublisher.publishEvent(new DeleteMessageEvent(this, message)); - return CommandResult.fromString("Message deleted"); - } - return CommandResult.fromString("This is not your message"); - } - @UserCommand(pattern = "^D #(\\d+)(\\.|\\-|\\/)(\\d+)$", help = "D #1234/5 - Delete comment", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandDeleteReply(User user, URI attachment, String... args) { - int mid = Integer.valueOf(args[0]); - int rid = Integer.valueOf(args[2]); - if (messagesService.deleteReply(user.getUid(), mid, rid)) { - return CommandResult.fromString("Reply deleted"); - } else { - return CommandResult.fromString("This is not your reply"); - } - } - @UserCommand(pattern = "^(D L|DL|D LAST)$", help = "D L - Delete last message", patternFlags = Pattern.CASE_INSENSITIVE) - public CommandResult commandDeleteLast(User user, URI attachment, String... args) { - return CommandResult.fromString("Temporarily unavailable"); - } - @UserCommand(pattern = "^\\?\\s+\\@([a-zA-Z0-9\\-\\.\\@]+)\\s+([\\s\\S]+)$", help = "? @user string - search in user messages") - public CommandResult commandSearch(User user, URI attachment, String... args) { - return CommandResult.fromString("Temporarily unavailable"); - } - @UserCommand(pattern = "^\\?\\s+([\\s\\S]+)$", help = "? string - search in all messages") - public CommandResult commandSearchAll(User user, URI attachment, String... args) { - return CommandResult.fromString("Temporarily unavailable"); - } - @UserCommand(pattern = "^(#+)$", help = "# - Show last messages from your feed (## - second page, ...)") - public CommandResult commandMyFeed(User user, URI attachment, String... arguments) { - // number of # is the page count - int page = arguments[0].length() - 1; - List<Integer> mids = messagesService.getMyFeed(user.getUid(), page, false); - if (mids.size() > 0) { - return CommandResult.fromString("Your feed: \n" + printMessages(user, mids, true)); - } - return CommandResult.fromString("Your feed is empty"); - } - @UserCommand(pattern = "^(#|\\.)(\\d+)((\\.|\\-|\\/)(\\d+))?\\s([\\s\\S]+)?", - help = "#1234 *tag *tag2 - edit tags\n#1234 text - reply to message") - public CommandResult EditOrReply(User user, @Nonnull URI attachment, String... args) throws Exception { - int mid = NumberUtils.toInt(args[1]); - int rid = NumberUtils.toInt(args[4], 0); - String txt = StringUtils.defaultString(args[5]); - Message msg = messagesService.getMessage(mid); - Pair<String, List<Tag>> messageTags = tagService.fromString(txt); - if (messageTags.getRight().size() > 0) { - if (user.getUid() != msg.getUser().getUid()) { - return CommandResult.fromString("It is not your message"); - } - if (!CollectionUtils.isEqualCollection(tagService.updateTags(mid, messageTags.getRight()), msg.getTags())) { - return CommandResult.fromString("Tags are updated"); - } else { - return CommandResult.fromString("Tags are NOT updated (5 tags maximum?)"); - } - } else { - boolean haveAttachment = StringUtils.isNotEmpty(attachment.toString()); - String attachmentFName = null; - String attachmentType = null; - if (haveAttachment) { - attachmentFName = attachment.getScheme().equals("juick") ? attachment.getHost() - : HttpUtils.downloadImage(attachment.toURL(), tmpDir).getHost(); - attachmentType = attachmentFName.substring(attachmentFName.length() - 3); - } - int newrid = messagesService.createReply(mid, rid, user, txt, attachmentType); - if (haveAttachment) { - String fname = String.format("%d-%d.%s", mid, newrid, attachmentType); - imagesService.saveImageWithPreviews(attachmentFName, fname); - } - applicationEventPublisher.publishEvent( - new MessageReadEvent(this, user, msg)); - Message original = messagesService.getMessage(mid); - subscriptionService.subscribeMessage(original, user); - Message reply = messagesService.getReply(mid, newrid); - applicationEventPublisher.publishEvent(new MessageEvent(this, reply, subscriptionService.getUsersSubscribedToComments(original, reply))); - return CommandResult.build(reply,"Reply posted.\n#" + mid + "/" + newrid + " " - + "https://juick.com/m/" + mid + "#" + newrid, - String.format("[Reply](%s) posted", PlainTextFormatter.formatUrl(reply))); - } - } - - String printMessages(User visitor, List<Integer> mids, boolean crop) { - return messagesService.getMessages(visitor, mids).stream() - .sorted(Collections.reverseOrder()) - .map(PlainTextFormatter::formatPostSummary).collect(Collectors.joining("\n\n")); - } -} diff --git a/juick-server/src/main/java/com/juick/server/EmailManager.java b/juick-server/src/main/java/com/juick/server/EmailManager.java deleted file mode 100644 index 1cdafac6..00000000 --- a/juick-server/src/main/java/com/juick/server/EmailManager.java +++ /dev/null @@ -1,165 +0,0 @@ -package com.juick.server; - -import com.juick.Message; -import com.juick.User; -import com.juick.service.EmailService; -import com.juick.service.MessagesService; -import com.juick.service.UserService; -import com.juick.service.component.*; -import com.juick.util.MessageUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import javax.annotation.Nonnull; -import javax.inject.Inject; -import javax.mail.MessagingException; -import javax.mail.Multipart; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import static com.juick.formatters.PlainTextFormatter.formatPost; -import static com.juick.formatters.PlainTextFormatter.formatUrl; - -@Component -public class EmailManager implements NotificationListener { - - public static final String MSGID_PATTERN = "\\.|@|<"; - - private static final Logger logger = LoggerFactory.getLogger(EmailManager.class); - @Inject - private EmailService emailService; - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - @Override - public void processMessageEvent(@Nonnull MessageEvent event) { - Message msg = event.getMessage(); - List<User> subscribedUsers = event.getUsers(); - if (msg.isService()) { - return; - } - if (MessageUtils.isPM(msg)) { - String subject = String.format("Private message from %s", msg.getUser().getName()); - emailService.getEmails(msg.getTo().getUid(), true).forEach(email -> { - emailNotify(email, subject, msg); - }); - } else if (MessageUtils.isReply(msg)) { - Message originalMessage = messagesService.getMessage(msg.getMid()); - String subject = String.format("New reply to %s", originalMessage.getUser().getName()); - subscribedUsers.stream() - .flatMap(user -> emailService.getEmails(user.getUid(), true).stream()) - .forEach(email -> emailNotify(email, subject, msg)); - } else { - String subject = String.format("New message from %s", msg.getUser().getName()); - subscribedUsers - .forEach(user -> emailService.getEmails(user.getUid(), true) - .forEach(email -> emailNotify(email, subject, msg))); - } - } - - @Override - public void processSubscribeEvent(SubscribeEvent subscribeEvent) { - - } - - @Override - public void processLikeEvent(LikeEvent likeEvent) { - - } - - @Override - public void processPingEvent(PingEvent pingEvent) { - - } - - @Override - public void processMessageReadEvent(MessageReadEvent messageReadEvent) { - - } - - @Override - public void processTopEvent(TopEvent topEvent) { - - } - - private void emailNotify(String email, String subject, Message msg) { - Map<String, String> headers = new HashMap<>(); - if (!MessageUtils.isPM(msg)) { - headers.put("Message-ID", String.format("<%d.%d@juick.com>", msg.getMid(), msg.getRid())); - } - if (MessageUtils.isReply(msg)) { - if (msg.getReplyto() > 0) { - Message replyto = messagesService.getReply(msg.getMid(), msg.getReplyto()); - headers.put("In-Reply-To", String.format("<%d.%d@juick.com>", replyto.getMid(), replyto.getRid())); - } else { - Message original = messagesService.getMessage(msg.getMid()); - headers.put("In-Reply-To", String.format("<%d.%d@juick.com>", original.getMid(), original.getRid())); - } - } - String plainText = String.format("%s\n\n--\nYou are receiving this because you are subscribed to this user," + - " discussion, tag or mentioned. Reply to this email directly or view it on Juick: %s.", - formatPost(msg), formatUrl(msg)); - String hash = userService.getHashByUID(userService.getUserByEmail(email).getUid()); - String htmlText = String.format("%s<br /><br />--<br />You are receiving this because you are subscribed to this user" + - ", discussion, tag or mentioned. Reply to this email directly or <a href=\"%s\"><img src=\"https://api.juick.com/thread/mark_read/%d-%d.gif?hash=%s\" />view it</a> on Juick." + - "<br /><a href=\"https://juick.com/settings?hash=%s\">Configure or disable notifications</a>", - msg.isHtml() ? msg.getText() : MessageUtils.formatHtml(msg), formatUrl(msg), - msg.getMid(), msg.getRid(), hash, hash); - sendEmail(email, subject, plainText, htmlText, headers); - } - public void sendEmail(String to, String subject, String textPart, String htmlPart, Map<String, String> messageHeaders) { - Properties prop = System.getProperties(); - prop.put("mail.smtp.starttls.enable", "true"); - Session session = Session.getDefaultInstance(prop); - try { - Transport transport = session.getTransport("smtp"); - MimeMessage message = new MimeMessage(session) { - protected void updateMessageID() throws MessagingException { - for (Map.Entry<String, String> entry: messageHeaders.entrySet()) { - setHeader(entry.getKey(), entry.getValue()); - } - } - }; - message.setFrom(new InternetAddress("juick@juick.com")); - message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to)); - message.setSubject(subject); - MimeBodyPart textBodyPart = new MimeBodyPart(); - textBodyPart.setContent(textPart, "text/plain; charset=UTF-8"); - - Multipart multipart = new MimeMultipart("alternative"); - multipart.addBodyPart(textBodyPart); - if (StringUtils.isNotBlank(htmlPart)) { - MimeBodyPart htmlBodyPart = new MimeBodyPart(); - htmlBodyPart.setContent(htmlPart, "text/html; charset=UTF-8"); - multipart.addBodyPart(htmlBodyPart); - } - message.setContent(multipart); - User emailUser = userService.getUserByEmail(to); - if (!emailUser.isAnonymous()) { - message.setHeader("List-Id", "Juick notifications <mail-notifications.juick.com>"); - message.setHeader("List-Post", "<mailto:juick@juick.com>"); - message.setHeader("List-Owner", "<mailto:support@juick.com>"); - message.setHeader("List-Archive", "<https://juick.com/>"); - message.setHeader("List-Unsubscribe", String.format("https://juick.com/settings/unsubscribe?hash=%s", - userService.getHashByUID(emailUser.getUid()))); - message.setHeader("List-Unsubscribe-Post", "List-Unsubscribe=One-Click"); - } - message.saveChanges(); - transport.connect(); - transport.sendMessage(message, message.getAllRecipients()); - } catch (MessagingException ex) { - logger.error("mail exception", ex); - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/KeystoreManager.java b/juick-server/src/main/java/com/juick/server/KeystoreManager.java deleted file mode 100644 index 97c3a224..00000000 --- a/juick-server/src/main/java/com/juick/server/KeystoreManager.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.juick.server; - -import com.juick.server.api.activity.model.objects.Person; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.util.Base64Utils; - -import javax.annotation.PostConstruct; -import javax.net.ssl.KeyManagerFactory; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.*; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.spec.X509EncodedKeySpec; -import java.util.Arrays; -import java.util.stream.Collectors; - -@Component -public class KeystoreManager { - private static final Logger logger = LoggerFactory.getLogger("com.juick.server"); - @Value("${keystore:../juick.p12}") - private String keystore; - @Value("${keystore_password:secret}") - private String keystorePassword; - - private KeyStore ks; - - private KeyManagerFactory kmf; - - @PostConstruct - public void init() { - try (InputStream ksIs = new FileInputStream(keystore)) { - ks = KeyStore.getInstance("PKCS12"); - ks.load(ksIs, keystorePassword.toCharArray()); - kmf = KeyManagerFactory.getInstance(KeyManagerFactory - .getDefaultAlgorithm()); - kmf.init(ks, keystorePassword.toCharArray()); - } catch (IOException | KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException e) { - logger.error("Keystore error", e); - } - } - - public KeyStore getKeystore() { - return ks; - } - - public KeyManagerFactory getKeymanagerFactory() { - return kmf; - } - - private KeyPair getKeyPair() { - Key privateKey = null; - try { - privateKey = ks.getKey("1", keystorePassword.toCharArray()); - Certificate certificate = ks.getCertificate("1"); - return new KeyPair(certificate.getPublicKey(), (PrivateKey) privateKey); - } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) { - e.printStackTrace(); - } - return null; - } - public PrivateKey getPrivateKey() { - return getKeyPair().getPrivate(); - } - public PublicKey getPublicKey() { - return getKeyPair().getPublic(); - } - public String getPublicKeyPem() { - String[] key = Base64Utils.encodeToString(getKeyPair().getPublic().getEncoded()).split("(?<=\\G.{64})"); - return String.format("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n", - Arrays.asList(key).stream().collect(Collectors.joining("\n"))); - } - public static PublicKey publicKeyOf(Person person) { - String pubkeyPem = person.getPublicKey().getPublicKeyPem(); - String[] rawKey = pubkeyPem.split("\\n"); - String pubkeyData = String.join("", Arrays.asList(rawKey).subList(1, rawKey.length - 1)); - try{ - byte[] byteKey = Base64Utils.decodeFromString(pubkeyData); - X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(byteKey); - KeyFactory kf = KeyFactory.getInstance("RSA"); - return kf.generatePublic(X509publicKey); - } - catch(Exception e){ - e.printStackTrace(); - } - return null; - } -} diff --git a/juick-server/src/main/java/com/juick/server/ServerManager.java b/juick-server/src/main/java/com/juick/server/ServerManager.java deleted file mode 100644 index ef848526..00000000 --- a/juick-server/src/main/java/com/juick/server/ServerManager.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.server; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.juick.Message; -import com.juick.User; -import com.juick.model.AnonymousUser; -import com.juick.service.MessagesService; -import com.juick.service.SubscriptionService; -import com.juick.service.UserService; -import com.juick.service.component.LikeEvent; -import com.juick.service.component.MessageEvent; -import com.juick.service.component.MessageReadEvent; -import com.juick.service.component.NotificationListener; -import com.juick.service.component.PingEvent; -import com.juick.service.component.SubscribeEvent; -import com.juick.service.component.TopEvent; -import com.juick.util.MessageUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import org.springframework.web.socket.TextMessage; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.stream.Collectors; - -/** - * @author Ugnich Anton - */ -@Component -public class ServerManager implements NotificationListener { - private static Logger logger = LoggerFactory.getLogger(ServerManager.class); - - @Inject - private ObjectMapper jsonMapper; - @Inject - private MessagesService messagesService; - @Inject - private WebsocketManager wsHandler; - @Inject - private SubscriptionService subscriptionService; - @Inject - private UserService userService; - private CopyOnWriteArrayList<EventSession> sessions = new CopyOnWriteArrayList<>(); - - @Value("${service_user:juick}") - private String serviceUsername; - - private User serviceUser; - - @PostConstruct - public void init() { - serviceUser = userService.getUserByName(serviceUsername); - } - - - private void onJuickPM(final User to, final com.juick.Message jmsg) { - try { - String json = jsonMapper.writeValueAsString(jmsg); - synchronized (wsHandler.getClients()) { - wsHandler.getClients().stream().filter(c -> - (!c.legacy && c.visitor.getUid() == to.getUid()) || c.visitor.equals(serviceUser)) - .forEach(c -> { - try { - logger.debug("sending pm to {}", c.visitor.getUid()); - c.sendMessage(new TextMessage(json)); - } catch (IOException e) { - logger.warn("ws error", e); - } - }); - } - } catch (JsonProcessingException e) { - logger.warn("Invalid JSON", e); - } - messageEvent(jmsg, Collections.singletonList(to)); - } - - private void onJuickMessagePost(final com.juick.Message jmsg, List<User> subscribedUsers) { - try { - String json = jsonMapper.writeValueAsString(jmsg); - List<Integer> uids = subscribedUsers - .stream().map(User::getUid).collect(Collectors.toList()); - synchronized (wsHandler.getClients()) { - wsHandler.getClients().stream().filter(c -> - (!c.legacy && c.visitor.isAnonymous()) // anonymous users - || c.visitor.equals(serviceUser) // services - || (!c.legacy && uids.contains(c.visitor.getUid()))) // subscriptions - .forEach(c -> { - try { - logger.debug("sending message to {}", c.visitor.getUid()); - c.sendMessage(new TextMessage(json)); - } catch (IOException e) { - logger.warn("ws error", e); - } - }); - wsHandler.getClients().stream().filter(c -> - c.legacy && c.allMessages) // legacy all posts - .forEach(c -> { - try { - logger.debug("sending message to legacy client {}", c.visitor.getUid()); - c.sendMessage(new TextMessage(json)); - } catch (IOException e) { - logger.warn("ws error", e); - } - }); - } - } catch (JsonProcessingException e) { - logger.warn("Invalid JSON", e); - } - messageEvent(jmsg, subscribedUsers); - messageEvent(jmsg, Collections.singletonList(AnonymousUser.INSTANCE)); - } - - private void onJuickMessageReply(final com.juick.Message jmsg, final List<User> subscribedUsers) { - try { - - String json = jsonMapper.writeValueAsString(jmsg); - com.juick.Message op = messagesService.getMessage(jmsg.getMid()); - List<Integer> threadUsers = - subscribedUsers - .stream().map(User::getUid).collect(Collectors.toList()); - synchronized (wsHandler.getClients()) { - wsHandler.getClients().stream().filter(c -> - (!c.legacy && c.visitor.isAnonymous()) // anonymous users - || c.visitor.equals(serviceUser) // services - || (!c.legacy && threadUsers.contains(c.visitor.getUid()))) // subscriptions - .forEach(c -> { - try { - logger.debug("sending reply to {}", c.visitor.getUid()); - c.sendMessage(new TextMessage(json)); - } catch (IOException e) { - logger.warn("ws error", e); - } - }); - wsHandler.getClients().stream().filter(c -> - (c.legacy && c.allReplies) || (c.legacy && c.MID == jmsg.getMid())) // legacy replies - .forEach(c -> { - try { - logger.debug("sending reply to legacy client {}", c.visitor.getUid()); - c.sendMessage(new TextMessage(json)); - } catch (IOException e) { - logger.warn("ws error", e); - } - }); - } - } catch (JsonProcessingException e) { - logger.warn("Invalid JSON", e); - } - messageEvent(jmsg, subscribedUsers); - messageEvent(jmsg, Collections.singletonList(AnonymousUser.INSTANCE)); - } - - @Override - public void processMessageEvent(MessageEvent event) { - com.juick.Message jmsg = event.getMessage(); - List<User> subscribedUsers = event.getUsers(); - if (jmsg.isService()) { - readEvent(jmsg, Collections.singletonList(serviceUser)); - return; - } - if (MessageUtils.isPM(jmsg)) { - onJuickPM(jmsg.getTo(), jmsg); - } else if (!MessageUtils.isReply(jmsg)) { - // to get full message with attachment, etc. - onJuickMessagePost(messagesService.getMessage(jmsg.getMid()), subscribedUsers); - } else { - // to get quote and attachment - Message op = messagesService.getMessage(jmsg.getMid()); - com.juick.Message reply = messagesService.getReply(jmsg.getMid(), jmsg.getRid()); - subscriptionService.getUsersSubscribedToComments(op, reply, true).stream() - .filter(u -> userService.isReplyToBL(u, reply)) - .forEach(b -> messagesService.setLastReadComment(b, reply.getMid(), reply.getRid())); - onJuickMessageReply(reply, subscribedUsers); - } - messageEvent(jmsg, Collections.singletonList(serviceUser)); - } - - @Override - public void processSubscribeEvent(SubscribeEvent subscribeEvent) { - - } - - @Override - public void processLikeEvent(LikeEvent likeEvent) { - - } - - @Override - public void processPingEvent(PingEvent pingEvent) { - - } - - @Override - public void processMessageReadEvent(MessageReadEvent messageReadEvent) { - User user = messageReadEvent.getUser(); - Message source = messageReadEvent.getMessage(); - - logger.info("Message read event from {} for {}", user.getUid(), source.getMid()); - Message serviceMessage = new Message(); - serviceMessage.setService(true); - serviceMessage.setUser(user); - serviceMessage.setMid(source.getMid()); - serviceMessage.setUnread(false); - wsHandler.getClients().stream().filter(c -> - (!c.legacy && c.visitor == user) || c.visitor.equals(serviceUser) - ).forEach(u -> { - try { - u.sendMessage(new TextMessage(jsonMapper.writeValueAsString(serviceMessage))); - } catch (IOException e) { - logger.error("JSON error", e); - } - }); - readEvent(serviceMessage, Collections.singletonList(serviceUser)); - } - - @Override - public void processTopEvent(TopEvent topEvent) { - User topUser = topEvent.getMessage().getUser(); - topEvent(topEvent.getMessage(), Arrays.asList(topUser, serviceUser)); - } - - public void topEvent(Message msg, List<User> subscribers){ - sendSseEvent(msg, "top", subscribers); - } - - public void readEvent(Message msg, List<User> subscribers){ - sendSseEvent(msg, "read", subscribers); - } - - public void messageEvent(Message msg, List<User> subscribers){ - sendSseEvent(msg, "msg", subscribers); - } - - private void sendSseEvent(Message msg, String name, List<User> subscribers) { - List<EventSession> deadEmitters = new ArrayList<>(); - this.sessions.stream().filter(s -> subscribers.contains(s.user)).forEach(session -> { - try { - SseEmitter.SseEventBuilder builder = SseEmitter.event() - .name(name) - .data(msg); - session.getEmitter().send(builder); - } catch (Exception e) { - deadEmitters.add(session); - } - }); - this.sessions.removeAll(deadEmitters); - } - - public static class EventSession { - private User user; - private SseEmitter emitter; - - public EventSession(User user, SseEmitter sseEmitter) { - this.user = user; - this.emitter = sseEmitter; - } - - public User getUser() { - return user; - } - - public SseEmitter getEmitter() { - return emitter; - } - } - - public CopyOnWriteArrayList<EventSession> getSessions() { - return sessions; - } -} diff --git a/juick-server/src/main/java/com/juick/server/SignatureManager.java b/juick-server/src/main/java/com/juick/server/SignatureManager.java deleted file mode 100644 index b3b7a301..00000000 --- a/juick-server/src/main/java/com/juick/server/SignatureManager.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.juick.server; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.juick.server.api.activity.model.Context; -import com.juick.server.api.activity.model.objects.Person; -import com.juick.server.api.webfinger.model.Account; -import com.juick.server.api.webfinger.model.Link; -import com.juick.util.DateFormattersHolder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; -import org.tomitribe.auth.signatures.Signature; -import org.tomitribe.auth.signatures.Signer; -import org.tomitribe.auth.signatures.Verifier; -import rocks.xmpp.addr.Jid; - -import javax.inject.Inject; -import java.io.IOException; -import java.net.URI; -import java.security.Key; -import java.security.NoSuchAlgorithmException; -import java.security.SignatureException; -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -import static com.juick.server.api.activity.model.Context.ACTIVITY_MEDIA_TYPE; - -@Component -public class SignatureManager { - private static final Logger logger = LoggerFactory.getLogger(ActivityPubManager.class); - @Inject - private KeystoreManager keystoreManager; - @Inject - private ObjectMapper jsonMapper; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - @Inject - private RestTemplate apClient; - - public void post(Person from, Person to, Context data) throws IOException { - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(to.getInbox()); - URI inbox = uriComponentsBuilder.build().toUri(); - Instant now = Instant.now(); - String requestDate = DateFormattersHolder.getHttpDateFormatter().format(now); - Signature templateSignature = new Signature(from.getPublicKey().getId(), "rsa-sha256", null, - "(request-target)", "host", "date"); - Signer signer = new Signer(keystoreManager.getPrivateKey(), templateSignature); - Map<String, String> headers = new HashMap<>(); - headers.put("host", inbox.getHost()); - headers.put("date", requestDate); - Signature signature = signer.sign("POST", inbox.getPath(), headers); - HttpHeaders requestHeaders = new HttpHeaders(); - requestHeaders.add("Content-Type", Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE); - requestHeaders.add("Date", requestDate); - requestHeaders.add("Signature", signature.toString().substring(10)); - HttpEntity<Context> request = new HttpEntity<>(Context.build(data), requestHeaders); - //boolean valid = verifySignature(Signature.fromString(requestHeaders.getFirst("Signature")), - // keystoreManager.getPublicKey(), "POST", inbox.getPath(), headers); - logger.info("Sending context: {}", jsonMapper.writeValueAsString(data)); - logger.info("Request date: {}", requestDate); - ResponseEntity<Void> response = apClient.postForEntity(inbox, request, Void.class); - logger.info("accepted follower: {}", response.getStatusCodeValue()); - - } - public boolean verifySignature(String signatureString, URI actor, String method, String path, Map<String, String> headers) { - Optional<Context> context = getContext(actor); - if (context.isPresent() && context.get() instanceof Person) { - Person person = (Person) context.get(); - Key key = KeystoreManager.publicKeyOf(person); - Verifier verifier = new Verifier(key, Signature.fromString(signatureString)); - try { - boolean result = verifier.verify(method, path, headers); - logger.info("signature is valid: {}", result); - return result; - } catch (NoSuchAlgorithmException | SignatureException | IOException e) { - logger.info("signature exception", e); - return false; - } - } - logger.info("person not found"); - return false; - } - public Optional<Context> getContext(URI contextUri) { - Context context = apClient.getForEntity(contextUri, Context.class).getBody(); - if (context == null) { - logger.warn("Cannot identify {}", contextUri); - return Optional.empty(); - } - return Optional.of(context); - } - public Optional<Context> discoverPerson(String acct) { - Jid acctId = Jid.of(acct); - URI resourceUri = UriComponentsBuilder.fromUriString( - String.format("https://%s/.well-known/webfinger?resource=acct:%s", acctId.getDomain(), acct)).build().toUri(); - Account acctData = apClient.getForEntity(resourceUri, Account.class).getBody(); - if (acctData != null) { - for (Link l : acctData.getLinks()) { - if (l.getRel().equals("self") && l.getType().equals(ACTIVITY_MEDIA_TYPE)) { - return getContext(URI.create(l.getHref())); - } - } - } - return Optional.empty(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/TelegramBotManager.java b/juick-server/src/main/java/com/juick/server/TelegramBotManager.java deleted file mode 100644 index 8e8d0104..00000000 --- a/juick-server/src/main/java/com/juick/server/TelegramBotManager.java +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server; - -import com.juick.User; -import com.juick.service.component.*; -import com.juick.model.AnonymousUser; -import com.juick.model.CommandResult; -import com.juick.server.util.HttpUtils; -import com.juick.service.MessagesService; -import com.juick.service.TelegramService; -import com.juick.service.UserService; -import com.juick.util.MessageUtils; -import com.pengrad.telegrambot.Callback; -import com.pengrad.telegrambot.TelegramBot; -import com.pengrad.telegrambot.UpdatesListener; -import com.pengrad.telegrambot.model.Message; -import com.pengrad.telegrambot.model.MessageEntity; -import com.pengrad.telegrambot.model.PhotoSize; -import com.pengrad.telegrambot.model.Update; -import com.pengrad.telegrambot.model.request.ParseMode; -import com.pengrad.telegrambot.request.GetFile; -import com.pengrad.telegrambot.request.SendMessage; -import com.pengrad.telegrambot.request.SendPhoto; -import com.pengrad.telegrambot.request.SetWebhook; -import com.pengrad.telegrambot.response.GetFileResponse; -import com.pengrad.telegrambot.response.SendResponse; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import java.io.IOException; -import java.net.URI; -import java.net.URL; -import java.util.*; - -import static com.juick.formatters.PlainTextFormatter.formatPost; -import static com.juick.formatters.PlainTextFormatter.formatUrl; - -/** - * Created by vt on 12/05/16. - */ -public class TelegramBotManager implements NotificationListener { - private static final Logger logger = LoggerFactory.getLogger(TelegramBotManager.class); - - private TelegramBot bot; - - @Value("${telegram_token:12345678}") - private String telegramToken; - @Value("${telegram_debug:false}") - private boolean telegramDebug; - @Inject - private TelegramService telegramService; - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - @Inject - private CommandsManager commandsManager; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - - private static final String MSG_LINK = "🔗"; - - @PostConstruct - public void init() { - if (StringUtils.isBlank(telegramToken)) { - logger.info("telegram token is not set, exiting"); - return; - } - bot = new TelegramBot(telegramToken); - if (!telegramDebug) { - try { - SetWebhook webhook = new SetWebhook().url("https://api.juick.com/tlgmbtwbhk"); - if (!bot.execute(webhook).isOk()) { - logger.error("error setting webhook"); - } - } catch (Exception e) { - logger.warn("couldn't initialize telegram bot", e); - } - } else { - bot.setUpdatesListener(updates -> { - updates.forEach(this::processUpdate); - return UpdatesListener.CONFIRMED_UPDATES_ALL; - }); - } - } - - public void processUpdate(Update update) { - Message message = update.message(); - if (update.message() == null) { - message = update.editedMessage(); - if (message == null) { - logger.error("error parsing telegram update: {}", update); - return; - } - } - User user_from = userService.getUserByUID(telegramService.getUser(message.chat().id())).orElse(AnonymousUser.INSTANCE); - logger.info("Found juick user {}", user_from.getUid()); - - String username = message.from().username(); - if (username == null) { - username = message.from().firstName(); - } - if (!user_from.isAnonymous()) { - URI attachment = URI.create(StringUtils.EMPTY); - if (message.photo() != null) { - String fileId = Arrays.stream(message.photo()).max(Comparator.comparingInt(PhotoSize::fileSize)) - .orElse(new PhotoSize()).fileId(); - if (StringUtils.isNotEmpty(fileId)) { - GetFile request = new GetFile(fileId); - GetFileResponse response = bot.execute(request); - logger.info("got file {}", response.file()); - try { - URL fileURL = new URL(bot.getFullFilePath(response.file())); - attachment = HttpUtils.downloadImage(fileURL, tmpDir); - } catch (Exception e) { - logger.warn("attachment exception", e); - } - logger.info("received {}", attachment); - } - } - String text = message.text(); - if (StringUtils.isBlank(text)) { - text = message.caption(); - } - if (StringUtils.isBlank(text)) { - text = StringUtils.EMPTY; - } - if (StringUtils.isNotEmpty(text) || StringUtils.isNotEmpty(attachment.toString())) { - if (text.equalsIgnoreCase("LOGIN") - || text.equalsIgnoreCase("PING") - || text.equalsIgnoreCase("HELP") - || text.equalsIgnoreCase("/login") - || text.equalsIgnoreCase("/logout") - || text.equalsIgnoreCase("/start") - || text.equalsIgnoreCase("/help")) { - String msgUrl = "http://juick.com/login?hash=" + userService.getHashByUID(user_from.getUid()); - String msg = String.format("Hi, %s!\nYou can post messages and images to Juick there.\n" + - "Tap to [log into website](%s) to get more info", user_from.getName(), msgUrl); - telegramNotify(message.from().id().longValue(), msg, new com.juick.Message()); - } else { - Message replyMessage = message.replyToMessage(); - if (replyMessage != null) { - MessageEntity[] entities = replyMessage.entities(); - if (entities == null) { - entities = replyMessage.captionEntities(); - } - if (entities != null) { - Optional<MessageEntity> juickLink = Arrays.stream(entities) - .filter(this::isJuickLink) - .findFirst(); - if (juickLink.isPresent()) { - if (StringUtils.isNotEmpty(juickLink.get().url())) { - UriComponents uriComponents = UriComponentsBuilder.fromUriString( - juickLink.get().url()).build(); - String path = uriComponents.getPath(); - if (StringUtils.isNotEmpty(path) && path.length() > 1) { - int mid = 0; - try { - mid = Integer.valueOf(path.substring(3)); - } catch (NumberFormatException e) { - logger.warn("wrong mid received"); - return; - } - String prefix = String.format("#%d ", mid); - if (StringUtils.isNotEmpty(uriComponents.getFragment())) { - int rid = Integer.valueOf(uriComponents.getFragment()); - prefix = String.format("#%d/%d ", mid, rid); - } - CommandResult result = null; - try { - result = commandsManager.processCommand(user_from, prefix + text, attachment); - String messageTxt = StringUtils.isNotEmpty(result.getMarkdown()) ? result.getMarkdown() - : "Unknown error or unsupported command"; - telegramNotify(message.from().id().longValue(), messageTxt, new com.juick.Message()); - } catch (Exception e) { - logger.warn("telegram exception", e); - } - } else { - logger.warn("invalid path: {}", path); - } - } else { - logger.warn("invalid entity: {}", juickLink); - } - } else { - telegramNotify(message.from().id().longValue(), - "Can not reply to this message", replyMessage.messageId(), new com.juick.Message()); - } - } else { - telegramNotify(message.from().id().longValue(), - "Can not reply to this message", replyMessage.messageId(), new com.juick.Message()); - } - } else { - try { - CommandResult result = commandsManager.processCommand(user_from, text, attachment); - String messageTxt = StringUtils.isNotEmpty(result.getMarkdown()) ? result.getMarkdown() - : "Unknown error or unsupported command"; - telegramNotify(message.from().id().longValue(), messageTxt, new com.juick.Message()); - } catch (Exception e) { - logger.warn("telegram reply exception", e); - } - } - } - } - } else { - List<Long> chats = telegramService.getAnonymous(); - if (!chats.contains(message.chat().id())) { - logger.info("added chat with {}", username); - telegramService.createTelegramUser(message.from().id(), username); - } - telegramSignupNotify(message.from().id().longValue(), userService.getSignUpHashByTelegramID(message.from().id().longValue(), username)); - } - } - - private boolean isJuickLink(MessageEntity e) { - return e.offset() == 0 && e.type().equals(MessageEntity.Type.text_link) && e.length() == 2; - } - - public void telegramNotify(Long chatId, String msg, @Nonnull com.juick.Message source) { - telegramNotify(chatId, msg, 0, source); - } - - public void telegramNotify(Long chatId, String msg, Integer replyTo, @Nonnull com.juick.Message source) { - String attachment = MessageUtils.attachmentUrl(source); - if (StringUtils.isEmpty(attachment)) { - SendMessage telegramMessage = new SendMessage(chatId, msg); - if (replyTo > 0) { - telegramMessage.replyToMessageId(replyTo); - } - telegramMessage.parseMode(ParseMode.Markdown).disableWebPagePreview(true); - bot.execute(telegramMessage, new Callback<SendMessage, SendResponse>() { - @Override - public void onResponse(SendMessage request, SendResponse response) { - processTelegramResponse(chatId, response, source); - } - - @Override - public void onFailure(SendMessage request, IOException e) { - logger.warn("telegram failure", e); - } - }); - } else { - SendPhoto telegramPhoto = new SendPhoto(chatId, attachment); - String trimmedPost = msg.length() > 1024 ? msg.substring(0, 1023) + "..." : msg; - telegramPhoto.caption(trimmedPost); - if (replyTo > 0) { - telegramPhoto.replyToMessageId(replyTo); - } - telegramPhoto.parseMode(ParseMode.Markdown); - bot.execute(telegramPhoto, new Callback<SendPhoto, SendResponse>() { - @Override - public void onResponse(SendPhoto request, SendResponse response) { - processTelegramResponse(chatId, response, source); - } - - @Override - public void onFailure(SendPhoto request, IOException e) { - logger.warn("telegram failure", e); - } - }); - } - } - - private void processTelegramResponse(Long chatId, SendResponse response, com.juick.Message source) { - int userId = telegramService.getUser(chatId); - if (!response.isOk()) { - if (response.errorCode() == 403) { - // remove from anonymous users - telegramService.getAnonymous().stream().filter(c -> c.equals(chatId)).findFirst().ifPresent( - d -> { - telegramService.deleteAnonymous(d); - logger.info("deleted {} chat", d); - } - ); - if (userId > 0) { - User userToDelete = userService.getUserByUID(userId) - .orElseThrow(IllegalStateException::new); - boolean status = telegramService.deleteTelegramUser(userToDelete.getUid()); - logger.info("deleting telegram id of @{} : {}", userToDelete.getName(), status); - } - } else { - logger.warn("error response, isOk: {}, errorCode: {}, description: {}", - response.isOk(), response.errorCode(), response.description()); - } - } else { - if (MessageUtils.isReply(source)) { - messagesService.setLastReadComment(userService.getUserByUID(userId) - .orElseThrow(IllegalStateException::new), source.getMid(), source.getRid()); - User user = userService.getUserByUID(userId).orElseThrow(IllegalStateException::new); - userService.updateLastSeen(user); - applicationEventPublisher.publishEvent( - new MessageReadEvent(this, user, source)); - } - } - } - - public void telegramSignupNotify(Long telegramId, String hash) { - bot.execute(new SendMessage(telegramId, - String.format("You are subscribed to all Juick messages. " + - "[Create or link](http://juick.com/signup?type=durov&hash=%s) " + - "an existing Juick account to get your subscriptions and ability to post messages", hash)) - .parseMode(ParseMode.Markdown), new Callback<SendMessage, SendResponse>() { - @Override - public void onResponse(SendMessage request, SendResponse response) { - logger.info("got response: {}", response.message()); - } - - @Override - public void onFailure(SendMessage request, IOException e) { - logger.warn("telegram failure", e); - } - }); - } - - @Override - public void processMessageEvent(MessageEvent messageEvent) { - com.juick.Message jmsg = messageEvent.getMessage(); - List<User> subscribedUsers = messageEvent.getUsers(); - if (jmsg.isService()) { - return; - } - String msgUrl = formatUrl(jmsg); - if (MessageUtils.isPM(jmsg)) { - telegramService.getTelegramIdentifiers(Collections.singletonList(jmsg.getTo())) - .forEach(c -> telegramNotify(c, formatPost(jmsg, true), jmsg)); - } else if (MessageUtils.isReply(jmsg)) { - com.juick.Message op = messagesService.getMessage(jmsg.getMid()); - String fmsg = String.format("[%s](%s) %s", MSG_LINK, msgUrl, formatPost(jmsg, true)); - telegramService.getTelegramIdentifiers( - subscribedUsers - ).forEach(c -> telegramNotify(c, fmsg, jmsg)); - } else { - String msg = String.format("[%s](%s) %s", MSG_LINK, msgUrl, formatPost(jmsg, true)); - - List<Long> users = telegramService.getTelegramIdentifiers(subscribedUsers); - List<Long> chats = telegramService.getAnonymous(); - // registered subscribed users - - users.forEach(c -> telegramNotify(c, msg, jmsg)); - // anonymous - chats.stream().filter(u -> telegramService.getUser(u) == 0).forEach(c -> telegramNotify(c, msg, jmsg)); - } - } - - @Override - public void processLikeEvent(LikeEvent likeEvent) { - User liker = likeEvent.getUser(); - com.juick.Message message = likeEvent.getMessage(); - List<User> subscribers = likeEvent.getSubscribers(); - logger.info("Like received in tg listener"); - if (!userService.isInBLAny(message.getUser().getUid(), liker.getUid())) { - telegramService.getTelegramIdentifiers(Collections.singletonList(message.getUser())) - .forEach(c -> telegramNotify(c, String.format("%s recommends your [post](%s)", - MessageUtils.getMarkdownUser(liker), formatUrl(message)), new com.juick.Message())); - } - telegramService.getTelegramIdentifiers(subscribers) - .forEach(c -> telegramNotify(c, String.format("%s recommends you someone's [post](%s)", - MessageUtils.getMarkdownUser(liker), formatUrl(message)), new com.juick.Message())); - } - - @Override - public void processPingEvent(PingEvent pingEvent) { - - } - - @Override - public void processMessageReadEvent(MessageReadEvent messageReadEvent) { - - } - - @Override - public void processTopEvent(TopEvent topEvent) { - com.juick.Message message = topEvent.getMessage(); - telegramService.getTelegramIdentifiers(Collections.singletonList(message.getUser())) - .forEach(c -> telegramNotify(c, String.format("Your [post](%s) became popular!", - formatUrl(message)), new com.juick.Message())); - } - - @Override - public void processSubscribeEvent(SubscribeEvent subscribeEvent) { - User subscriber = subscribeEvent.getUser(); - User target = subscribeEvent.getToUser(); - telegramService.getTelegramIdentifiers(Collections.singletonList(target)) - .forEach(c -> telegramNotify(c, String.format("%s subscribed to your blog", - MessageUtils.getMarkdownUser(subscriber)), new com.juick.Message())); - } -} diff --git a/juick-server/src/main/java/com/juick/server/TopManager.java b/juick-server/src/main/java/com/juick/server/TopManager.java deleted file mode 100644 index e5c00242..00000000 --- a/juick-server/src/main/java/com/juick/server/TopManager.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server; - -import com.juick.Message; -import com.juick.Tag; -import com.juick.service.MessagesService; -import com.juick.service.component.TopEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -import javax.inject.Inject; -import java.util.List; -import java.util.stream.Collectors; - -@Component -public class TopManager { - private static Logger logger = LoggerFactory.getLogger(TopManager.class); - @Inject - private MessagesService messagesService; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - - @Scheduled(fixedRate = 3600000) - public void updateTop() { - messagesService.getPopularCandidates().forEach(m -> { - Message jmsg = messagesService.getMessage(m); - logger.info("added {} to popular", m); - messagesService.setMessagePopular(m, 1); - List<String> tags = jmsg.getTags().stream().map(Tag::getName).map(String::toLowerCase).collect(Collectors.toList()); - if (!tags.contains("juick")) { - applicationEventPublisher.publishEvent(new TopEvent(this, jmsg)); - } - }); - } -} diff --git a/juick-server/src/main/java/com/juick/server/TwitterManager.java b/juick-server/src/main/java/com/juick/server/TwitterManager.java deleted file mode 100644 index 613594e6..00000000 --- a/juick-server/src/main/java/com/juick/server/TwitterManager.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.server; - -import com.juick.Message; -import com.juick.User; -import com.juick.service.UserService; -import com.juick.service.component.*; -import com.juick.service.CrosspostService; -import com.juick.util.MessageUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import twitter4j.TwitterFactory; -import twitter4j.conf.ConfigurationBuilder; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; - -/** - * @author Ugnich Anton - */ -@Component -public class TwitterManager implements NotificationListener { - - private static Logger logger = LoggerFactory.getLogger(TwitterManager.class); - - @Inject - private CrosspostService crosspostService; - - @Value("${twitter_consumer_key:12345678}") - private String twitter_consumer_key; - @Value("${twitter_consumer_secret:secret}") - private String twitter_consumer_secret; - @Inject - private UserService userService; - - @Value("${service_user:juick}") - private String serviceUsername; - - private User serviceUser; - - @PostConstruct - public void init() { - serviceUser = userService.getUserByName(serviceUsername); - } - - void twitterPost(final com.juick.Message jmsg) { - crosspostService.getTwitterToken(jmsg.getUser().getUid()).ifPresent(t -> { - String status = MessageUtils.getMessageHashTags(jmsg) + StringUtils.defaultString(jmsg.getText()); - if (status.length() > 253) { - status = status.substring(0, 252) + "…"; - } - status += " http://juick.com/" + jmsg.getMid(); - ConfigurationBuilder configurationBuilder = new ConfigurationBuilder() - .setDebugEnabled(true) - .setOAuthConsumerKey(twitter_consumer_key) - .setOAuthConsumerSecret(twitter_consumer_secret); - TwitterFactory tf = new TwitterFactory(configurationBuilder - .setOAuthAccessToken(t.getToken()) - .setOAuthAccessTokenSecret(t.getSecret()).build()); - try { - tf.getInstance().updateStatus(status); - } catch (Exception e) { - logger.info("Twitter exception", e); - } - }); - } - - @Override - public void processMessageEvent(MessageEvent messageEvent) { - Message msg = messageEvent.getMessage(); - if (MessageUtils.isPM(msg) || MessageUtils.isReply(msg) || msg.isService()) { - return; - } - if (StringUtils.isNotEmpty(crosspostService.getTwitterName(msg.getUser().getUid()))) { - if (msg.getTags().stream().noneMatch(t -> t.getName().equals("notwitter"))) { - twitterPost(msg); - } - } - } - - @Override - public void processSubscribeEvent(SubscribeEvent subscribeEvent) { - - } - - @Override - public void processLikeEvent(LikeEvent likeEvent) { - - } - - @Override - public void processPingEvent(PingEvent pingEvent) { - - } - - @Override - public void processMessageReadEvent(MessageReadEvent messageReadEvent) { - - } - - @Override - public void processTopEvent(TopEvent topEvent) { - Message jmsg = topEvent.getMessage(); - jmsg.setUser(serviceUser); - twitterPost(jmsg); - } -} diff --git a/juick-server/src/main/java/com/juick/server/Utils.java b/juick-server/src/main/java/com/juick/server/Utils.java deleted file mode 100644 index 23768ed2..00000000 --- a/juick-server/src/main/java/com/juick/server/Utils.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.server; - -import javax.servlet.http.HttpServletRequest; -import java.util.Optional; - -/** - * - * @author Ugnich Anton - */ -public class Utils { - - - public static String encodeSphinx(String str) { - return str.replaceAll("@", "\\\\@") - .replaceAll("\\'", "\\\\'") - .replaceAll("=", "\\\\\\\\="); - } - /** - * Returns the viewName to return for coming back to the sender url - * - * @param request Instance of {@link HttpServletRequest} or use an injected instance - * @return Optional with the view name. Recomended to use an alternativa url with - * {@link Optional#orElse(java.lang.Object)} - */ - public static Optional<String> getPreviousPageByRequest(HttpServletRequest request) - { - return Optional.ofNullable(request.getHeader("Referer")); - } -} diff --git a/juick-server/src/main/java/com/juick/server/WebsocketManager.java b/juick-server/src/main/java/com/juick/server/WebsocketManager.java deleted file mode 100644 index 1b62b984..00000000 --- a/juick-server/src/main/java/com/juick/server/WebsocketManager.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server; - -import com.juick.User; -import com.juick.model.AnonymousUser; -import com.juick.model.CommandResult; -import com.juick.server.util.HttpForbiddenException; -import com.juick.server.util.HttpNotFoundException; -import com.juick.service.MessagesService; -import com.juick.service.UserService; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpHeaders; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; -import org.springframework.web.socket.CloseStatus; -import org.springframework.web.socket.PingMessage; -import org.springframework.web.socket.TextMessage; -import org.springframework.web.socket.WebSocketSession; -import org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator; -import org.springframework.web.socket.handler.TextWebSocketHandler; -import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.annotation.Nonnull; -import javax.inject.Inject; -import java.io.IOException; -import java.net.URI; -import java.time.Instant; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * Created by vitalyster on 28.06.2016. - */ -@Component -public class WebsocketManager extends TextWebSocketHandler { - private static final Logger logger = LoggerFactory.getLogger(WebsocketManager.class); - - private final List<UserSession> clients = new CopyOnWriteArrayList<>(); - - @Inject - private UserService userService; - @Inject - private MessagesService messagesService; - @Inject - private CommandsManager commandsManager; - - - @Override - public void afterConnectionEstablished(WebSocketSession session) { - - UserSession userSession = new UserSession(session); - URI hLocation = session.getUri(); - - // Auth - UriComponents uriComponents = UriComponentsBuilder.fromUri(hLocation).build(); - List<String> hash = uriComponents.getQueryParams().get("hash"); - if (hash != null && hash.get(0).length() == 16) { - userSession.visitor = userService.getUserByHash(hash.get(0)); - } else { - logger.debug("wrong hash for {} from {}", userSession.visitor.getUid(), userSession); - } - - if (hLocation.getPath().equals("/ws/")) { - logger.debug("user {} connected", userSession.visitor.getUid()); - } else if (hLocation.getPath().equals("/ws/_all")) { - logger.debug("user {} connected to legacy _all ({})", userSession.visitor.getUid(), hLocation.getPath()); - userSession.legacy = true; - userSession.allMessages = true; - } else if (hLocation.getPath().equals("/ws/_replies")) { - logger.debug("user {} connected to legacy _replies ({})", userSession.visitor.getUid(), hLocation.getPath()); - userSession.legacy = true; - userSession.allReplies = true; - } else if (hLocation.getPath().matches("^/ws/(\\d)+$")) { - int MID = NumberUtils.toInt(hLocation.getPath().substring(4), 0); - if (MID > 0) { - if (messagesService.canViewThread(MID, userSession.visitor.getUid())) { - logger.debug("user {} connected to legacy thread ({}) from {}", userSession.visitor.getUid(), MID, userSession); - userSession.legacy = true; - userSession.MID = MID; - } else { - throw new HttpForbiddenException(); - } - } - } else { - throw new HttpNotFoundException(); - } - clients.add(userSession); - logger.debug("{} clients connected", clients.size()); - } - - @Override - public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { - logger.debug("session closed with status {}: {}", status.getCode(), status.getReason()); - clients.removeIf(c -> c.getDelegate().getId().equals(session.getId())); - logger.debug("{} clients connected", clients.size()); - } - - @Scheduled(fixedRate = 30000) - public void ping() { - clients.forEach(c -> { - try { - if (c.isOpen()) { - c.sendMessage(new PingMessage()); - } - } catch (IOException e) { - logger.error("WebSocket PING exception", e); - } - }); - } - - @Override - protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { - UserSession ws = clients.stream().filter(c -> c.getDelegate().equals(session)) - .findFirst().orElseThrow(IllegalStateException::new); - if (!ws.visitor.isAnonymous()) { - String command = message.getPayload().trim(); - if (StringUtils.isNotEmpty(command)) { - CommandResult result = commandsManager.processCommand(ws.visitor, command, URI.create("")); - ws.sendMessage(new TextMessage(result.getText())); - } - } else { - ws.sendMessage(new TextMessage("Authorization required")); - } - } - - public List<UserSession> getClients() { - return clients; - } - - class UserSession extends ConcurrentWebSocketSessionDecorator { - User visitor; - int MID; - boolean allMessages; - boolean allReplies; - Instant tsConnected; - Instant tsLastData; - boolean legacy; - - UserSession(WebSocketSession session) { - super(session, 60000, 65536); - this.visitor = AnonymousUser.INSTANCE; - tsConnected = tsLastData = Instant.now(); - } - - @Nonnull - @Override - public String toString() { - HttpHeaders headers = getHandshakeHeaders(); - return headers.getOrDefault("X-Real-IP", - Collections.singletonList(getRemoteAddress().toString())).get(0); - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/XMPPConnection.java b/juick-server/src/main/java/com/juick/server/XMPPConnection.java deleted file mode 100644 index 9c0c09e1..00000000 --- a/juick-server/src/main/java/com/juick/server/XMPPConnection.java +++ /dev/null @@ -1,693 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server; - -import com.juick.User; -import com.juick.formatters.PlainTextFormatter; -import com.juick.service.component.*; -import com.juick.model.CommandResult; -import com.juick.model.UserInfo; -import com.juick.server.xmpp.iq.MessageQuery; -import com.juick.server.xmpp.s2s.BasicXmppSession; -import com.juick.server.xmpp.s2s.StanzaListener; -import com.juick.service.*; -import com.juick.util.MessageUtils; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationEventPublisher; -import rocks.xmpp.addr.Jid; -import rocks.xmpp.core.XmppException; -import rocks.xmpp.core.session.XmppSession; -import rocks.xmpp.core.stanza.AbstractIQHandler; -import rocks.xmpp.core.stanza.model.*; -import rocks.xmpp.core.stanza.model.client.ClientMessage; -import rocks.xmpp.core.stanza.model.client.ClientPresence; -import rocks.xmpp.core.stanza.model.errors.Condition; -import rocks.xmpp.extensions.caps.EntityCapabilitiesManager; -import rocks.xmpp.extensions.component.accept.ExternalComponent; -import rocks.xmpp.extensions.disco.ServiceDiscoveryManager; -import rocks.xmpp.extensions.disco.model.info.Identity; -import rocks.xmpp.extensions.filetransfer.FileTransfer; -import rocks.xmpp.extensions.filetransfer.FileTransferManager; -import rocks.xmpp.extensions.nick.model.Nickname; -import rocks.xmpp.extensions.oob.model.x.OobX; -import rocks.xmpp.extensions.ping.PingManager; -import rocks.xmpp.extensions.receipts.MessageDeliveryReceiptsManager; -import rocks.xmpp.extensions.vcard.temp.model.VCard; -import rocks.xmpp.extensions.version.SoftwareVersionManager; -import rocks.xmpp.extensions.version.model.SoftwareVersion; -import rocks.xmpp.util.XmppUtils; - -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.xml.bind.JAXBException; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; -import java.io.IOException; -import java.io.StringWriter; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; - -/** - * @author ugnich - */ -public class XMPPConnection implements StanzaListener, NotificationListener { - - private static final Logger logger = LoggerFactory.getLogger("com.juick.server.xmpp"); - - private ExternalComponent router; - @Inject - private XMPPServer xmpp; - @Inject - private CommandsManager commandsManager; - @Value("${xmppbot_jid:juick@localhost}") - private Jid jid; - @Value("${componentname:localhost}") - private String componentName; - @Value("${component_port:5347}") - private int componentPort; - @Value("${xmpp_password:secret}") - private String password; - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - @Inject - private SubscriptionService subscriptionService; - @Inject - private PMQueriesService pmQueriesService; - @Inject - private BasicXmppSession session; - @Inject - private ExecutorService service; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - @Value("${service_user:juick}") - private String serviceUsername; - - private User serviceUser; - - @PostConstruct - public void init() { - logger.info("stream router start connecting to {}", componentPort); - xmpp.addStanzaListener(this); - router = ExternalComponent.create(componentName, password, session.getConfiguration(), "localhost", componentPort); - ServiceDiscoveryManager serviceDiscoveryManager = router.getManager(ServiceDiscoveryManager.class); - serviceDiscoveryManager.addIdentity(Identity.clientBot().withName("Juick")); - EntityCapabilitiesManager entityCapabilitiesManager = router.getManager(EntityCapabilitiesManager.class); - entityCapabilitiesManager.setNode("https://juick.com/caps"); - MessageDeliveryReceiptsManager messageDeliveryReceiptsManager = router.getManager(MessageDeliveryReceiptsManager.class); - messageDeliveryReceiptsManager.setEnabled(true); - PingManager pingManager = router.getManager(PingManager.class); - pingManager.setEnabled(true); - SoftwareVersionManager softwareVersionManager = router.getManager(SoftwareVersionManager.class); - softwareVersionManager.setSoftwareVersion(new SoftwareVersion("Juick", "2.x", - System.getProperty("os.name", "generic"))); - VCard vCard = new VCard(); - vCard.setFormattedName("Juick"); - vCard.setBirthday(LocalDate.of(2008, 10, 22)); - try { - vCard.setUrl(new URL("http://juick.com/")); - vCard.setPhoto(new VCard.Image("image/png", IOUtils.toByteArray( - getClass().getClassLoader().getResource("juick.png")))); - } catch (MalformedURLException e) { - logger.error("invalid url", e); - } catch (IOException e) { - logger.warn("invalid resource", e); - } - router.addIQHandler(MessageQuery.class, iq -> { - Message warningMessage = new Message(iq.getFrom(), Message.Type.CHAT); - warningMessage.setFrom(jid); - warningMessage.setBody("Your XMPP client constantly polls us with XMPP query which is unsupported for years, please find http://juick.com/query#messages in your client code and remove that"); - router.send(warningMessage); - return iq.createError(new StanzaError(Condition.BAD_REQUEST, "Please stop this spam")); - }); - router.addIQHandler(VCard.class, new AbstractIQHandler(IQ.Type.GET) { - @Override - protected IQ processRequest(IQ iq) { - if (iq.getTo().equals(jid) || iq.getTo().asBareJid().equals(jid.asBareJid()) - || iq.getTo().asBareJid().toEscapedString().equals(jid.getDomain())) { - return iq.createResult(vCard); - } - User user = userService.getUserByName(iq.getTo().getLocal()); - if (!user.isAnonymous()) { - UserInfo info = userService.getUserInfo(user); - VCard userVCard = new VCard(); - userVCard.setFormattedName(info.getFullName()); - userVCard.setNickname(user.getName()); - try { - userVCard.setPhoto(new VCard.Image(new URI("http://i.juick.com/a/" + user.getUid() + ".png"))); - if (info.getUrl() != null) { - userVCard.setUrl(new URL(info.getUrl())); - } - } catch (MalformedURLException | URISyntaxException e) { - logger.warn("url exception", e); - } - return iq.createResult(userVCard); - } - return iq.createError(Condition.BAD_REQUEST); - } - }); - router.addInboundMessageListener(e -> { - ClientMessage result = incomingMessage(e.getMessage()); - if (result != null) { - router.send(result); - } - }); - router.addInboundIQListener(e -> { - IQ iq = e.getIQ(); - Jid jid = iq.getTo(); - if (!jid.getDomain().equals(this.jid.getDomain())) { - router.send(iq); - } - }); - FileTransferManager fileTransferManager = router.getManager(FileTransferManager.class); - fileTransferManager.addFileTransferOfferListener(e -> { - try { - List<String> allowedTypes = new ArrayList<String>() {{ - add("png"); - add("jpg"); - }}; - String attachmentExtension = FilenameUtils.getExtension(e.getName()).toLowerCase(); - String targetFilename = String.format("%s.%s", - DigestUtils.md5Hex(String.format("%s-%s", - e.getInitiator().toString(), e.getSessionId()).getBytes()), attachmentExtension); - if (allowedTypes.contains(attachmentExtension)) { - Path filePath = Paths.get(tmpDir, targetFilename); - FileTransfer ft = e.accept(filePath).get(); - ft.addFileTransferStatusListener(st -> { - logger.debug("{}: received {} of {}", e.getName(), st.getBytesTransferred(), e.getSize()); - if (st.getStatus().equals(FileTransfer.Status.COMPLETED)) { - logger.info("transfer completed"); - try { - Jid initiator = e.getInitiator(); - ClientMessage result = incomingMessageJuick( - userService.getUserByJID(initiator.asBareJid().toEscapedString()), initiator, - e.getDescription(), URI.create(String.format("juick://%s", targetFilename))); - if (result != null) { - router.send(result); - } - } catch (Exception e1) { - logger.error("ft error", e1); - } - - } else if (st.getStatus().equals(FileTransfer.Status.FAILED)) { - logger.info("transfer failed", ft.getException()); - Message msg = new Message(); - msg.setType(Message.Type.CHAT); - msg.setFrom(jid); - msg.setTo(e.getInitiator()); - msg.setBody("File transfer failed, please report to us"); - router.sendMessage(msg); - } else if (st.getStatus().equals(FileTransfer.Status.CANCELED)) { - logger.info("transfer cancelled"); - } - }); - ft.transfer(); - logger.info("transfer started"); - } else { - e.reject(); - logger.info("transfer rejected"); - } - } catch (IOException | InterruptedException | ExecutionException e1) { - logger.error("ft error", e1); - } - }); - router.addConnectionListener(event -> { - if (event.getType().equals(rocks.xmpp.core.session.ConnectionEvent.Type.RECONNECTION_SUCCEEDED)) { - logger.info("component connected"); - } - }); - router.addSessionStatusListener(event -> { - logger.info("event: " + event.getStatus(), event.getThrowable()); - if (event.getStatus().equals(XmppSession.Status.AUTHENTICATED)) { - logger.info("Authenticated, broadcasting..."); - broadcastPresence(null); - } - }); - router.addInboundPresenceListener(event -> { - incomingPresence(event.getPresence()); - }); - service.submit(() -> { - try { - router.connect(); - } catch (XmppException e) { - logger.warn("xmpp exception", e); - } - }); - serviceUser = userService.getUserByName(serviceUsername); - } - - private String stanzaToString(Stanza stanza) throws XMLStreamException, JAXBException { - StringWriter stanzaWriter = new StringWriter(); - XMLStreamWriter xmppStreamWriter = XmppUtils.createXmppStreamWriter( - router.getConfiguration().getXmlOutputFactory().createXMLStreamWriter(stanzaWriter)); - router.createMarshaller().marshal(stanza, xmppStreamWriter); - xmppStreamWriter.flush(); - xmppStreamWriter.close(); - return stanzaWriter.toString(); - } - - private void sendJuickMessage(com.juick.Message jmsg, List<User> users) { - List<String> jids = new ArrayList<>(); - - for (User user : users) { - jids.addAll(userService.getJIDsbyUID(user.getUid())); - } - com.juick.Message fullMsg = messagesService.getMessage(jmsg.getMid()); - String txt = "@" + jmsg.getUser().getName() + ":" + MessageUtils.getTagsString(fullMsg) + "\n"; - String attachmentUrl = MessageUtils.attachmentUrl(fullMsg); - if (StringUtils.isNotEmpty(attachmentUrl)) { - txt += attachmentUrl + "\n"; - } - txt += StringUtils.defaultString(jmsg.getText()) + "\n\n"; - txt += "#" + jmsg.getMid() + " http://juick.com/m/" + jmsg.getMid(); - - Nickname nick = new Nickname("@" + jmsg.getUser().getName()); - - Message msg = new Message(); - msg.setFrom(jid); - msg.setBody(txt); - msg.setType(Message.Type.CHAT); - msg.setThread("juick-" + jmsg.getMid()); - msg.addExtension(jmsg); - msg.addExtension(nick); - if (StringUtils.isNotEmpty(attachmentUrl)) { - try { - OobX oob = new OobX(new URI(attachmentUrl)); - msg.addExtension(oob); - } catch (URISyntaxException e) { - logger.warn("uri exception", e); - } - } - for (String jid : jids) { - msg.setTo(Jid.of(jid)); - router.send(ClientMessage.from(msg)); - } - } - - public void sendJuickComment(com.juick.Message jmsg, List<User> users) { - String replyQuote; - String replyTo; - - com.juick.Message replyMessage = jmsg.getReplyto() > 0 ? messagesService.getReply(jmsg.getMid(), jmsg.getReplyto()) - : messagesService.getMessage(jmsg.getMid()); - replyTo = replyMessage.getUser().getName(); - com.juick.Message fullReply = messagesService.getReply(jmsg.getMid(), jmsg.getRid()); - replyQuote = StringUtils.defaultString(fullReply.getReplyQuote()); - - String txt = "Reply by @" + jmsg.getUser().getName() + ":\n" + replyQuote + "\n@" + replyTo + " "; - String attachmentUrl = MessageUtils.attachmentUrl(fullReply); - if (StringUtils.isNotEmpty(attachmentUrl)) { - txt += attachmentUrl + "\n"; - } - txt += StringUtils.defaultString(jmsg.getText()) + "\n\n" + "#" + jmsg.getMid() + "/" + jmsg.getRid() + " http://juick.com/m/" + jmsg.getMid() + "#" + jmsg.getRid(); - - Message msg = new Message(); - msg.setFrom(jid); - msg.setBody(txt); - msg.setType(Message.Type.CHAT); - msg.addExtension(jmsg); - for (User user : users) { - for (String jid : userService.getJIDsbyUID(user.getUid())) { - msg.setTo(Jid.of(jid)); - router.send(ClientMessage.from(msg)); - } - } - } - - @Override - public void processMessageEvent(MessageEvent event) { - com.juick.Message msg = event.getMessage(); - List<User> subscribers = event.getUsers(); - if (msg.isService()) { - return; - } - if (MessageUtils.isPM(msg)) { - userService.getJIDsbyUID(msg.getTo().getUid()) - .forEach(userJid -> { - Message mm = new Message(); - mm.setTo(Jid.of(userJid)); - mm.setType(Message.Type.CHAT); - boolean inroster = pmQueriesService.havePMinRoster(msg.getUser().getUid(), userJid); - if (inroster) { - mm.setFrom(Jid.of(msg.getUser().getName(), "juick.com", "Juick")); - mm.setBody(msg.getText()); - } else { - mm.setFrom(jid); - mm.setBody("Private message from @" + msg.getUser().getName() + ":\n" + msg.getText()); - } - router.send(ClientMessage.from(mm)); - }); - } else if (MessageUtils.isReply(msg)) { - sendJuickComment(msg, subscribers); - } - else { - sendJuickMessage(msg, subscribers); - } - } - - private ClientMessage makeReply(Jid jidTo, String txt) { - Message reply = new Message(); - reply.setFrom(jid); - reply.setTo(jidTo); - reply.setType(Message.Type.CHAT); - reply.setBody(txt); - return ClientMessage.from(reply); - } - - @Override - public void processSubscribeEvent(SubscribeEvent subscribeEvent) { - - } - - @Override - public void processLikeEvent(LikeEvent likeEvent) { - List<User> users = likeEvent.getSubscribers(); - com.juick.Message jmsg = likeEvent.getMessage(); - User liker = likeEvent.getUser(); - - if (!userService.isInBLAny(jmsg.getUser().getUid(), liker.getUid())) { - userService.getJIDsbyUID(jmsg.getUser().getUid()).forEach(authorJid -> { - Message xmppMessage = new Message(); - xmppMessage.setFrom(jid); - xmppMessage.setTo(Jid.of(authorJid)); - xmppMessage.setType(Message.Type.CHAT); - xmppMessage.addExtension(jmsg); - xmppMessage.setBody(String.format("%s recommended your post #%d. %s", - liker.getName(), jmsg.getMid(), PlainTextFormatter.formatUrl(jmsg))); - router.send(ClientMessage.from(xmppMessage)); - }); - } - - String txt = "Recommended by @" + liker.getName() + ":\n"; - txt += "@" + jmsg.getUser().getName() + ":" + MessageUtils.getTagsString(jmsg) + "\n"; - String attachmentUrl = MessageUtils.attachmentUrl(jmsg); - if (StringUtils.isNotEmpty(attachmentUrl)) { - txt += attachmentUrl + "\n"; - } - txt += StringUtils.defaultString(jmsg.getText()) + "\n\n"; - txt += "#" + jmsg.getMid(); - if (jmsg.getReplies() > 0) { - if (jmsg.getReplies() % 10 == 1 && jmsg.getReplies() % 100 != 11) { - txt += " (" + jmsg.getReplies() + " reply)"; - } else { - txt += " (" + jmsg.getReplies() + " replies)"; - } - } - txt += " http://juick.com/m/" + jmsg.getMid(); - - Nickname nick = new Nickname("@" + jmsg.getUser().getName()); - - Message msg = new Message(); - msg.setFrom(jid); - msg.setBody(txt); - msg.setType(Message.Type.CHAT); - msg.setThread("juick-" + jmsg.getMid()); - msg.addExtension(jmsg); - msg.addExtension(nick); - if (StringUtils.isNotEmpty(attachmentUrl)) { - try { - OobX oob = new OobX(new URI(attachmentUrl)); - msg.addExtension(oob); - } catch (URISyntaxException e) { - logger.warn("uri exception", e); - } - } - - for (User user : users) { - for (String jid : userService.getJIDsbyUID(user.getUid())) { - msg.setTo(Jid.of(jid)); - router.send(ClientMessage.from(msg)); - } - } - } - - @Override - public void processPingEvent(PingEvent pingEvent) { - userService.getJIDsbyUID(pingEvent.getPinger().getUid()) - .forEach(userJid -> { - Presence p = new Presence(Jid.of(userJid)); - p.setFrom(jid); - p.setPriority((byte) 10); - router.send(ClientPresence.from(p)); - }); - } - - @Override - public void processMessageReadEvent(MessageReadEvent messageReadEvent) { - - } - - @Override - public void processTopEvent(TopEvent topEvent) { - com.juick.Message message = topEvent.getMessage(); - try { - commandsManager.processCommand(serviceUser, String.format("! #%d", message.getMid()), URI.create(StringUtils.EMPTY)); - } catch (Exception e) { - logger.warn("XMPP error", e); - } - } - - private void incomingPresence(Presence p) { - final String username = p.getTo().getLocal(); - final boolean toJuick = username.equals(jid.getLocal()); - - if (p.getType() == null) { - Presence reply = new Presence(); - reply.setFrom(p.getTo().asBareJid()); - reply.setTo(p.getFrom().asBareJid()); - reply.setType(Presence.Type.UNSUBSCRIBE); - router.send(ClientPresence.from(reply)); - } else if (p.getType().equals(Presence.Type.PROBE)) { - int uid_to = 0; - if (!toJuick) { - uid_to = userService.getUIDbyName(username); - } else { - User visitor = userService.getUserByJID(p.getFrom().asBareJid().toEscapedString()); - if (visitor != null) { - userService.updateLastSeen(visitor); - } - } - - if (toJuick || uid_to > 0) { - Presence reply = new Presence(); - reply.setFrom(p.getTo().withResource(jid.getResource())); - reply.setTo(p.getFrom()); - reply.setPriority((byte)10); - if (!userService.getActiveJIDs().contains(p.getFrom().asBareJid().toEscapedString())) { - reply.setStatus("Send ON to enable notifications"); - } - router.send(ClientPresence.from(reply)); - } else { - Presence reply = new Presence(); - reply.setFrom(p.getTo()); - reply.setTo(p.getFrom()); - reply.setType(Presence.Type.ERROR); - reply.setId(p.getId()); - reply.setError(new StanzaError(StanzaError.Type.CANCEL, Condition.ITEM_NOT_FOUND)); - router.send(ClientPresence.from(reply)); - } - } else if (p.getType().equals(Presence.Type.SUBSCRIBE)) { - boolean canSubscribe = false; - if (toJuick) { - canSubscribe = true; - } else { - int uid_to = userService.getUIDbyName(username); - if (uid_to > 0) { - pmQueriesService.addPMinRoster(uid_to, p.getFrom().asBareJid().toEscapedString()); - canSubscribe = true; - } - } - if (canSubscribe) { - Presence reply = new Presence(); - reply.setFrom(p.getTo()); - reply.setTo(p.getFrom()); - reply.setType(Presence.Type.SUBSCRIBED); - router.send(ClientPresence.from(reply)); - - reply.setFrom(reply.getFrom().withResource(jid.getResource())); - reply.setPriority((byte) 10); - reply.setType(null); - router.send(ClientPresence.from(reply)); - } else { - Presence reply = new Presence(); - reply.setFrom(p.getTo()); - reply.setTo(p.getFrom()); - reply.setType(Presence.Type.ERROR); - reply.setId(p.getId()); - reply.setError(new StanzaError(StanzaError.Type.CANCEL, Condition.ITEM_NOT_FOUND)); - router.send(ClientPresence.from(reply)); - } - } else if (p.getType().equals(Presence.Type.UNSUBSCRIBE)) { - if (!toJuick) { - int uid_to = userService.getUIDbyName(username); - if (uid_to > 0) { - pmQueriesService.removePMinRoster(uid_to, p.getFrom().asBareJid().toEscapedString()); - } - } - - Presence reply = new Presence(); - reply.setFrom(p.getTo()); - reply.setTo(p.getFrom()); - reply.setType(Presence.Type.UNSUBSCRIBED); - router.send(ClientPresence.from(reply)); - } - } - - public ClientMessage incomingMessage(Message msg) { - ClientMessage result = null; - if (msg.getType() != null && msg.getType().equals(Message.Type.ERROR)) { - StanzaError error = msg.getError(); - if (error != null && error.getCondition().equals(Condition.RESOURCE_CONSTRAINT)) { - // offline query is full, deactivating this jid - if (userService.setActiveStatusForJID(msg.getFrom().toEscapedString(), UserService.ActiveStatus.Inactive)) { - logger.info("{} is inactive now", msg.getFrom()); - return null; - } - } - return null; - } - Jid to = msg.getTo(); - if (to.getDomain().equals(router.getDomain().toEscapedString()) || to.equals(this.jid)) { - User user_from = userService.getUserByJID(msg.getFrom().asBareJid().toEscapedString()); - if ((user_from == null || user_from.isAnonymous()) && !msg.getFrom().equals(jid)) { - String signuphash = userService.getSignUpHashByJID(msg.getFrom().asBareJid().toEscapedString()); - return makeReply(msg.getFrom(), "Для того, чтобы начать пользоваться сервисом, пожалуйста пройдите быструю регистрацию: http://juick.com/signup?type=xmpp&hash=" + signuphash + "\nЕсли у вас уже есть учетная запись на Juick, вы сможете присоединить этот JabberID к ней.\n\nTo start using Juick, please sign up: http://juick.com/signup?type=xmpp&hash=" + signuphash + "\nIf you already have an account on Juick, you will be proposed to attach this JabberID to your existing account."); - } - - com.juick.Message jmsg = msg.getExtension(com.juick.Message.class); - if (jmsg != null) { - if (MessageUtils.isReply(jmsg)) { - // to get quote and attachment - com.juick.Message original = messagesService.getMessage(jmsg.getMid()); - com.juick.Message reply = messagesService.getReply(jmsg.getMid(), jmsg.getRid()); - applicationEventPublisher.publishEvent(new MessageEvent(this, reply, - subscriptionService.getUsersSubscribedToComments(original, reply))); - } else if (!MessageUtils.isPM(jmsg)) { - applicationEventPublisher.publishEvent(new MessageEvent(this, - messagesService.getMessage(jmsg.getMid()), subscriptionService.getSubscribedUsers(jmsg.getUser().getUid(), jmsg))); - } - } else { - URI attachment = URI.create(StringUtils.EMPTY); - OobX oobX = msg.getExtension(OobX.class); - if (oobX != null) { - attachment = oobX.getUri(); - } - try { - if (msg.getTo().asBareJid().equals(jid.asBareJid())) { - return incomingMessageJuick(user_from, msg.getFrom(), StringUtils.defaultString(msg.getBody()), attachment); - } else { - // PM - result = incomingMessageJuick(user_from, msg.getFrom(), - String.format("@%s %s", msg.getTo().getLocal(), StringUtils.defaultString(msg.getBody())), attachment); - } - } catch (Exception e1) { - logger.warn("message exception", e1); - } - } - } else if (to.getDomain().endsWith(jid.getDomain()) && (to.getDomain().equals(jid.getDomain()) - || to.getDomain().endsWith("." + jid.getDomain()))) { - if (logger.isInfoEnabled()) { - try { - logger.info("unhandled message: {}", stanzaToString(msg)); - } catch (JAXBException | XMLStreamException ex) { - logger.error("JAXB exception", ex); - } - } - } else { - return ClientMessage.from(msg); - } - return result; - } - private ClientMessage incomingMessageJuick(User user_from, Jid from, String command, @Nonnull URI attachment) { - if (StringUtils.isBlank(command) && attachment.toString().isEmpty()) { - return null; - } - - int commandlen = command.length(); - - // COMPATIBILITY - if (commandlen > 7 && command.substring(0, 3).equalsIgnoreCase("PM ")) { - command = command.substring(3); - } - - try { - CommandResult result = commandsManager.processCommand(user_from, command.trim(), attachment); - if (StringUtils.isNotBlank(result.getText())) { - return makeReply(from, result.getText()); - } - } catch (Exception e) { - logger.warn("xmpp command exception", e); - return makeReply(from, "Error processing command"); - } - return null; - } - - @Override - public void stanzaReceived(Stanza xmlValue) { - router.send(xmlValue); - } - - private void broadcastPresence(Presence.Type type) { - Presence presence = new Presence(); - presence.setFrom(jid); - if (type != null) { - presence.setType(type); - } - userService.getActiveJIDs().forEach(j -> { - try { - presence.setTo(Jid.of(j)); - router.send(ClientPresence.from(presence)); - } catch (IllegalArgumentException ex) { - logger.warn("Invalid jid: {}", j, ex); - } - }); - } - - @PreDestroy - public void close() throws Exception { - broadcastPresence(Presence.Type.UNAVAILABLE); - if (router != null) { - router.close(); - } - } - - public ExternalComponent getRouter() { - return router; - } -} diff --git a/juick-server/src/main/java/com/juick/server/XMPPServer.java b/juick-server/src/main/java/com/juick/server/XMPPServer.java deleted file mode 100644 index 86ab6a78..00000000 --- a/juick-server/src/main/java/com/juick/server/XMPPServer.java +++ /dev/null @@ -1,429 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server; - -import com.juick.server.xmpp.router.StreamError; -import com.juick.server.xmpp.s2s.*; -import com.juick.service.UserService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.scheduling.annotation.Scheduled; -import org.xmlpull.v1.XmlPullParserException; -import rocks.xmpp.addr.Jid; -import rocks.xmpp.core.stanza.model.Stanza; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.net.ssl.*; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import java.io.IOException; -import java.io.StringReader; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketException; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.SecureRandom; -import java.security.cert.*; -import java.time.Duration; -import java.time.Instant; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * @author ugnich - */ -public class XMPPServer implements ConnectionListener { - private static final Logger logger = LoggerFactory.getLogger("com.juick.server.xmpp"); - - private static final int TIMEOUT_MINUTES = 15; - - @Inject - public ExecutorService service; - @Value("${hostname:localhost}") - private Jid jid; - @Value("${s2s_port:5269}") - private int s2sPort; - @Value("${broken_ssl_hosts:}") - public String[] brokenSSLhosts; - @Value("${banned_hosts:}") - public String[] bannedHosts; - - private final List<ConnectionIn> inConnections = new CopyOnWriteArrayList<>(); - private final Map<ConnectionOut, Optional<Socket>> outConnections = new ConcurrentHashMap<>(); - private final List<CacheEntry> outCache = new CopyOnWriteArrayList<>(); - private final List<StanzaListener> stanzaListeners = new CopyOnWriteArrayList<>(); - private final AtomicBoolean closeFlag = new AtomicBoolean(false); - - SSLContext sc; - CertificateFactory cf; - CertPathValidator cpv; - PKIXParameters params; - private TrustManager[] trustAllCerts = new TrustManager[]{ - new X509TrustManager() { - public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) { - } - - public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) { - } - - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - } - }; - private boolean tlsConfigured = false; - - - private ServerSocket listener; - - @Inject - private BasicXmppSession session; - @Inject - private UserService userService; - @Inject - private KeystoreManager keystoreManager; - - @PostConstruct - public void init() throws KeyStoreException { - closeFlag.set(false); - try { - sc = SSLContext.getInstance("TLSv1.2"); - sc.init(keystoreManager.getKeymanagerFactory().getKeyManagers(), trustAllCerts, new SecureRandom()); - TrustManagerFactory trustManagerFactory = - TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - Set<TrustAnchor> ca = new HashSet<>(); - trustManagerFactory.init((KeyStore)null); - Arrays.stream(trustManagerFactory.getTrustManagers()).forEach(t -> Arrays.stream(((X509TrustManager)t).getAcceptedIssuers()).forEach(cert -> ca.add(new TrustAnchor(cert, null)))); - params = new PKIXParameters(ca); - params.setRevocationEnabled(false); - cpv = CertPathValidator.getInstance("PKIX"); - cf = CertificateFactory.getInstance( "X.509" ); - tlsConfigured = true; - } catch (Exception e) { - logger.warn("tls unavailable"); - } - service.submit(() -> { - try { - listener = new ServerSocket(s2sPort); - logger.info("s2s listener ready"); - while (!listener.isClosed()) { - if (Thread.currentThread().isInterrupted()) break; - Socket socket = listener.accept(); - ConnectionIn client = new ConnectionIn(this, socket); - addConnectionIn(client); - service.submit(client); - } - } catch (SocketException e) { - // shutdown - } catch (IOException | XmlPullParserException e) { - logger.warn("xmpp exception", e); - } - }); - } - - public void addConnectionIn(ConnectionIn c) { - c.setListener(this); - inConnections.add(c); - } - - public void addConnectionOut(ConnectionOut c, Optional<Socket> socket) { - c.setListener(this); - outConnections.put(c, socket); - } - - public void removeConnectionIn(ConnectionIn c) { - inConnections.remove(c); - } - - public void removeConnectionOut(ConnectionOut c) { - outConnections.remove(c); - } - - public String getFromCache(Jid to) { - final String[] cache = new String[1]; - outCache.stream().filter(c -> c.hostname != null && c.hostname.equals(to)).findFirst().ifPresent(c -> { - cache[0] = c.xml; - outCache.remove(c); - }); - return cache[0]; - } - - public Optional<ConnectionOut> getConnectionOut(Jid hostname, boolean needReady) { - return outConnections.keySet().stream().filter(c -> c.to != null && - c.to.equals(hostname) && (!needReady || c.streamReady)).findFirst(); - } - - public Optional<ConnectionIn> getConnectionIn(String streamID) { - return inConnections.stream().filter(c -> c.streamID != null && c.streamID.equals(streamID)).findFirst(); - } - - public void sendOut(Jid hostname, String xml) { - boolean haveAnyConn = false; - - ConnectionOut connOut = null; - for (ConnectionOut c : outConnections.keySet()) { - if (c.to != null && c.to.equals(hostname)) { - if (c.streamReady) { - connOut = c; - break; - } else { - haveAnyConn = true; - break; - } - } - } - if (connOut != null) { - connOut.send(xml); - return; - } - - boolean haveCache = false; - for (CacheEntry c : outCache) { - if (c.hostname != null && c.hostname.equals(hostname)) { - c.xml += xml; - c.updated = Instant.now(); - haveCache = true; - break; - } - } - if (!haveCache) { - outCache.add(new CacheEntry(hostname, xml)); - } - - if (!haveAnyConn && !closeFlag.get()) { - try { - createDialbackConnection(hostname.toEscapedString(), null, null); - } catch (Exception e) { - logger.warn("dialback error", e); - } - } - } - - void createDialbackConnection(String to, String checkSID, String dbKey) throws Exception { - ConnectionOut connectionOut = new ConnectionOut(getJid(), Jid.of(to), null, null, checkSID, dbKey); - addConnectionOut(connectionOut, Optional.empty()); - service.submit(() -> { - try { - Socket socket = new Socket(); - socket.connect(DNSQueries.getServerAddress(to)); - connectionOut.setInputStream(socket.getInputStream()); - connectionOut.setOutputStream(socket.getOutputStream()); - addConnectionOut(connectionOut, Optional.of(socket)); - connectionOut.connect(); - } catch (IOException e) { - logger.info("dialback to " + to + " exception", e); - } - }); - } - - public void startDialback(Jid from, String streamId, String dbKey) throws Exception { - Optional<ConnectionOut> c = getConnectionOut(from, false); - if (c.isPresent()) { - c.get().sendDialbackVerify(streamId, dbKey); - } else { - createDialbackConnection(from.toEscapedString(), streamId, dbKey); - } - } - - public void addStanzaListener(StanzaListener listener) { - stanzaListeners.add(listener); - } - - public void onStanzaReceived(String xmlValue) { - logger.info("S2S: {}", xmlValue); - Stanza stanza = parse(xmlValue); - stanzaListeners.forEach(l -> l.stanzaReceived(stanza)); - } - - public BasicXmppSession getSession() { - return session; - } - - public List<ConnectionIn> getInConnections() { - return inConnections; - } - - public Map<ConnectionOut, Optional<Socket>> getOutConnections() { - return outConnections; - } - - @Override - public boolean isTlsAvailable() { - return tlsConfigured; - } - - @Override - public void starttls(ConnectionIn connection) { - logger.debug("stream {} securing", connection.streamID); - connection.sendStanza("<proceed xmlns=\"" + Connection.NS_TLS + "\" />"); - try { - connection.setSocket(sc.getSocketFactory().createSocket(connection.getSocket(), connection.getSocket().getInetAddress().getHostAddress(), - connection.getSocket().getPort(), false)); - SSLSocket sslSocket = (SSLSocket) connection.getSocket(); - sslSocket.addHandshakeCompletedListener(handshakeCompletedEvent -> { - try { - CertPath certPath = cf.generateCertPath(Arrays.asList(handshakeCompletedEvent.getPeerCertificates())); - cpv.validate(certPath, params); - connection.setTrusted(true); - logger.info("connection from {} is trusted", connection.from); - } catch (SSLPeerUnverifiedException | CertificateException | CertPathValidatorException | InvalidAlgorithmParameterException e) { - logger.info("connection from {} is NOT trusted, falling back to dialback", connection.from); - } - }); - sslSocket.setUseClientMode(false); - sslSocket.setNeedClientAuth(true); - sslSocket.startHandshake(); - connection.setSecured(true); - logger.debug("stream from {} secured", connection.streamID); - connection.restartParser(); - } catch (XmlPullParserException | IOException sex) { - logger.warn("stream {} ssl error {}", connection.streamID, sex); - connection.sendStanza("<failure xmlns=\"" + Connection.NS_TLS + "\" />"); - removeConnectionIn(connection); - connection.closeConnection(); - } - } - - @Override - public void proceed(ConnectionOut connection) { - try { - Socket socket = outConnections.get(connection).get(); - socket = sc.getSocketFactory().createSocket(socket, socket.getInetAddress().getHostAddress(), - socket.getPort(), false); - SSLSocket sslSocket = (SSLSocket) socket; - sslSocket.addHandshakeCompletedListener(handshakeCompletedEvent -> { - try { - CertPath certPath = cf.generateCertPath(Arrays.asList(handshakeCompletedEvent.getPeerCertificates())); - cpv.validate(certPath, params); - connection.setTrusted(true); - logger.info("connection to {} is trusted", connection.to); - } catch (SSLPeerUnverifiedException | CertificateException | CertPathValidatorException | InvalidAlgorithmParameterException e) { - logger.info("connection to {} is NOT trusted, falling back to dialback", connection.to); - } - }); - sslSocket.setNeedClientAuth(true); - sslSocket.startHandshake(); - connection.setSecured(true); - logger.debug("stream to {} secured", connection.getStreamID()); - connection.setInputStream(socket.getInputStream()); - connection.setOutputStream(socket.getOutputStream()); - connection.restartStream(); - connection.sendOpenStream(); - } catch (NoSuchElementException | XmlPullParserException | IOException sex) { - logger.error("s2s ssl error: {} {}, error {}", connection.to, connection.getStreamID(), sex); - connection.send("<failure xmlns=\"" + Connection.NS_TLS + "\" />"); - removeConnectionOut(connection); - connection.logoff(); - } - } - - @Override - public void verify(ConnectionOut connection, String from, String type, String sid) { - if (from != null && from.equals(connection.to.toEscapedString()) && sid != null && !sid.isEmpty() && type != null) { - getConnectionIn(sid).ifPresent(c -> c.sendDialbackResult(Jid.of(from), type)); - } - } - - @Override - public void dialbackError(ConnectionOut connection, StreamError error) { - logger.warn("Stream error from {}: {}", connection.getStreamID(), error.getCondition()); - removeConnectionOut(connection); - connection.logoff(); - } - - @Override - public void finished(ConnectionOut connection, boolean dirty) { - logger.warn("stream to {} {} finished, dirty={}", connection.to, connection.getStreamID(), dirty); - removeConnectionOut(connection); - connection.logoff(); - } - - @Override - public void exception(ConnectionOut connection, Exception ex) { - logger.error("s2s out exception: {} {}, exception {}", connection.to, connection.getStreamID(), ex); - removeConnectionOut(connection); - connection.logoff(); - } - - @Override - public void ready(ConnectionOut connection) { - logger.debug("stream to {} {} ready", connection.to, connection.getStreamID()); - String cache = getFromCache(connection.to); - if (cache != null) { - logger.debug("stream to {} {} sending cache", connection.to, connection.getStreamID()); - connection.send(cache); - } - } - - @Override - public boolean securing(ConnectionOut connection) { - return tlsConfigured && !Arrays.asList(brokenSSLhosts).contains(connection.to.toEscapedString()); - } - - public Stanza parse(String xml) { - try { - Unmarshaller unmarshaller = session.createUnmarshaller(); - return (Stanza)unmarshaller.unmarshal(new StringReader(xml)); - } catch (JAXBException e) { - logger.error("JAXB exception", e); - } - return null; - } - - public Jid getJid() { - return jid; - } - @Scheduled(fixedDelay = 10000) - public void cleanUp() { - Instant now = Instant.now(); - outConnections.keySet().stream().filter(c -> Duration.between(c.getUpdated(), now).toMinutes() > TIMEOUT_MINUTES) - .forEach(c -> { - logger.info("closing idle outgoing connection to {}", c.to); - c.logoff(); - outConnections.remove(c); - }); - - inConnections.stream().filter(c -> Duration.between(c.updated, now).toMinutes() > TIMEOUT_MINUTES) - .forEach(c -> { - logger.info("closing idle incoming connection from {}", c.from); - c.closeConnection(); - inConnections.remove(c); - }); - } - @PreDestroy - public void preDestroy() throws IOException { - closeFlag.set(true); - if (listener != null && !listener.isClosed()) { - listener.close(); - } - service.shutdown(); - logger.info("XMPP server destroyed"); - } - - public int getServerPort() { - return s2sPort; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/ApiSocialLogin.java b/juick-server/src/main/java/com/juick/server/api/ApiSocialLogin.java deleted file mode 100644 index 8d9f9402..00000000 --- a/juick-server/src/main/java/com/juick/server/api/ApiSocialLogin.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.server.api; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.scribejava.apis.FacebookApi; -import com.github.scribejava.apis.VkontakteApi; -import com.github.scribejava.core.builder.ServiceBuilder; -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthRequest; -import com.github.scribejava.core.model.Verb; -import com.github.scribejava.core.oauth.OAuth20Service; -import com.juick.model.facebook.User; -import com.juick.server.util.HttpBadRequestException; -import com.juick.service.CrosspostService; -import com.juick.service.EmailService; -import com.juick.service.TelegramService; -import com.juick.service.UserService; -import com.juick.model.vk.UsersResponse; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import java.io.IOException; -import java.util.UUID; -import java.util.concurrent.ExecutionException; - -/** - * - * @author Ugnich Anton - */ -@Controller -public class ApiSocialLogin { - - private static final Logger logger = LoggerFactory.getLogger(ApiSocialLogin.class); - - @Value("${facebook_appid:appid}") - private String FACEBOOK_APPID; - @Value("${facebook_secret:secret}") - private String FACEBOOK_SECRET; - private static final String FACEBOOK_REDIRECT = "https://api.juick.com/_fblogin"; - private static final String VK_REDIRECT = "https://api.juick.com/_vklogin"; - private static final String TWITTER_VERIFY_URL = "https://api.twitter.com/1.1/account/verify_credentials.json"; - @Inject - private ObjectMapper jsonMapper; - private ServiceBuilder facebookBuilder, twitterBuilder, vkBuilder; - - @Value("${twitter_consumer_key:appid}") - private String twitterConsumerKey; - @Value("${twitter_consumer_secret:secret}") - private String twitterConsumerSecret; - @Value("${vk_appid:appid}") - private String VK_APPID; - @Value("${vk_secret:secret}") - private String VK_SECRET; - @Value("${telegram_token:secret}") - private String telegramToken; - - @Inject - private CrosspostService crosspostService; - @Inject - private UserService userService; - @Inject - private EmailService emailService; - @Inject - private TelegramService telegramService; - - @PostConstruct - public void init() { - facebookBuilder = new ServiceBuilder(FACEBOOK_APPID); - twitterBuilder = new ServiceBuilder(twitterConsumerKey); - vkBuilder = new ServiceBuilder(VK_APPID); - } - - @GetMapping("/api/_fblogin") - protected String doFacebookLogin(@RequestParam(required = false) String code, - @RequestParam(required = false) String state) throws IOException, ExecutionException, InterruptedException { - if (StringUtils.isBlank(code)) { - String fbstate = UUID.randomUUID().toString(); - crosspostService.addFacebookState(fbstate, state); - OAuth20Service facebookAuthService = facebookBuilder - .apiSecret(FACEBOOK_SECRET) - .callback(FACEBOOK_REDIRECT) - .scope("email") - .state(fbstate) - .build(FacebookApi.instance()); - return "redirect:" + facebookAuthService.getAuthorizationUrl(); - } - - String redirectUrl = crosspostService.verifyFacebookState(state); - - if (StringUtils.isEmpty(redirectUrl)) { - logger.error("state is missing"); - throw new HttpBadRequestException(); - } - OAuth20Service facebookService = facebookBuilder - .apiKey(FACEBOOK_APPID) - .apiSecret(FACEBOOK_SECRET) - .callback(FACEBOOK_REDIRECT) - .scope("email") - .state(state) - .build(FacebookApi.instance()); - OAuth2AccessToken token = facebookService.getAccessToken(code); - final OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://graph.facebook.com/v2.10/me?fields=id,name,link,verified,email"); - facebookService.signRequest(token, meRequest); - String graph = facebookService.execute(meRequest).getBody(); - if (StringUtils.isBlank(graph)) { - logger.error("FACEBOOK GRAPH ERROR"); - throw new HttpBadRequestException(); - } - User fb = jsonMapper.readValue(graph, User.class); - long fbID = NumberUtils.toLong(fb.getId(), 0); - if (fbID == 0 || StringUtils.isBlank(fb.getName()) || StringUtils.isBlank(fb.getLink())) { - logger.error("Missing required fields, id: {}, name: {}, link: {}", fbID, fb.getName(), fb.getLink()); - throw new HttpBadRequestException(); - } - - int uid = crosspostService.getUIDbyFBID(fbID); - if (uid > 0) { - if (!crosspostService.updateFacebookUser(fbID, token.getAccessToken(), fb.getName(), fb.getLink())) { - logger.error("error updating facebook user, id: {}, token: {}", fbID, token.getAccessToken()); - throw new HttpBadRequestException(); - } - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(redirectUrl); - uriComponentsBuilder.queryParam("hash", userService.getHashByUID(uid)); - return "redirect:" + uriComponentsBuilder.build().toUriString(); - } else if (fb.getVerified()) { - if (!crosspostService.createFacebookUser(fbID, state, token.getAccessToken(), fb.getName(), fb.getLink())) { - if (StringUtils.isNotEmpty(fb.getEmail())) { - logger.info("found {} for facebook user {}", fb.getEmail(), fb.getLink()); - Integer userId = crosspostService.getUIDbyFBID(fbID); - if (!emailService.getEmails(userId, false).contains(fb.getEmail())) { - emailService.addEmail(userId, fb.getEmail()); - } - } - logger.info("email not found for facebook user {}", fb.getLink()); - throw new HttpBadRequestException(); - } - return "redirect:/signup?type=fb&hash=" + state; - } else { - logger.error("Facebook account is not verified, id: {}", fbID); - throw new HttpBadRequestException(); - } - }/* - @GetMapping("/_twitter") - protected void doTwitterLogin(HttpServletRequest request, HttpServletResponse response) - throws IOException, ExecutionException, InterruptedException { - String hash = StringUtils.EMPTY, request_token = StringUtils.EMPTY, request_token_secret = StringUtils.EMPTY; - String verifier = request.getParameter("oauth_verifier"); - Cookie[] cookies = request.getCookies(); - for (Cookie cookie : cookies) { - if (cookie.getName().equals("hash")) { - hash = cookie.getValue(); - } - if (cookie.getName().equals("request_token")) { - request_token = cookie.getValue(); - } - if (cookie.getName().equals("request_token_secret")) { - request_token_secret = cookie.getValue(); - } - } - com.juick.User user = UserUtils.getCurrentUser(); - OAuth10aService oAuthService = twitterBuilder - .apiSecret(twitterConsumerSecret) - .callback("http://juick.com/_twitter") - .build(TwitterApi.instance()); - - if (request_token.isEmpty() && request_token_secret.isEmpty() - && (verifier == null || verifier.isEmpty())) { - OAuth1RequestToken requestToken = oAuthService.getRequestToken(); - String authUrl = oAuthService.getAuthorizationUrl(requestToken); - response.addCookie(new Cookie("request_token", requestToken.getToken())); - response.addCookie(new Cookie("request_token_secret", requestToken.getTokenSecret())); - response.setStatus(HttpServletResponse.SC_FOUND); - response.setHeader("Location", authUrl); - } else { - if (verifier != null && verifier.length() > 0) { - OAuth1RequestToken requestToken = new OAuth1RequestToken(request_token, request_token_secret); - OAuth1AccessToken accessToken = oAuthService.getAccessToken(requestToken, verifier); - OAuthRequest oAuthRequest = new OAuthRequest(Verb.GET, TWITTER_VERIFY_URL); - oAuthService.signRequest(accessToken, oAuthRequest); - com.juick.twitter.User twitterUser = jsonMapper.readValue(oAuthService.execute(oAuthRequest).getBody(), - com.juick.twitter.User.class); - if (userService.linkTwitterAccount(user, accessToken.getToken(), accessToken.getTokenSecret(), - twitterUser.getScreenName())) { - response.setStatus(HttpServletResponse.SC_FOUND); - response.setHeader("Location", "http://juick.com/settings"); - } else { - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - } - } - } - }*/ - @GetMapping("/api/_vklogin") - protected String doVKLogin(@RequestParam(required = false) String code, - @RequestParam String state) throws IOException, ExecutionException, InterruptedException { - if (StringUtils.isBlank(code)) { - String vkstate = UUID.randomUUID().toString(); - crosspostService.addVKState(vkstate, state); - OAuth20Service vkAuthService = vkBuilder - .apiSecret(VK_SECRET) - .scope("friends,wall,offline") - .state(vkstate) - .callback(VK_REDIRECT) - .build(VkontakteApi.instance()); - return "redirect:" + vkAuthService.getAuthorizationUrl(); - } - - String redirectUrl = crosspostService.verifyVKState(state); - if (StringUtils.isBlank(redirectUrl)) { - logger.error("state is missing"); - throw new HttpBadRequestException(); - } - - OAuth20Service vkService = vkBuilder - .apiKey(VK_APPID) - .apiSecret(VK_SECRET) - .build(VkontakteApi.instance()); - OAuth2AccessToken token = vkService.getAccessToken(code); - - OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://api.vk.com/method/users.get?fields=screen_name&v=5.73"); - vkService.signRequest(token, meRequest); - String graph = vkService.execute(meRequest).getBody(); - - com.juick.model.vk.User jsonUser = jsonMapper.readValue(graph, UsersResponse.class).getUsers().get(0); - String vkName = jsonUser.getFirstName() + " " + jsonUser.getLastName(); - String vkLink = jsonUser.getScreenName(); - - if (vkName.length() == 1 || StringUtils.isBlank(vkLink)) { - logger.error("vk user error"); - throw new HttpBadRequestException(); - } - - Long vkID = NumberUtils.toLong(jsonUser.getId(), 0); - int uid = crosspostService.getUIDbyVKID(vkID); - if (uid > 0) { - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(redirectUrl); - uriComponentsBuilder.queryParam("hash", userService.getHashByUID(uid)); - return "redirect:" + uriComponentsBuilder.build().toUriString(); - } else { - String loginhash = UUID.randomUUID().toString(); - if (!crosspostService.createVKUser(vkID, loginhash, token.getAccessToken(), vkName, vkLink)) { - logger.error("create vk user error"); - throw new HttpBadRequestException(); - } - return "redirect:/signup?type=vk&hash=" + loginhash; - } - } - /* - @GetMapping("/_tglogin") - public String doDurovLogin(HttpServletRequest request, - @RequestParam Map<String, String> params, - HttpServletResponse response) { - String dataCheckString = params.entrySet().stream() - .filter(p -> !p.getKey().equals("hash")) - .sorted(Map.Entry.comparingByKey()) - .map(p -> p.getKey() + "=" + p.getValue()) - .collect(Collectors.joining("\n")); - String hash = params.get("hash"); - byte[] secretKey = DigestUtils.sha256(telegramToken); - String resultString = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, secretKey).hmacHex(dataCheckString); - if (hash.equals(resultString)) { - Long tgUser = Long.valueOf(params.get("id")); - int uid = telegramService.getUser(tgUser); - if (uid > 0) { - Cookie c = new Cookie("hash", userService.getHashByUID(uid)); - c.setMaxAge(50 * 24 * 60 * 60); - response.addCookie(c); - return Utils.getPreviousPageByRequest(request).orElse("redirect:/"); - } else { - String username = StringUtils.defaultString(params.get("username"), params.get("first_name")); - telegramService.createTelegramUser(tgUser, username); - return "redirect:/signup?type=durov&hash=" + userService.getSignUpHashByTelegramID(tgUser, username); - } - } else { - logger.warn("invalid tg hash {} for {}", resultString, hash); - } - throw new HttpBadRequestException(); - }*/ -} diff --git a/juick-server/src/main/java/com/juick/server/api/Index.java b/juick-server/src/main/java/com/juick/server/api/Index.java deleted file mode 100644 index 56f01370..00000000 --- a/juick-server/src/main/java/com/juick/server/api/Index.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.api; - -import com.juick.Status; -import com.juick.server.WebsocketManager; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import springfox.documentation.annotations.ApiIgnore; - -import javax.inject.Inject; -import java.net.URI; - -/** - * Created by vitalyster on 25.07.2016. - */ -@RestController -public class Index { - @Inject - private WebsocketManager wsHandler; - - @ApiIgnore - @RequestMapping(value = { "/api/", "/ws/" }, method = RequestMethod.GET, headers = "Connection!=Upgrade") - public ResponseEntity<Void> description() { - URI redirectUri = ServletUriComponentsBuilder.fromCurrentRequestUri().path("/swagger-ui.html").build().toUri(); - return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY).location(redirectUri).build(); - } - @ApiIgnore - @RequestMapping(value = "/api/status", method = RequestMethod.GET, - produces = MediaType.APPLICATION_JSON_UTF8_VALUE, headers = "Connection!=Upgrade") - public Status status() { - return Status.getStatus(String.valueOf(wsHandler.getClients().size())); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/Messages.java b/juick-server/src/main/java/com/juick/server/api/Messages.java deleted file mode 100644 index 4f0009dd..00000000 --- a/juick-server/src/main/java/com/juick/server/api/Messages.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.api; - -import com.juick.Message; -import com.juick.Tag; -import com.juick.User; -import com.juick.server.Utils; -import com.juick.service.component.MessageReadEvent; -import com.juick.model.CommandResult; -import com.juick.server.util.HttpBadRequestException; -import com.juick.server.util.HttpNotFoundException; -import com.juick.server.util.UserUtils; -import com.juick.service.MessagesService; -import com.juick.service.TagService; -import com.juick.service.UserService; -import org.apache.commons.io.IOUtils; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.*; - -import javax.inject.Inject; -import java.io.IOException; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; - -/** - * @author ugnich - */ -@RestController -@RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE) -public class Messages { - - private static final ResponseEntity<List<com.juick.Message>> NOT_FOUND = ResponseEntity - .status(HttpStatus.NOT_FOUND) - .body(Collections.emptyList()); - - private static final ResponseEntity<List<com.juick.Message>> FORBIDDEN = ResponseEntity - .status(HttpStatus.FORBIDDEN) - .body(Collections.emptyList()); - - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - @Inject - private TagService tagService; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - - // TODO: serialize image urls - - @GetMapping("/api/home") - public ResponseEntity<List<com.juick.Message>> getHome( - @RequestParam(defaultValue = "0") int before_mid) { - User visitor = UserUtils.getCurrentUser(); - if (!visitor.isAnonymous()) { - int vuid = visitor.getUid(); - List<Integer> mids = messagesService.getMyFeed(vuid, before_mid, true); - return ResponseEntity.ok(messagesService.getMessages(visitor, mids)); - } - return FORBIDDEN; - } - - @GetMapping("/api/messages") - public ResponseEntity<List<com.juick.Message>> getMessages( - @RequestParam(required = false) String uname, - @RequestParam(name = "before_mid", defaultValue = "0") Integer before, - @RequestParam(required = false, defaultValue = "0") Integer daysback, - @RequestParam(required = false) String withrecommended, - @RequestParam(required = false) String popular, - @RequestParam(required = false) String search, - @RequestParam(required = false, defaultValue = "0") Integer page, - @RequestParam(required = false) String media, - @RequestParam(required = false) String tag) { - - User visitor = UserUtils.getCurrentUser(); - - List<Integer> mids; - if (!StringUtils.isEmpty(uname)) { - User user = userService.getUserByName(uname); - if (!user.isAnonymous()) { - if (!StringUtils.isEmpty(media)) { - mids = messagesService.getUserPhotos(user.getUid(), 0, before); - } else if (!StringUtils.isEmpty(tag)) { - Tag tagObject = tagService.getTag(tag, false); - if (tagObject != null) { - mids = messagesService.getUserTag(user.getUid(), tagObject.TID, 0, before); - } else { - return NOT_FOUND; - } - } else if (!StringUtils.isEmpty(withrecommended)) { - mids = messagesService.getUserBlogWithRecommendations(user.getUid(), 0, before); - } else if (daysback > 0) { - mids = messagesService.getUserBlogAtDay(user.getUid(), 0, daysback); - } else if (!StringUtils.isEmpty(search)) { - mids = messagesService.getUserSearch(visitor, user.getUid(), Utils.encodeSphinx(search), 0, page); - } else { - mids = messagesService.getUserBlog(user.getUid(), 0, before); - } - } else { - return NOT_FOUND; - } - } else { - if (!StringUtils.isEmpty(popular)) { - mids = messagesService.getPopular(visitor.getUid(), before); - } else if (!StringUtils.isEmpty(media)) { - mids = messagesService.getPhotos(visitor.getUid(), before); - } else if (!StringUtils.isEmpty(tag)) { - Tag tagObject = tagService.getTag(tag, false); - if (tagObject != null) { - mids = messagesService.getTag(tagObject.TID, visitor.getUid(), before, 20); - } else { - return NOT_FOUND; - } - } else if (!StringUtils.isEmpty(search)) { - mids = messagesService.getSearch(visitor, Utils.encodeSphinx(search), page); - } else { - mids = messagesService.getAll(visitor.getUid(), before); - } - } - return ResponseEntity.ok(messagesService.getMessages(visitor, mids)); - } - @DeleteMapping("/api/messages") - public CommandResult deleteMessage(@RequestParam int mid, @RequestParam(required = false, defaultValue = "0") int rid) { - User visitor = UserUtils.getCurrentUser(); - if (rid > 0) { - if (messagesService.deleteReply(visitor.getUid(), mid, rid)) { - return CommandResult.fromString("Reply deleted"); - } - } - if (messagesService.deleteMessage(visitor.getUid(), mid)) { - return CommandResult.fromString("Message deleted"); - } - throw new HttpBadRequestException(); - } - @GetMapping("/api/messages/discussions") - public List<Message> getDiscussions( - @RequestParam(required = false, defaultValue = "0") Long to) { - return messagesService.getMessages(UserUtils.getCurrentUser(), messagesService.getDiscussions(UserUtils.getCurrentUser().getUid(), to)); - } - @GetMapping("/api/thread") - public ResponseEntity<List<com.juick.Message>> getThread( - @RequestParam(defaultValue = "0") int mid) { - User visitor = UserUtils.getCurrentUser(); - com.juick.Message msg = messagesService.getMessage(mid); - if (msg != null) { - if (!messagesService.canViewThread(mid, visitor.getUid())) { - return FORBIDDEN; - } else { - if (userService.getUserByName(msg.getUser().getName()).isBanned()) { - throw new HttpNotFoundException(); - } - msg.setRecommendations(new HashSet<>(messagesService.getMessageRecommendations(msg.getMid()))); - List<com.juick.Message> replies = messagesService.getReplies(visitor, mid); - if (!visitor.isAnonymous()) { - userService.updateLastSeen(visitor); - applicationEventPublisher.publishEvent( - new MessageReadEvent(this, visitor, msg)); - } - replies.add(0, msg); - return ResponseEntity.ok(replies); - } - } - return NOT_FOUND; - } - @GetMapping(value = "/api/thread/mark_read/{mid}-{rid}.gif", produces = MediaType.IMAGE_GIF_VALUE) - public byte[] markThreadRead(@PathVariable int mid, @PathVariable int rid) throws IOException { - User visitor = UserUtils.getCurrentUser(); - if (!visitor.isAnonymous()) { - messagesService.setLastReadComment(visitor, mid, rid); - Message msg = messagesService.getMessage(mid); - userService.updateLastSeen(visitor); - applicationEventPublisher.publishEvent( - new MessageReadEvent(this, visitor, msg)); - return IOUtils.toByteArray( - Objects.requireNonNull(getClass().getClassLoader().getResource("Transparent.gif"))); - } - throw new HttpBadRequestException(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/Notifications.java b/juick-server/src/main/java/com/juick/server/api/Notifications.java deleted file mode 100644 index 62275f5a..00000000 --- a/juick-server/src/main/java/com/juick/server/api/Notifications.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.api; - -import com.juick.Message; -import com.juick.Status; -import com.juick.ExternalToken; -import com.juick.User; -import com.juick.model.AnonymousUser; -import com.juick.server.util.HttpBadRequestException; -import com.juick.server.util.HttpForbiddenException; -import com.juick.service.MessagesService; -import com.juick.service.PushQueriesService; -import com.juick.service.SubscriptionService; -import com.juick.server.util.UserUtils; -import com.juick.service.UserService; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import springfox.documentation.annotations.ApiIgnore; - -import javax.inject.Inject; -import java.io.IOException; -import java.security.Principal; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Created by vitalyster on 24.10.2016. - */ -@RestController -public class Notifications { - - @Inject - private PushQueriesService pushQueriesService; - @Inject - private MessagesService messagesService; - @Inject - private SubscriptionService subscriptionService; - @Inject - private UserService userService; - - - private User collectTokens(Integer uid) { - User user = userService.getUserByUID(uid).orElse(AnonymousUser.INSTANCE); - user.setUnreadCount(messagesService.getUnread(user).size()); - pushQueriesService.getGCMRegID(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "gcm", t, null))); - pushQueriesService.getAPNSToken(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "apns", t, null))); - pushQueriesService.getMPNSURL(uid).forEach(t -> user.getTokens().add(new ExternalToken(null, "mpns", t, null))); - return user; - } - - @ApiIgnore - @RequestMapping(value = "/api/notifications", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public ResponseEntity<List<User>> doGet( - @RequestParam(required = false, defaultValue = "0") int uid, - @RequestParam(required = false, defaultValue = "0") int mid, - @RequestParam(required = false, defaultValue = "0") int rid) { - User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous() || !(visitor.getName().equals("juick"))) { - throw new HttpForbiddenException(); - } - if (uid > 0 && mid == 0) { - // PM - return ResponseEntity.ok(Collections.singletonList(collectTokens(uid))); - } else { - if (mid > 0) { - // reply - Message msg = messagesService.getMessage(mid); - if (msg != null) { - List<User> users; - if (rid > 0) { - Message op = messagesService.getMessage(mid); - Message reply = messagesService.getReply(mid, rid); - users = subscriptionService.getUsersSubscribedToComments(op, reply); - } else { - users = subscriptionService.getSubscribedUsers(msg.getUser().getUid(), msg); - } - - return ResponseEntity.ok(users.stream().map(User::getUid) - .map(this::collectTokens).collect(Collectors.toList())); - } - } else { - // read - return ResponseEntity.ok(Collections.singletonList(collectTokens(uid))); - } - } - throw new HttpBadRequestException(); - } - - @ApiIgnore - @RequestMapping(value = "/api/notifications", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Status doDelete( - @RequestBody List<ExternalToken> list) { - User visitor = UserUtils.getCurrentUser(); - if ((visitor.isAnonymous()) || !(visitor.getName().equals("juick"))) { - throw new HttpForbiddenException(); - } - list.forEach(t -> { - switch (t.getType()) { - case "gcm": - pushQueriesService.deleteGCMToken(t.getToken()); - break; - case "apns": - pushQueriesService.deleteAPNSToken(t.getToken()); - break; - case "mpns": - pushQueriesService.deleteMPNSToken(t.getToken()); - break; - default: - throw new HttpBadRequestException(); - } - }); - - return Status.OK; - } - @ApiIgnore - @RequestMapping(value = "/api/notifications/delete", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Status doDeleteTokens( - @RequestBody List<ExternalToken> list) { - User visitor = UserUtils.getCurrentUser(); - if ((visitor.isAnonymous()) || !(visitor.getName().equals("juick"))) { - throw new HttpForbiddenException(); - } - list.forEach(t -> { - switch (t.getType()) { - case "gcm": - pushQueriesService.deleteGCMToken(t.getToken()); - break; - case "apns": - pushQueriesService.deleteAPNSToken(t.getToken()); - break; - case "mpns": - pushQueriesService.deleteMPNSToken(t.getToken()); - break; - default: - throw new HttpBadRequestException(); - } - }); - - return Status.OK; - } - - @ApiIgnore - @RequestMapping(value = "/api/notifications", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Status doPut( - @RequestBody List<ExternalToken> list) throws IOException { - User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - list.forEach(t -> { - switch (t.getType()) { - case "gcm": - pushQueriesService.addGCMToken(visitor.getUid(), t.getToken()); - break; - case "apns": - pushQueriesService.addAPNSToken(visitor.getUid(), t.getToken()); - break; - case "mpns": - pushQueriesService.addMPNSToken(visitor.getUid(), t.getToken()); - break; - default: - throw new HttpBadRequestException(); - } - }); - return Status.OK; - } - - @Deprecated - @RequestMapping(value = "/api/android/register", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Status doAndroidRegister( - @RequestParam(name = "regid") String regId) { - User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - pushQueriesService.addGCMToken(visitor.getUid(), regId); - return Status.OK; - } - - @Deprecated - @RequestMapping(value = "/api/android/unregister", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Status doAndroidUnRegister(@RequestParam(name = "regid") String regId) { - pushQueriesService.deleteGCMToken(regId); - return Status.OK; - } - - @Deprecated - @RequestMapping(value = "/api/winphone/register", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Status doWinphoneRegister( - Principal principal, - @RequestParam(name = "url") String regId) { - User visitor = UserUtils.getCurrentUser(); - pushQueriesService.addMPNSToken(visitor.getUid(), regId); - return Status.OK; - } - - @Deprecated - @RequestMapping(value = "/api/winphone/unregister", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Status doWinphoneUnRegister(@RequestParam(name = "url") String regId) { - pushQueriesService.deleteMPNSToken(regId); - return Status.OK; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/PM.java b/juick-server/src/main/java/com/juick/server/api/PM.java deleted file mode 100644 index 0c36fe00..00000000 --- a/juick-server/src/main/java/com/juick/server/api/PM.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.api; - -import com.juick.Chat; -import com.juick.User; -import com.juick.service.component.MessageEvent; -import com.juick.model.AnonymousUser; -import com.juick.model.PrivateChats; -import com.juick.server.util.*; -import com.juick.service.PMQueriesService; -import com.juick.service.UserService; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import javax.inject.Inject; -import java.util.Collections; -import java.util.List; - -/** - * @author ugnich - */ -@RestController -public class PM { - @Inject - private UserService userService; - @Inject - private PMQueriesService pmQueriesService; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - - @RequestMapping(value = "/api/pm", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public List<com.juick.Message> doGetPM( - @RequestParam(required = false) String uname) { - User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - int uid = 0; - if (uname != null && uname.matches("^[a-zA-Z0-9\\-]{2,16}$")) { - uid = userService.getUIDbyName(uname); - } - - if (uid == 0) { - throw new HttpBadRequestException(); - } - - return pmQueriesService.getPMMessages(visitor.getUid(), uid); - } - - @RequestMapping(value = "/api/pm", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public com.juick.Message doPostPM( - @RequestParam String uname, - @RequestParam String body) { - User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - User userTo = AnonymousUser.INSTANCE; - if (WebUtils.isUserName(uname)) { - userTo = userService.getUserByName(uname); - } - - if (userTo.getUid() == 0 || body == null || body.length() < 1 || body.length() > 10240) { - throw new HttpBadRequestException(); - } - - if (userService.isInBLAny(userTo.getUid(), visitor.getUid())) { - throw new HttpForbiddenException(); - } - - if (pmQueriesService.createPM(visitor.getUid(), userTo.getUid(), body)) { - com.juick.Message jmsg = new com.juick.Message(); - jmsg.setUser(visitor); - jmsg.setText(body); - jmsg.setTo(userTo); - applicationEventPublisher.publishEvent(new MessageEvent(this, jmsg, Collections.singletonList(jmsg.getTo()))); - return jmsg; - - } - throw new HttpBadRequestException(); - } - @RequestMapping(value = "/api/groups_pms", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public PrivateChats doGetGroupsPMs( - @RequestParam(defaultValue = "5") int cnt) { - User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - // TODO: ignore cnt param for now but make sure paging param will not be cnt - - List<Chat> lastconv = pmQueriesService.getLastChats(visitor); - PrivateChats pms = new PrivateChats(); - pms.setUsers(lastconv); - return pms; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/Post.java b/juick-server/src/main/java/com/juick/server/api/Post.java deleted file mode 100644 index 303ff109..00000000 --- a/juick-server/src/main/java/com/juick/server/api/Post.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.api; - -import com.juick.Message; -import com.juick.Reaction; -import com.juick.Status; -import com.juick.User; -import com.juick.server.CommandsManager; -import com.juick.model.CommandResult; -import com.juick.server.util.*; -import com.juick.service.MessagesService; -import com.juick.service.SubscriptionService; -import com.juick.service.UserService; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - -import javax.inject.Inject; -import javax.validation.constraints.NotNull; -import java.net.URI; -import java.net.URL; -import java.util.List; - -/** - * Created by vt on 24/11/2016. - */ -@RestController -public class Post { - private static Logger logger = LoggerFactory.getLogger(Post.class); - - @Inject - private UserService userService; - @Inject - private MessagesService messagesService; - @Inject - private SubscriptionService subscriptionService; - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String imgDir; - @Inject - CommandsManager commandsManager; - - @RequestMapping(value = "/api/post", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - @ResponseStatus(value = HttpStatus.OK) - public CommandResult doPostMessage( - @RequestParam(required = false, defaultValue = StringUtils.EMPTY) String body, - @RequestParam(required = false) String img, - @RequestParam(required = false) MultipartFile attach) throws Exception { - User visitor = UserUtils.getCurrentUser(); - - if (visitor.isAnonymous()) - throw new HttpForbiddenException(); - - if (body.length() > 4096) { - throw new HttpBadRequestException(); - } - body = body.replace("\r", StringUtils.EMPTY); - - URI attachmentFName = HttpUtils.receiveMultiPartFile(attach, tmpDir); - - if (StringUtils.isBlank(attachmentFName.toString()) && img != null && img.length() > 10) { - URI juickUri = URI.create(img); - if (juickUri.getScheme().equals("juick")) { - attachmentFName = juickUri; - } else { - try { - URL imgUrl = new URL(img); - attachmentFName = HttpUtils.downloadImage(imgUrl, tmpDir); - } catch (Exception e) { - logger.error("DOWNLOAD ERROR", e); - throw new HttpBadRequestException(); - } - } - } - if (StringUtils.isBlank(body) && StringUtils.isBlank(attachmentFName.toString())) { - // Should be there for compatibility - throw new HttpBadRequestException(); - } - return commandsManager.processCommand(visitor, body, attachmentFName); - } - - @RequestMapping(value = "/api/comment", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public CommandResult doPostComment( - @RequestParam(defaultValue = "0") int mid, - @RequestParam(defaultValue = "0") int rid, - @RequestParam(required = false, defaultValue = StringUtils.EMPTY) String body, - @RequestParam(required = false) String img, - @RequestParam(required = false) MultipartFile attach) - throws Exception { - User visitor = UserUtils.getCurrentUser(); - int vuid = visitor.getUid(); - if (vuid == 0) { - throw new HttpForbiddenException(); - } - if (mid == 0) { - throw new HttpBadRequestException(); - } - com.juick.Message msg = messagesService.getMessage(mid); - if (msg == null) { - throw new HttpNotFoundException(); - } - - com.juick.Message reply = null; - if (rid > 0) { - reply = messagesService.getReply(mid, rid); - if (reply == null) { - throw new HttpNotFoundException(); - } - } - - if (body.length() > 4096) { - throw new HttpBadRequestException(); - } - body = body.replace("\r", StringUtils.EMPTY); - - if ((msg.ReadOnly && msg.getUser().getUid() != vuid) || userService.isInBLAny(msg.getUser().getUid(), vuid) - || (reply != null && userService.isInBLAny(reply.getUser().getUid(), vuid))) { - throw new HttpForbiddenException(); - } - - URI attachmentFName = HttpUtils.receiveMultiPartFile(attach, tmpDir); - - if (StringUtils.isBlank(attachmentFName.toString()) && img != null && img.length() > 10) { - try { - attachmentFName = HttpUtils.downloadImage(new URL(img), tmpDir); - } catch (Exception e) { - logger.error("DOWNLOAD ERROR", e); - throw new HttpBadRequestException(); - } - } - if (StringUtils.isBlank(body) && StringUtils.isBlank(attachmentFName.toString())) { - // Should be there for compatibility - throw new HttpBadRequestException(); - } - return commandsManager.processCommand(visitor, String.format("#%d/%d %s", mid, rid, body), - attachmentFName); - } - - @PostMapping("/api/like") - @ResponseStatus(value = HttpStatus.OK) - public Status doPostRecomm(@RequestParam Integer mid) throws Exception { - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - com.juick.Message msg = messagesService.getMessage(mid); - if (msg == null) { - throw new HttpNotFoundException(); - } - if (msg.getUser().getUid() == visitor.getUid()) { - throw new HttpForbiddenException(); - } - CommandResult status = commandsManager.processCommand(visitor, String.format("! #%d", mid), - URI.create(StringUtils.EMPTY)); - return Status.getStatus(status.getText()); - } - - @PostMapping("/api/subscribe") - @ResponseStatus(value = HttpStatus.OK) - public Status doPostSubscribe(@RequestParam Integer mid) throws Exception { - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - com.juick.Message msg = messagesService.getMessage(mid); - if (msg == null) { - throw new HttpNotFoundException(); - } - if (msg.getUser().getUid() == visitor.getUid()) { - throw new HttpForbiddenException(); - } - CommandResult status = commandsManager.processCommand(visitor, String.format("S #%d", mid), - URI.create(StringUtils.EMPTY)); - return Status.getStatus(status.getText()); - } - - @GetMapping("/api/reactions") - @ResponseStatus(value = HttpStatus.OK) - public List<Reaction> reactionsList() { - return messagesService.listReactions(); - } - - @PostMapping("/api/react") - @ResponseStatus(value = HttpStatus.OK) - public Status doPostReact(@RequestParam Integer mid,@RequestParam @NotNull int reactionId, - @RequestParam (required = false, defaultValue = "1") int count) { - - logger.info("got reaction with type: {}", reactionId); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - com.juick.Message msg = messagesService.getMessage(mid); - if (msg == null) { - throw new HttpNotFoundException(); - } - if (msg.getUser().getUid() == visitor.getUid()) { - throw new HttpForbiddenException(); - } - MessagesService.RecommendStatus recommendStatus = MessagesService.RecommendStatus.Error; - for (int i = 0; i < count; i++) - recommendStatus = messagesService.likeMessage(mid, visitor.getUid(), - reactionId); - - return recommendStatus == MessagesService.RecommendStatus.Error ? Status.ERROR :Status.OK; - } - - @PostMapping("/api/update") - public CommandResult updateMessage(@RequestParam Integer mid, - @RequestParam(required = false, defaultValue = "0") Integer rid, - @RequestParam String body) { - User visitor = UserUtils.getCurrentUser(); - User author = rid == 0 ? messagesService.getMessageAuthor(mid) : messagesService.getReply(mid, rid).getUser(); - if (visitor.equals(author)) { - if (messagesService.updateMessage(mid, rid, body)) { - Message result = rid == 0 ? messagesService.getMessage(mid) : messagesService.getReply(mid, rid); - return CommandResult.build(result, "Message updated", StringUtils.EMPTY); - } - throw new HttpBadRequestException(); - } - throw new HttpForbiddenException(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/Service.java b/juick-server/src/main/java/com/juick/server/api/Service.java deleted file mode 100644 index ed62886f..00000000 --- a/juick-server/src/main/java/com/juick/server/api/Service.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.juick.server.api; - -import com.juick.Message; -import com.juick.User; -import com.juick.server.CommandsManager; -import com.juick.server.EmailManager; -import com.juick.server.ServerManager; -import com.juick.server.util.HttpForbiddenException; -import com.juick.server.util.UserUtils; -import com.juick.service.EmailService; -import com.juick.service.UserService; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.mail.util.MimeMessageParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.context.request.async.AsyncRequestTimeoutException; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import springfox.documentation.annotations.ApiIgnore; - -import javax.inject.Inject; -import javax.mail.Session; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.nio.file.Paths; -import java.util.*; - -@Controller -public class Service { - private static Logger logger = LoggerFactory.getLogger(Service.class); - @Inject - private UserService userService; - @Inject - private EmailService emailService; - @Inject - private CommandsManager commandsManager; - @Inject - private EmailManager emailManager; - @Value("${api_user:juick}") - private String serviceUser; - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - @Value("${banned_emails:}") - private String[] ignoredEmails; - @Inject - private ServerManager serverManager; - - private Session session = Session.getDefaultInstance(new Properties()); - - @ApiIgnore - @PostMapping("/api/mail") - @ResponseStatus(value = HttpStatus.OK) - public void processMail(InputStream data) throws Exception { - if (UserUtils.getCurrentUser().getName().equals(serviceUser)) { - MimeMessage msg = new MimeMessage(session, data); - String[] returnPaths = msg.getHeader("Return-Path"); - if (returnPaths != null) { - logger.info("got msg with return path {}", returnPaths[0]); - if (returnPaths[0].equals("<>")) { - return; - } - } - String from = msg.getFrom() == null || msg.getFrom().length > 1 ? ((InternetAddress) msg.getSender()).getAddress() - : ((InternetAddress) msg.getFrom()[0]).getAddress(); - - User visitor = userService.getUserByEmail(from); - if (!visitor.isAnonymous()) { - MimeMessageParser parser = new MimeMessageParser(msg); - parser.parse(); - final String[] body = {parser.getPlainContent()}; - if (body[0] == null) { - parser.getAttachmentList().stream() - .filter(a -> a.getContentType().equals("text/plain")).findFirst() - .ifPresent(a -> { - try { - body[0] = IOUtils.toString(a.getInputStream(), StandardCharsets.UTF_8); - logger.info("got text: {}", body[0]); - } catch (IOException e) { - logger.info("attachment error: {}", e); - } - }); - } - final String[] attachmentFName = new String[1]; - parser.getAttachmentList().stream().filter(a -> - a.getContentType().equals("image/jpeg") || a.getContentType().equals("image/png")) - .findFirst().ifPresent(a -> { - logger.info("got attachment: {}", a.getContentType()); - String attachmentType; - if (a.getContentType().equals("image/jpeg")) { - attachmentType = "jpg"; - } else { - attachmentType = "png"; - } - attachmentFName[0] = DigestUtils.md5Hex(UUID.randomUUID().toString()) + "." + attachmentType; - try { - logger.info("got inputstream: {}", a.getInputStream()); - FileOutputStream fos = new FileOutputStream(Paths.get(tmpDir, attachmentFName[0]).toString()); - IOUtils.copy(a.getInputStream(), fos); - fos.close(); - } catch (IOException e) { - logger.info("attachment error: {}", e); - } - }); - String[] inReplyToHeaders = msg.getHeader("In-Reply-To"); - if (inReplyToHeaders != null && inReplyToHeaders.length > 0) { - Scanner inReplyToScanner = new Scanner(inReplyToHeaders[0].trim()).useDelimiter(EmailManager.MSGID_PATTERN); - int mid = Integer.parseInt(inReplyToScanner.next()); - int rid = Integer.parseInt(inReplyToScanner.next()); - logger.info("Message is reply to #{}/{}", mid, rid); - body[0] = rid > 0 ? String.format("#%d/%d %s", mid, rid, body[0]) - : String.format("#%d %s", mid, body[0]); - } - URI attachmentUri = StringUtils.isNotEmpty(attachmentFName[0]) ? URI.create(String.format("juick://%s", attachmentFName[0])) - : URI.create(StringUtils.EMPTY); - commandsManager.processCommand(visitor, body[0], attachmentUri); - } else { - if (!Arrays.asList(ignoredEmails).contains(from)) { - String verificationCode = RandomStringUtils.randomAlphanumeric(8).toUpperCase(); - emailService.addVerificationCode(null, from, verificationCode); - String signupUrl = String.format("Follow this link to create Juick account: https://juick.com/signup?type=email&hash=%s", verificationCode); - emailManager.sendEmail(from, "Juick registration", signupUrl, StringUtils.EMPTY, Collections.emptyMap()); - } - } - } else { - throw new HttpForbiddenException(); - } - } - private void endSession(SseEmitter emitter) { - serverManager.getSessions().stream() - .filter(s -> s.getEmitter().equals(emitter)) - .forEach(session -> serverManager.getSessions().remove(session)); - } - @GetMapping("/api/events") - public SseEmitter handle() throws IOException { - User visitor = UserUtils.getCurrentUser(); - logger.info("{} connected", visitor.getName()); - if (!visitor.isAnonymous()) { - userService.updateLastSeen(visitor); - } - SseEmitter emitter = new SseEmitter(600000L); - serverManager.getSessions().add(new ServerManager.EventSession(visitor, emitter)); - - emitter.onCompletion(() -> endSession(emitter)); - emitter.onTimeout(() -> endSession(emitter)); - - return emitter; - } - @ExceptionHandler(AsyncRequestTimeoutException.class) - public void eventErrorHandler(Exception ex) { - logger.debug("SSE timeout", ex); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/Tags.java b/juick-server/src/main/java/com/juick/server/api/Tags.java deleted file mode 100644 index 7a8e572a..00000000 --- a/juick-server/src/main/java/com/juick/server/api/Tags.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.api; - -import com.juick.User; -import com.juick.model.TagStats; -import com.juick.server.util.UserUtils; -import com.juick.service.TagService; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import javax.inject.Inject; -import java.util.List; - -/** - * Created by vitalyster on 29.11.2016. - */ -@RestController -public class Tags { - @Inject - private TagService tagService; - - @RequestMapping(value = "/api/tags", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public List<TagStats> tags( - @RequestParam(required = false, defaultValue = "0") int user_id - ) { - User visitor = UserUtils.getCurrentUser(); - if (user_id == 0) { - user_id = visitor.getUid(); - } - if (user_id > 0) { - return tagService.getUserTagStats(user_id); - } - return tagService.getTagStats(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/Users.java b/juick-server/src/main/java/com/juick/server/api/Users.java deleted file mode 100644 index 7686d722..00000000 --- a/juick-server/src/main/java/com/juick/server/api/Users.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.api; - -import com.juick.User; -import com.juick.model.ApplicationStatus; -import com.juick.model.UserInfo; -import com.juick.server.util.HttpForbiddenException; -import com.juick.server.util.HttpNotFoundException; -import com.juick.service.CrosspostService; -import com.juick.service.EmailService; -import com.juick.service.MessagesService; -import com.juick.service.UserService; -import com.juick.server.util.UserUtils; -import com.juick.server.util.WebUtils; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.*; - -import javax.inject.Inject; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * @author ugnich - */ -@RestController -public class Users { - @Inject - private UserService userService; - @Inject - private MessagesService messagesService; - @Inject - private CrosspostService crosspostService; - @Inject - private EmailService emailService; - - @RequestMapping(value = "/api/auth", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public String getAuthToken() { - return userService.getHashByUID(UserUtils.getCurrentUser().getUid()); - } - - @RequestMapping(value = "/api/users", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public List<User> doGetUsers( - @RequestParam(value = "uname", required = false) List<String> unames) { - List<com.juick.User> users = new ArrayList<>(); - - if (unames != null) { - unames.removeIf(WebUtils::isNotUserName); - - if (!unames.isEmpty() && unames.size() < 20) - users.addAll(userService.getUsersByName(unames)); - } - - if (!users.isEmpty()) - return users; - if (!UserUtils.getCurrentUser().isAnonymous()) { - return Collections.singletonList(UserUtils.getCurrentUser()); - } - - throw new HttpNotFoundException(); - } - - @GetMapping("/api/me") - public SecureUser getMe() { - User visitor = UserUtils.getCurrentUser(); - SecureUser me = new SecureUser(); - me.setUid(visitor.getUid()); - me.setName(visitor.getName()); - me.setAuthHash(getAuthToken()); - List<Integer> unread = messagesService.getUnread(visitor); - me.setUnread(unread); - me.setUnreadCount(unread.size()); - me.setRead(userService.getUserFriends(visitor.getUid())); - me.setReaders(userService.getUserReaders(visitor.getUid())); - return me; - } - - @RequestMapping(value = "/api/users/read", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public List<User> doGetUserRead( - @RequestParam String uname) { - User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - int uid = 0; - if (uname == null) { - uid = visitor.getUid(); - } else { - if (WebUtils.isUserName(uname)) { - com.juick.User u = userService.getUserByName(uname); - if (!u.isAnonymous()) { - uid = u.getUid(); - } - } - } - - if (uid > 0) { - return userService.getUserFriends(uid); - } - throw new HttpNotFoundException(); - } - - @RequestMapping(value = "/api/users/readers", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public List<User> doGetUserReaders( - @RequestParam String uname) { - User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpForbiddenException(); - } - int uid = 0; - if (uname == null) { - uid = visitor.getUid(); - } else { - if (WebUtils.isUserName(uname)) { - com.juick.User u = userService.getUserByName(uname); - if (!u.isAnonymous()) { - uid = u.getUid(); - } - } - } - - if (uid > 0) { - return userService.getUserReaders(uid); - } - throw new HttpNotFoundException(); - } - - @GetMapping("/api/info/{uname}") - public UserInfo getUserInfo(@PathVariable String uname) { - User user = userService.getUserByName(uname); - if (!user.isBanned()) { - return userService.getUserInfo(user); - } - throw new HttpNotFoundException(); - } - - class SecureUser extends User { - public String getHash() { - return getAuthHash(); - } - public UserInfo getUserInfo() { - return userService.getUserInfo(this); - } - public List<String> getJIDs() { - return userService.getAllJIDs(this); - } - public List<String> getEmails() { - return userService.getEmails(this); - } - public String getActiveEmail() { - return emailService.getNotificationsEmail(this.getUid()); - } - public String getTwitterName() { - return crosspostService.getTwitterName(this.getUid()); - } - public String getTelegramName() { - return crosspostService.getTelegramName(this.getUid()); - } - public ApplicationStatus getFacebookStatus() { - return crosspostService.getFbCrossPostStatus(this.getUid()); - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/Profile.java b/juick-server/src/main/java/com/juick/server/api/activity/Profile.java deleted file mode 100644 index 10390ea1..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/Profile.java +++ /dev/null @@ -1,379 +0,0 @@ -package com.juick.server.api.activity; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.juick.Message; -import com.juick.User; -import com.juick.model.CommandResult; -import com.juick.server.ActivityPubManager; -import com.juick.server.CommandsManager; -import com.juick.server.KeystoreManager; -import com.juick.server.SignatureManager; -import com.juick.server.api.activity.model.Activity; -import com.juick.server.api.activity.model.Context; -import com.juick.server.api.activity.model.activities.Announce; -import com.juick.server.api.activity.model.activities.Create; -import com.juick.server.api.activity.model.activities.Delete; -import com.juick.server.api.activity.model.activities.Follow; -import com.juick.server.api.activity.model.activities.Undo; -import com.juick.server.api.activity.model.objects.Image; -import com.juick.server.api.activity.model.objects.Key; -import com.juick.server.api.activity.model.objects.Note; -import com.juick.server.api.activity.model.objects.OrderedCollection; -import com.juick.server.api.activity.model.objects.OrderedCollectionPage; -import com.juick.server.api.activity.model.objects.Person; -import com.juick.server.util.HttpBadRequestException; -import com.juick.server.util.HttpNotFoundException; -import com.juick.server.util.UserUtils; -import com.juick.service.MessagesService; -import com.juick.service.UserService; -import com.juick.service.activities.DeleteUserEvent; -import com.juick.service.activities.FollowEvent; -import com.juick.service.activities.UndoFollowEvent; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.inject.Inject; -import java.net.URI; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -@RestController -public class Profile { - private static final Logger logger = LoggerFactory.getLogger(Profile.class); - @Inject - private UserService userService; - @Inject - private MessagesService messagesService; - @Inject - private KeystoreManager keystoreManager; - @Inject - private SignatureManager signatureManager; - @Inject - private ActivityPubManager activityPubManager; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - @Inject - private CommandsManager commandsManager; - @Value("${web_domain:localhost}") - private String domain; - @Value("${ap_base_uri:http://localhost:8080/}") - private String baseUri; - @Value("${img_url:http://localhost:8080/i/}") - private String baseImagesUri; - @Inject - private ObjectMapper jsonMapper; - - @GetMapping(value = "/u/{userName}", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public Person getUser(@PathVariable String userName) { - User user = userService.getUserByName(userName); - if (!user.isAnonymous()) { - Person person = new Person(); - person.setId(activityPubManager.personUri(user)); - person.setUrl(activityPubManager.personWebUri(user)); - person.setName(userName); - person.setPreferredUsername(userName); - Key publicKey = new Key(); - publicKey.setId(person.getId() + "#main-key"); - publicKey.setOwner(person.getId()); - publicKey.setPublicKeyPem(keystoreManager.getPublicKeyPem()); - person.setPublicKey(publicKey); - person.setInbox(activityPubManager.inboxUri()); - person.setOutbox(activityPubManager.outboxUri(user)); - person.setFollowers(activityPubManager.followersUri(user)); - person.setFollowing(activityPubManager.followingUri(user)); - UriComponentsBuilder image = UriComponentsBuilder.fromUriString(baseImagesUri); - image.path(String.format("/a/%d.png", user.getUid())); - Image avatar = new Image(); - avatar.setUrl(image.toUriString()); - avatar.setMediaType("image/png"); - person.setIcon(avatar); - return (Person) Context.build(person); - } - throw new HttpNotFoundException(); - } - - @GetMapping(value = "/u/{userName}/blog/toc", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public OrderedCollection getOutbox(@PathVariable String userName) { - User user = userService.getUserByName(userName); - if (!user.isAnonymous()) { - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri); - OrderedCollection blog = new OrderedCollection(); - blog.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString()); - blog.setTotalItems(userService.getStatsMessages(user.getUid())); - blog.setFirst(uriComponentsBuilder.path(String.format("/u/%s/blog", userName)).toUriString()); - return (OrderedCollection) Context.build(blog); - } - throw new HttpNotFoundException(); - } - - @GetMapping(value = "/u/{userName}/blog", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public OrderedCollectionPage getOutboxPage(@PathVariable String userName, - @RequestParam(required = false, defaultValue = "0") int before) { - User visitor = UserUtils.getCurrentUser(); - User user = userService.getUserByName(userName); - if (!user.isAnonymous()) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - String personUri = uri.path(String.format("/u/%s", userName)).toUriString(); - List<Integer> mids = messagesService.getUserBlog(user.getUid(), 0, before); - List<Note> notes = messagesService.getMessages(visitor, mids).stream().map(activityPubManager::makeNote).collect(Collectors.toList()); - OrderedCollectionPage page = new OrderedCollectionPage(); - page.setPartOf(uri.replacePath(String.format("/u/%s/blog/toc", userName)).toUriString()); - page.setFirst(uri.replacePath(String.format("/u/%s/blog", userName)).toUriString()); - page.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString()); - page.setOrderedItems(notes.stream().map(a -> { - Create create = new Create(); - create.setId(a.getId() + "#Create"); - create.setTo(a.getTo()); - create.setActor(personUri); - create.setObject(a); - create.setPublished(a.getPublished()); - return create; - }).collect(Collectors.toList())); - int beforeNext = mids.stream().reduce((fst, second) -> second).orElse(0); - if (beforeNext > 0) { - page.setNext(uri.queryParam("before", beforeNext).toUriString()); - } - page.setLast(uri.replaceQueryParam("before", "1").toUriString()); - return (OrderedCollectionPage) Context.build(page); - } - throw new HttpNotFoundException(); - } - - @GetMapping(value = "/u/{userName}/followers/toc", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public OrderedCollection getFollowers(@PathVariable String userName) { - User user = userService.getUserByName(userName); - if (!user.isAnonymous()) { - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri); - OrderedCollection followers = new OrderedCollection(); - followers.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString()); - followers.setTotalItems(userService.getStatsMyReaders(user.getUid())); - followers.setFirst(uriComponentsBuilder.path(String.format("/u/%s/followers", userName)).toUriString()); - return (OrderedCollection) Context.build(followers); - } - throw new HttpNotFoundException(); - } - - @GetMapping(value = "/u/{userName}/followers", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public OrderedCollectionPage getFollowersPage(@PathVariable String userName, - @RequestParam(required = false, defaultValue = "0") int page) { - User user = userService.getUserByName(userName); - if (!user.isAnonymous()) { - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri); - uriComponentsBuilder.path(String.format("/u/%s/followers", userName)); - List<User> followers = userService.getUserReaders(user.getUid()); - Stream<User> followersPage = followers.stream().skip(20 * page).limit(20); - - OrderedCollectionPage result = new OrderedCollectionPage(); - result.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString()); - result.setOrderedItems(followersPage.map(a -> { - Person follower = new Person(); - follower.setName(a.getName()); - follower.setPreferredUsername(a.getName()); - follower.setUrl(activityPubManager.personWebUri(a)); - return follower; - }).collect(Collectors.toList())); - boolean hasNext = followers.size() <= 20 * page; - if (hasNext) { - result.setNext(uriComponentsBuilder.queryParam("page", page + 1).toUriString()); - } - return (OrderedCollectionPage) Context.build(result); - } - throw new HttpNotFoundException(); - } - - @GetMapping(value = "/u/{userName}/following/toc", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public OrderedCollection getFollowing(@PathVariable String userName) { - User user = userService.getUserByName(userName); - if (!user.isAnonymous()) { - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri); - OrderedCollection following = new OrderedCollection(); - following.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString()); - following.setTotalItems(userService.getUserFriends(user.getUid()).size()); - following.setFirst(uriComponentsBuilder.path(String.format("/u/%s/followers", userName)).toUriString()); - return (OrderedCollection) Context.build(following); - } - throw new HttpNotFoundException(); - } - - @GetMapping(value = "/u/{userName}/following", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public OrderedCollectionPage getFollowingPage(@PathVariable String userName, - @RequestParam(required = false, defaultValue = "0") int page) { - User user = userService.getUserByName(userName); - if (!user.isAnonymous()) { - UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri); - uriComponentsBuilder.path(String.format("/u/%s/following", userName)); - List<User> following = userService.getUserFriends(user.getUid()); - Stream<User> followingPage = following.stream().skip(20 * page).limit(20); - - OrderedCollectionPage result = new OrderedCollectionPage(); - result.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString()); - result.setOrderedItems(followingPage.map(a -> { - Person follower = new Person(); - follower.setName(a.getName()); - follower.setPreferredUsername(a.getName()); - follower.setUrl(activityPubManager.personWebUri(a)); - return follower; - }).collect(Collectors.toList())); - boolean hasNext = following.size() <= 20 * page; - if (hasNext) { - result.setNext(uriComponentsBuilder.queryParam("page", page + 1).toUriString()); - } - return (OrderedCollectionPage) Context.build(result); - } - throw new HttpNotFoundException(); - } - - @GetMapping(value = "/n/{mid}-{rid}", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public Context showNote(@PathVariable int mid, @PathVariable int rid) { - if (rid > 0) { - // reply - return Context.build(activityPubManager.makeNote( - messagesService.getReply(mid, rid))); - } - return Context.build(activityPubManager.makeNote( - messagesService.getMessage(mid))); - } - - @PostMapping(value = "/api/inbox", consumes = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public ResponseEntity<Void> processInbox(@RequestBody Activity activity, - @RequestHeader(name = "Host") String host, - @RequestHeader(name = "Date") String date, - @RequestHeader(name = "Digest", required = false) String digest, - @RequestHeader(name = "Content-Type") String contentType, - @RequestHeader(name = "User-Agent", required = false) String userAgent, - @RequestHeader(name = "Accept-Encoding", required = false) String acceptEncoding, - @RequestHeader(name = "Signature", required = false) String signature) throws Exception { - UriComponents componentsBuilder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); - Map<String, String> headers = new HashMap<>(); - headers.put("host", host.split(":", 2)[0]); - headers.put("date", date); - headers.put("digest", digest); - headers.put("content-type", contentType); - headers.put("user-agent", userAgent); - headers.put("accept-encoding", acceptEncoding); - boolean valid = signatureManager.verifySignature(signature, URI.create(activity.getActor()), "POST", - componentsBuilder.getPath(), headers); - if (valid) { - if (activity instanceof Follow) { - Follow followRequest = (Follow) activity; - String actor = followRequest.getActor(); - Person follower = (Person) signatureManager.getContext(URI.create(actor)).orElseThrow(HttpBadRequestException::new); - applicationEventPublisher.publishEvent( - new FollowEvent(this, followRequest)); - return new ResponseEntity<>(HttpStatus.ACCEPTED); - - } - if (activity instanceof Undo) { - String follower = (String) ((Map) activity.getObject()).get("object"); - applicationEventPublisher.publishEvent(new UndoFollowEvent(this, activity.getActor(), follower)); - return new ResponseEntity<>(HttpStatus.OK); - } - if (activity instanceof Delete) { - if (activity.getObject() instanceof String) { - // Delete user - applicationEventPublisher.publishEvent(new DeleteUserEvent(this, (String)activity.getObject())); - return new ResponseEntity<>(HttpStatus.OK); - } - } - if (activity instanceof Create) { - if (activity.getObject() instanceof Map) { - Map<String, Object> note = (Map<String, Object>) activity.getObject(); - if (note.get("type").equals("Note")) { - URI noteId = URI.create((String) note.get("id")); - if (messagesService.replyExists(noteId)) { - return new ResponseEntity<>(HttpStatus.OK); - } else { - String inReplyTo = (String) note.get("inReplyTo"); - if (StringUtils.isNotBlank(inReplyTo)) { - if (inReplyTo.startsWith(baseUri)) { - UriComponents uri = UriComponentsBuilder.fromUriString(inReplyTo).build(); - String postId = uri.getPath().substring(uri.getPath().lastIndexOf('/') + 1).replace("-", "/"); - User user = new User(); - user.setUri(URI.create(activity.getActor())); - String attachment = StringUtils.EMPTY; - if (note.get("attachment") != null && ((List) note.get("attachment")).size() > 0) { - Map<String, Object> attachmentObj = (Map<String, Object>) ((List<Object>) note.get("attachment")).get(0); - attachment = (String) attachmentObj.get("url"); - } - CommandResult result = commandsManager.processCommand(user, String.format("#%s %s", postId, note.get("content")), URI.create(attachment)); - logger.info(jsonMapper.writeValueAsString(result)); - if (result.getNewMessage().isPresent()) { - messagesService.updateReplyUri(result.getNewMessage().get(), noteId); - return new ResponseEntity<>(HttpStatus.OK); - } else { - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); - } - } else { - Message reply = messagesService.getReplyByUri(inReplyTo); - if (reply != null) { - User user = new User(); - user.setUri(URI.create(activity.getActor())); - String attachment = StringUtils.EMPTY; - if (note.get("attachment") != null && ((List) note.get("attachment")).size() > 0) { - Map<String, Object> attachmentObj = (Map<String, Object>) ((List<Object>) note.get("attachment")).get(0); - attachment = (String) attachmentObj.get("url"); - } - CommandResult result = commandsManager.processCommand(user, String.format("#%d/%d %s", reply.getMid(), reply.getRid(), note.get("content")), URI.create(attachment)); - logger.info(jsonMapper.writeValueAsString(result)); - if (result.getNewMessage().isPresent()) { - messagesService.updateReplyUri(result.getNewMessage().get(), noteId); - return new ResponseEntity<>(HttpStatus.OK); - } else { - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); - } - } - } - } - } - } - } - } - if (activity instanceof Delete) { - Map<String, Object> tombstone = (Map<String, Object>) activity.getObject(); - if (tombstone.get("type").equals("Tombstone")) { - URI actor = URI.create(activity.getActor()); - URI reply = URI.create((String)tombstone.get("id")); - messagesService.deleteReply(actor, reply); - return new ResponseEntity<>(HttpStatus.OK); - } - } - if (activity instanceof Announce) { - logger.info("Announce: {}", jsonMapper.writeValueAsString(activity)); - return new ResponseEntity<>(HttpStatus.OK); - } - logger.warn("Unknown activity: {}", jsonMapper.writeValueAsString(activity)); - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); - } - return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); - } - @PostMapping(value = "/u/", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public User fetchUser(@RequestParam URI uri) { - Person person = (Person) signatureManager.getContext(uri).orElseThrow(HttpBadRequestException::new); - User user = new User(); - user.setUri(URI.create(person.getUrl())); - user.setName(person.getPreferredUsername()); - if (person.getIcon() != null) { - user.setAvatar(person.getIcon().getUrl()); - } - return user; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/Activity.java b/juick-server/src/main/java/com/juick/server/api/activity/model/Activity.java deleted file mode 100644 index ec126b88..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/Activity.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.juick.server.api.activity.model; - -public abstract class Activity extends Context { - - private String actor; - private Object object; - - public String getActor() { - return actor; - } - - public void setActor(String actor) { - this.actor = actor; - } - - public Object getObject() { - return object; - } - - public void setObject(Object object) { - this.object = object; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/Context.java b/juick-server/src/main/java/com/juick/server/api/activity/model/Context.java deleted file mode 100644 index 0df8f8c7..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/Context.java +++ /dev/null @@ -1,123 +0,0 @@ -package com.juick.server.api.activity.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.juick.server.api.activity.model.activities.*; -import com.juick.server.api.activity.model.objects.*; - -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property="type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = Create.class, name = "Create"), - @JsonSubTypes.Type(value = Delete.class, name = "Delete"), - @JsonSubTypes.Type(value = Follow.class, name = "Follow"), - @JsonSubTypes.Type(value = Accept.class, name = "Accept"), - @JsonSubTypes.Type(value = Undo.class, name = "Undo"), - @JsonSubTypes.Type(value = Like.class, name = "Like"), - @JsonSubTypes.Type(value = Block.class, name = "Block"), - @JsonSubTypes.Type(value = Announce.class, name = "Announce"), - @JsonSubTypes.Type(value = Activity.class, name = "Activity"), - @JsonSubTypes.Type(value = Image.class, name = "Image"), - @JsonSubTypes.Type(value = Key.class, name = "Key"), - @JsonSubTypes.Type(value = Link.class, name = "Link"), - @JsonSubTypes.Type(value = Hashtag.class, name = "Hashtag"), - @JsonSubTypes.Type(value = Mention.class, name = "Mention"), - @JsonSubTypes.Type(value = Note.class, name = "Note"), - @JsonSubTypes.Type(value = OrderedCollection.class, name = "OrderedCollection"), - @JsonSubTypes.Type(value = OrderedCollectionPage.class, name = "OrderedCollectionPage"), - @JsonSubTypes.Type(value = Person.class, name = "Person") -}) -public abstract class Context { - - private List<Object> context; - - private String id; - - private String name; - - private Instant published; - - private String url; - - private List<String> to; - - private List<Context> tags; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getType() { - return getClass().getSimpleName(); - } - - @JsonProperty("@context") - public List<Object> getContext() { - return context; - } - - public final static String ACTIVITY_STREAMS_URI = "https://www.w3.org/ns/activitystreams"; - public final static String SECURITY_URI = "https://w3id.org/security/v1"; - public final static String LD_JSON_MEDIA_TYPE = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""; - public final static String ACTIVITY_MEDIA_TYPE = "application/activity+json"; - public final static String ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE = ACTIVITY_MEDIA_TYPE + "; profile=\"https://www.w3.org/ns/activitystreams\""; - - public Instant getPublished() { - return published; - } - - public void setPublished(Instant published) { - this.published = published; - } - - public List<String> getTo() { - return to; - } - - public void setTo(List<String> to) { - this.to = to; - } - - public static Context build(Context response) { - response.context = new ArrayList(Arrays.asList(ACTIVITY_STREAMS_URI, SECURITY_URI)); - response.context.add(Collections.singletonMap("Hashtag", "as:Hashtag")); - return response; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - @JsonProperty("tag") - public List<Context> getTags() { - return tags; - } - - public void setTags(List<Context> tags) { - this.tags = tags; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Accept.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Accept.java deleted file mode 100644 index 1e0a9968..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Accept.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.activities; - -import com.juick.server.api.activity.model.Activity; - -public class Accept extends Activity { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Announce.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Announce.java deleted file mode 100644 index f2859404..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Announce.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.activities; - -import com.juick.server.api.activity.model.Activity; - -public class Announce extends Activity { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Block.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Block.java deleted file mode 100644 index 0e5a02d4..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Block.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.activities; - -import com.juick.server.api.activity.model.Activity; - -public class Block extends Activity { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Create.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Create.java deleted file mode 100644 index 52507373..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Create.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.activities; - -import com.juick.server.api.activity.model.Activity; - -public class Create extends Activity { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Delete.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Delete.java deleted file mode 100644 index f4392020..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Delete.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.activities; - -import com.juick.server.api.activity.model.Activity; - -public class Delete extends Activity { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Follow.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Follow.java deleted file mode 100644 index 573ecc6e..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Follow.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.activities; - -import com.juick.server.api.activity.model.Activity; - -public class Follow extends Activity { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Like.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Like.java deleted file mode 100644 index 3670293d..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Like.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.activities; - -import com.juick.server.api.activity.model.Activity; - -public class Like extends Activity { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Undo.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Undo.java deleted file mode 100644 index 4e87e9d0..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Undo.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.activities; - -import com.juick.server.api.activity.model.Activity; - -public class Undo extends Activity { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Hashtag.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Hashtag.java deleted file mode 100644 index 34e73be6..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Hashtag.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.juick.server.api.activity.model.Context; - -public class Hashtag extends Context { -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Image.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Image.java deleted file mode 100644 index e067f729..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Image.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.juick.server.api.activity.model.Context; - -public class Image extends Context { - private String mediaType; - - public String getMediaType() { - return mediaType; - } - - public void setMediaType(String mediaType) { - this.mediaType = mediaType; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Key.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Key.java deleted file mode 100644 index 075c51dd..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Key.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.juick.server.api.activity.model.Context; - -public class Key extends Context { - private String owner; - private String publicKeyPem; - - public String getOwner() { - return owner; - } - - public void setOwner(String owner) { - this.owner = owner; - } - - public String getPublicKeyPem() { - return publicKeyPem; - } - - public void setPublicKeyPem(String publicKeyPem) { - this.publicKeyPem = publicKeyPem; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Link.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Link.java deleted file mode 100644 index 0c4f26dc..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Link.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.juick.server.api.activity.model.Context; - -public class Link extends Context { - private String href; - - public String getHref() { - return href; - } - - public void setHref(String href) { - this.href = href; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Mention.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Mention.java deleted file mode 100644 index bcb52d37..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Mention.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class Mention extends Link { - @JsonCreator - public Mention(@JsonProperty("href") String href, @JsonProperty("name") String name) { - this.setHref(href); - this.setName(name); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Note.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Note.java deleted file mode 100644 index baad2d3b..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Note.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.fasterxml.jackson.annotation.JsonFormat; -import com.juick.server.api.activity.model.Context; - -import java.util.List; - -public class Note extends Context { - private String content; - private String attributedTo; - private String inReplyTo; - private List<Image> attachment; - private List<String> to; - private List<String> cc; - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getAttributedTo() { - return attributedTo; - } - - public void setAttributedTo(String attributedTo) { - this.attributedTo = attributedTo; - } - - @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) - public List<Image> getAttachment() { - return attachment; - } - - public void setAttachment(List<Image> attachment) { - this.attachment = attachment; - } - - public List<String> getTo() { - return to; - } - - public void setTo(List<String> to) { - this.to = to; - } - - public List<String> getCc() { - return cc; - } - - public void setCc(List<String> cc) { - this.cc = cc; - } - - public String getInReplyTo() { - return inReplyTo; - } - - public void setInReplyTo(String inReplyTo) { - this.inReplyTo = inReplyTo; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/OrderedCollection.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/OrderedCollection.java deleted file mode 100644 index 426cf331..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/OrderedCollection.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.juick.server.api.activity.model.Context; - -public class OrderedCollection extends Context { - - private int totalItems; - - public int getTotalItems() { - return totalItems; - } - - public void setTotalItems(int totalItems) { - this.totalItems = totalItems; - } - private String first; - - public String getFirst() { - return first; - } - - public void setFirst(String first) { - this.first = first; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/OrderedCollectionPage.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/OrderedCollectionPage.java deleted file mode 100644 index 601919ba..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/OrderedCollectionPage.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.juick.server.api.activity.model.Context; - -import java.util.List; - -public class OrderedCollectionPage extends Context { - - private String partOf; - - private String first; - - private String next; - - private String last; - - private List<? extends Context> orderedItems; - - public String getNext() { - return next; - } - - public void setNext(String next) { - this.next = next; - } - - public List<? extends Context> getOrderedItems() { - return orderedItems; - } - - public void setOrderedItems(List<? extends Context> orderedItems) { - this.orderedItems = orderedItems; - } - - public String getPartOf() { - return partOf; - } - - public void setPartOf(String partOf) { - this.partOf = partOf; - } - - public String getFirst() { - return first; - } - - public void setFirst(String first) { - this.first = first; - } - - public String getLast() { - return last; - } - - public void setLast(String last) { - this.last = last; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Person.java b/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Person.java deleted file mode 100644 index 2d3a45d7..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/objects/Person.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.juick.server.api.activity.model.objects; - -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.juick.server.api.activity.model.Context; - -public class Person extends Context { - - private String name; - private String preferredUsername; - private Image icon; - private String inbox; - private String outbox; - private String following; - private String followers; - private Key publicKey; - - @Override - public String getType() { - return "Person"; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) - public Image getIcon() { - return icon; - } - - public void setIcon(Image icon) { - this.icon = icon; - } - - public String getOutbox() { - return outbox; - } - - public void setOutbox(String outbox) { - this.outbox = outbox; - } - - public String getInbox() { - return inbox; - } - - public void setInbox(String inbox) { - this.inbox = inbox; - } - - public String getFollowing() { - return following; - } - - public void setFollowing(String following) { - this.following = following; - } - - public String getFollowers() { - return followers; - } - - public void setFollowers(String followers) { - this.followers = followers; - } - - @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) - public Key getPublicKey() { - return publicKey; - } - - public void setPublicKey(Key publicKey) { - this.publicKey = publicKey; - } - - public String getPreferredUsername() { - return preferredUsername; - } - - public void setPreferredUsername(String preferredUsername) { - this.preferredUsername = preferredUsername; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/apple/AppSiteAssociation.java b/juick-server/src/main/java/com/juick/server/api/apple/AppSiteAssociation.java deleted file mode 100644 index 81ab6960..00000000 --- a/juick-server/src/main/java/com/juick/server/api/apple/AppSiteAssociation.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.juick.server.api.apple; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; - -import java.util.Collections; -import java.util.List; - -@RestController -public class AppSiteAssociation { - @Value("${ios_app_id:}") - private String appId; - - @GetMapping("/.well-known/apple-app-site-association") - @ResponseBody - public SiteAssociations appSiteAssociations() { - WebCredentials webCredentials = new WebCredentials(); - webCredentials.setApps(Collections.singletonList(appId)); - SiteAssociations siteAssociations = new SiteAssociations(); - siteAssociations.setWebcredentials(webCredentials); - return siteAssociations; - } - - private class SiteAssociations { - private WebCredentials webcredentials; - - public WebCredentials getWebcredentials() { - return webcredentials; - } - - public void setWebcredentials(WebCredentials webcredentials) { - this.webcredentials = webcredentials; - } - } - - private class WebCredentials { - private List<String> apps; - - public List<String> getApps() { - return apps; - } - - public void setApps(List<String> apps) { - this.apps = apps; - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/hostmeta/HostMeta.java b/juick-server/src/main/java/com/juick/server/api/hostmeta/HostMeta.java deleted file mode 100644 index fa4d2a3f..00000000 --- a/juick-server/src/main/java/com/juick/server/api/hostmeta/HostMeta.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.juick.server.api.hostmeta; - -import com.cliqset.xrd.Link; -import com.cliqset.xrd.XRD; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.Collections; - -import static com.cliqset.xrd.XRDConstants.XRD_MEDIA_TYPE; - -@RestController -public class HostMeta { - @Value("${ap_base_uri:http://localhost:8080/}") - private String baseUri; - @GetMapping(value = "/.well-known/host-meta", produces = XRD_MEDIA_TYPE) - public XRD hostMetaResponse() { - Link webfinger = new Link(); - webfinger.setTemplate(String.format("%swebfinger?resource={uri}", baseUri)); - XRD xrd = new XRD(); - xrd.setLinks(Collections.singletonList(webfinger)); - return xrd; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/rss/Feeds.java b/juick-server/src/main/java/com/juick/server/api/rss/Feeds.java deleted file mode 100644 index c72f3a5e..00000000 --- a/juick-server/src/main/java/com/juick/server/api/rss/Feeds.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.api.rss; - -import com.juick.User; -import com.juick.server.util.HttpBadRequestException; -import com.juick.server.util.UserUtils; -import com.juick.service.MessagesService; -import com.juick.service.UserService; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.servlet.ModelAndView; - -import javax.inject.Inject; -import java.util.List; - -/** - * Created by vitalyster on 13.12.2016. - */ -@Controller -public class Feeds { - - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - - @RequestMapping(value = "/rss/{userName}/blog", method = RequestMethod.GET, produces = "text/xml; charset=utf-8") - public ModelAndView getBlog(@PathVariable String userName) { - User user = userService.getUserByName(userName); - if (!user.isAnonymous()) { - List<Integer> mids = messagesService.getUserBlog(user.getUid(), 0, 0); - ModelAndView modelAndView = new ModelAndView(); - modelAndView.setViewName("messagesView"); - modelAndView.addObject("user", user); - modelAndView.addObject("messages", messagesService.getMessages(UserUtils.getCurrentUser(), mids)); - return modelAndView; - } - throw new HttpBadRequestException(); - } - - @RequestMapping(value = "/rss/", method = RequestMethod.GET, produces = "text/xml; charset=utf-8") - public ModelAndView getLast(@RequestParam(value = "hours", required = false, defaultValue = "0") Integer hours) { - List<Integer> mids = messagesService.getLastMessages(hours); - ModelAndView modelAndView = new ModelAndView(); - modelAndView.setViewName("messagesView"); - modelAndView.addObject("messages", messagesService.getMessages(UserUtils.getCurrentUser(),mids)); - return modelAndView; - } - @RequestMapping(value = "/rss/comments", method = RequestMethod.GET, produces = "text/xml; charset=utf-8") - public ModelAndView getLastReplies(@RequestParam(value = "hours", required = false, defaultValue = "0") Integer hours) { - ModelAndView modelAndView = new ModelAndView(); - modelAndView.setViewName("repliesView"); - modelAndView.addObject("messages", messagesService.getLastReplies(hours)); - return modelAndView; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/rss/MessagesView.java b/juick-server/src/main/java/com/juick/server/api/rss/MessagesView.java deleted file mode 100644 index c0ae4a97..00000000 --- a/juick-server/src/main/java/com/juick/server/api/rss/MessagesView.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.api.rss; - -import com.juick.Message; -import com.juick.User; -import com.juick.server.api.rss.extension.JuickModule; -import com.juick.server.api.rss.extension.JuickModuleImpl; -import com.juick.util.MessageUtils; -import com.rometools.modules.atom.modules.AtomLinkModule; -import com.rometools.modules.atom.modules.AtomLinkModuleImpl; -import com.rometools.modules.mediarss.MediaEntryModuleImpl; -import com.rometools.modules.mediarss.MediaModule; -import com.rometools.modules.mediarss.MediaModuleImpl; -import com.rometools.modules.mediarss.types.MediaContent; -import com.rometools.modules.mediarss.types.Metadata; -import com.rometools.modules.mediarss.types.Thumbnail; -import com.rometools.modules.mediarss.types.UrlReference; -import com.rometools.rome.feed.atom.Link; -import com.rometools.rome.feed.rss.*; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.web.servlet.view.feed.AbstractRssFeedView; - -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * Created by vitalyster on 13.12.2016. - */ -public class MessagesView extends AbstractRssFeedView { - - private static final Logger logger = LoggerFactory.getLogger(MessagesView.class); - - @PostConstruct - public void init() { - setContentType("application/rss+xml;charset=UTF-8"); - } - - @SuppressWarnings("unchecked") - @Override - protected List<Item> buildFeedItems(@Nonnull Map<String, Object> model, - @Nonnull HttpServletRequest request, - @Nonnull HttpServletResponse response) { - List<Message> msgs = (List<Message>)model.get("messages"); - return msgs.stream().map(this::createRssItem).collect(Collectors.toList()); - } - - @Override - protected void buildFeedMetadata(Map<String, Object> model, Channel feed, HttpServletRequest request) { - Object userObj = model.get("user"); - if (userObj != null) { - User user = (User) userObj; - feed.setDescription(String.format("The latest messages by @%s at Juick", user.getName())); - String title = String.format("%s - Juick", user.getName()); - feed.setTitle(title); - String link = String.format("http://juick.com/%s/", user.getName()); - feed.setLink(link); - Image rssImage = new Image(); - rssImage.setUrl(String.format("http://juick.com/a/%d.png", user.getUid())); - rssImage.setTitle(title); - rssImage.setLink(link); - feed.setImage(rssImage); - String href = String.format("http://rss.juick.com/%s/blog", user.getName()); - AtomLinkModule atomLinkModule = new AtomLinkModuleImpl(); - Link atomLink = new Link(); - atomLink.setHref(href); - atomLink.setType("application/rss+xml"); - atomLink.setRel("self"); - atomLinkModule.setLinks(Collections.singletonList(atomLink)); - - feed.getModules().add(atomLinkModule); - } else { - feed.setDescription("The latest messages at Juick"); - feed.setLink("http://juick.com/"); - feed.setTitle("Juick"); - } - - MediaModule mediaModule = new MediaModuleImpl(); - feed.getModules().add(mediaModule); - - - } - - private Item createRssItem(Message msg) { - Item item = new Item(); - String messageUrl = String.format("http://juick.com/%s/%d", msg.getUser().getName(), msg.getMid()); - String messageTitle = String.format("@%s: %s", msg.getUser().getName(), MessageUtils.getTagsString(msg)); - boolean isCode = msg.getTags().stream().anyMatch(t -> t.getName().equals("code")); - String messageDescription = isCode ? MessageUtils.formatMessageCode(StringUtils.defaultString(msg.getText())) - : MessageUtils.formatMessage(StringUtils.defaultString(msg.getText())); - item.setLink(messageUrl); - //item.setGuid(messageUrl); - item.setTitle(messageTitle); - Description description = new Description(); - description.setType("text/html"); - description.setValue(messageDescription); - item.setDescription(description); - item.setPubDate(Date.from(msg.getTimestamp())); - item.setComments(messageUrl); - msg.getTags().stream().map(t -> { - Category category = new Category(); - category.setValue(t.getName()); - return category; - }).forEach(c -> item.getCategories().add(c)); - JuickModule juickModule = new JuickModuleImpl(); - juickModule.setUid(msg.getUser().getUid()); - item.getModules().add(juickModule); - if (StringUtils.isNotEmpty(msg.getAttachmentType())) { - String type = msg.getAttachmentType().equals("jpg") ? "image/jpeg" : "image/png"; - MediaEntryModuleImpl module = new MediaEntryModuleImpl(); - try { - UrlReference reference = new UrlReference(MessageUtils.attachmentUrl(msg)); - MediaContent mediaContent = new MediaContent(reference); - mediaContent.setType(type); - Metadata metadata = new Metadata(); - metadata.setThumbnail(new Thumbnail[]{new Thumbnail(new URI(msg.getPhoto().getThumbnail()))}); - module.setMetadata(metadata); - module.setMediaContents(new MediaContent[]{mediaContent}); - item.getModules().add(module); - } catch (URISyntaxException e) { - logger.error("Invalid URI", e); - } - - } - return item; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/rss/RepliesView.java b/juick-server/src/main/java/com/juick/server/api/rss/RepliesView.java deleted file mode 100644 index a0ab801e..00000000 --- a/juick-server/src/main/java/com/juick/server/api/rss/RepliesView.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.api.rss; - -import com.juick.model.ResponseReply; -import com.juick.util.MessageUtils; -import com.rometools.modules.mediarss.MediaEntryModuleImpl; -import com.rometools.modules.mediarss.MediaModule; -import com.rometools.modules.mediarss.MediaModuleImpl; -import com.rometools.modules.mediarss.types.MediaContent; -import com.rometools.modules.mediarss.types.Metadata; -import com.rometools.modules.mediarss.types.Thumbnail; -import com.rometools.modules.mediarss.types.UrlReference; -import com.rometools.rome.feed.rss.Channel; -import com.rometools.rome.feed.rss.Description; -import com.rometools.rome.feed.rss.Item; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.web.servlet.view.feed.AbstractRssFeedView; - -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * Created by vitalyster on 13.12.2016. - */ -public class RepliesView extends AbstractRssFeedView { - - private static final Logger logger = LoggerFactory.getLogger(RepliesView.class); - - @PostConstruct - public void init() { - setContentType("application/rss+xml;charset=UTF-8"); - } - - @SuppressWarnings("unchecked") - @Override - protected @Nonnull List<Item> buildFeedItems(@Nonnull Map<String, Object> model, - @Nonnull HttpServletRequest request, - @Nonnull HttpServletResponse response) { - List<ResponseReply> msgs = (List<ResponseReply>)model.get("messages"); - return msgs.stream().map(this::createRssItem).collect(Collectors.toList()); - } - - @Override - protected void buildFeedMetadata(Map<String, Object> model, Channel feed, HttpServletRequest request) { - feed.setTitle("Juick"); - feed.setLink("http://juick.com/"); - feed.setDescription("The latest comments at Juick"); - MediaModule mediaModule = new MediaModuleImpl(); - feed.getModules().add(mediaModule); - } - - private Item createRssItem(ResponseReply msg) { - Item item = new Item(); - String messageUrl = String.format("http://juick.com/m/%d#%d", msg.getMid(), msg.getRid()); - String messageTitle = String.format("@%s:", msg.getUname()); - String messageDescription = msg.isHtml() ? msg.getDescription() : MessageUtils.formatMessage(msg.getDescription()); - item.setLink(messageUrl); - //item.setGuid(messageUrl); - item.setTitle(messageTitle); - Description description = new Description(); - description.setType("text/html"); - description.setValue(messageDescription); - item.setDescription(description); - item.setPubDate(msg.getPubDate()); - if (StringUtils.isNotEmpty(msg.getAttachmentType())) { - String type = msg.getAttachmentType().equals("jpg") ? "image/jpeg" : "image/png"; - MediaEntryModuleImpl module = new MediaEntryModuleImpl(); - try { - UrlReference reference = new UrlReference( - String.format("http://i.juick.com/photos-1024/%d-%d.%s", msg.getMid(), msg.getRid(), type)); - MediaContent mediaContent = new MediaContent(reference); - mediaContent.setType(type); - Metadata metadata = new Metadata(); - metadata.setThumbnail(new Thumbnail[]{new Thumbnail( - new URI(String.format("http://i.juick.com/ps/%d-%d.%s", msg.getMid(), msg.getRid(), type)))}); - module.setMetadata(metadata); - module.setMediaContents(new MediaContent[]{mediaContent}); - item.getModules().add(module); - } catch (URISyntaxException e) { - logger.error("Invalid URI", e); - } - - } - return item; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModule.java b/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModule.java deleted file mode 100644 index a4198518..00000000 --- a/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModule.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.api.rss.extension; - -import com.rometools.rome.feed.module.Module; - -/** - * Created by vitalyster on 13.12.2016. - */ -public interface JuickModule extends Module { - - String URI = "http://juick.com/"; - - Integer getUid(); - - void setUid(Integer uid); - -} diff --git a/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleGenerator.java b/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleGenerator.java deleted file mode 100644 index 90dec35f..00000000 --- a/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleGenerator.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.api.rss.extension; - -import com.rometools.rome.feed.module.Module; -import com.rometools.rome.io.ModuleGenerator; -import org.jdom2.Element; -import org.jdom2.Namespace; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -/** - * Created by vt on 13/12/2016. - */ -public class JuickModuleGenerator implements ModuleGenerator { - - private static final Namespace JUICK_NS = Namespace.getNamespace("juick", JuickModule.URI); - - @Override - public String getNamespaceUri() { - return JuickModule.URI; - } - - private static final Set<Namespace> NAMESPACES; - - static { - Set<Namespace> nss = new HashSet<>(); - nss.add(JUICK_NS); - NAMESPACES = Collections.unmodifiableSet(nss); - } - - @Override - public Set<Namespace> getNamespaces() { - return NAMESPACES; - } - - @Override - public void generate(Module module, Element element) { - // this is not necessary, it is done to avoid the namespace definition in every item. - Element root = element; - while (root.getParent()!=null && root.getParent() instanceof Element) { - root = element.getParentElement(); - } - root.addNamespaceDeclaration(JUICK_NS); - - JuickModule juickModule = (JuickModule) module; - if (juickModule.getUid() > 0) { - Element user = new Element("user", JUICK_NS); - user.setAttribute("uid", String.valueOf(juickModule.getUid()), JUICK_NS); - element.addContent(user); - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleImpl.java b/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleImpl.java deleted file mode 100644 index dbdd8c85..00000000 --- a/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleImpl.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.api.rss.extension; - -import com.rometools.rome.feed.CopyFrom; -import com.rometools.rome.feed.module.ModuleImpl; - -/** - * Created by vitalyster on 13.12.2016. - */ -public class JuickModuleImpl extends ModuleImpl implements JuickModule { - - private Integer uid; - - public JuickModuleImpl() { - super(JuickModule.class, JuickModule.URI); - } - - @Override - public Integer getUid() { - return uid; - } - - @Override - public void setUid(Integer uid) { - this.uid = uid; - } - - @Override - public Class<? extends CopyFrom> getInterface() { - return JuickModule.class; - } - - @Override - public void copyFrom(CopyFrom obj) { - JuickModule juickModule = (JuickModule) obj; - setUid(juickModule.getUid()); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleParser.java b/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleParser.java deleted file mode 100644 index a3d0e175..00000000 --- a/juick-server/src/main/java/com/juick/server/api/rss/extension/JuickModuleParser.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.api.rss.extension; - -import com.rometools.rome.feed.module.Module; -import com.rometools.rome.io.ModuleParser; -import org.apache.commons.lang3.math.NumberUtils; -import org.jdom2.Element; - -import java.util.Locale; - -/** - * Created by vitalyster on 13.12.2016. - */ -public class JuickModuleParser implements ModuleParser { - @Override - public String getNamespaceUri() { - return JuickModule.URI; - } - - @Override - public Module parse(Element element, Locale locale) { - JuickModuleImpl juickModule = new JuickModuleImpl(); - juickModule.setUid(NumberUtils.toInt(element.getAttributeValue("uid", JuickModule.URI), 0)); - return juickModule; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/webfinger/Resource.java b/juick-server/src/main/java/com/juick/server/api/webfinger/Resource.java deleted file mode 100644 index 71a0ca31..00000000 --- a/juick-server/src/main/java/com/juick/server/api/webfinger/Resource.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.juick.server.api.webfinger; - -import com.juick.User; -import com.juick.server.api.webfinger.model.Account; -import com.juick.server.api.webfinger.model.Link; -import com.juick.server.util.HttpNotFoundException; -import com.juick.service.UserService; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.util.UriComponentsBuilder; -import rocks.xmpp.addr.Jid; - -import javax.inject.Inject; -import java.util.Collections; - -import static com.juick.server.api.activity.model.Context.ACTIVITY_MEDIA_TYPE; - -@RestController -public class Resource { - @Inject - private UserService userService; - @Value("${web_domain:localhost}") - private String domain; - @Value("${ap_base_uri:http://localhost:8080/}") - private String baseUri; - - @GetMapping("/.well-known/webfinger") - public Account getWebResource(@RequestParam String resource) { - if (resource.startsWith("acct:")) { - Jid account = Jid.of(resource.substring(5)); - if (account.getDomain().equals(domain)) { - User user = userService.getUserByName(account.getLocal()); - if (!user.isAnonymous()) { - UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(baseUri); - builder.path(String.format("/u/%s", user.getName())); - Link blog = new Link(); - blog.setRel("self"); - blog.setType(ACTIVITY_MEDIA_TYPE); - blog.setHref(builder.toUriString()); - Account result = new Account(); - result.setSubject(resource); - result.setLinks(Collections.singletonList(blog)); - return result; - } - } - } - throw new HttpNotFoundException(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/webfinger/model/Account.java b/juick-server/src/main/java/com/juick/server/api/webfinger/model/Account.java deleted file mode 100644 index 892fa303..00000000 --- a/juick-server/src/main/java/com/juick/server/api/webfinger/model/Account.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.juick.server.api.webfinger.model; - -import java.util.List; - -public class Account { - private String subject; - private List<Link> links; - - public String getSubject() { - return subject; - } - - public void setSubject(String subject) { - this.subject = subject; - } - - public List<Link> getLinks() { - return links; - } - - public void setLinks(List<Link> links) { - this.links = links; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/webfinger/model/Link.java b/juick-server/src/main/java/com/juick/server/api/webfinger/model/Link.java deleted file mode 100644 index 48e7ab67..00000000 --- a/juick-server/src/main/java/com/juick/server/api/webfinger/model/Link.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.juick.server.api.webfinger.model; - -public class Link { - private String rel; - private String type; - private String href; - - public String getRel() { - return rel; - } - - public void setRel(String rel) { - this.rel = rel; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getHref() { - return href; - } - - public void setHref(String href) { - this.href = href; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/webhooks/TelegramWebhook.java b/juick-server/src/main/java/com/juick/server/api/webhooks/TelegramWebhook.java deleted file mode 100644 index 7a5cebda..00000000 --- a/juick-server/src/main/java/com/juick/server/api/webhooks/TelegramWebhook.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.api.webhooks; - -import com.juick.server.TelegramBotManager; -import com.pengrad.telegrambot.BotUtils; -import com.pengrad.telegrambot.model.Update; -import org.apache.commons.io.IOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; -import springfox.documentation.annotations.ApiIgnore; - -import javax.inject.Inject; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; - -/** - * Created by vt on 24/11/2016. - */ -@ApiIgnore -@RestController -@ConditionalOnProperty({"telegram_token"}) -public class TelegramWebhook { - private static final Logger logger = LoggerFactory.getLogger(TelegramWebhook.class); - @Inject - private TelegramBotManager telegramBotManager; - - @RequestMapping(value = "/api/tlgmbtwbhk", method = RequestMethod.POST) - @ResponseStatus(value = HttpStatus.OK) - public void processUpdate(InputStream body) throws Exception { - String data = IOUtils.toString(body, StandardCharsets.UTF_8); - logger.info("Telegram update: {}", data); - Update update = BotUtils.parseUpdate(data); - telegramBotManager.processUpdate(update); - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/Info.java b/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/Info.java deleted file mode 100644 index c12df55f..00000000 --- a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/Info.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.juick.server.api.xnodeinfo2; - -import com.juick.server.api.xnodeinfo2.model.*; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.inject.Inject; -import java.time.Instant; -import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; -import java.util.Arrays; - -@RestController -public class Info { - @Value("${ap_base_uri:http://localhost:8080/}") - private String baseUri; - @Inject - private JdbcTemplate jdbcTemplate; - - @GetMapping("/.well-known/x-nodeinfo2") - public NodeInfo showNodeInfo() { - NodeInfo nodeInfo = new NodeInfo(); - Server server = new Server(); - server.setBaseUrl(baseUri); - server.setName("Juick"); - server.setSoftware("Juick"); - server.setVersion("2.x"); - nodeInfo.setServer(server); - nodeInfo.setProtocols(Arrays.asList("xmpp", "activitypub", "smtp")); - ServiceInfo serviceInfo = new ServiceInfo(); - serviceInfo.setInbound(Arrays.asList("jabber", "mastodon", "email", "telegram")); - serviceInfo.setOutbound(Arrays.asList("jabber", "mastodon", "telegram", "twitter", "email", "rss")); - nodeInfo.setServices(serviceInfo); - UserStats userStats = new UserStats(); - userStats.setTotal(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM users WHERE banned=0", Integer.class)); - userStats.setActiveMonth(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM users WHERE banned=0 AND last_seen > ?", - Integer.class, ZonedDateTime.now().minus(1, ChronoUnit.MONTHS).toInstant())); - userStats.setActiveHalfyear(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM users WHERE banned=0 AND last_seen > ?", - Integer.class, ZonedDateTime.now().minus(6, ChronoUnit.MONTHS).toInstant())); - Usage usage = new Usage(); - usage.setUsers(userStats); - usage.setLocalPosts(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM messages", - Integer.class)); - usage.setLocalComments(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM replies", - Integer.class)); - nodeInfo.setUsage(usage); - return nodeInfo; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/NodeInfo.java b/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/NodeInfo.java deleted file mode 100644 index 06fe354f..00000000 --- a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/NodeInfo.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.juick.server.api.xnodeinfo2.model; - -import java.util.List; - -public class NodeInfo { - - private Server server; - - private List<String> protocols; - - private ServiceInfo services; - - public String getVersion() { - return "1.0"; - } - - public Server getServer() { - return server; - } - - public void setServer(Server server) { - this.server = server; - } - - public List<String> getProtocols() { - return protocols; - } - - public void setProtocols(List<String> protocols) { - this.protocols = protocols; - } - - public ServiceInfo getServices() { - return services; - } - - public void setServices(ServiceInfo services) { - this.services = services; - } - - public boolean getOpenRegistrations() { - return true; - } - - private Usage usage; - - public Usage getUsage() { - return usage; - } - - public void setUsage(Usage usage) { - this.usage = usage; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/Server.java b/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/Server.java deleted file mode 100644 index a772d268..00000000 --- a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/Server.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.juick.server.api.xnodeinfo2.model; - -public class Server { - private String baseUrl; - private String name; - private String software; - private String version; - - public String getBaseUrl() { - return baseUrl; - } - - public void setBaseUrl(String baseUrl) { - this.baseUrl = baseUrl; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getSoftware() { - return software; - } - - public void setSoftware(String software) { - this.software = software; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/ServiceInfo.java b/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/ServiceInfo.java deleted file mode 100644 index 5b6d2baa..00000000 --- a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/ServiceInfo.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.juick.server.api.xnodeinfo2.model; - -import java.util.List; - -public class ServiceInfo { - private List<String> inbound; - private List<String> outbound; - - public List<String> getInbound() { - return inbound; - } - - public void setInbound(List<String> inbound) { - this.inbound = inbound; - } - - public List<String> getOutbound() { - return outbound; - } - - public void setOutbound(List<String> outbound) { - this.outbound = outbound; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/Usage.java b/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/Usage.java deleted file mode 100644 index e04ea48b..00000000 --- a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/Usage.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.juick.server.api.xnodeinfo2.model; - -public class Usage { - private UserStats users; - private int localPosts; - private int localComments; - - public UserStats getUsers() { - return users; - } - - public void setUsers(UserStats users) { - this.users = users; - } - - public int getLocalPosts() { - return localPosts; - } - - public void setLocalPosts(int localPosts) { - this.localPosts = localPosts; - } - - public int getLocalComments() { - return localComments; - } - - public void setLocalComments(int localComments) { - this.localComments = localComments; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/UserStats.java b/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/UserStats.java deleted file mode 100644 index 515661e3..00000000 --- a/juick-server/src/main/java/com/juick/server/api/xnodeinfo2/model/UserStats.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.juick.server.api.xnodeinfo2.model; - -public class UserStats { - private int total; - private int activeHalfyear; - private int activeMonth; - - public int getTotal() { - return total; - } - - public void setTotal(int total) { - this.total = total; - } - - public int getActiveHalfyear() { - return activeHalfyear; - } - - public void setActiveHalfyear(int activeHalfyear) { - this.activeHalfyear = activeHalfyear; - } - - public int getActiveMonth() { - return activeMonth; - } - - public void setActiveMonth(int activeMonth) { - this.activeMonth = activeMonth; - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/ActivityPubClientConfig.java b/juick-server/src/main/java/com/juick/server/configuration/ActivityPubClientConfig.java deleted file mode 100644 index 9bc1b656..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/ActivityPubClientConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.juick.server.configuration; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.client.SimpleClientHttpRequestFactory; -import org.springframework.web.client.RestTemplate; - -import javax.inject.Inject; - -@Configuration -public class ActivityPubClientConfig { - @Inject - ActivityPubClientErrorHandler activityPubClientErrorHandler; - @Bean - public RestTemplate apClient() { - SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); - requestFactory.setOutputStreaming(false); - RestTemplate restTemplate = new RestTemplate(requestFactory); - restTemplate.setErrorHandler(activityPubClientErrorHandler); - return restTemplate; - } -}
\ No newline at end of file diff --git a/juick-server/src/main/java/com/juick/server/configuration/ActivityPubClientErrorHandler.java b/juick-server/src/main/java/com/juick/server/configuration/ActivityPubClientErrorHandler.java deleted file mode 100644 index e535b3e5..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/ActivityPubClientErrorHandler.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.juick.server.configuration; - -import com.juick.service.activities.DeleteUserEvent; -import org.apache.commons.io.IOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.stereotype.Component; -import org.springframework.web.client.DefaultResponseErrorHandler; - -import javax.annotation.Nonnull; -import javax.inject.Inject; -import java.io.IOException; -import java.net.URI; -import java.nio.charset.StandardCharsets; - -@Component -public class ActivityPubClientErrorHandler extends DefaultResponseErrorHandler { - private static final Logger logger = LoggerFactory.getLogger(ActivityPubClientErrorHandler.class); - - @Inject - private ApplicationEventPublisher applicationEventPublisher; - @Override - public void handleError(URI contextUri, HttpMethod method, @Nonnull ClientHttpResponse response) throws IOException { - logger.warn("HTTP ERROR {} {} : {}", response.getStatusCode().value(), - response.getStatusText(), IOUtils.toString(response.getBody(), StandardCharsets.UTF_8)); - if (response.getStatusCode().equals(HttpStatus.GONE)) { - logger.warn("Server report {} is gone, deleting", contextUri.toASCIIString()); - applicationEventPublisher.publishEvent(new DeleteUserEvent(this, contextUri.toASCIIString())); - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java b/juick-server/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java deleted file mode 100644 index 5a5d2c7b..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/ApiAppConfiguration.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.configuration; - -import com.juick.server.WebsocketManager; -import com.juick.server.api.rss.MessagesView; -import com.juick.server.api.rss.RepliesView; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.Ordered; -import org.springframework.scheduling.annotation.EnableAsync; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.springframework.web.servlet.view.BeanNameViewResolver; -import org.springframework.web.servlet.view.feed.AbstractRssFeedView; -import org.springframework.web.socket.config.annotation.EnableWebSocket; -import org.springframework.web.socket.config.annotation.ServletWebSocketHandlerRegistry; -import org.springframework.web.socket.config.annotation.WebSocketConfigurer; -import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; -import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean; - -import javax.annotation.Nonnull; -import javax.inject.Inject; - -/** - * Created by aalexeev on 11/12/16. - */ -@Configuration -@EnableAsync(proxyTargetClass = true) -@EnableScheduling -@EnableWebSocket -public class ApiAppConfiguration implements WebMvcConfigurer, WebSocketConfigurer { - @Inject - private WebsocketManager websocketManager; - - @Override - public void registerWebSocketHandlers(@Nonnull WebSocketHandlerRegistry registry) { - ((ServletWebSocketHandlerRegistry) registry).setOrder(Ordered.HIGHEST_PRECEDENCE); - registry.addHandler(websocketManager, "/ws/**").setAllowedOrigins("*"); - } - - @Bean - public ServletServerContainerFactoryBean createWebSocketContainer() { - ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); - container.setMaxTextMessageBufferSize(8192); - container.setMaxBinaryMessageBufferSize(8192); - return container; - } - @Bean - public BeanNameViewResolver beanNameViewResolver() { - return new BeanNameViewResolver(); - } - @Bean - AbstractRssFeedView messagesView() { - return new MessagesView(); - } - @Bean - AbstractRssFeedView repliesView() { - return new RepliesView(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/BaseWebConfiguration.java b/juick-server/src/main/java/com/juick/server/configuration/BaseWebConfiguration.java deleted file mode 100644 index 23a35384..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/BaseWebConfiguration.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.configuration; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.annotation.SchedulingConfigurer; -import org.springframework.scheduling.config.ScheduledTaskRegistrar; -import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter; - -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * Created by vitalyster on 28.06.2016. - */ -@Configuration -public class BaseWebConfiguration implements WebMvcConfigurer, SchedulingConfigurer { - - - @Override - public void configurePathMatch(PathMatchConfigurer configurer) { - configurer.setUseSuffixPatternMatch(false); - } - - @Bean - public ResourceUrlEncodingFilter resourceUrlEncodingFilter() { - return new ResourceUrlEncodingFilter(); - } - - @Override - public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { - taskRegistrar.setScheduler(taskExecutor()); - } - - @Bean(destroyMethod="shutdown") - public Executor taskExecutor() { - return Executors.newScheduledThreadPool(100); - } - - @Bean - public ExecutorService executorService() { - return Executors.newCachedThreadPool(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/SapeConfiguration.java b/juick-server/src/main/java/com/juick/server/configuration/SapeConfiguration.java deleted file mode 100644 index 9727fbb1..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/SapeConfiguration.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.configuration; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import ru.sape.Sape; - -/** - * Created by vitalyster on 29.03.2017. - */ -@Configuration -@ConditionalOnProperty("sape_user") -public class SapeConfiguration { - @Value("${sape_user:}") - private String token; - - @Bean - public Sape sape() { - return new Sape(token, "juick.com", 2000, 3600); - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/SecurityConfig.java b/juick-server/src/main/java/com/juick/server/configuration/SecurityConfig.java deleted file mode 100644 index f02083d5..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/SecurityConfig.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.configuration; - -import com.juick.service.UserService; -import com.juick.service.security.HashParamAuthenticationFilter; -import com.juick.service.security.JuickUserDetailsService; -import com.juick.service.security.deprecated.RequestParamHashRememberMeServices; -import com.juick.service.security.entities.JuickUser; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.Order; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.builders.WebSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.authentication.HttpStatusEntryPoint; -import org.springframework.security.web.authentication.RememberMeServices; -import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices; -import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; - -import javax.annotation.Resource; -import javax.inject.Inject; -import java.util.Arrays; -import java.util.Collections; -import java.util.concurrent.TimeUnit; - -/** - * Created by aalexeev on 11/21/16. - */ -@EnableWebSecurity -public class SecurityConfig { - @Resource - private UserService userService; - @Value("${auth_remember_me_key:secret}") - private String rememberMeKey; - @Value("${web_domain:localhost}") - private String webDomain; - - private static final String COOKIE_NAME = "juick-remember-me"; - - @Bean - public UserDetailsService userDetailsService() { - return new JuickUserDetailsService(userService); - } - - @Configuration - @Order(1) - public static class ApiConfig extends WebSecurityConfigurerAdapter { - @Value("${auth_remember_me_key:secret}") - private String rememberMeKey; - @Value("${web_domain:localhost}") - private String webDomain; - @Resource - private UserService userService; - ApiConfig() { - super(true); - } - @Bean - RememberMeServices apiTokenServices(){ - return new RequestParamHashRememberMeServices(rememberMeKey, userService); - } - @Bean - public HashParamAuthenticationFilter apiAuthenticationFilter() { - return new HashParamAuthenticationFilter(userService, apiTokenServices()); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - http.antMatcher("/api/**") - .addFilterBefore(apiAuthenticationFilter(), BasicAuthenticationFilter.class) - .authorizeRequests() - .antMatchers(HttpMethod.OPTIONS).permitAll() - .antMatchers("/api/", "/api/messages", "/api/messages/discussions", "/api/users", "/api/thread", "/api/tags", "/api/tlgmbtwbhk", "/api/fbwbhk", - "/api/skypebotendpoint", "/api/_fblogin", "/api/_vklogin", "/api/_tglogin", "/api/inbox", "/api/u/**", "/.well-known/webfinger", "/.well-known/x-nodeinfo2", "/rss/**", "/api/events").permitAll() - .anyRequest().hasRole("USER") - .and() - .anonymous().principal(JuickUser.ANONYMOUS_USER).authorities(JuickUser.ANONYMOUS_AUTHORITY) - .and() - .httpBasic().authenticationEntryPoint(juickAuthenticationEntryPoint()) - .and().cors().configurationSource(corsConfigurationSource()) - .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and().exceptionHandling().authenticationEntryPoint(juickAuthenticationEntryPoint()) - .and() - .rememberMe() - .alwaysRemember(true) - .tokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(6 * 30)) - .rememberMeServices(apiTokenServices()) - .key(rememberMeKey) - .and() - .headers().defaultsDisabled().cacheControl(); - } - - @Bean - public AuthenticationEntryPoint juickAuthenticationEntryPoint() { - return new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED); - } - - @Bean - public CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration configuration = new CorsConfiguration(); - - configuration.setAllowedOrigins(Collections.singletonList("*")); - configuration.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "OPTIONS", "DELETE")); - configuration.setAllowedHeaders(Collections.singletonList("*")); - - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/api/**", configuration); - - return source; - } - @Override - public void configure(WebSecurity web) { - web.debug(false); - web.ignoring().antMatchers("/api/v2/api-docs", "/api/configuration/ui", "/api/swagger-resources/**", - "/api/configuration/**", "/swagger-ui.html", "/webjars/**", "/h2-console/**"); - } - } - - @Configuration - public static class WebConfig extends WebSecurityConfigurerAdapter { - @Value("${auth_remember_me_key:secret}") - private String rememberMeKey; - @Value("${web_domain:localhost}") - private String webDomain; - @Resource - private UserService userService; - @Inject - private UserDetailsService userDetailsService; - @Bean - @Qualifier("www") - public HashParamAuthenticationFilter wwwAuthenticationFilter() { - return new HashParamAuthenticationFilter(userService, hashCookieServices()); - } - @Bean - @Qualifier("www") - public RememberMeServices hashCookieServices() { - TokenBasedRememberMeServices services = new TokenBasedRememberMeServices( - rememberMeKey, userDetailsService); - - services.setCookieName(COOKIE_NAME); - services.setCookieDomain(webDomain); - services.setAlwaysRemember(true); - services.setTokenValiditySeconds(6 * 30 * 24 * 3600); - services.setUseSecureCookie(false); // TODO set true if https is supports - - return services; - } - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .addFilterBefore(wwwAuthenticationFilter(), BasicAuthenticationFilter.class) - .authorizeRequests() - .antMatchers("/settings", "/pm/**", "/**/bl", "/_twitter", "/post", "/post2", "/comment") - .authenticated() - .anyRequest().permitAll() - .and() - .anonymous().principal(JuickUser.ANONYMOUS_USER).authorities(JuickUser.ANONYMOUS_AUTHORITY) - .and() - .sessionManagement().invalidSessionUrl("/") - .and() - .logout() - .invalidateHttpSession(true) - .logoutUrl("/logout") - .logoutSuccessUrl("/login?logout") - .deleteCookies("hash", COOKIE_NAME) - .and() - .formLogin() - .loginPage("/login") - .permitAll() - .defaultSuccessUrl("/") - .loginProcessingUrl("/login") - .usernameParameter("username") - .passwordParameter("password") - .failureUrl("/login?error=1") - .and() - .rememberMe() - .rememberMeCookieDomain(webDomain).key(rememberMeKey) - .rememberMeServices(hashCookieServices()) - .and() - .csrf().disable() - .headers().defaultsDisabled().cacheControl(); - } - @Override - public void configure(WebSecurity web) { - web.debug(false); - web.ignoring().antMatchers("/style*.css", "/scripts*.js", "/h2-console/**", "/.well-known/**", "/ws/**"); - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/StorageConfiguration.java b/juick-server/src/main/java/com/juick/server/configuration/StorageConfiguration.java deleted file mode 100644 index 4101f37d..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/StorageConfiguration.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.juick.server.configuration; - -import com.juick.service.ImagesService; -import com.juick.service.ImagesServiceImpl; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class StorageConfiguration { - - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String imgDir; - @Bean - public ImagesService imagesService() { - return new ImagesServiceImpl(imgDir, tmpDir); - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/TelegramConfig.java b/juick-server/src/main/java/com/juick/server/configuration/TelegramConfig.java deleted file mode 100644 index ebd1fd15..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/TelegramConfig.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.juick.server.configuration; - -import com.juick.server.TelegramBotManager; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -@ConditionalOnProperty(name = "telegram_token") -public class TelegramConfig { - @Bean - public TelegramBotManager telegramBotManager() { - return new TelegramBotManager(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/WwwAppConfiguration.java b/juick-server/src/main/java/com/juick/server/configuration/WwwAppConfiguration.java deleted file mode 100644 index 72889f96..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/WwwAppConfiguration.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.configuration; - -import com.juick.server.www.HelpService; -import com.juick.service.TagService; -import com.juick.service.UserService; -import com.mitchellbosecke.pebble.PebbleEngine; -import com.mitchellbosecke.pebble.extension.FormatterExtension; -import com.mitchellbosecke.pebble.loader.ClasspathLoader; -import com.mitchellbosecke.pebble.loader.Loader; -import com.mitchellbosecke.pebble.spring.PebbleViewResolver; -import com.mitchellbosecke.pebble.spring.extension.SpringExtension; -import org.apache.commons.codec.CharEncoding; -import org.commonmark.ext.autolink.AutolinkExtension; -import org.commonmark.node.Link; -import org.commonmark.parser.Parser; -import org.commonmark.renderer.html.HtmlRenderer; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.cache.caffeine.CaffeineCacheManager; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.ViewResolver; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -import javax.inject.Inject; -import java.util.Collections; - -/** - * Created by aalexeev on 11/22/16. - */ -@Configuration -@EnableCaching -public class WwwAppConfiguration implements WebMvcConfigurer { - @Inject - private UserService userService; - @Inject - private TagService tagService; - @Bean - public CaffeineCacheManager cacheManager() { - return new CaffeineCacheManager("help"); - } - - @Bean - public HelpService helpService() { - return new HelpService("help"); - } - - @Bean - public Parser cmParser() { - return Parser.builder().extensions(Collections.singletonList(AutolinkExtension.create())).build(); - } - @Bean - public HtmlRenderer helpRenderer() { - return HtmlRenderer.builder() - .attributeProviderFactory(context -> (node, tagName, attributes) -> { - if (node instanceof Link) { - Link link = (Link) node; - if (link.getDestination().startsWith("/")) { - String destination = "/" + helpService().getHelpPath() + link.getDestination(); - link.setDestination(destination); - attributes.put("href", destination); - } - } - }) - .build(); - } - @Bean - public Loader templateLoader() { - return new ClasspathLoader(); - } - - @Bean - public SpringExtension springExtension() { - return new SpringExtension(); - } - - @Bean - public PebbleEngine pebbleEngine() { - boolean devToolsArePresent = false; - try { - Class.forName("org.springframework.boot.devtools.livereload.Connection"); - devToolsArePresent = true; - } catch (ClassNotFoundException e) { - // release mode - } - return new PebbleEngine.Builder() - .loader(this.templateLoader()) - .cacheActive(!devToolsArePresent) - .extension(springExtension()) - .extension(new FormatterExtension()) - .strictVariables(true) - .build(); - } - - @Bean - public ViewResolver viewResolver() { - PebbleViewResolver viewResolver = new PebbleViewResolver(); - viewResolver.setPrefix("templates"); - viewResolver.setSuffix(".html"); - viewResolver.setPebbleEngine(pebbleEngine()); - viewResolver.setCharacterEncoding(CharEncoding.UTF_8); - return viewResolver; - } -} diff --git a/juick-server/src/main/java/com/juick/server/configuration/XMPPConfig.java b/juick-server/src/main/java/com/juick/server/configuration/XMPPConfig.java deleted file mode 100644 index 2feef286..00000000 --- a/juick-server/src/main/java/com/juick/server/configuration/XMPPConfig.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.juick.server.configuration; - -import com.juick.server.XMPPConnection; -import com.juick.server.XMPPServer; -import com.juick.server.xmpp.JidConverter; -import com.juick.server.xmpp.iq.MessageQuery; -import com.juick.server.xmpp.router.XMPPRouter; -import com.juick.server.xmpp.s2s.BasicXmppSession; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; -import org.springframework.core.convert.ConversionService; -import org.springframework.format.support.DefaultFormattingConversionService; -import rocks.xmpp.core.session.Extension; -import rocks.xmpp.core.session.XmppSessionConfiguration; -import rocks.xmpp.core.session.debug.LogbackDebugger; - -import java.time.Duration; - -@Configuration -@ConditionalOnProperty("xmppbot_jid") -public class XMPPConfig { - @Value("${hostname:localhost}") - private String hostname; - @Bean - public BasicXmppSession session() { - XmppSessionConfiguration configuration = XmppSessionConfiguration.builder() - .extensions(Extension.of(com.juick.Message.class), Extension.of(MessageQuery.class)) - .debugger(LogbackDebugger.class) - .defaultResponseTimeout(Duration.ofMillis(120000)) - .build(); - return BasicXmppSession.create(hostname, configuration); - } - @Bean - public static ConversionService conversionService() { - DefaultFormattingConversionService cs = new DefaultFormattingConversionService(); - cs.addConverter(new JidConverter()); - return cs; - } - @Bean - public XMPPServer xmppServer() { - return new XMPPServer(); - } - @Bean - public XMPPRouter xmppRouter() { - return new XMPPRouter(); - } - @Bean - @DependsOn("xmppRouter") - public XMPPConnection xmppConnection() { - return new XMPPConnection(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/helpers/annotation/UserCommand.java b/juick-server/src/main/java/com/juick/server/helpers/annotation/UserCommand.java deleted file mode 100644 index 4f07001c..00000000 --- a/juick-server/src/main/java/com/juick/server/helpers/annotation/UserCommand.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.helpers.annotation; - -import org.apache.commons.lang3.StringUtils; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Created by oxpa on 22.03.16. - */ -@Target({ElementType.TYPE, ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface UserCommand { - /** - * - * @return a command pattern - */ - String pattern() default StringUtils.EMPTY; - - /** - * - * @return pattern flags - */ - int patternFlags() default 0; - - /** - * - * @return a string used in HELP command output. Basically, only 1 string - */ - String help() default StringUtils.EMPTY; -} diff --git a/juick-server/src/main/java/com/juick/server/util/HttpBadRequestException.java b/juick-server/src/main/java/com/juick/server/util/HttpBadRequestException.java deleted file mode 100644 index 242f2b09..00000000 --- a/juick-server/src/main/java/com/juick/server/util/HttpBadRequestException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.util; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -/** - * Created by vt on 24/11/2016. - */ -@ResponseStatus(value = HttpStatus.BAD_REQUEST) -public class HttpBadRequestException extends RuntimeException { - public HttpBadRequestException() { - super("the request was bad", null, false, false); - } -} diff --git a/juick-server/src/main/java/com/juick/server/util/HttpForbiddenException.java b/juick-server/src/main/java/com/juick/server/util/HttpForbiddenException.java deleted file mode 100644 index 3251ca38..00000000 --- a/juick-server/src/main/java/com/juick/server/util/HttpForbiddenException.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.util; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -/** - * Created by vt on 24/11/2016. - */ -@ResponseStatus(value = HttpStatus.FORBIDDEN) -public class HttpForbiddenException extends RuntimeException { - public HttpForbiddenException() { - super(StringUtils.EMPTY, null, false, false); - } - -} diff --git a/juick-server/src/main/java/com/juick/server/util/HttpNotFoundException.java b/juick-server/src/main/java/com/juick/server/util/HttpNotFoundException.java deleted file mode 100644 index f66ece8b..00000000 --- a/juick-server/src/main/java/com/juick/server/util/HttpNotFoundException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.util; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -/** - * Created by vt on 24/11/2016. - */ -@ResponseStatus(value = HttpStatus.NOT_FOUND) -public class HttpNotFoundException extends RuntimeException { - public HttpNotFoundException() { - super(StringUtils.EMPTY, null, false, false); - } -} diff --git a/juick-server/src/main/java/com/juick/server/util/HttpUtils.java b/juick-server/src/main/java/com/juick/server/util/HttpUtils.java deleted file mode 100644 index b70eb3ad..00000000 --- a/juick-server/src/main/java/com/juick/server/util/HttpUtils.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.server.util; - -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.MediaType; -import org.springframework.web.multipart.MultipartFile; - -import javax.imageio.ImageIO; -import javax.imageio.ImageReader; -import javax.imageio.stream.ImageInputStream; -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URL; -import java.net.URLConnection; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.Iterator; -import java.util.UUID; - -/** - * - * @author Ugnich Anton - */ -public class HttpUtils { - private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class); - - public static URI receiveMultiPartFile(MultipartFile attach, String tmpDir) throws IOException { - if (attach != null && !attach.isEmpty()) { - ImageInputStream iis = ImageIO.createImageInputStream(attach.getInputStream()); - Iterator<ImageReader> readers = ImageIO.getImageReaders(iis); - - String format = StringUtils.EMPTY; - while (readers.hasNext()) { - ImageReader read = readers.next(); - format = read.getFormatName(); - } - String attachmentType = attachmentTypeFromFormat(format); - if (attachmentType.equals("jpg") || attachmentType.equals("png")) { - String attachmentFName = DigestUtils.md5Hex(UUID.randomUUID().toString()) + "." + attachmentType; - try { - Files.write(Paths.get(tmpDir, attachmentFName), - attach.getBytes()); - return URI.create(String.format("juick://%s", attachmentFName)); - } catch (IOException e) { - logger.warn("file receive error", e); - } - } - logger.warn("file type is unknown: {}", attach.getOriginalFilename()); - } - return URI.create(StringUtils.EMPTY); - } - - private static String attachmentTypeFromFormat(String format) throws IOException { - if (format != null && format.equals("JPEG")) { - return "jpg"; - } else if (format != null && format.equals("png")) { - return "png"; - } else { - throw new IOException("Wrong file type: " + format); - } - } - - public static String mediaType(String attachmentType) { - return attachmentType.equals("jpg") ? MediaType.IMAGE_JPEG_VALUE : MediaType.IMAGE_PNG_VALUE; - } - - public static URI downloadImage(URL url, String tmpDir) throws IOException { - ImageInputStream iis = ImageIO.createImageInputStream(url.openStream()); - Iterator<ImageReader> readers = ImageIO.getImageReaders(iis); - - String format = StringUtils.EMPTY; - while (readers.hasNext()) { - ImageReader read = readers.next(); - format = read.getFormatName(); - } - URLConnection urlConn; - try { - urlConn = url.openConnection(); - } catch (IOException e) { - logger.error(String.format("Failed open url: %s", url.toString())); - throw e; - } - - try (InputStream is = new BufferedInputStream(urlConn.getInputStream())) { - String attachmentType = attachmentTypeFromFormat(format); - - String attachmentFName = DigestUtils.md5Hex(UUID.randomUUID().toString()) + "." + attachmentType; - Files.copy(is, Paths.get(tmpDir, attachmentFName)); - return URI.create(String.format("juick://%s", attachmentFName)); - } catch (IOException e) { - logger.error(String.format("Failed download image by url: %s", url.toString()), e); - throw e; - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/util/ImageUtils.java b/juick-server/src/main/java/com/juick/server/util/ImageUtils.java deleted file mode 100644 index d16faf8f..00000000 --- a/juick-server/src/main/java/com/juick/server/util/ImageUtils.java +++ /dev/null @@ -1,175 +0,0 @@ - -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.util; - -import com.juick.Attachment; -import org.apache.commons.imaging.ImageReadException; -import org.apache.commons.imaging.Imaging; -import org.apache.commons.imaging.common.ImageMetadata; -import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata; -import org.apache.commons.imaging.formats.tiff.TiffField; -import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.imgscalr.Scalr; -import org.imgscalr.Scalr.Rotation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.imageio.ImageIO; -import javax.imageio.ImageReader; -import javax.imageio.stream.FileImageInputStream; -import javax.imageio.stream.ImageInputStream; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.Iterator; - -public class ImageUtils { - private static final Logger logger = LoggerFactory.getLogger(ImageUtils.class); - - private String imgDir; - private String tmpDir; - - public ImageUtils(String imgDir, String tmpDir) { - this.imgDir = imgDir; - this.tmpDir = tmpDir; - } -/** - * Returns <code>BufferedImage</code>, same as <code>ImageIO.read()</code> does. - * - * <p>JPEG images with EXIF metadata are rotated according to Orientation tag. - * - * @param imageFile a <code>File</code> to read from. - */ - private static BufferedImage readImageWithOrientation(File imageFile) - throws IOException { - - BufferedImage image = ImageIO.read(imageFile); - if (!FilenameUtils.getExtension(imageFile.getName()).equals("jpg")) { - return image; - } - - try { - ImageMetadata metadata = Imaging.getMetadata(imageFile); - - if (metadata instanceof JpegImageMetadata) { - JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata; - TiffField orientationField = jpegMetadata.findEXIFValue(TiffTagConstants.TIFF_TAG_ORIENTATION); - - if (orientationField != null) { - int orientation = orientationField.getIntValue(); - switch (orientation) { - case TiffTagConstants.ORIENTATION_VALUE_ROTATE_90_CW: - image = Scalr.rotate(image, Rotation.CW_90); - break; - case TiffTagConstants.ORIENTATION_VALUE_ROTATE_180: - image = Scalr.rotate(image, Rotation.CW_180); - break; - case TiffTagConstants.ORIENTATION_VALUE_ROTATE_270_CW: - image = Scalr.rotate(image, Rotation.CW_270); - break; - case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL: - image = Scalr.rotate(image, Rotation.FLIP_HORZ); - break; - case TiffTagConstants.ORIENTATION_VALUE_MIRROR_VERTICAL: - image = Scalr.rotate(image, Rotation.FLIP_VERT); - break; - case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL_AND_ROTATE_90_CW: - image = Scalr.rotate(Scalr.rotate(image, Rotation.FLIP_HORZ), Rotation.CW_90); - break; - case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL_AND_ROTATE_270_CW: - image = Scalr.rotate(Scalr.rotate(image, Rotation.FLIP_HORZ), Rotation.CW_270); - break; - case TiffTagConstants.ORIENTATION_VALUE_HORIZONTAL_NORMAL: - default: - // do nothing - break; - } - } - } - } catch (ImageReadException e) { - // failed to read metadata. - // nothing to do here, return image as is. - } - - return image; - } - - public void saveImageWithPreviews(String tempFilename, String outputFilename) - throws IOException { - String ext = FilenameUtils.getExtension(outputFilename); - - Path outputImagePath = Paths.get(imgDir, "p", outputFilename); - // this throws strange exceptions - // Files.move(Paths.get(tmpDir, tempFilename), outputImagePath); - FileUtils.moveFile(Paths.get(tmpDir, tempFilename).toFile(), outputImagePath.toFile()); - BufferedImage originalImage = readImageWithOrientation(outputImagePath.toFile()); - - int width = originalImage.getWidth(); - int height = originalImage.getHeight(); - int maxDimension = (width > height) ? width : height; - BufferedImage image1024 = (maxDimension > 1024) ? Scalr.resize(originalImage, 1024) : originalImage; - BufferedImage image0512 = (maxDimension > 512) ? Scalr.resize(originalImage, 512) : originalImage; - BufferedImage image0160 = (maxDimension > 160) ? Scalr.resize(originalImage, 160) : originalImage; - ImageIO.write(image1024, ext, Paths.get(imgDir, "photos-1024", outputFilename).toFile()); - ImageIO.write(image0512, ext, Paths.get(imgDir, "photos-512", outputFilename).toFile()); - ImageIO.write(image0160, ext, Paths.get(imgDir, "ps", outputFilename).toFile()); - } - - public void saveAvatar(String tempFilename, int uid) - throws IOException { - String ext = FilenameUtils.getExtension(tempFilename); - String originalName = String.format("%s.%s", uid, ext); - Path originalPath = Paths.get(imgDir, "ao", originalName); - Files.move(Paths.get(tmpDir, tempFilename), originalPath, StandardCopyOption.REPLACE_EXISTING); - BufferedImage originalImage = ImageIO.read(originalPath.toFile()); - - String targetExt = "png"; - String targetName = String.format("%s.%s", uid, targetExt); - ImageIO.write(Scalr.resize(originalImage, 96), targetExt, Paths.get(imgDir, "a", targetName).toFile()); - ImageIO.write(Scalr.resize(originalImage, 32), targetExt, Paths.get(imgDir, "as", targetName).toFile()); - } - public Attachment getAttachment(File imgFile) throws IOException { - Attachment attachment = new Attachment(); - try (ImageInputStream stream = ImageIO.createImageInputStream(imgFile)) { - Iterator<ImageReader> iter = ImageIO.getImageReaders(stream); - while (iter.hasNext()) { - ImageReader reader = iter.next(); - try { - reader.setInput(stream); - attachment.setWidth(reader.getWidth(reader.getMinIndex())); - attachment.setHeight(reader.getHeight(reader.getMinIndex())); - return attachment; - } catch (Exception e) { - logger.debug("Error reading {}, trying next reader", imgFile.getAbsolutePath()); - } finally { - reader.dispose(); - } - } - } - - logger.warn("Not a known image file {}", imgFile.getAbsolutePath()); - return attachment; - } -}
\ No newline at end of file diff --git a/juick-server/src/main/java/com/juick/server/util/TagUtils.java b/juick-server/src/main/java/com/juick/server/util/TagUtils.java deleted file mode 100644 index cb828933..00000000 --- a/juick-server/src/main/java/com/juick/server/util/TagUtils.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.util; - -import com.juick.Tag; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; - -import java.util.List; -import java.util.stream.Collectors; - -/** - * Created by aalexeev on 11/13/16. - */ -public class TagUtils { - private TagUtils() { - throw new IllegalStateException(); - } - - public static String toString(final List<Tag> tags) { - if (CollectionUtils.isEmpty(tags)) - return StringUtils.EMPTY; - - return tags.stream().map(t -> "*" + t.getName()) - .collect(Collectors.joining(" ")); - } -} diff --git a/juick-server/src/main/java/com/juick/server/util/UserUtils.java b/juick-server/src/main/java/com/juick/server/util/UserUtils.java deleted file mode 100644 index 1adc85ab..00000000 --- a/juick-server/src/main/java/com/juick/server/util/UserUtils.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.util; - -import com.juick.User; -import com.juick.model.AnonymousUser; -import com.juick.service.security.entities.JuickUser; -import javax.annotation.Nonnull; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; - -/** - * Created by aalexeev on 11/14/16. - */ -public class UserUtils { - private UserUtils() { - throw new IllegalStateException(); - } - - public static Authentication getAuthentication() { - return SecurityContextHolder.getContext().getAuthentication(); - } - - public static Object getPrincipal(final Authentication authentication) { - return authentication == null ? null : authentication.getPrincipal(); - } - - @Nonnull - public static User getCurrentUser() { - Object principal = getPrincipal(getAuthentication()); - - if (principal instanceof JuickUser) - return ((JuickUser) principal).getUser(); - - if (principal instanceof User) - return (User) principal; - - return AnonymousUser.INSTANCE; - } -} diff --git a/juick-server/src/main/java/com/juick/server/util/WebUtils.java b/juick-server/src/main/java/com/juick/server/util/WebUtils.java deleted file mode 100644 index 9dd628ee..00000000 --- a/juick-server/src/main/java/com/juick/server/util/WebUtils.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.util; - -import java.util.regex.Pattern; - -/** - * Created by aalexeev on 11/28/16. - */ -public class WebUtils { - private WebUtils() { - throw new IllegalStateException(); - } - - private static final Pattern USER_NAME_PATTERN = Pattern.compile("[a-zA-Z-_\\d]{2,16}"); - - private static final Pattern POST_NUMBER_PATTERN = Pattern.compile("-?\\d+"); - - private static final Pattern JID_PATTERN = Pattern.compile("^[a-zA-Z0-9\\\\-\\\\_\\\\@\\\\.]{6,64}$"); - - - public static boolean isPostNumber(final String aString) { - return aString != null && POST_NUMBER_PATTERN.matcher(aString).matches(); - } - - public static boolean isNotPostNumber(final String aString) { - return !isPostNumber(aString); - } - - public static boolean isUserName(final String aString) { - return aString != null && USER_NAME_PATTERN.matcher(aString).matches(); - } - - public static boolean isNotUserName(final String aString) { - return !isUserName(aString); - } - - public static boolean isJid(final String aString) { - return aString != null && JID_PATTERN.matcher(aString).matches(); - } - - public static boolean isNotJid(final String aString) { - return !isJid(aString); - } - - -} diff --git a/juick-server/src/main/java/com/juick/server/www/HelpService.java b/juick-server/src/main/java/com/juick/server/www/HelpService.java deleted file mode 100644 index 25727962..00000000 --- a/juick-server/src/main/java/com/juick/server/www/HelpService.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.www; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; -import org.springframework.cache.annotation.Cacheable; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.regex.Pattern; - -/** - * Created by aalexeev on 12/11/16. - */ -public class HelpService { - private static final Pattern LANG_PATTERN = Pattern.compile("[a-z]{2}"); - - private static final Pattern PAGE_PATTERN = Pattern.compile("[a-zA-Z0-9\\-_]+"); - - private final String helpPath; - - - public HelpService(String helpPath) { - this.helpPath = helpPath; - } - - @Cacheable("help") - public String getHelp(final String page, final String lang) { - if (canBePage(page) && canBeLang(lang)) { - String path = StringUtils.joinWith("/", helpPath, lang, page + ".md"); - - try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path)) { - if (is != null) - return IOUtils.toString(is, StandardCharsets.UTF_8); - } catch (IOException e) { - } - } - return null; - } - - public boolean canBePage(final String anything) { - return anything != null && PAGE_PATTERN.matcher(anything).matches(); - } - - public boolean canBeLang(final String anything) { - return anything != null && LANG_PATTERN.matcher(anything).matches(); - } - - public String getHelpPath() { - return helpPath; - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/WebApp.java b/juick-server/src/main/java/com/juick/server/www/WebApp.java deleted file mode 100644 index 98327a5d..00000000 --- a/juick-server/src/main/java/com/juick/server/www/WebApp.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.server.www; - -import com.juick.Tag; -import com.juick.service.TagService; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.resource.ResourceUrlProvider; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Stream; - -/** - * - * @author Ugnich Anton - */ -@Component -public class WebApp { - @Inject - private TagService tagService; - @Inject - private ResourceUrlProvider resourceUrlProvider; - - public List<Tag> parseTags(String tagsStr) { - List<Tag> tags = new ArrayList<>(); - if (tagsStr != null && !tagsStr.isEmpty()) { - Stream<String> tagsList = Arrays.stream(tagsStr.split("[ \\,]")) - .distinct().map( t -> { - if (t.startsWith("*")) { - t = t.substring(1); - } - if (t.length() > 64) { - t = t.substring(0, 64); - } - return t; - }); - tags = tagService.getTags(tagsList, true); - while (tags.size() > 5) { - tags.remove(5); - } - } - return tags; - } - - public String getStyleUrl() { - return resourceUrlProvider.getForLookupPath("/style.css"); - } - - public String getScriptsUrl() { - return resourceUrlProvider.getForLookupPath("/scripts.js"); - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/controllers/AnythingFilter.java b/juick-server/src/main/java/com/juick/server/www/controllers/AnythingFilter.java deleted file mode 100644 index cdbeafc0..00000000 --- a/juick-server/src/main/java/com/juick/server/www/controllers/AnythingFilter.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.juick.server.www.controllers; - -import com.juick.server.util.WebUtils; -import com.juick.service.MessagesService; -import com.juick.service.UserService; -import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import org.springframework.web.util.UriComponents; - -import javax.annotation.Nonnull; -import javax.inject.Inject; -import javax.servlet.*; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -@Component -public class AnythingFilter extends OncePerRequestFilter { - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - - @Override - public void doFilterInternal(@Nonnull HttpServletRequest servletRequest, - @Nonnull HttpServletResponse servletResponse, - @Nonnull FilterChain filterChain) throws IOException, ServletException { - String upgrade = servletRequest.getHeader("Connection"); - if (upgrade != null && upgrade.equals("Upgrade")) { - filterChain.doFilter(servletRequest, servletResponse); - return; - } - UriComponents components = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); - String anything = components.getPath().substring(1); - int before = NumberUtils.toInt(components.getQueryParams().getFirst("before"), 0); - if (before == 0) { - boolean isPostNumber = WebUtils.isPostNumber(anything); - int messageId = isPostNumber ? - NumberUtils.toInt(anything) : 0; - - if (isPostNumber && anything.equals(Integer.toString(messageId))) { - if (messageId > 0) { - com.juick.User author = messagesService.getMessageAuthor(messageId); - - if (author != null) { - servletResponse.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); - servletResponse.setHeader("Location", "/" + author.getName() + "/" + anything); - return; - } - } - } - com.juick.User user = userService.getUserByName(anything); - if (user.getUid() > 0) { - ((HttpServletResponse)servletResponse).sendRedirect("/" + user.getName() + "/"); - } else { - filterChain.doFilter(servletRequest, servletResponse); - } - } else { - com.juick.User user = userService.getUserByName(anything); - if (!user.isAnonymous()) { - ((HttpServletResponse) servletResponse).sendRedirect("/" + user.getName() + "/?before=" + before); - } else { - filterChain.doFilter(servletRequest, servletResponse); - } - } - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/controllers/Help.java b/juick-server/src/main/java/com/juick/server/www/controllers/Help.java deleted file mode 100644 index 61b58a9d..00000000 --- a/juick-server/src/main/java/com/juick/server/www/controllers/Help.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.www.controllers; - -import com.juick.server.util.HttpNotFoundException; -import com.juick.server.util.UserUtils; -import com.juick.service.MessagesService; -import com.juick.server.www.HelpService; -import org.commonmark.parser.Parser; -import org.commonmark.renderer.html.HtmlRenderer; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; - -import javax.inject.Inject; -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.Locale; -import java.util.Objects; - -/** - * Created by aalexeev on 11/21/16. - */ -@Controller -public class Help { - @Inject - private HelpService helpService; - @Inject - private MessagesService messagesService; - @Inject - private Parser cmParser; - @Inject - private HtmlRenderer helpRenderer; - - @GetMapping({"/help/", "/help", "/help/{langOrPage}", "/help/{lang}/{page}"}) - public String showHelp( - Locale locale, - @PathVariable(required = false, name = "lang") String lang, - @PathVariable(required = false, name = "page") String page, - @PathVariable(required = false, name = "langOrPage") String langOrPage, - Model model) throws IOException, URISyntaxException { - com.juick.User visitor = UserUtils.getCurrentUser(); - - String navigation = null; - - if (langOrPage != null) { - if (helpService.canBeLang(langOrPage)) { - navigation = helpService.getHelp("navigation", langOrPage); - if (navigation != null) - lang = langOrPage; - } - - if (navigation == null && helpService.canBePage(langOrPage)) - page = langOrPage; - } - - if (lang == null) { - lang = locale.getLanguage(); - } - - String content = helpService.getHelp(page, lang); - if (content == null && !Objects.equals("tos", page)) - content = helpService.getHelp("tos", lang); - - if (navigation == null) - navigation = helpService.getHelp("navigation", lang); - - if (content == null || navigation == null) - throw new HttpNotFoundException(); - - model.addAttribute("navigation", helpRenderer.render(cmParser.parse(navigation))); - model.addAttribute("content", helpRenderer.render(cmParser.parse(content))); - model.addAttribute("visitor", visitor); - - return "views/help"; - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/controllers/Login.java b/juick-server/src/main/java/com/juick/server/www/controllers/Login.java deleted file mode 100644 index d933934e..00000000 --- a/juick-server/src/main/java/com/juick/server/www/controllers/Login.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.server.www.controllers; - -import com.juick.server.util.UserUtils; -import com.juick.service.UserService; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; - -import javax.inject.Inject; - -/** - * @author Ugnich Anton - */ -@Controller -public class Login { - @Inject - private UserService userService; - - @GetMapping("/login") - public String getloginForm(@RequestParam(required = false, defaultValue = "true") boolean redirect) { - com.juick.User visitor = UserUtils.getCurrentUser(); - - if (!visitor.isAnonymous()) { - return redirect ? "redirect:/" : "redirect:/login/success"; - } - return "views/login"; - } - @GetMapping("/login/success") - public String getSuccessLogin(ModelMap model) { - model.addAttribute("hash", userService.getHashByUID(UserUtils.getCurrentUser().getUid())); - return "views/login_success"; - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/controllers/MessagesWWW.java b/juick-server/src/main/java/com/juick/server/www/controllers/MessagesWWW.java deleted file mode 100644 index 0708e27f..00000000 --- a/juick-server/src/main/java/com/juick/server/www/controllers/MessagesWWW.java +++ /dev/null @@ -1,593 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.server.www.controllers; - -import com.juick.Message; -import com.juick.Tag; -import com.juick.formatters.PlainTextFormatter; -import com.juick.server.Utils; -import com.juick.server.util.HttpForbiddenException; -import com.juick.server.util.HttpNotFoundException; -import com.juick.server.util.UserUtils; -import com.juick.server.util.WebUtils; -import com.juick.service.*; -import com.juick.util.MessageUtils; -import org.apache.commons.codec.CharEncoding; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.text.StringEscapeUtils; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import org.springframework.web.util.UriComponents; -import ru.sape.Sape; - -import javax.inject.Inject; -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -/** - * - * @author Ugnich Anton - */ -@Controller -public class MessagesWWW { - @Inject - private UserService userService; - @Inject - private TagService tagService; - @Inject - private MessagesService messagesService; - @Inject - private Optional<Sape> sape; - @Inject - private PMQueriesService pmQueriesService; - @Inject - private CrosspostService crosspostService; - @Inject - private ApplicationEventPublisher applicationEventPublisher; - - void fillUserModel(ModelMap model, com.juick.User user, com.juick.User visitor) { - model.addAttribute("user", user); - model.addAttribute("isSubscribed", userService.isSubscribed(visitor.getUid(), user.getUid())); - model.addAttribute("isInBL", userService.isInBL(visitor.getUid(), user.getUid())); - model.addAttribute("isInBLAny", userService.isInBLAny(user.getUid(), visitor.getUid())); - model.addAttribute("statsIRead", userService.getUserFriends(user.getUid()).size()); - model.addAttribute("statsMyReaders", userService.getStatsMyReaders(user.getUid())); - model.addAttribute("statsMyBL", userService.getUserBLUsers(user.getUid()).size()); - model.addAttribute("statsMessages", userService.getStatsMessages(user.getUid())); - model.addAttribute("statsReplies", userService.getStatsReplies(user.getUid())); - model.addAttribute("iread", userService.getUserReadLeastPopular(user.getUid(), 8)); - model.addAttribute("tagStats", tagService.getUserTagStats(user.getUid()) - .stream().sorted((e1, e2) -> Integer.compare(e2.getUsageCount(), e1.getUsageCount())).limit(20).map(t -> t.getTag().getName()).collect(Collectors.toList())); - } - - @GetMapping("/") - protected String doGet( - @RequestParam(required = false) String tag, - @RequestParam(name = "show", required = false) String paramShow, - @RequestParam(name = "search", required = false) String paramSearch, - @RequestParam(name = "before", required = false, defaultValue = "0") Integer paramBefore, - @RequestParam(name = "to", required = false, defaultValue = "0") Long paramTo, - @RequestParam(name = "page", required = false, defaultValue = "0") Integer page, - @CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie, - ModelMap model) throws IOException { - if (tag != null) { - return "redirect:/tag/" + URLEncoder.encode(tag, CharEncoding.UTF_8); - } - com.juick.User visitor = UserUtils.getCurrentUser(); - - if (paramSearch != null && paramSearch.length() > 64) { - paramSearch = null; - } - - model.addAttribute("discover", false); - - String title; - List<Integer> mids; - - if (paramSearch != null) { - title = "Поиск: " + StringEscapeUtils.escapeHtml4(paramSearch); - mids = messagesService.getSearch(visitor, Utils.encodeSphinx(paramSearch), page); - } else if (paramShow == null) { - title = "Обсуждения"; - mids = messagesService.getDiscussions(visitor.getUid(), paramTo); - } else if (paramShow.equals("top")) { - title = "Популярные"; - mids = messagesService.getPopular(visitor.getUid(), paramBefore); - model.addAttribute("discover", true); - } else if (paramShow.equals("my") && !visitor.isAnonymous()) { - title = "Моя лента"; - mids = messagesService.getMyFeed(visitor.getUid(), paramBefore, true); - } else if (paramShow.equals("private") && !visitor.isAnonymous()) { - title = "Приватные"; - mids = messagesService.getPrivate(visitor.getUid(), paramBefore); - } else if (paramShow.equals("discuss")) { - return "redirect:/"; - } else if (paramShow.equals("recommended") && !visitor.isAnonymous()) { - title = "Рекомендации"; - mids = messagesService.getRecommended(visitor.getUid(), paramBefore); - } else if (paramShow.equals("photos")) { - title = "Фотографии"; - mids = messagesService.getPhotos(visitor.getUid(), paramBefore); - model.addAttribute("discover", true); - } else if (paramShow.equals("all")) { - title = "Все сообщения"; - mids = messagesService.getAll(visitor.getUid(), paramBefore); - model.addAttribute("discover", true); - } else { - throw new HttpNotFoundException(); - } - - String head = "<meta name=\"Description\" content=\"" + title + "\" />\n";; - - if (paramBefore > 0 || paramShow != null) { - head = "<meta name=\"robots\" content=\"noindex\"/>"; - } - model.addAttribute("title", title); - model.addAttribute("headers", head); - model.addAttribute("visitor", visitor); - model.addAttribute("noindex", !(paramShow == null && paramBefore == 0)); - List<com.juick.Message> msgs = messagesService.getMessages(visitor, mids); - - if (!visitor.isAnonymous()) { - fillUserModel(model, visitor, visitor); - List<Integer> unread = messagesService.getUnread(visitor); - visitor.setUnreadCount(unread.size()); - List<Integer> blUIDs = userService.checkBL(visitor.getUid(), - msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList())); - msgs.forEach(m -> { - m.ReadOnly |= blUIDs.contains(m.getUser().getUid()); - m.setUnread(unread.contains(m.getMid())); - }); - } - model.addAttribute("msgs", msgs); - model.addAttribute("tags", tagService.getPopularTags()); - model.addAttribute("headers", head); - model.addAttribute("showAdv", - paramShow == null && paramBefore == 0 && paramSearch == null && visitor.isAnonymous()); - if (mids.size() >= 20) { - String nextpage = (paramShow == null) ? "?to=" + msgs.get(msgs.size() - 1).getUpdated().toEpochMilli() : paramSearch != null ? String.format("?page=%d", page + 1) : "?before=" + mids.get(mids.size() - 1); - if (paramShow != null) { - nextpage += "&show=" + paramShow; - } - if (paramSearch != null) { - nextpage += "&search=" + URLEncoder.encode(paramSearch, CharEncoding.UTF_8); - } - model.addAttribute("nextpage", nextpage); - } - UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); - String queryString = builder.getQuery(); - String requestURI = builder.toUri().getPath(); - if (sape.isPresent() && visitor.isAnonymous() && queryString == null) { - String links = sape.get().getPageLinks(requestURI, sapeCookie).render(); - model.addAttribute("links", links); - } - return "views/index"; - } - - @GetMapping(path = "/{uname}/", headers = "Connection!=Upgrade") - protected String doGetBlog( - @RequestParam(required = false, name = "show") String paramShow, - @RequestParam(required = false, name = "tag") String paramTagStr, - @RequestParam(required = false, name = "search") String paramSearch, - @RequestParam(required = false, name = "page", defaultValue = "0") Integer page, - @PathVariable String uname, - @RequestParam(required = false, defaultValue = "0") Integer before, - @CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie, - ModelMap model) throws IOException { - com.juick.User user = userService.getUserByName(uname); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (user.isBanned() || user.isAnonymous()) { - throw new HttpNotFoundException(); - } - - List<Integer> mids; - - com.juick.Tag paramTag = null; - if (paramTagStr != null) { - if (paramTagStr.length() < 64) { - paramTag = tagService.getTag(paramTagStr, false); - } - if (paramTag == null) { - throw new HttpNotFoundException(); - } else if (!paramTag.getName().equals(paramTagStr)) { - String url = user.getName() + "/?tag=" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8); - return "redirect:/" + url; - } - } - if (paramSearch != null && paramSearch.length() > 64) { - paramSearch = null; - } - - int privacy = 0; - if (!visitor.isAnonymous()) { - if (user.getUid() == visitor.getUid() || visitor.getUid() == 1) { - privacy = -3; - } else if (userService.isInWL(user.getUid(), visitor.getUid())) { - privacy = -2; - } - } - - String title; - if (paramShow == null) { - if (paramTag != null) { - title = "Блог " + user.getName() + ": *" + StringEscapeUtils.escapeHtml4(paramTag.getName()); - mids = messagesService.getUserTag(user.getUid(), paramTag.TID, privacy, before); - } else if (paramSearch != null) { - title = "Блог " + user.getName() + ": " + StringEscapeUtils.escapeHtml4(paramSearch); - mids = messagesService.getUserSearch(visitor, user.getUid(), Utils.encodeSphinx(paramSearch), privacy, page); - } else { - title = "Блог " + user.getName(); - mids = messagesService.getUserBlog(user.getUid(), privacy, before); - } - } else if (paramShow.equals("recomm")) { - title = "Рекомендации " + user.getName(); - mids = messagesService.getUserRecommendations(user.getUid(), before); - } else if (paramShow.equals("photos")) { - title = "Фотографии " + user.getName(); - mids = messagesService.getUserPhotos(user.getUid(), privacy, before); - } else { - throw new HttpNotFoundException(); - } - - String head = "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"@" + - user.getName() + "\" href=\"//rss.juick.com/" + user.getName() + "/blog\"/>"; - head += "<meta name=\"Description\" content=\"" + title + "\" />\n"; - if (paramTag != null && tagService.getTagNoIndex(paramTag.TID)) { - head += "<meta name=\"robots\" content=\"noindex,nofollow\"/>"; - } else if (before > 0 || paramShow != null) { - head += "<meta name=\"robots\" content=\"noindex\"/>"; - } - model.addAttribute("pageUrl", "http://juick.com/" + user.getName()); - model.addAttribute("title", title); - model.addAttribute("headers", head); - model.addAttribute("visitor", visitor); - model.addAttribute("noindex", paramShow == null && before == 0); - fillUserModel(model, user, visitor); - model.addAttribute("paramTag", paramTag); - List<com.juick.Message> msgs = messagesService.getMessages(visitor, mids); - - if (!visitor.isAnonymous()) { - List<Integer> unread = messagesService.getUnread(visitor); - visitor.setUnreadCount(unread.size()); - List<Integer> blUIDs = userService.checkBL(visitor.getUid(), - msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList())); - msgs.forEach(m -> { - m.ReadOnly |= blUIDs.contains(m.getUser().getUid()); - m.setUnread(unread.contains(m.getMid())); - }); - } - model.addAttribute("msgs", msgs); - model.addAttribute("headers", head); - model.addAttribute("showAdv", - paramShow == null && before == 0 && paramSearch == null && visitor.getUid() == 0); - if (mids.size() >= 20) { - String nextpage = paramSearch != null ? String.format("?page=%d", page + 1) : "?before=" + mids.get(mids.size() - 1); - if (paramShow != null) { - nextpage += "&show=" + paramShow; - } - if (paramSearch != null) { - nextpage += "&search=" + URLEncoder.encode(paramSearch, CharEncoding.UTF_8); - } - if (paramTag != null) { - nextpage += "&tag=" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8); - } - model.addAttribute("nextpage", nextpage); - } - UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); - String queryString = builder.getQuery(); - String requestURI = builder.toUri().getPath(); - if (sape.isPresent() && visitor.isAnonymous() && queryString == null) { - String links = sape.get().getPageLinks(requestURI, sapeCookie).render(); - model.addAttribute("links", links); - } - return "views/blog"; - } - - @GetMapping("/{uname}/tags") - protected String doGetTags(@PathVariable String uname, ModelMap model) throws IOException { - com.juick.User user = userService.getUserByName(uname); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isBanned()) { - throw new HttpNotFoundException(); - } - - model.addAttribute("title", "Теги " + user.getName()); - model.addAttribute("headers", "<meta name=\"robots\" content=\"noindex,nofollow\"/>"); - model.addAttribute("visitor", visitor); - fillUserModel(model, user, visitor); - model.addAttribute("tags", tagService.getUserTagStats(user.getUid()).stream() - .sorted((e1, e2) -> Integer.compare(e2.getUsageCount(), e1.getUsageCount())).map(t -> t.getTag().getName()).collect(Collectors.toList())); - - return "views/blog_tags"; - } - - @GetMapping("/{uname}/friends") - protected String doGetFriends(@PathVariable String uname, ModelMap model) throws IOException { - com.juick.User user = userService.getUserByName(uname); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isBanned()) { - throw new HttpNotFoundException(); - } - model.addAttribute("title", "Подписки " + user.getName()); - model.addAttribute("headers", "<meta name=\"robots\" content=\"noindex\"/>"); - model.addAttribute("visitor", visitor); - fillUserModel(model, user, visitor); - model.addAttribute("users", userService.getUserFriends(user.getUid())); - - return "views/users"; - } - - @GetMapping("/{uname}/readers") - protected String doGetReaders(@PathVariable String uname, ModelMap model) throws IOException { - com.juick.User user = userService.getUserByName(uname); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isBanned()) { - throw new HttpForbiddenException(); - } - model.addAttribute("title", "Читатели " + user.getName()); - model.addAttribute("headers", "<meta name=\"robots\" content=\"noindex\"/>"); - model.addAttribute("visitor", visitor); - fillUserModel(model, user, visitor); - model.addAttribute("users", userService.getUserReaders(user.getUid())); - - return "views/users"; - } - - @GetMapping("/{uname}/bl") - protected String doGetBL(@PathVariable String uname, ModelMap model) throws IOException { - com.juick.User user = userService.getUserByName(uname); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isBanned() || visitor.getUid() != user.getUid()) { - throw new HttpForbiddenException(); - } - model.addAttribute("title", "Черный список " + user.getName()); - model.addAttribute("headers", "<meta name=\"robots\" content=\"noindex\"/>"); - model.addAttribute("visitor", visitor); - fillUserModel(model, user, visitor); - model.addAttribute("users", userService.getUserBLUsers(user.getUid())); - - return "views/users"; - } - @GetMapping("/tag/{tagName}") - protected String tagAction(HttpServletRequest request, - @PathVariable String tagName, - @CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie, - @RequestParam(required = false, defaultValue = "0") int before, - ModelMap model) throws IOException { - com.juick.User visitor = UserUtils.getCurrentUser(); - - String paramTagStr = StringEscapeUtils.unescapeHtml4(tagName); - com.juick.Tag paramTag = tagService.getTag(paramTagStr, false); - if (paramTag == null) { - throw new HttpNotFoundException(); - } else if (paramTag.SynonymID > 0 && paramTag.TID != paramTag.SynonymID) { - com.juick.Tag synTag = tagService.getTag(paramTag.SynonymID); - String url = "/tag/" + URLEncoder.encode(StringEscapeUtils.escapeHtml4(synTag.getName()), CharEncoding.UTF_8); - if (request.getQueryString() != null) { - url += "?" + request.getQueryString(); - } - return "redirect:" + url; - } else if (!paramTag.getName().equals(paramTagStr)) { - String url = "/tag/" + URLEncoder.encode(StringEscapeUtils.escapeHtml4(paramTag.getName()), CharEncoding.UTF_8); - if (request.getQueryString() != null) { - url += "?" + request.getQueryString(); - } - return "redirect:" + url; - } - - String title = "*" + StringEscapeUtils.escapeHtml4(paramTag.getName()); - model.addAttribute("title", title); - List<Integer> mids = messagesService.getTag(paramTag.TID, visitor.getUid(), before, (visitor.isAnonymous()) ? 40 : 20); - List<com.juick.Message> msgs = messagesService.getMessages(visitor, mids); - if (!visitor.isAnonymous()) { - List<Integer> unread = messagesService.getUnread(visitor); - visitor.setUnreadCount(unread.size()); - List<Integer> blUIDs = userService.checkBL( - visitor.getUid(), - msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList()) - ); - msgs.forEach(m -> { - m.ReadOnly |= blUIDs.contains(m.getUser().getUid()); - m.setUnread(unread.contains(m.getMid())); - }); - fillUserModel(model, visitor, visitor); - } - - String head = StringUtils.EMPTY; - if (tagService.getTagNoIndex(paramTag.TID)) { - head = "<meta name=\"robots\" content=\"noindex,nofollow\"/>"; - } else if (before > 0 || mids.size() < 5) { - head = "<meta name=\"robots\" content=\"noindex\"/>"; - } - model.addAttribute("headers", head); - model.addAttribute("visitor", visitor); - model.addAttribute("tag", paramTag); - model.addAttribute("title", title); - model.addAttribute("msgs", msgs); - model.addAttribute("tags", tagService.getPopularTags()); - model.addAttribute("noindex", before > 0); - model.addAttribute("showAdv", before == 0 && visitor.isAnonymous()); - model.addAttribute("isSubscribed", tagService.isSubscribed(visitor, paramTag)); - model.addAttribute("isInBL", tagService.isInBL(visitor, paramTag)); - if (mids.size() >= 20) { - String nextpage = "/tag/" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8) + "?before=" + mids.get(mids.size() - 1); - model.addAttribute("nextpage", nextpage); - } - UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); - String queryString = builder.getQuery(); - String requestURI = builder.toUri().getPath(); - if (sape.isPresent() && visitor.isAnonymous() && queryString == null) { - String links = sape.get().getPageLinks(requestURI, sapeCookie).render(); - model.addAttribute("links", links); - } - return "views/index"; - } - @GetMapping("/pm/inbox") - protected String doGetInbox(ModelMap model) { - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - return "redirect:/login"; - } - String title = "PM: Inbox"; - List<com.juick.Message> msgs = pmQueriesService.getLastPMInbox(visitor.getUid()); - fillUserModel(model, visitor, visitor); - model.addAttribute("title", title); - model.addAttribute("visitor", visitor); - model.addAttribute("msgs", msgs); - model.addAttribute("tags", tagService.getPopularTags()); - return "views/pm_inbox"; - } - - @GetMapping("/pm/sent") - protected String doGetSent(@RequestParam(required = false) String uname, - ModelMap model) { - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - return "redirect:/login"; - } - String title = "PM: Sent"; - List<com.juick.Message> msgs = pmQueriesService.getLastPMSent(visitor.getUid()); - - if (WebUtils.isNotUserName(uname)) { - uname = StringUtils.EMPTY; - } - fillUserModel(model, visitor, visitor); - model.addAttribute("title", title); - model.addAttribute("visitor", visitor); - model.addAttribute("msgs", msgs); - model.addAttribute("tags", tagService.getPopularTags()); - model.addAttribute("uname", uname); - return "views/pm_sent"; - } - @GetMapping(value = "/{uname}/{mid}", produces = MediaType.TEXT_HTML_VALUE) - protected String threadAction(ModelMap model, - @PathVariable String uname, - @PathVariable int mid, - @CookieValue(name = "sape_cookie", - required = false, defaultValue = StringUtils.EMPTY) String sapeCookie) { - com.juick.User visitor = UserUtils.getCurrentUser(); - - if (!messagesService.canViewThread(mid, visitor.getUid())) { - throw new HttpForbiddenException(); - } - - com.juick.Message msg = messagesService.getMessage(mid); - - if (msg == null || msg.getUser().isBanned()) { - throw new HttpNotFoundException(); - } - - com.juick.User user = userService.getUserByName(uname); - if (user.isAnonymous() || !msg.getUser().equals(user)) { - return String.format("redirect:/%s/%d", msg.getUser().getName(), mid); - } - msg.VisitorCanComment = !visitor.isAnonymous(); - List<com.juick.Message> replies = messagesService.getReplies(visitor, msg.getMid()); - // this should be after getReplies to mark thread as read - fillUserModel(model, user, visitor); - if (!visitor.isAnonymous()) { - List<Integer> unread = messagesService.getUnread(visitor); - visitor.setUnreadCount(unread.size()); - boolean isMsgAuthor = visitor.getUid() == msg.getUser().getUid(); - boolean isInBL = userService.isInBLAny(msg.getUser().getUid(), visitor.getUid()); - msg.VisitorCanComment = isMsgAuthor || !(msg.ReadOnly || isInBL); - } - model.addAttribute("msg", msg); - - String title = msg.getUser().getName() + ": " + MessageUtils.getTagsString(msg); - - model.addAttribute("title", title); - model.addAttribute("visitor", visitor); - String headers = "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"@" + msg.getUser().getName() + "\" href=\"//rss.juick.com/" + msg.getUser().getName() + "/blog\"/>"; - String pageUrl = "https://juick.com/" + msg.getUser().getName() + "/" + msg.getMid(); - if (msg.Hidden) { - headers += "<meta name=\"robots\" content=\"noindex\"/>"; - } - String cardType = StringUtils.isNotEmpty(msg.getAttachmentType()) ? "summary_large_image" : "summary"; - if (StringUtils.isNotEmpty(msg.getAttachmentType())) { - // additional check in case of broken images - if (msg.getAttachment() != null) { - String msgImage = msg.getAttachment().getMedium().getUrl(); - headers += "<meta property=\"og:image\" content=\"" + msgImage + "\" />"; - } - } else { - String msgImage ="https://i.juick.com/a/" + msg.getUser().getUid() + ".png"; - headers += "<meta property=\"og:image\" content=\"" + msgImage + "\" />"; - } - model.addAttribute("ogtype", "article"); - String cardDescription = StringEscapeUtils.escapeHtml4(PlainTextFormatter.formatTwitterCard(msg)); - headers += "<meta name=\"twitter:card\" content=\"" + cardType + "\" />\n" + - "<meta name=\"twitter:site\" content=\"@juick\" />\n" + - "<meta property=\"og:url\" content=\"" + pageUrl + "\" />\n" + - "<meta property=\"og:title\" content=\"" + msg.getUser().getName() + " at Juick\" />\n" + - "<meta property=\"og:description\" content=\"" + cardDescription + "\" />\n" + - "<meta name=\"Description\" content=\"" + cardDescription + "\" />\n"; - String twitterName = crosspostService.getTwitterName(msg.getUser().getUid()); - if (StringUtils.isNotEmpty(twitterName)) { - headers += "<meta name=\"twitter:creator\" content=\"@" + twitterName + "\" />\n"; - } - if (msg.getTags().size() > 0) { - headers += "<meta name=\"Keywords\" content=\"" + msg.getTags().stream().map(Tag::getName) - .collect(Collectors.joining(", ")) + "\" />\n"; - } - model.addAttribute("headers", headers); - model.addAttribute("visitorSubscribed", messagesService.isSubscribed(visitor.getUid(), msg.getMid())); - model.addAttribute("visitorInBL", userService.isInBL(msg.getUser().getUid(), visitor.getUid())); - model.addAttribute("recomm", messagesService.getMessageRecommendations(msg.getMid())); - List<Integer> blUIDs = new ArrayList<>(); - for (Message reply : replies) { - if (reply.getUser().getUid() != msg.getUser().getUid() - && !blUIDs.contains(reply.getUser().getUid())) { - blUIDs.add(reply.getUser().getUid()); - } - reply.VisitorCanComment = !visitor.isAnonymous(); - if (!visitor.isAnonymous()) { - boolean isMsgAuthor = visitor.getUid() == msg.getUser().getUid(); - boolean isReplyAuthor = visitor.getUid() == reply.getUser().getUid(); - reply.VisitorCanComment = isMsgAuthor || (!msg.ReadOnly - && msg.VisitorCanComment && (isReplyAuthor || !userService.isInBLAny(visitor.getUid(), reply.getUser().getUid()))); - } - } - model.addAttribute("replies", replies); - model.addAttribute("showAdv", visitor.isAnonymous()); - UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); - String queryString = builder.getQuery(); - String requestURI = builder.toUri().getPath(); - if (sape.isPresent() && visitor.isAnonymous() && queryString == null) { - String links = sape.get().getPageLinks(requestURI, sapeCookie).render(); - model.addAttribute("links", links); - } - return "views/thread"; - } - - // when message id is not fit to int - @ExceptionHandler(NumberFormatException.class) - public ResponseEntity<String> notFoundAction() { - return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/controllers/NewMessage.java b/juick-server/src/main/java/com/juick/server/www/controllers/NewMessage.java deleted file mode 100644 index 6b5938a5..00000000 --- a/juick-server/src/main/java/com/juick/server/www/controllers/NewMessage.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.server.www.controllers; - -import com.juick.server.util.UserUtils; -import com.juick.service.TagService; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.text.StringEscapeUtils; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; - -import javax.inject.Inject; -import java.util.stream.Collectors; - -/** - * @author Ugnich Anton - */ -@Controller -public class NewMessage { - - @Inject - private TagService tagService; - @GetMapping("/post") - protected String postAction(@RequestParam(required = false) String body, ModelMap model) { - com.juick.User visitor = UserUtils.getCurrentUser(); - model.addAttribute("title", "Написать"); - model.addAttribute("headers", ""); - model.addAttribute("visitor", visitor); - if (body == null) { - body = StringUtils.EMPTY; - } else { - if (body.length() > 4096) { - body = body.substring(0, 4096); - } - body = StringEscapeUtils.escapeHtml4(body); - } - model.addAttribute("body", body); - model.addAttribute("visitor", visitor); - model.addAttribute("tags", tagService.getUserTagStats(visitor.getUid()).stream() - .sorted((e1, e2) -> Integer.compare(e2.getUsageCount(), e1.getUsageCount())).map(t -> t.getTag().getName()).collect(Collectors.toList())); - return "views/post"; - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/controllers/Settings.java b/juick-server/src/main/java/com/juick/server/www/controllers/Settings.java deleted file mode 100644 index cc8f43eb..00000000 --- a/juick-server/src/main/java/com/juick/server/www/controllers/Settings.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.server.www.controllers; - -import com.juick.User; -import com.juick.model.NotifyOpts; -import com.juick.model.UserInfo; -import com.juick.server.util.HttpBadRequestException; -import com.juick.server.util.HttpUtils; -import com.juick.server.util.UserUtils; -import com.juick.service.*; -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.multipart.MultipartFile; - -import javax.inject.Inject; -import javax.mail.Message; -import javax.mail.MessagingException; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -/** - * - * @author Ugnich Anton - */ -@Controller -public class Settings { - private static final Logger logger = LoggerFactory.getLogger(Settings.class); - - @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String imgDir; - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - @Inject - private TagService tagService; - @Inject - private UserService userService; - @Inject - private CrosspostService crosspostService; - @Inject - private SubscriptionService subscriptionService; - @Inject - private EmailService emailService; - @Inject - private TelegramService telegramService; - @Inject - private ImagesService imagesService; - - @GetMapping("/settings") - protected String doGet(HttpServletRequest request, HttpServletResponse response, ModelMap model) throws IOException { - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - response.sendRedirect("/login"); - } - List<String> pages = Arrays.asList("main", "password", "about", "auth-email", "privacy"); - String page = request.getParameter("page"); - if (StringUtils.isEmpty(page) || !pages.contains(page)) { - page = "main"; - } - - model.addAttribute("title", "Настройки"); - model.addAttribute("visitor", visitor); - model.addAttribute("tags", tagService.getPopularTags()); - model.addAttribute("auths", userService.getAuthCodes(visitor)); - model.addAttribute("email_active", emailService.getNotificationsEmail(visitor.getUid())); - model.addAttribute("ehash", userService.getEmailHash(visitor)); - model.addAttribute("emails", userService.getEmails(visitor)); - model.addAttribute("jids", userService.getAllJIDs(visitor)); - List<String> hours = IntStream.rangeClosed(0, 23).boxed() - .map(i -> StringUtils.leftPad(String.format("%d", i), 2, "0")).collect(Collectors.toList()); - model.addAttribute("hours", hours); - model.addAttribute("fbstatus", crosspostService.getFbCrossPostStatus(visitor.getUid())); - model.addAttribute("twitter_name", crosspostService.getTwitterName(visitor.getUid())); - model.addAttribute("telegram_name", crosspostService.getTelegramName(visitor.getUid())); - model.addAttribute("notify_options", subscriptionService.getNotifyOptions(visitor)); - model.addAttribute("userinfo", userService.getUserInfo(visitor)); - if (page.equals("auth-email")) { - if (emailService.verifyAddressByCode(visitor.getUid(), request.getParameter("code"))) { - ; - model.addAttribute("result", "OK!"); - } else { - model.addAttribute("result", "Sorry, code unknown."); - } - } - return String.format("views/settings_%s", page); - } - - @PostMapping("/settings") - protected String doPost(HttpServletRequest request, HttpServletResponse response, - @RequestParam(required = false) MultipartFile avatar, - ModelMap model) - throws IOException { - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isAnonymous()) { - throw new HttpBadRequestException(); - } - List<String> pages = Arrays.asList("main", "password", "about", "email", "email-add", "email-del", - "email-subscr", "auth-email", "privacy", "jid-del", "twitter-del", "telegram-del", "facebook-disable", - "facebook-enable", "vk-del"); - String page = request.getParameter("page"); - if (StringUtils.isEmpty(page) || !pages.contains(page)) { - throw new HttpBadRequestException(); - } - String result = StringUtils.EMPTY; - switch (page) { - case "password": - if (userService.updatePassword(visitor, request.getParameter("password"))) { - result = "<p>Password has been changed.</p>"; - String hash = userService.getHashByUID(visitor.getUid()); - Cookie c = new Cookie("hash", hash); - c.setMaxAge(365 * 24 * 60 * 60); - response.addCookie(c); - } - break; - case "main": - NotifyOpts opts = new NotifyOpts(); - opts.setRepliesEnabled(StringUtils.isNotEmpty(request.getParameter("jnotify"))); - opts.setSubscriptionsEnabled(StringUtils.isNotEmpty(request.getParameter("subscr_notify"))); - opts.setRecommendationsEnabled(StringUtils.isNotEmpty(request.getParameter("recomm"))); - if (subscriptionService.setNotifyOptions(visitor, opts)) { - result = "<p>Notification options has been updated</p>"; - } - break; - case "about": - UserInfo info = new UserInfo(); - info.setFullName(request.getParameter("fullname")); - info.setCountry(request.getParameter("country")); - info.setUrl(request.getParameter("url")); - info.setDescription(request.getParameter("descr")); - String avatarTmpPath = HttpUtils.receiveMultiPartFile(avatar, tmpDir).getHost(); - if (StringUtils.isNotEmpty(avatarTmpPath)) { - imagesService.saveAvatar(avatarTmpPath, visitor.getUid()); - } - if (userService.updateUserInfo(visitor, info)) { - result = String.format("<p>Your info is updated.</p><p><a href='/%s/'>Back to blog</a>.</p>", visitor.getName()); - } - break; - case "jid-del": - // FIXME: stop using ugnich-csv in parameters - String[] params = request.getParameter("delete").split(";", 2); - boolean res = false; - if (params[0].equals("xmpp")) { - res = userService.deleteJID(visitor.getUid(), params[1]); - } else if (params[0].equals("xmpp-unauth")) { - res = userService.unauthJID(visitor.getUid(), params[1]); - } - if (res) { - result = "<p>Deleted. <a href=\"/settings\">Back</a>.</p>"; - } else { - result = "<p>Error</p>"; - } - break; - case "email-add": - if (!emailService.verifyAddressByCode(visitor.getUid(), request.getParameter("account"))) { - String authCode = RandomStringUtils.randomAlphanumeric(8).toUpperCase(); - if (emailService.addVerificationCode(visitor.getUid(), request.getParameter("account"), authCode)) { - Session session = Session.getDefaultInstance(System.getProperties()); - try { - MimeMessage message = new MimeMessage(session); - message.setFrom(new InternetAddress("noreply@mail.juick.com")); - message.addRecipient(Message.RecipientType.TO, new InternetAddress(request.getParameter("account"))); - message.setSubject("Juick authorization link"); - message.setText(String.format("Follow link to attach this email to Juick account:\n" + - "http://juick.com/settings?page=auth-email&code=%s\n\n" + - "If you don't know, what this mean - just ignore this mail.\n", authCode)); - Transport.send(message); - result = "<p>Authorization link has been sent to your email. Follow it to proceed.</p>" + - "<p><a href=\"/settings\">Back</a></p>"; - - } catch (MessagingException ex) { - logger.error("mail exception", ex); - throw new HttpBadRequestException(); - } - } - } - break; - case "email-del": - if (emailService.deleteEmail(visitor.getUid(), request.getParameter("account"))) { - result = "<p>Deleted. <a href=\"/settings\">Back</a>.</p>"; - } else { - result = "<p>An error occured while deleting.</p>"; - } - break; - case "email-subscr": - if (emailService.setNotificationsEmail(visitor.getUid(), request.getParameter("account"))) { - result = String.format("<p>Saved! Will send notifications to <strong>%s</strong>." + - "</p><p><a href=\"/settings\">Back</a></p>", request.getParameter("account")); - } else { - result = "<p>Disabled.</p><p><a href=\"/settings\">Back</a></p>"; - } - break; - case "twitter-del": - crosspostService.deleteTwitterToken(visitor.getUid()); - for (Cookie cookie : request.getCookies()) { - if (cookie.getName().equals("request_token")) { - cookie.setMaxAge(0); - response.addCookie(cookie); - } - if (cookie.getName().equals("request_token_secret")) { - cookie.setMaxAge(0); - response.addCookie(cookie); - } - } - result = "<p><a href=\"/settings\">Back</a></p>"; - break; - case "telegram-del": - telegramService.deleteTelegramUser(visitor.getUid()); - result = "<p><a href=\"/settings\">Back</a></p>"; - break; - case "facebook-disable": - crosspostService.disableFBCrosspost(visitor.getUid()); - result = "<p><a href=\"/settings\">Back</a></p>"; - break; - case "facebook-enable": - crosspostService.enableFBCrosspost(visitor.getUid()); - result = "<p><a href=\"/settings\">Back</a></p>"; - break; - case "vk-del": - crosspostService.deleteVKUser(visitor.getUid()); - result = "<p><a href=\"/settings\">Back</a></p>"; - break; - default: - throw new HttpBadRequestException(); - } - - model.addAttribute("title", "Настройки"); - model.addAttribute("visitor", visitor); - model.addAttribute("result", result); - return "views/settings_result"; - } - @PostMapping("/settings/unsubscribe") - public String unsubscribeOneClick(@RequestParam(name = "List-Unsubscribe") String unsubscribe, - ModelMap model) { - User user = UserUtils.getCurrentUser(); - if (!user.isAnonymous()) { - if (unsubscribe.equals("One-Click")) { - emailService.setNotificationsEmail(user.getUid(), StringUtils.EMPTY); - model.addAttribute("title", "Настройки"); - model.addAttribute("visitor", user); - model.addAttribute("result", "Unsubscribed"); - return "views/settings_result"; - } - } - throw new HttpBadRequestException(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/controllers/SignUp.java b/juick-server/src/main/java/com/juick/server/www/controllers/SignUp.java deleted file mode 100644 index 6a4fe063..00000000 --- a/juick-server/src/main/java/com/juick/server/www/controllers/SignUp.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.server.www.controllers; - -import com.juick.server.util.HttpBadRequestException; -import com.juick.server.util.HttpForbiddenException; -import com.juick.server.util.UserUtils; -import com.juick.service.CrosspostService; -import com.juick.service.EmailService; -import com.juick.service.MessengerService; -import com.juick.service.UserService; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; - -import javax.inject.Inject; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletResponse; - -/** - * - * @author Ugnich Anton - */ -@Controller -public class SignUp { - - @Inject - private UserService userService; - @Inject - private CrosspostService crosspostService; - @Inject - private MessengerService messengerService; - @Inject - private EmailService emailService; - - - @GetMapping("/signup") - protected String doGet(@RequestParam String type, @RequestParam String hash, ModelMap model) { - com.juick.User visitor = UserUtils.getCurrentUser(); - - if (hash.length() > 36 || !type.matches("^[a-zA-Z0-9\\-]+$") - || !hash.matches("^[a-zA-Z0-9\\-]+$")) { - throw new HttpBadRequestException(); - } - - String account = null; - switch (type) { - case "fb": - account = crosspostService.getFacebookNameByHash(hash); - break; - case "vk": - account = crosspostService.getVKNameByHash(hash); - break; - case "xmpp": - account = crosspostService.getJIDByHash(hash); - break; - case "durov": - account = crosspostService.getTelegramNameByHash(hash); - break; - case "messenger": - account = messengerService.getDisplayName(hash); - break; - case "email": - account = emailService.getEmailByAuthCode(hash); - } - if (account == null) { - throw new HttpBadRequestException(); - } - - model.addAttribute("title", "Новый пользователь"); - model.addAttribute("visitor", visitor); - model.addAttribute("account", account); - model.addAttribute("type", type); - model.addAttribute("hash", hash); - return "views/signup"; - } - - @PostMapping("/signup") - protected String doPost( - HttpServletResponse response, - @RequestParam String type, - @RequestParam String hash, - @RequestParam String action, - @RequestParam(required = false) String username, - @RequestParam(required = false) String password) { - com.juick.User visitor = UserUtils.getCurrentUser(); - int uid = 0; - - if (hash.length() > 36 || !type.matches("^[a-zA-Z0-9\\-]+$") || !hash.matches("^[a-zA-Z0-9\\-]+$")) { - throw new HttpBadRequestException(); - } - - if (action.charAt(0) == 'l') { - - if (visitor.isAnonymous()) { - if (username.length() > 32) { - throw new HttpBadRequestException(); - } - uid = userService.checkPassword(username, password); - } else { - uid = visitor.getUid(); - } - - if (uid <= 0) { - throw new HttpForbiddenException(); - } - - if (!(type.charAt(0) == 'f' && crosspostService.setFacebookUser(hash, uid)) - && !(type.charAt(0) == 'v' && crosspostService.setVKUser(hash, uid)) - && !(type.charAt(0) == 'd' && crosspostService.setTelegramUser(hash, uid)) - && !(type.charAt(0) == 'x' && userService.getAllJIDs(visitor).size() > 0 && crosspostService.setJIDUser(hash, uid)) - && !(type.charAt(0) == 'm' && messengerService.linkMessengerUser(hash, uid))) { - if (type.equals("email")) { - String email = emailService.getEmailByAuthCode(hash); - emailService.addEmail(uid, email); - emailService.deleteAuthCode(hash); - } else { - throw new HttpBadRequestException(); - } - } - - } else { // Create new account - if (username.length() < 2 || username.length() > 16 || !username.matches("^[a-zA-Z0-9\\-]+$") || password.length() < 6 || password.length() > 32) { - throw new HttpBadRequestException(); - } - - // CHECK USERNAME - - uid = userService.createUser(username, password); - if (uid <= 0) { - throw new HttpBadRequestException(); - } - - if (!(type.charAt(0) == 'f' && crosspostService.setFacebookUser(hash, uid)) - && !(type.charAt(0) == 'v' && crosspostService.setVKUser(hash, uid)) - && !(type.charAt(0) == 'd' && crosspostService.setTelegramUser(hash, uid)) - && !(type.charAt(0) == 'm' && messengerService.linkMessengerUser(hash, uid))) { - if (type.equals("email")) { - String email = emailService.getEmailByAuthCode(hash); - emailService.addEmail(uid, email); - emailService.deleteAuthCode(hash); - } else { - throw new HttpBadRequestException(); - } - } - } - - if (visitor.isAnonymous()) { - hash = userService.getHashByUID(uid); - Cookie c = new Cookie("hash", hash); - c.setMaxAge(365 * 24 * 60 * 60); - response.addCookie(c); - } - return "redirect:/"; - } -} diff --git a/juick-server/src/main/java/com/juick/server/www/controllers/SocialLogin.java b/juick-server/src/main/java/com/juick/server/www/controllers/SocialLogin.java deleted file mode 100644 index bc631a1a..00000000 --- a/juick-server/src/main/java/com/juick/server/www/controllers/SocialLogin.java +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.server.www.controllers; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.scribejava.apis.FacebookApi; -import com.github.scribejava.apis.TwitterApi; -import com.github.scribejava.apis.VkontakteApi; -import com.github.scribejava.core.builder.ServiceBuilder; -import com.github.scribejava.core.model.*; -import com.github.scribejava.core.oauth.OAuth10aService; -import com.github.scribejava.core.oauth.OAuth20Service; -import com.juick.model.facebook.User; -import com.juick.server.Utils; -import com.juick.server.util.HttpBadRequestException; -import com.juick.server.util.UserUtils; -import com.juick.service.CrosspostService; -import com.juick.service.EmailService; -import com.juick.service.TelegramService; -import com.juick.service.UserService; -import com.juick.model.vk.UsersResponse; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.codec.digest.HmacAlgorithms; -import org.apache.commons.codec.digest.HmacUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.CookieValue; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; - -/** - * - * @author Ugnich Anton - */ -@Controller -public class SocialLogin { - - private static final Logger logger = LoggerFactory.getLogger(SocialLogin.class); - - @Value("${facebook_appid:appid}") - private String FACEBOOK_APPID; - @Value("${facebook_secret:secret}") - private String FACEBOOK_SECRET; - @Value("${ap_base_uri:http://localhost:8080/}") - private String baseUri; - private String facebookRedirectUri; - private static final String VK_REDIRECT = "http://juick.com/_vklogin"; - private static final String TWITTER_VERIFY_URL = "https://api.twitter.com/1.1/account/verify_credentials.json"; - @Inject - private ObjectMapper jsonMapper; - private ServiceBuilder facebookBuilder, twitterBuilder, vkBuilder; - - @Value("${twitter_consumer_key:appid}") - private String twitterConsumerKey; - @Value("${twitter_consumer_secret:secret}") - private String twitterConsumerSecret; - @Value("${vk_appid:appid}") - private String VK_APPID; - @Value("${vk_secret:secret}") - private String VK_SECRET; - @Value("${telegram_token:secret}") - private String telegramToken; - - @Inject - private CrosspostService crosspostService; - @Inject - private UserService userService; - @Inject - private EmailService emailService; - @Inject - private TelegramService telegramService; - - @PostConstruct - public void init() { - facebookBuilder = new ServiceBuilder(FACEBOOK_APPID); - twitterBuilder = new ServiceBuilder(twitterConsumerKey); - vkBuilder = new ServiceBuilder(VK_APPID); - UriComponentsBuilder facebookRedirectBuilder = UriComponentsBuilder.fromUriString(baseUri); - facebookRedirectUri = facebookRedirectBuilder.replacePath("/_fblogin").build().toUriString(); - } - - @GetMapping("/_fblogin") - protected String doFacebookLogin(HttpServletRequest request, - @RequestParam(required = false) String code, - @RequestParam(required = false) String state, - HttpServletResponse response) throws IOException, ExecutionException, InterruptedException { - if (StringUtils.isBlank(code)) { - String fbstate = UUID.randomUUID().toString(); - if (StringUtils.isBlank(state)) { - state = Utils.getPreviousPageByRequest(request).orElse("https://juick.com/"); - } - crosspostService.addFacebookState(fbstate, state); - OAuth20Service facebookAuthService = facebookBuilder - .apiSecret(FACEBOOK_SECRET) - .callback(facebookRedirectUri) - .scope("email") - .state(fbstate) - .build(FacebookApi.instance()); - return "redirect:" + facebookAuthService.getAuthorizationUrl(); - } - - String redirectUrl = crosspostService.verifyFacebookState(state); - if (StringUtils.isEmpty(redirectUrl)) { - logger.error("state is missing"); - throw new HttpBadRequestException(); - } - OAuth20Service facebookService = facebookBuilder - .apiKey(FACEBOOK_APPID) - .apiSecret(FACEBOOK_SECRET) - .callback(facebookRedirectUri) - .scope("email") - .state(state) - .build(FacebookApi.instance()); - OAuth2AccessToken token = facebookService.getAccessToken(code); - final OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://graph.facebook.com/v2.10/me?fields=id,name,link,verified,email"); - facebookService.signRequest(token, meRequest); - String graph = facebookService.execute(meRequest).getBody(); - if (StringUtils.isBlank(graph)) { - logger.error("FACEBOOK GRAPH ERROR"); - throw new HttpBadRequestException(); - } - User fb = jsonMapper.readValue(graph, User.class); - long fbID = NumberUtils.toLong(fb.getId(), 0); - if (fbID == 0 || StringUtils.isBlank(fb.getName()) || StringUtils.isBlank(fb.getLink())) { - logger.error("Missing required fields, id: {}, name: {}, link: {}", fbID, fb.getName(), fb.getLink()); - throw new HttpBadRequestException(); - } - - int uid = crosspostService.getUIDbyFBID(fbID); - if (uid > 0) { - if (!crosspostService.updateFacebookUser(fbID, token.getAccessToken(), fb.getName(), fb.getLink())) { - logger.error("error updating facebook user, id: {}, token: {}", fbID, token.getAccessToken()); - throw new HttpBadRequestException(); - } - Cookie c = new Cookie("hash", userService.getHashByUID(uid)); - c.setMaxAge(50 * 24 * 60 * 60); - response.addCookie(c); - return "redirect:" + redirectUrl; - } else if (fb.getVerified()) { - if (!crosspostService.createFacebookUser(fbID, state, token.getAccessToken(), fb.getName(), fb.getLink())) { - if (StringUtils.isNotEmpty(fb.getEmail())) { - logger.info("found {} for facebook user {}", fb.getEmail(), fb.getLink()); - Integer userId = crosspostService.getUIDbyFBID(fbID); - if (!emailService.getEmails(userId, false).contains(fb.getEmail())) { - emailService.addEmail(userId, fb.getEmail()); - } - } - logger.info("email not found for facebook user {}", fb.getLink()); - throw new HttpBadRequestException(); - } - return "redirect:/signup?type=fb&hash=" + state; - } else { - logger.error("Facebook account is not verified, id: {}", fbID); - throw new HttpBadRequestException(); - } - } - @GetMapping("/_twitter") - protected void doTwitterLogin(HttpServletRequest request, HttpServletResponse response) - throws IOException, ExecutionException, InterruptedException { - String hash = StringUtils.EMPTY, request_token = StringUtils.EMPTY, request_token_secret = StringUtils.EMPTY; - String verifier = request.getParameter("oauth_verifier"); - Cookie[] cookies = request.getCookies(); - for (Cookie cookie : cookies) { - if (cookie.getName().equals("hash")) { - hash = cookie.getValue(); - } - if (cookie.getName().equals("request_token")) { - request_token = cookie.getValue(); - } - if (cookie.getName().equals("request_token_secret")) { - request_token_secret = cookie.getValue(); - } - } - com.juick.User user = UserUtils.getCurrentUser(); - OAuth10aService oAuthService = twitterBuilder - .apiSecret(twitterConsumerSecret) - .callback("https://juick.com/_twitter") - .build(TwitterApi.instance()); - - if (request_token.isEmpty() && request_token_secret.isEmpty() - && (verifier == null || verifier.isEmpty())) { - OAuth1RequestToken requestToken = oAuthService.getRequestToken(); - String authUrl = oAuthService.getAuthorizationUrl(requestToken); - response.addCookie(new Cookie("request_token", requestToken.getToken())); - response.addCookie(new Cookie("request_token_secret", requestToken.getTokenSecret())); - response.setStatus(HttpServletResponse.SC_FOUND); - response.setHeader("Location", authUrl); - } else { - if (verifier != null && verifier.length() > 0) { - OAuth1RequestToken requestToken = new OAuth1RequestToken(request_token, request_token_secret); - OAuth1AccessToken accessToken = oAuthService.getAccessToken(requestToken, verifier); - OAuthRequest oAuthRequest = new OAuthRequest(Verb.GET, TWITTER_VERIFY_URL); - oAuthService.signRequest(accessToken, oAuthRequest); - com.juick.model.twitter.User twitterUser = jsonMapper.readValue(oAuthService.execute(oAuthRequest).getBody(), - com.juick.model.twitter.User.class); - if (userService.linkTwitterAccount(user, accessToken.getToken(), accessToken.getTokenSecret(), - twitterUser.getScreenName())) { - response.setStatus(HttpServletResponse.SC_FOUND); - response.setHeader("Location", "http://juick.com/settings"); - } else { - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - } - } - } - } - @GetMapping("/_vklogin") - protected String doVKLogin(HttpServletRequest request, - @RequestParam(required = false) String code, - @RequestParam(required = false) String state, - @CookieValue(required = false) String vkstate, - HttpServletResponse response) throws IOException, ExecutionException, InterruptedException { - if (StringUtils.isBlank(code)) { - vkstate = UUID.randomUUID().toString(); - Cookie c = new Cookie("vkstate", vkstate); - response.addCookie(c); - OAuth20Service vkAuthService = vkBuilder - .apiSecret(VK_SECRET) - .scope("friends,wall,offline") - .state(vkstate) - .callback(VK_REDIRECT) - .build(VkontakteApi.instance()); - return "redirect:" + vkAuthService.getAuthorizationUrl(); - } - - if (StringUtils.isBlank(vkstate) || !vkstate.equals(state)) { - throw new HttpBadRequestException(); - } else { - Cookie c = new Cookie("vkstate", "-"); - c.setMaxAge(0); - response.addCookie(c); - } - - OAuth20Service vkService = vkBuilder - .apiKey(VK_APPID) - .apiSecret(VK_SECRET) - .build(VkontakteApi.instance()); - OAuth2AccessToken token = vkService.getAccessToken(code); - - OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://api.vk.com/method/users.get?fields=screen_name&v=5.73"); - vkService.signRequest(token, meRequest); - String graph = vkService.execute(meRequest).getBody(); - - com.juick.model.vk.User jsonUser = jsonMapper.readValue(graph, UsersResponse.class).getUsers().get(0); - String vkName = jsonUser.getFirstName() + " " + jsonUser.getLastName(); - String vkLink = jsonUser.getScreenName(); - - if (vkName.length() == 1 || StringUtils.isBlank(vkLink)) { - logger.error("vk user error"); - throw new HttpBadRequestException(); - } - - Long vkID = NumberUtils.toLong(jsonUser.getId(), 0); - int uid = crosspostService.getUIDbyVKID(vkID); - if (uid > 0) { - Cookie c = new Cookie("hash", userService.getHashByUID(uid)); - c.setMaxAge(50 * 24 * 60 * 60); - response.addCookie(c); - return "redirect:/" + Utils.getPreviousPageByRequest(request).orElse(StringUtils.EMPTY); - } else { - String loginhash = UUID.randomUUID().toString(); - if (!crosspostService.createVKUser(vkID, loginhash, token.getAccessToken(), vkName, vkLink)) { - logger.error("create vk user error"); - throw new HttpBadRequestException(); - } - return "redirect:/signup?type=vk&hash=" + loginhash; - } - } - - @GetMapping("/_tglogin") - public String doDurovLogin(HttpServletRequest request, - @RequestParam Map<String, String> params, - HttpServletResponse response) { - String dataCheckString = params.entrySet().stream() - .filter(p -> !p.getKey().equals("hash")) - .sorted(Map.Entry.comparingByKey()) - .map(p -> p.getKey() + "=" + p.getValue()) - .collect(Collectors.joining("\n")); - String hash = params.get("hash"); - byte[] secretKey = DigestUtils.sha256(telegramToken); - String resultString = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, secretKey).hmacHex(dataCheckString); - if (hash.equals(resultString)) { - Long tgUser = Long.valueOf(params.get("id")); - int uid = telegramService.getUser(tgUser); - if (uid > 0) { - Cookie c = new Cookie("hash", userService.getHashByUID(uid)); - c.setMaxAge(50 * 24 * 60 * 60); - response.addCookie(c); - return "redirect:/" + Utils.getPreviousPageByRequest(request).orElse(StringUtils.EMPTY); - } else { - String username = StringUtils.defaultString(params.get("username"), params.get("first_name")); - telegramService.createTelegramUser(tgUser, username); - return "redirect:/signup?type=durov&hash=" + userService.getSignUpHashByTelegramID(tgUser, username); - } - } else { - logger.warn("invalid tg hash {} for {}", resultString, hash); - } - throw new HttpBadRequestException(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/JidConverter.java b/juick-server/src/main/java/com/juick/server/xmpp/JidConverter.java deleted file mode 100644 index e9a9707e..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/JidConverter.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.juick.server.xmpp; - -import org.springframework.core.convert.converter.Converter; -import org.springframework.lang.Nullable; -import rocks.xmpp.addr.Jid; - -public class JidConverter implements Converter<String, Jid> { - @Nullable - @Override - public Jid convert(String jidStr) { - return Jid.of(jidStr); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/XMPPStatusPage.java b/juick-server/src/main/java/com/juick/server/xmpp/XMPPStatusPage.java deleted file mode 100644 index 231696ec..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/XMPPStatusPage.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.juick.server.xmpp; - -import com.juick.server.XMPPServer; -import com.juick.server.xmpp.helpers.XMPPStatus; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; -import rocks.xmpp.addr.Jid; -import springfox.documentation.annotations.ApiIgnore; - -import javax.inject.Inject; -import java.util.stream.Collectors; - -@RestController -@ConditionalOnProperty("xmppbot_jid") -public class XMPPStatusPage { - @Inject - private XMPPServer xmpp; - @ApiIgnore - @RequestMapping(method = RequestMethod.GET, value = "/api/xmpp-status", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public XMPPStatus xmppStatus() { - XMPPStatus status = new XMPPStatus(); - if (xmpp != null) { - status.setInbound(xmpp.getInConnections().stream().map(c -> c.from).flatMap(j -> j.stream().map(Jid::getDomain)).collect(Collectors.toList())); - status.setOutbound(xmpp.getOutConnections().keySet().stream() - .map(c -> c.to).map(Jid::getDomain).collect(Collectors.toList())); - } - return status; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/helpers/XMPPStatus.java b/juick-server/src/main/java/com/juick/server/xmpp/helpers/XMPPStatus.java deleted file mode 100644 index 99d89866..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/helpers/XMPPStatus.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.helpers; - -import com.juick.server.xmpp.s2s.ConnectionIn; -import com.juick.server.xmpp.s2s.ConnectionOut; - -import java.util.List; -import java.util.Set; - -/** - * Created by vitalyster on 16.02.2017. - */ -public class XMPPStatus { - private List<String> inbound; - private List<String> outbound; - - public List<String> getInbound() { - return inbound; - } - - public void setInbound(List<String> inbound) { - this.inbound = inbound; - } - - public List<String> getOutbound() { - return outbound; - } - - public void setOutbound(List<String> outbound) { - this.outbound = outbound; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/iq/MessageQuery.java b/juick-server/src/main/java/com/juick/server/xmpp/iq/MessageQuery.java deleted file mode 100644 index 7500cbf8..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/iq/MessageQuery.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.juick.server.xmpp.iq; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement(name = "query") -public class MessageQuery { - private MessageQuery() { - - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/iq/package-info.java b/juick-server/src/main/java/com/juick/server/xmpp/iq/package-info.java deleted file mode 100644 index dada8289..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/iq/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlSchema(namespace = "http://juick.com/query#messages", elementFormDefault = XmlNsForm.QUALIFIED) -package com.juick.server.xmpp.iq; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlNsForm; -import javax.xml.bind.annotation.XmlSchema;
\ No newline at end of file diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/Handshake.java b/juick-server/src/main/java/com/juick/server/xmpp/router/Handshake.java deleted file mode 100644 index 0bc501dd..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/Handshake.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.juick.server.xmpp.router; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; - -/** - * Created by vitalyster on 30.01.2017. - */ -public class Handshake { - private String value; - - public static Handshake parse(XmlPullParser parser) throws IOException, XmlPullParserException { - parser.next(); - Handshake handshake = new Handshake(); - handshake.setValue(XmlUtils.getTagText(parser)); - return handshake; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - @Override - public String toString() { - StringBuilder str = new StringBuilder("<handshake"); - if (getValue() != null) { - str.append(">").append(getValue()).append("</handshake>"); - } else { - str.append("/>"); - } - return str.toString(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/Stream.java b/juick-server/src/main/java/com/juick/server/xmpp/router/Stream.java deleted file mode 100644 index 2154edf6..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/Stream.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Juick - * Copyright (C) 2008-2011, Ugnich Anton - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.server.xmpp.router; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlPullParserFactory; -import rocks.xmpp.addr.Jid; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.UUID; - -/** - * - * @author Ugnich Anton - */ -public abstract class Stream { - - public boolean isLoggedIn() { - return loggedIn; - } - - public void setLoggedIn(boolean loggedIn) { - this.loggedIn = loggedIn; - } - - public Jid from; - public Jid to; - private InputStream is; - private OutputStream os; - private XmlPullParserFactory factory; - protected XmlPullParser parser; - private OutputStreamWriter writer; - StreamHandler streamHandler; - private boolean loggedIn; - private Instant created; - private Instant updated; - String streamId; - private boolean secured; - - public Stream(final Jid from, final Jid to, final InputStream is, final OutputStream os) throws XmlPullParserException { - this.from = from; - this.to = to; - this.is = is; - this.os = os; - factory = XmlPullParserFactory.newInstance(); - created = updated = Instant.now(); - streamId = UUID.randomUUID().toString(); - } - - public void restartStream() throws XmlPullParserException { - parser = factory.newPullParser(); - parser.setInput(new InputStreamReader(is, StandardCharsets.UTF_8)); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - writer = new OutputStreamWriter(os, StandardCharsets.UTF_8); - } - - public void connect() { - try { - restartStream(); - handshake(); - parse(); - } catch (XmlPullParserException e) { - StreamError invalidXmlError = new StreamError("invalid-xml"); - send(invalidXmlError.toString()); - connectionFailed(new Exception(invalidXmlError.getCondition())); - } catch (IOException e) { - connectionFailed(e); - } - } - - public void setHandler(final StreamHandler streamHandler) { - this.streamHandler = streamHandler; - } - - public abstract void handshake() throws XmlPullParserException, IOException; - - public void logoff() { - setLoggedIn(false); - try { - writer.flush(); - writer.close(); - //TODO close parser - } catch (final Exception e) { - connectionFailed(e); - } - } - - public void send(final String str) { - try { - updated = Instant.now(); - writer.write(str); - writer.flush(); - } catch (final Exception e) { - connectionFailed(e); - } - } - - private void parse() throws IOException, XmlPullParserException { - while (parser.next() != XmlPullParser.END_DOCUMENT) { - if (parser.getEventType() == XmlPullParser.IGNORABLE_WHITESPACE) { - setUpdated(); - } - if (parser.getEventType() != XmlPullParser.START_TAG) { - continue; - } - setUpdated(); - final String tag = parser.getName(); - switch (tag) { - case "message": - case "presence": - case "iq": - streamHandler.stanzaReceived(XmlUtils.parseToString(parser, false)); - break; - case "error": - StreamError error = StreamError.parse(parser); - connectionFailed(new Exception(error.getCondition())); - return; - default: - XmlUtils.skip(parser); - break; - } - } - } - - /** - * This method is used to be called on a parser or a connection error. - * It tries to close the XML-Reader and XML-Writer one last time. - */ - private void connectionFailed(final Exception ex) { - if (isLoggedIn()) { - try { - writer.close(); - //TODO close parser - } catch (Exception e) { - } - } - if (streamHandler != null) { - streamHandler.fail(ex); - } - } - - public Instant getCreated() { - return created; - } - - public Instant getUpdated() { - return updated; - } - public String getStreamId() { - return streamId; - } - - public boolean isSecured() { - return secured; - } - - public void setSecured(boolean secured) { - this.secured = secured; - } - - public void setUpdated() { - this.updated = Instant.now(); - } - - public InputStream getInputStream() { - return is; - } - - public void setInputStream(InputStream is) { - this.is = is; - } - - public OutputStream getOutputStream() { - return os; - } - - public void setOutputStream(OutputStream os) { - this.os = os; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamComponentServer.java b/juick-server/src/main/java/com/juick/server/xmpp/router/StreamComponentServer.java deleted file mode 100644 index a58adfc5..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamComponentServer.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.juick.server.xmpp.router; - -import org.apache.commons.codec.digest.DigestUtils; -import org.xmlpull.v1.XmlPullParserException; -import rocks.xmpp.addr.Jid; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.UUID; - -/** - * Created by vitalyster on 30.01.2017. - */ -public class StreamComponentServer extends Stream { - - private String streamId, secret; - - public String getStreamId() { - return streamId; - } - - - public StreamComponentServer(InputStream is, OutputStream os, String password) throws XmlPullParserException { - super(null, null, is, os); - secret = password; - streamId = UUID.randomUUID().toString(); - } - @Override - public void handshake() throws XmlPullParserException, IOException { - parser.next(); - if (!parser.getName().equals("stream") - || !parser.getNamespace(null).equals(StreamNamespaces.NS_COMPONENT_ACCEPT) - || !parser.getNamespace("stream").equals(StreamNamespaces.NS_STREAM)) { - throw new IOException("invalid stream"); - } - Jid domain = Jid.of(parser.getAttributeValue(null, "to")); - if (streamHandler.filter(null, domain)) { - send(new XMPPError(XMPPError.Type.cancel, "forbidden").toString()); - throw new IOException("invalid domain"); - } - from = domain; - to = domain; - send(String.format("<stream:stream xmlns:stream='%s' " + - "xmlns='%s' from='%s' id='%s'>", StreamNamespaces.NS_STREAM, StreamNamespaces.NS_COMPONENT_ACCEPT, from.asBareJid().toEscapedString(), streamId)); - Handshake handshake = Handshake.parse(parser); - boolean authenticated = handshake.getValue().equals(DigestUtils.sha1Hex(streamId + secret)); - setLoggedIn(authenticated); - if (!authenticated) { - send(new XMPPError(XMPPError.Type.cancel, "not-authorized").toString()); - streamHandler.fail(new IOException("stream:stream, failed authentication")); - return; - } - send(new Handshake().toString()); - streamHandler.ready(this); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamError.java b/juick-server/src/main/java/com/juick/server/xmpp/router/StreamError.java deleted file mode 100644 index f731f039..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamError.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.juick.server.xmpp.router; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; - - -/** - * Created by vitalyster on 03.02.2017. - */ -public class StreamError { - - private String condition; - private String text; - - public StreamError() {} - - public StreamError(String condition) { - this.condition = condition; - } - - public static StreamError parse(XmlPullParser parser) throws IOException, XmlPullParserException { - StreamError streamError = new StreamError(); - final int initial = parser.getDepth(); - while (true) { - int eventType = parser.next(); - if (eventType == XmlPullParser.START_TAG && parser.getDepth() == initial + 1) { - final String tag = parser.getName(); - final String xmlns = parser.getNamespace(); - if (tag.equals("text") && xmlns.equals(StreamNamespaces.NS_XMPP_STREAMS)) { - streamError.text = XmlUtils.getTagText(parser); - } else if (xmlns.equals(StreamNamespaces.NS_XMPP_STREAMS)) { - streamError.condition = tag; - } else { - XmlUtils.skip(parser); - } - } else if (eventType == XmlPullParser.END_TAG && parser.getDepth() == initial) { - break; - } - } - return streamError; - } - - public String getCondition() { - return condition; - } - - @Override - public String toString() { - return String.format("<stream:error><%s xmlns='%s'/></stream:error>", condition, StreamNamespaces.NS_XMPP_STREAMS); - } - - public String getText() { - return text; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamFeatures.java b/juick-server/src/main/java/com/juick/server/xmpp/router/StreamFeatures.java deleted file mode 100644 index e8fc324f..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamFeatures.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Juick - * Copyright (C) 2008-2013, Ugnich Anton - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.server.xmpp.router; - -import java.io.IOException; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -/** - * - * @author Ugnich Anton - */ -public class StreamFeatures { - - public static final int NOTAVAILABLE = -1; - public static final int AVAILABLE = 0; - public static final int REQUIRED = 1; - public int STARTTLS = NOTAVAILABLE; - public int ZLIB = NOTAVAILABLE; - public int PLAIN = NOTAVAILABLE; - public int DIGEST_MD5 = NOTAVAILABLE; - public int REGISTER = NOTAVAILABLE; - public int EXTERNAL = NOTAVAILABLE; - - public static StreamFeatures parse(final XmlPullParser parser) throws XmlPullParserException, IOException { - StreamFeatures features = new StreamFeatures(); - final int initial = parser.getDepth(); - while (true) { - int eventType = parser.next(); - if (eventType == XmlPullParser.START_TAG && parser.getDepth() == initial + 1) { - final String tag = parser.getName(); - final String xmlns = parser.getNamespace(); - if (tag.equals("starttls") && xmlns != null && xmlns.equals("urn:ietf:params:xml:ns:xmpp-tls")) { - features.STARTTLS = AVAILABLE; - while (parser.next() == XmlPullParser.START_TAG) { - if (parser.getName().equals("required")) { - features.STARTTLS = REQUIRED; - } else { - XmlUtils.skip(parser); - } - } - } else if (tag.equals("compression") && xmlns != null && xmlns.equals("http://jabber.org/features/compress")) { - while (parser.next() == XmlPullParser.START_TAG) { - if (parser.getName().equals("method")) { - final String method = XmlUtils.getTagText(parser).toUpperCase(); - if (method.equals("ZLIB")) { - features.ZLIB = AVAILABLE; - } - } else { - XmlUtils.skip(parser); - } - } - } else if (tag.equals("mechanisms") && xmlns != null && xmlns.equals("urn:ietf:params:xml:ns:xmpp-sasl")) { - while (parser.next() == XmlPullParser.START_TAG) { - if (parser.getName().equals("mechanism")) { - final String mechanism = XmlUtils.getTagText(parser).toUpperCase(); - if (mechanism.equals("PLAIN")) { - features.PLAIN = AVAILABLE; - } else if (mechanism.equals("DIGEST-MD5")) { - features.DIGEST_MD5 = AVAILABLE; - } else if (mechanism.equals("EXTERNAL")) { - features.EXTERNAL = AVAILABLE; - } - } else { - XmlUtils.skip(parser); - } - } - } else if (tag.equals("register") && xmlns != null && xmlns.equals("http://jabber.org/features/iq-register")) { - features.REGISTER = AVAILABLE; - XmlUtils.skip(parser); - } else { - XmlUtils.skip(parser); - } - } else if (eventType == XmlPullParser.END_TAG && parser.getDepth() == initial) { - break; - } - } - return features; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamHandler.java b/juick-server/src/main/java/com/juick/server/xmpp/router/StreamHandler.java deleted file mode 100644 index 048c61ec..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamHandler.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.juick.server.xmpp.router; - -import rocks.xmpp.addr.Jid; - -/** - * Created by vitalyster on 01.02.2017. - */ -public interface StreamHandler { - void ready(StreamComponentServer componentServer); - void fail(final Exception ex); - boolean filter(Jid from, Jid to); - void stanzaReceived(String stanza); -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamNamespaces.java b/juick-server/src/main/java/com/juick/server/xmpp/router/StreamNamespaces.java deleted file mode 100644 index 1b9b1965..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/StreamNamespaces.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.juick.server.xmpp.router; - -public class StreamNamespaces { - public static final String NS_STREAM = "http://etherx.jabber.org/streams"; - public static final String NS_TLS = "urn:ietf:params:xml:ns:xmpp-tls"; - public static final String NS_DB = "jabber:server:dialback"; - public static final String NS_SERVER = "jabber:server"; - public static final String NS_COMPONENT_ACCEPT = "jabber:component:accept"; - public static final String NS_XMPP_STREAMS = "urn:ietf:params:xml:ns:xmpp-streams"; -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPError.java b/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPError.java deleted file mode 100644 index 0cf9a3bc..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPError.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Juick - * Copyright (C) 2008-2013, ugnich - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.server.xmpp.router; - -import org.apache.commons.text.StringEscapeUtils; - -/** - * - * @author ugnich - */ -public class XMPPError { - - public static final class Type { - - public static final String auth = "auth"; - public static final String cancel = "cancel"; - public static final String continue_ = "continue"; - public static final String modify = "modify"; - public static final String wait = "wait"; - } - private final static String TagName = "error"; - public String by = null; - private String type; - private String condition; - private String text = null; - - public XMPPError(String type, String condition) { - this.type = type; - this.condition = condition; - } - - @Override - public String toString() { - StringBuilder str = new StringBuilder("<").append(TagName).append(""); - if (by != null) { - str.append(" by=\"").append(StringEscapeUtils.escapeXml10(by)).append("\""); - } - if (type != null) { - str.append(" type=\"").append(StringEscapeUtils.escapeXml10(type)).append("\""); - } - - if (condition != null) { - str.append(">"); - str.append("<").append(StringEscapeUtils.escapeXml10(condition)).append(" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\""); - if (text != null) { - str.append(">").append(StringEscapeUtils.escapeXml10(text)).append("</").append(StringEscapeUtils.escapeXml10(condition)) - .append(">"); - } else { - str.append("/>"); - } - str.append("</").append(TagName).append(">"); - } else { - str.append("/>"); - } - - return str.toString(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPRouter.java b/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPRouter.java deleted file mode 100644 index 6d67fa9c..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/XMPPRouter.java +++ /dev/null @@ -1,220 +0,0 @@ -package com.juick.server.xmpp.router; - -import com.juick.server.XMPPServer; -import com.juick.server.xmpp.s2s.BasicXmppSession; -import com.juick.server.xmpp.s2s.CacheEntry; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.xmlpull.v1.XmlPullParserException; -import rocks.xmpp.addr.Jid; -import rocks.xmpp.core.stanza.model.IQ; -import rocks.xmpp.core.stanza.model.Message; -import rocks.xmpp.core.stanza.model.Presence; -import rocks.xmpp.core.stanza.model.Stanza; -import rocks.xmpp.core.stanza.model.server.ServerIQ; -import rocks.xmpp.core.stanza.model.server.ServerMessage; -import rocks.xmpp.core.stanza.model.server.ServerPresence; -import rocks.xmpp.util.XmppUtils; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; -import java.io.IOException; -import java.io.StringReader; -import java.io.StringWriter; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketException; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ExecutorService; - -public class XMPPRouter implements StreamHandler { - private static final Logger logger = LoggerFactory.getLogger("com.juick.server.xmpp"); - - @Inject - private ExecutorService service; - - private final List<StreamComponentServer> connections = Collections.synchronizedList(new ArrayList<>()); - private final List<CacheEntry> outCache = new CopyOnWriteArrayList<>(); - - private ServerSocket listener; - - @Inject - private BasicXmppSession session; - - @Value("${router_port:5347}") - private int routerPort; - - @Inject - private XMPPServer xmppServer; - - @PostConstruct - public void init() { - logger.info("component router initialized"); - service.submit(() -> { - try { - listener = new ServerSocket(routerPort); - logger.info("component router listening on {}", routerPort); - while (!listener.isClosed()) { - if (Thread.currentThread().isInterrupted()) break; - Socket socket = listener.accept(); - service.submit(() -> { - try { - StreamComponentServer client = new StreamComponentServer(socket.getInputStream(), socket.getOutputStream(), "secret"); - addConnectionIn(client); - client.setHandler(this); - client.connect(); - } catch (IOException e) { - logger.error("component error", e); - } catch (XmlPullParserException e) { - e.printStackTrace(); - } - }); - } - } catch (SocketException e) { - // shutdown - } catch (IOException e) { - logger.warn("io exception", e); - } - }); - } - - @PreDestroy - public void close() throws Exception { - if (!listener.isClosed()) { - listener.close(); - } - synchronized (getConnections()) { - for (Iterator<StreamComponentServer> i = getConnections().iterator(); i.hasNext(); ) { - StreamComponentServer c = i.next(); - c.logoff(); - i.remove(); - } - } - service.shutdown(); - logger.info("XMPP router destroyed"); - } - - private void addConnectionIn(StreamComponentServer c) { - synchronized (getConnections()) { - getConnections().add(c); - } - } - - private void sendOut(Stanza s) { - try { - StringWriter stanzaWriter = new StringWriter(); - XMLStreamWriter xmppStreamWriter = XmppUtils.createXmppStreamWriter( - session.getConfiguration().getXmlOutputFactory().createXMLStreamWriter(stanzaWriter)); - session.createMarshaller().marshal(s, xmppStreamWriter); - xmppStreamWriter.flush(); - xmppStreamWriter.close(); - String xml = stanzaWriter.toString(); - logger.info("XMPPRouter (out): {}", xml); - sendOut(s.getTo().getDomain(), xml); - } catch (XMLStreamException | JAXBException e1) { - logger.info("jaxb exception", e1); - } - } - - private void sendOut(String hostname, String xml) { - boolean haveAnyConn = false; - - StreamComponentServer connOut = null; - synchronized (getConnections()) { - for (StreamComponentServer c : getConnections()) { - if (c.to != null && c.to.getDomain().equals(hostname)) { - if (c.isLoggedIn()) { - connOut = c; - break; - } else { - logger.info("bouncing stanza to {} component until it will be ready", hostname); - boolean haveCache = false; - for (CacheEntry entry : outCache) { - if (entry.hostname != null && entry.hostname.equals(hostname)) { - entry.xml += xml; - entry.updated = Instant.now(); - haveCache = true; - break; - } - } - if (!haveCache) { - outCache.add(new CacheEntry(Jid.of(hostname), xml)); - } - } - } - } - } - if (connOut != null) { - connOut.send(xml); - return; - } - xmppServer.sendOut(Jid.of(hostname), xml); - - } - - public List<StreamComponentServer> getConnections() { - return connections; - } - - private Stanza parse(String xml) { - try { - Unmarshaller unmarshaller = session.createUnmarshaller(); - return (Stanza)unmarshaller.unmarshal(new StringReader(xml)); - } catch (JAXBException e) { - logger.error("JAXB exception", e); - } - return null; - } - @Override - public void stanzaReceived(String stanza) { - Stanza input = parse(stanza); - if (input instanceof Message) { - sendOut(ServerMessage.from((Message)input)); - } else if (input instanceof IQ) { - sendOut(ServerIQ.from((IQ)input)); - } else { - sendOut(ServerPresence.from((Presence) input)); - } - } - - public String getFromCache(Jid to) { - final String[] cache = new String[1]; - outCache.stream().filter(c -> c.hostname != null && c.hostname.equals(to)).findFirst().ifPresent(c -> { - cache[0] = c.xml; - outCache.remove(c); - }); - return cache[0]; - } - - @Override - public void ready(StreamComponentServer componentServer) { - logger.info("component {} ready", componentServer.to); - String cache = getFromCache(componentServer.to); - if (cache != null) { - logger.debug("sending cache to {}", componentServer.to); - componentServer.send(cache); - } - } - - @Override - public void fail(Exception e) { - - } - - @Override - public boolean filter(Jid jid, Jid jid1) { - return false; - } -}
\ No newline at end of file diff --git a/juick-server/src/main/java/com/juick/server/xmpp/router/XmlUtils.java b/juick-server/src/main/java/com/juick/server/xmpp/router/XmlUtils.java deleted file mode 100644 index 7579489f..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/router/XmlUtils.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Juick - * Copyright (C) 2008-2011, Ugnich Anton - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.juick.server.xmpp.router; - -import java.io.IOException; - -import org.apache.commons.text.StringEscapeUtils; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -/** - * - * @author Ugnich Anton - */ -public class XmlUtils { - - public static void skip(XmlPullParser parser) throws XmlPullParserException, IOException { - String tag = parser.getName(); - while (parser.getName() != null && !(parser.next() == XmlPullParser.END_TAG && parser.getName().equals(tag))) { - } - } - - public static String getTagText(XmlPullParser parser) throws XmlPullParserException, IOException { - String ret = ""; - String tag = parser.getName(); - - if (parser.next() == XmlPullParser.TEXT) { - ret = parser.getText(); - } - - while (!(parser.getEventType() == XmlPullParser.END_TAG && parser.getName().equals(tag))) { - parser.next(); - } - - return ret; - } - - public static String parseToString(XmlPullParser parser, boolean skipXMLNS) throws XmlPullParserException, IOException { - String tag = parser.getName(); - StringBuilder ret = new StringBuilder("<").append(tag); - - // skipXMLNS for xmlns="jabber:client" - - String ns = parser.getNamespace(); - if (!skipXMLNS && ns != null && !ns.isEmpty()) { - ret.append(" xmlns=\"").append(ns).append("\""); - } - - for (int i = 0; i < parser.getAttributeCount(); i++) { - String attr = parser.getAttributeName(i); - if ((!skipXMLNS || !attr.equals("xmlns")) && !attr.contains(":")) { - ret.append(" ").append(attr).append("=\"").append(StringEscapeUtils.escapeXml10(parser.getAttributeValue(i))).append("\""); - } - } - ret.append(">"); - - while (!(parser.next() == XmlPullParser.END_TAG && parser.getName().equals(tag))) { - int event = parser.getEventType(); - if (event == XmlPullParser.START_TAG) { - if (!parser.getName().contains(":")) { - ret.append(parseToString(parser, false)); - } else { - skip(parser); - } - } else if (event == XmlPullParser.TEXT) { - ret.append(StringEscapeUtils.escapeXml10(parser.getText())); - } - } - - ret.append("</").append(tag).append(">"); - return ret.toString(); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/BasicXmppSession.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/BasicXmppSession.java deleted file mode 100644 index ae28f827..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/BasicXmppSession.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.s2s; - -import rocks.xmpp.addr.Jid; -import rocks.xmpp.core.XmppException; -import rocks.xmpp.core.session.XmppSession; -import rocks.xmpp.core.session.XmppSessionConfiguration; -import rocks.xmpp.core.stanza.model.IQ; -import rocks.xmpp.core.stanza.model.Message; -import rocks.xmpp.core.stanza.model.Presence; -import rocks.xmpp.core.stanza.model.server.ServerIQ; -import rocks.xmpp.core.stanza.model.server.ServerMessage; -import rocks.xmpp.core.stanza.model.server.ServerPresence; -import rocks.xmpp.core.stream.model.StreamElement; - -/** - * Created by vitalyster on 06.02.2017. - */ -public class BasicXmppSession extends XmppSession { - protected BasicXmppSession(String xmppServiceDomain, XmppSessionConfiguration configuration) { - super(xmppServiceDomain, configuration); - } - - public static BasicXmppSession create(String xmppServiceDomain, XmppSessionConfiguration configuration) { - BasicXmppSession session = new BasicXmppSession(xmppServiceDomain, configuration); - notifyCreationListeners(session); - return session; - } - - @Override - public void connect(Jid from) throws XmppException { - - } - - @Override - public Jid getConnectedResource() { - return null; - } - - @Override - protected StreamElement prepareElement(StreamElement element) { - if (element instanceof Message) { - element = ServerMessage.from((Message) element); - } else if (element instanceof Presence) { - element = ServerPresence.from((Presence) element); - } else if (element instanceof IQ) { - element = ServerIQ.from((IQ) element); - } - - return element; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/CacheEntry.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/CacheEntry.java deleted file mode 100644 index 33e875bd..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/CacheEntry.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.s2s; - -import rocks.xmpp.addr.Jid; - -import java.time.Instant; - -/** - * - * @author ugnich - */ -public class CacheEntry { - - public Jid hostname; - public Instant created; - public Instant updated; - public String xml; - - public CacheEntry(Jid hostname, String xml) { - this.hostname = hostname; - this.created = this.updated =Instant.now(); - this.xml = xml; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/Connection.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/Connection.java deleted file mode 100644 index 4fa8e741..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/Connection.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.s2s; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.juick.server.XMPPServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlPullParserFactory; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.net.Socket; -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.UUID; - -/** - * - * @author ugnich - */ -public class Connection { - - protected static final Logger logger = LoggerFactory.getLogger(Connection.class); - - public String streamID; - public Instant created; - public Instant updated; - public long bytesLocal = 0; - public long packetsLocal = 0; - XMPPServer xmpp; - private Socket socket; - public static final String NS_DB = "jabber:server:dialback"; - public static final String NS_TLS = "urn:ietf:params:xml:ns:xmpp-tls"; - public static final String NS_SASL = "urn:ietf:params:xml:ns:xmpp-sasl"; - public static final String NS_STREAM = "http://etherx.jabber.org/streams"; - XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); - XmlPullParser parser = factory.newPullParser(); - OutputStreamWriter writer; - private boolean secured = false; - private boolean authenticated = false; - private boolean trusted = false; - - - - public Connection(XMPPServer xmpp) throws XmlPullParserException { - this.xmpp = xmpp; - created = updated = Instant.now(); - } - - public void logParser() { - if (streamID == null) { - return; - } - String tag = "IN: <" + parser.getName(); - for (int i = 0; i < parser.getAttributeCount(); i++) { - tag += " " + parser.getAttributeName(i) + "=\"" + parser.getAttributeValue(i) + "\""; - } - tag += ">...</" + parser.getName() + ">\n"; - logger.trace(tag); - } - - public void sendStanza(String xml) { - if (streamID != null) { - logger.trace("OUT: {}\n", xml); - } - try { - writer.write(xml); - writer.flush(); - } catch (IOException e) { - logger.error("send stanza failed", e); - } - - updated = Instant.now(); - bytesLocal += xml.length(); - packetsLocal++; - } - - public void closeConnection() { - if (streamID != null) { - logger.debug("closing stream {}", streamID); - } - - try { - writer.write("</stream:stream>"); - } catch (Exception e) { - } - - try { - writer.close(); - } catch (Exception e) { - } - - try { - socket.close(); - } catch (Exception e) { - } - } - - public boolean isSecured() { - return secured; - } - - public void setSecured(boolean secured) { - this.secured = secured; - } - - public void restartParser() throws XmlPullParserException, IOException { - streamID = UUID.randomUUID().toString(); - parser = factory.newPullParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new InputStreamReader(socket.getInputStream())); - writer = new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8); - } - - @JsonIgnore - public Socket getSocket() { - return socket; - } - - public void setSocket(Socket socket) { - this.socket = socket; - } - - public boolean isAuthenticated() { - return authenticated; - } - - public void setAuthenticated(boolean authenticated) { - this.authenticated = authenticated; - } - - public boolean isTrusted() { - return trusted; - } - - public void setTrusted(boolean trusted) { - this.trusted = trusted; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionIn.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionIn.java deleted file mode 100644 index 72c3ba8d..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionIn.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.s2s; - -import com.juick.server.XMPPServer; -import com.juick.server.xmpp.router.StreamError; -import com.juick.server.xmpp.router.XmlUtils; -import org.apache.commons.lang3.StringUtils; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import rocks.xmpp.addr.Jid; - -import java.io.EOFException; -import java.io.IOException; -import java.net.Socket; -import java.net.SocketException; -import java.time.Instant; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.stream.Collectors; - -/** - * @author ugnich - */ -public class ConnectionIn extends Connection implements Runnable { - - final public List<Jid> from = new CopyOnWriteArrayList<>(); - public Instant received; - public long packetsRemote = 0; - ConnectionListener listener; - - public ConnectionIn(XMPPServer xmpp, Socket socket) throws XmlPullParserException, IOException { - super(xmpp); - this.setSocket(socket); - restartParser(); - } - - @Override - public void run() { - try { - parser.next(); // stream:stream - updateTsRemoteData(); - if (!parser.getName().equals("stream") - || !parser.getNamespace("stream").equals(NS_STREAM)) { -// || !parser.getAttributeValue(null, "version").equals("1.0") -// || !parser.getAttributeValue(null, "to").equals(Main.HOSTNAME)) { - throw new Exception(String.format("stream from %s invalid", getSocket().getRemoteSocketAddress())); - } - streamID = parser.getAttributeValue(null, "id"); - if (streamID == null) { - streamID = UUID.randomUUID().toString(); - } - boolean xmppversionnew = parser.getAttributeValue(null, "version") != null; - String from = parser.getAttributeValue(null, "from"); - - if (Arrays.asList(xmpp.bannedHosts).contains(from)) { - closeConnection(); - return; - } - sendOpenStream(from, xmppversionnew); - - while (parser.next() != XmlPullParser.END_DOCUMENT) { - updateTsRemoteData(); - if (parser.getEventType() != XmlPullParser.START_TAG) { - continue; - } - logParser(); - - packetsRemote++; - - String tag = parser.getName(); - if (tag.equals("result") && parser.getNamespace().equals(NS_DB)) { - String dfrom = parser.getAttributeValue(null, "from"); - String to = parser.getAttributeValue(null, "to"); - logger.debug("stream from {} to {} {} asking for dialback", dfrom, to, streamID); - if (dfrom.endsWith(xmpp.getJid().toEscapedString()) && (dfrom.equals(xmpp.getJid().toEscapedString()) - || dfrom.endsWith("." + xmpp.getJid()))) { - logger.warn("stream from {} is invalid", dfrom); - break; - } - if (to != null && to.equals(xmpp.getJid().toEscapedString())) { - String dbKey = XmlUtils.getTagText(parser); - updateTsRemoteData(); - xmpp.startDialback(Jid.of(dfrom), streamID, dbKey); - } else { - logger.warn("stream from " + dfrom + " " + streamID + " invalid to " + to); - break; - } - } else if (tag.equals("verify") && parser.getNamespace().equals(NS_DB)) { - String vfrom = parser.getAttributeValue(null, "from"); - String vto = parser.getAttributeValue(null, "to"); - String vid = parser.getAttributeValue(null, "id"); - String vkey = XmlUtils.getTagText(parser); - updateTsRemoteData(); - final boolean[] valid = {false}; - if (vfrom != null && vto != null && vid != null && vkey != null) { - xmpp.getConnectionOut(Jid.of(vfrom), false).ifPresent(c -> { - String dialbackKey = c.dbKey; - valid[0] = vkey.equals(dialbackKey); - }); - } - if (valid[0]) { - sendStanza("<db:verify from='" + vto + "' to='" + vfrom + "' id='" + vid + "' type='valid'/>"); - logger.debug("stream from {} {} dialback verify valid", vfrom, streamID); - setAuthenticated(true); - } else { - sendStanza("<db:verify from='" + vto + "' to='" + vfrom + "' id='" + vid + "' type='invalid'/>"); - logger.warn("stream from {} {} dialback verify invalid", vfrom, streamID); - } - } else if (tag.equals("presence") && checkFromTo(parser) && isAuthenticated()) { - String xml = XmlUtils.parseToString(parser, false); - logger.debug("stream {} presence: {}", streamID, xml); - xmpp.onStanzaReceived(xml); - } else if (tag.equals("message") && checkFromTo(parser)) { - updateTsRemoteData(); - String xml = XmlUtils.parseToString(parser, false); - logger.debug("stream {} message: {}", streamID, xml); - xmpp.onStanzaReceived(xml); - - } else if (tag.equals("iq") && checkFromTo(parser) && isAuthenticated()) { - updateTsRemoteData(); - String type = parser.getAttributeValue(null, "type"); - String xml = XmlUtils.parseToString(parser, false); - if (type == null || !type.equals("error")) { - logger.debug("stream {} iq: {}", streamID, xml); - xmpp.onStanzaReceived(xml); - } - } else if (!isSecured() && tag.equals("starttls") && !isAuthenticated()) { - listener.starttls(this); - } else if (isSecured() && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) { - sendOpenStream(null, true); - } else if (isSecured() && tag.equals("auth") && parser.getNamespace().equals(NS_SASL) - && parser.getAttributeValue(null, "mechanism").equals("EXTERNAL") - && !isAuthenticated() && isTrusted()) { - sendStanza("<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>"); - logger.info("stream {} authenticated externally", streamID); - this.from.add(Jid.of(from)); - setAuthenticated(true); - restartParser(); - } else if (tag.equals("error")) { - StreamError streamError = StreamError.parse(parser); - logger.debug("Stream error {} from {}: {}", streamError.getCondition(), streamID, streamError.getText()); - xmpp.removeConnectionIn(this); - closeConnection(); - } else { - String unhandledStanza = XmlUtils.parseToString(parser, true); - logger.warn("Unhandled stanza from {}: {}", streamID, unhandledStanza); - } - } - logger.warn("stream {} finished", streamID); - xmpp.removeConnectionIn(this); - closeConnection(); - } catch (EOFException | SocketException ex) { - logger.debug("stream {} closed (dirty)", streamID); - xmpp.removeConnectionIn(this); - closeConnection(); - } catch (Exception e) { - logger.debug("stream {} error {}", streamID, e); - xmpp.removeConnectionIn(this); - closeConnection(); - } - } - - void updateTsRemoteData() { - received = Instant.now(); - } - - void sendOpenStream(String from, boolean xmppversionnew) throws IOException { - String openStream = "<?xml version='1.0'?><stream:stream xmlns='jabber:server' " + - "xmlns:stream='http://etherx.jabber.org/streams' xmlns:db='jabber:server:dialback' from='" + - xmpp.getJid().toEscapedString() + "' id='" + streamID + "' version='1.0'>"; - if (xmppversionnew) { - openStream += "<stream:features>"; - if (listener != null && listener.isTlsAvailable() && !Arrays.asList(xmpp.brokenSSLhosts).contains(from)) { - if (!isSecured()) { - openStream += "<starttls xmlns='" + NS_TLS + "'><optional/></starttls>"; - } else if (!isAuthenticated() && isTrusted()) { - openStream += "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" + - "<mechanism>EXTERNAL</mechanism>" + - "</mechanisms>"; - } - } - openStream += "</stream:features>"; - } - sendStanza(openStream); - } - - public void sendDialbackResult(Jid sfrom, String type) { - sendStanza("<db:result from='" + xmpp.getJid().toEscapedString() + "' to='" + sfrom + "' type='" + type + "'/>"); - if (type.equals("valid")) { - from.add(sfrom); - logger.debug("stream from {} {} ready", sfrom, streamID); - setAuthenticated(true); - } - } - - boolean checkFromTo(XmlPullParser parser) throws Exception { - String cfrom = parser.getAttributeValue(null, "from"); - String cto = parser.getAttributeValue(null, "to"); - if (StringUtils.isNotEmpty(cfrom) && StringUtils.isNotEmpty(cto)) { - Jid jidfrom = Jid.of(cfrom); - for (Jid aFrom : from) { - if (aFrom.equals(Jid.of(jidfrom.getDomain()))) { - return true; - } - } - } - logger.warn("rejected from {}, to {}, stream {}", cfrom, cto, from.stream().collect(Collectors.joining(","))); - return false; - } - public void setListener(ConnectionListener listener) { - this.listener = listener; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionListener.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionListener.java deleted file mode 100644 index 4c32b9ae..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionListener.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.juick.server.xmpp.s2s; - - -import com.juick.server.xmpp.router.StreamError; - -public interface ConnectionListener { - boolean isTlsAvailable(); - void starttls(ConnectionIn connection); - void proceed(ConnectionOut connection); - void verify(ConnectionOut connection, String from, String type, String sid); - void dialbackError(ConnectionOut connection, StreamError error); - void finished(ConnectionOut connection, boolean dirty); - void exception(ConnectionOut connection, Exception ex); - void ready(ConnectionOut connection); - boolean securing(ConnectionOut connection); -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionOut.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionOut.java deleted file mode 100644 index be485ab1..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/ConnectionOut.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.s2s; - -import com.juick.server.xmpp.router.Stream; -import com.juick.server.xmpp.router.StreamError; -import com.juick.server.xmpp.router.StreamFeatures; -import com.juick.server.xmpp.router.XmlUtils; -import com.juick.server.xmpp.s2s.util.DialbackUtils; -import org.apache.commons.codec.Charsets; -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.text.RandomStringGenerator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.xmlpull.v1.XmlPullParser; -import rocks.xmpp.addr.Jid; - -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.SocketException; -import java.util.UUID; - -import static com.juick.server.xmpp.router.StreamNamespaces.NS_STREAM; -import static com.juick.server.xmpp.s2s.Connection.NS_SASL; - -/** - * @author ugnich - */ -public class ConnectionOut extends Stream { - protected static final Logger logger = LoggerFactory.getLogger(ConnectionOut.class); - public static final String NS_TLS = "urn:ietf:params:xml:ns:xmpp-tls"; - public static final String NS_DB = "jabber:server:dialback"; - private boolean secured = false; - private boolean trusted = false; - public boolean streamReady = false; - String checkSID = null; - String dbKey = null; - private String streamID; - ConnectionListener listener; - RandomStringGenerator generator = new RandomStringGenerator.Builder().withinRange('a', 'z').build(); - - public ConnectionOut(Jid from, Jid to, InputStream is, OutputStream os, String checkSID, String dbKey) throws Exception { - super(from, to, is, os); - this.to = to; - this.checkSID = checkSID; - this.dbKey = dbKey; - if (dbKey == null) { - this.dbKey = DialbackUtils.generateDialbackKey(generator.generate(15), to, from, streamID); - } - streamID = UUID.randomUUID().toString(); - } - - public void sendOpenStream() throws IOException { - send("<?xml version='1.0'?><stream:stream xmlns='jabber:server' id='" + streamID + - "' xmlns:stream='http://etherx.jabber.org/streams' xmlns:db='jabber:server:dialback' from='" + - from.toEscapedString() + "' to='" + to.toEscapedString() + "' version='1.0'>"); - } - - void processDialback() throws Exception { - if (checkSID != null) { - sendDialbackVerify(checkSID, dbKey); - } - send("<db:result from='" + from.toEscapedString() + "' to='" + to.toEscapedString() + "'>" + - dbKey + "</db:result>"); - } - - @Override - public void handshake() { - try { - restartStream(); - - sendOpenStream(); - - parser.next(); // stream:stream - streamID = parser.getAttributeValue(null, "id"); - if (streamID == null || streamID.isEmpty()) { - throw new Exception("stream to " + to + " invalid first packet"); - } - - logger.debug("stream to {} {} open", to, streamID); - boolean xmppversionnew = parser.getAttributeValue(null, "version") != null; - if (!xmppversionnew) { - processDialback(); - } - - while (parser.next() != XmlPullParser.END_DOCUMENT) { - if (parser.getEventType() != XmlPullParser.START_TAG) { - continue; - } - - String tag = parser.getName(); - if (tag.equals("result") && parser.getNamespace().equals(NS_DB)) { - String type = parser.getAttributeValue(null, "type"); - if (type != null && type.equals("valid")) { - streamReady = true; - listener.ready(this); - } else { - logger.warn("stream to {} {} dialback fail", to, streamID); - } - XmlUtils.skip(parser); - } else if (tag.equals("verify") && parser.getNamespace().equals(NS_DB)) { - String from = parser.getAttributeValue(null, "from"); - String type = parser.getAttributeValue(null, "type"); - String sid = parser.getAttributeValue(null, "id"); - listener.verify(this, from, type, sid); - XmlUtils.skip(parser); - } else if (tag.equals("features") && parser.getNamespace().equals(NS_STREAM)) { - StreamFeatures features = StreamFeatures.parse(parser); - if (listener != null && !secured && features.STARTTLS >= 0 - && listener.securing(this)) { - logger.debug("stream to {} {} securing", to.toEscapedString(), streamID); - send("<starttls xmlns=\"" + NS_TLS + "\" />"); - } else if (secured && features.EXTERNAL >=0) { - String authid = Base64.encodeBase64String(from.toEscapedString().getBytes(Charsets.UTF_8)); - send(String.format("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='EXTERNAL'>%s</auth>", authid)); - } else if (secured && streamReady) { - listener.ready(this); - } else { - processDialback(); - } - } else if (tag.equals("proceed") && parser.getNamespace().equals(NS_TLS)) { - listener.proceed(this); - } else if (tag.equals("success") && parser.getNamespace().equals(NS_SASL)) { - streamReady = true; - restartStream(); - sendOpenStream(); - } else if (secured && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) { - streamID = parser.getAttributeValue(null, "id"); - } else if (tag.equals("error")) { - StreamError streamError = StreamError.parse(parser); - listener.dialbackError(this, streamError); - } else { - String unhandledStanza = XmlUtils.parseToString(parser, false); - logger.warn("Unhandled stanza from {} {} : {}", to, streamID, unhandledStanza); - } - } - listener.finished(this, false); - } catch (EOFException | SocketException eofex) { - listener.finished(this, true); - } catch (Exception e) { - listener.exception(this, e); - } - } - - public void sendDialbackVerify(String sid, String key) { - send("<db:verify from='" + from.toEscapedString() + "' to='" + to + "' id='" + sid + "'>" + - key + "</db:verify>"); - } - public void setListener(ConnectionListener listener) { - this.listener = listener; - } - - public String getStreamID() { - return streamID; - } - - public boolean isSecured() { - return secured; - } - - public void setSecured(boolean secured) { - this.secured = secured; - } - - public boolean isTrusted() { - return trusted; - } - - public void setTrusted(boolean trusted) { - this.trusted = trusted; - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/DNSQueries.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/DNSQueries.java deleted file mode 100644 index 1367d333..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/DNSQueries.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.s2s; - -import org.apache.commons.lang3.math.NumberUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.InetSocketAddress; -import java.util.Hashtable; -import java.util.Random; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.DirContext; -import javax.naming.directory.InitialDirContext; - -/** - * - * @author ugnich - */ -public class DNSQueries { - - private static final Logger logger = LoggerFactory.getLogger(DNSQueries.class); - - private static Random rand = new Random(); - - public static InetSocketAddress getServerAddress(String hostname) { - - String host = hostname; - int port = 5269; - - Hashtable<String, String> env = new Hashtable<>(5); - env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory"); - try { - DirContext ctx = new InitialDirContext(env); - Attribute att = ctx.getAttributes("_xmpp-server._tcp." + hostname, new String[]{"SRV"}).get("SRV"); - - if (att != null && att.size() > 0) { - int i = rand.nextInt(att.size()); - String srv[] = att.get(i).toString().split(" "); - port = NumberUtils.toInt(srv[2], 5269); - host = srv[3]; - } - ctx.close(); - } catch (NamingException e) { - logger.debug("SRV record for {} is not resolved, falling back to A record", hostname); - } - return new InetSocketAddress(host, port); - } -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/StanzaListener.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/StanzaListener.java deleted file mode 100644 index 6932298f..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/StanzaListener.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.s2s; - - -import rocks.xmpp.core.stanza.model.Stanza; - -/** - * Created by vitalyster on 07.12.2016. - */ -public interface StanzaListener { - void stanzaReceived(Stanza xmlValue); -} diff --git a/juick-server/src/main/java/com/juick/server/xmpp/s2s/util/DialbackUtils.java b/juick-server/src/main/java/com/juick/server/xmpp/s2s/util/DialbackUtils.java deleted file mode 100644 index d25dbad8..00000000 --- a/juick-server/src/main/java/com/juick/server/xmpp/s2s/util/DialbackUtils.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.xmpp.s2s.util; - -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.codec.digest.HmacAlgorithms; -import org.apache.commons.codec.digest.HmacUtils; -import rocks.xmpp.addr.Jid; - -/** - * Created by vitalyster on 05.12.2016. - */ -public class DialbackUtils { - private DialbackUtils() { - throw new IllegalStateException(); - } - - public static String generateDialbackKey(String secret, Jid to, Jid from, String id) { - return new HmacUtils(HmacAlgorithms.HMAC_SHA_256, DigestUtils.sha256(secret)) - .hmacHex(to.toEscapedString() + " " + from.toEscapedString() + " " + id); - } -} diff --git a/juick-server/src/main/java/com/juick/service/ActivityPubService.java b/juick-server/src/main/java/com/juick/service/ActivityPubService.java deleted file mode 100644 index 892022cf..00000000 --- a/juick-server/src/main/java/com/juick/service/ActivityPubService.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.juick.service; - -import com.juick.User; -import com.juick.model.AnonymousUser; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.annotation.Nonnull; -import javax.inject.Inject; -import java.util.List; - -@Repository -public class ActivityPubService extends BaseJdbcService implements SocialService { - @Value("${ap_base_uri:http://localhost:8080/}") - private String baseUri; - @Inject - private UserService userService; - - @Transactional(readOnly = true) - @Override - public @Nonnull User getUserByAccountUri(String acct) { - UriComponents baseUriComponents = UriComponentsBuilder.fromUriString(baseUri).build(); - UriComponents acctComponents = UriComponentsBuilder.fromUriString(acct).build(); - if (acctComponents.getHost().equals(baseUriComponents.getHost())) { - // /u/ugnich -> ugnich - String userName = acctComponents.getPath().substring(3); - return userService.getUserByName(userName); - } - return AnonymousUser.INSTANCE; - } - - @Transactional(readOnly = true) - @Override - public @Nonnull List<String> getFollowers(User user) { - return getJdbcTemplate().queryForList("SELECT acct FROM followers WHERE user_id=?", String.class, user.getUid()); - } - - @Transactional - @Override - public void addFollower(User user, String acct) { - getJdbcTemplate().update("INSERT INTO followers(user_id, acct) " + - "VALUES(?, ?)", user.getUid(), acct); - } - - @Transactional - @Override - public void removeFollower(User user, String acct) { - getJdbcTemplate().update("DELETE FROM followers WHERE user_id=? AND acct=?", user.getUid(), acct); - } - - @Transactional - @Override - public void removeAccount(String acct) { - getJdbcTemplate().update("DELETE FROM followers WHERE acct=?", acct); - } -} diff --git a/juick-server/src/main/java/com/juick/service/BaseJdbcService.java b/juick-server/src/main/java/com/juick/service/BaseJdbcService.java deleted file mode 100644 index 496a04ba..00000000 --- a/juick-server/src/main/java/com/juick/service/BaseJdbcService.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; - -import javax.inject.Inject; - -/** - * Created by aalexeev on 11/13/16. - */ -public class BaseJdbcService { - @Inject - JdbcTemplate jdbcTemplate; - @Inject - NamedParameterJdbcTemplate namedParameterJdbcTemplate; - - public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() { - return namedParameterJdbcTemplate; - } - - public JdbcTemplate getJdbcTemplate() { - return jdbcTemplate; - } -} diff --git a/juick-server/src/main/java/com/juick/service/CrosspostServiceImpl.java b/juick-server/src/main/java/com/juick/service/CrosspostServiceImpl.java deleted file mode 100644 index d190faba..00000000 --- a/juick-server/src/main/java/com/juick/service/CrosspostServiceImpl.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service; - -import com.juick.ExternalToken; -import com.juick.model.ApplicationStatus; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.Optional; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -public class CrosspostServiceImpl extends BaseJdbcService implements CrosspostService { - - @Transactional(readOnly = true) - @Override - public Optional<ExternalToken> getTwitterToken(final int uid) { - List<ExternalToken> list = getJdbcTemplate().query( - "SELECT uname, access_token, access_token_secret FROM twitter WHERE user_id = ? AND crosspost = 1", - (rs, num) -> new ExternalToken(rs.getString(1), "twitter", - rs.getString(2), rs.getString(3)), - uid); - - return list.isEmpty() ? - Optional.empty() : Optional.of(list.get(0)); - } - - @Transactional - @Override - public boolean deleteTwitterToken(Integer uid) { - return getJdbcTemplate().update("DELETE FROM twitter WHERE user_id=?", uid) > 0; - } - - @Override - public void addFacebookState(String state, String redirectUri) { - jdbcTemplate.update("INSERT INTO facebook(loginhash, fb_link) VALUES(?, ?)", state, redirectUri); - } - - @Override - public void addVKState(String state, String redirectUri) { - jdbcTemplate.update("INSERT INTO vk(loginhash, vk_link) VALUES(?, ?)", state, redirectUri); - } - - @Override - public String verifyFacebookState(String state) { - try { - return jdbcTemplate.queryForObject("SELECT fb_link FROM facebook WHERE loginhash=?", - String.class, state); - } catch (EmptyResultDataAccessException e) { - return StringUtils.EMPTY; - } - } - - @Override - public String verifyVKState(String state) { - try { - return jdbcTemplate.queryForObject("SELECT vk_link FROM vk WHERE loginhash=?", - String.class, state); - } catch (EmptyResultDataAccessException e) { - return StringUtils.EMPTY; - } - } - - @Transactional(readOnly = true) - @Override - public Optional<Pair<String, String>> getFacebookTokens(final int uid) { - List<Optional<Pair<String, String>>> list = getJdbcTemplate().query( - "SELECT fb_id, access_token FROM facebook WHERE user_id = ? AND access_token IS NOT NULL AND crosspost = 1", - (rs, num) -> Optional.of(Pair.of(rs.getString(1), rs.getString(2))), - uid); - return list.isEmpty() ? - Optional.empty() : list.get(0); - } - - @Transactional(readOnly = true) - @Override - public ApplicationStatus getFbCrossPostStatus(final int uid) { - List<ApplicationStatus> list = getJdbcTemplate().query( - "SELECT 1, crosspost FROM facebook WHERE user_id = ? LIMIT 1", - (rs, num) -> { - ApplicationStatus status = new ApplicationStatus(); - - status.setConnected(rs.getInt(1) > 0); - status.setCrosspostEnabled(rs.getBoolean(2)); - - return status; - }, - uid); - - return list.isEmpty() ? - new ApplicationStatus() : list.get(0); - } - - @Transactional - @Override - public boolean enableFBCrosspost(Integer uid) { - return getJdbcTemplate().update("UPDATE facebook SET crosspost=1 WHERE user_id=?", uid) > 0; - } - - @Transactional - @Override - public void disableFBCrosspost(Integer uid) { - getJdbcTemplate().update("UPDATE facebook SET crosspost=0 WHERE user_id=?", uid); - } - - @Transactional(readOnly = true) - @Override - public String getTwitterName(final int uid) { - List<String> list = getJdbcTemplate().queryForList( - "SELECT uname FROM twitter WHERE user_id = ?", - String.class, - uid); - - return list.isEmpty() ? - StringUtils.EMPTY : list.get(0); - } - - @Transactional(readOnly = true) - @Override - public String getTelegramName(final int uid) { - List<String> list = getJdbcTemplate().queryForList( - "SELECT tg_name FROM telegram WHERE user_id = ?", - String.class, - uid); - - return list.isEmpty() ? - StringUtils.EMPTY : list.get(0); - } - - @Transactional(readOnly = true) - @Override - public Optional<Pair<String, String>> getVkTokens(final int uid) { - List<Optional<Pair<String, String>>> list = getJdbcTemplate().query( - "SELECT vk_id, access_token FROM vk WHERE user_id = ? AND crosspost = 1", - (rs, num) -> Optional.of(Pair.of(rs.getString(1), rs.getString(2))), - uid); - - return list.isEmpty() ? - Optional.empty() : list.get(0); - } - - @Transactional - @Override - public void deleteVKUser(Integer uid) { - getJdbcTemplate().update("DELETE FROM vk WHERE user_id=?", uid); - } - - @Transactional(readOnly = true) - @Override - public int getUIDbyFBID(long fbID) { - try { - return getJdbcTemplate().queryForObject("SELECT user_id FROM facebook WHERE fb_id=? AND user_id IS NOT NULL", - Integer.class, fbID); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - - @Transactional - @Override - public boolean createFacebookUser(long fbID, String loginhash, String token, String fbName, String fbLink) { - return getJdbcTemplate().update("UPDATE facebook SET fb_id=?, access_token=?, fb_name=?, fb_link=? WHERE loginhash=?", - fbID, token, fbName, fbLink, loginhash) > 0; - } - - @Transactional - @Override - public boolean updateFacebookUser(long fbID, String token, String fbName, String fbLink) { - return getJdbcTemplate().update("UPDATE facebook SET access_token=?,fb_name=?,fb_link=? WHERE fb_id=?", - token, fbName, fbLink, fbID) > 0; - } - - @Transactional(readOnly = true) - @Override - public int getUIDbyVKID(long vkID) { - try { - return getJdbcTemplate().queryForObject("SELECT user_id FROM vk WHERE vk_id=? AND user_id IS NOT NULL", Integer.class, vkID); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - - @Transactional - @Override - public boolean createVKUser(long vkID, String loginhash, String token, String vkName, String vkLink) { - return getJdbcTemplate().update("INSERT INTO vk(vk_id,loginhash,access_token,vk_name,vk_link) VALUES (?,?,?,?,?)", - vkID, loginhash, token, vkName, vkLink) > 0; - } - - @Transactional(readOnly = true) - @Override - public String getFacebookNameByHash(String hash) { - try { - List<Pair<String, String>> fb = getJdbcTemplate().query("SELECT fb_name,fb_link FROM facebook WHERE loginhash=?", - (rs, num) -> Pair.of(rs.getString(1), rs.getString(2)), hash); - if (fb.size() > 0) { - return "<a href=\"" + fb.get(0).getRight() + "\" rel=\"nofollow\">" + fb.get(0).getLeft() + "</a>"; - } - return null; - } catch (EmptyResultDataAccessException e) { - return null; - } - } - - @Transactional - @Override - public String getTelegramNameByHash(String hash) { - try { - String name = getJdbcTemplate().queryForObject("SELECT tg_name FROM telegram WHERE loginhash=?", String.class, hash); - return "<a href=\"https://telegram.me/" + name + "\" rel=\"nofollow\">" + name + "</a>"; - } catch (EmptyResultDataAccessException e) { - return null; - } - } - - @Transactional - @Override - public boolean setFacebookUser(String hash, int uid) { - return getJdbcTemplate().update("UPDATE facebook SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; - } - - @Transactional - @Override - public String getVKNameByHash(String hash) { - List<Pair<String, String>> logins = getJdbcTemplate().query("SELECT vk_name,vk_link FROM vk WHERE loginhash=?", - (rs, num) -> Pair.of(rs.getString(1), rs.getString(2)), hash); - if (logins.size() > 0) { - return "<a href=\"http://vk.com/" + logins.get(0).getRight() + "\" rel=\"nofollow\">" + logins.get(0).getLeft() + "</a>"; - } - return null; - } - - @Transactional - @Override - public boolean setVKUser(String hash, int uid) { - return getJdbcTemplate().update("UPDATE vk SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; - } - - @Transactional - @Override - public boolean setTelegramUser(String hash, int uid) { - return getJdbcTemplate().update("UPDATE telegram SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; - } - - @Transactional(readOnly = true) - @Override - public String getJIDByHash(String hash) { - try { - return getJdbcTemplate().queryForObject("SELECT jid FROM jids WHERE loginhash=?", String.class, hash); - } catch (EmptyResultDataAccessException e) { - return null; - } - } - - @Transactional - @Override - public boolean setJIDUser(String hash, int uid) { - return getJdbcTemplate().update("UPDATE jids SET user_id=?,loginhash=NULL WHERE loginhash=?", uid, hash) > 0; - } -} diff --git a/juick-server/src/main/java/com/juick/service/EmailServiceImpl.java b/juick-server/src/main/java/com/juick/service/EmailServiceImpl.java deleted file mode 100644 index 78bdd42a..00000000 --- a/juick-server/src/main/java/com/juick/service/EmailServiceImpl.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import javax.inject.Inject; -import java.util.List; - -/** - * Created by vitalyster on 09.12.2016. - */ -@Repository -@Transactional -public class EmailServiceImpl extends BaseJdbcService implements EmailService { - - @Override - public boolean verifyAddressByCode(Integer userId, String code) { - try { - String address = getJdbcTemplate().queryForObject("SELECT account FROM auth WHERE user_id=? AND protocol='email' AND authcode=?", - String.class, userId, code); - addEmail(userId, address); - getJdbcTemplate().update("DELETE FROM auth WHERE user_id=? AND authcode=?", userId, code); - } catch (EmptyResultDataAccessException e) { - return false; - } - return true; - } - - @Override - public boolean addVerificationCode(Integer userId, String account, String code) { - return getJdbcTemplate().update("INSERT INTO auth(user_id,protocol,account,authcode) VALUES (?,'email',?,?)", - userId, account, code) > 0; - } - - @Override - public boolean addEmail(Integer userId, String email) { - return getJdbcTemplate().update("INSERT INTO emails(user_id,email, subscr_hour) VALUES (?,?, 1)", userId, email) > 0; - } - - @Override - public boolean deleteEmail(Integer userId, String account) { - return getNamedParameterJdbcTemplate().update("DELETE FROM emails " + - "WHERE (SELECT COUNT(*) cnt FROM (select user_id, email FROM emails e) c WHERE user_id=:uid) > 1 " + - "AND user_id=:uid AND email=:email", - new MapSqlParameterSource() - .addValue("uid", userId) - .addValue("email", account)) > 0; - } - - @Transactional(readOnly = true) - @Override - public String getNotificationsEmail(Integer userId) { - List<String> list = getJdbcTemplate().queryForList( - "SELECT email FROM emails WHERE user_id=? AND subscr_hour IS NOT NULL", String.class, userId); - return list.isEmpty() ? StringUtils.EMPTY : list.get(0); - } - - @Override - public boolean setNotificationsEmail(Integer userId, String account) { - getJdbcTemplate().update("UPDATE emails SET subscr_hour=NULL WHERE user_id=?", userId); - return StringUtils.isNotEmpty(account) && getJdbcTemplate().update( - "UPDATE emails SET subscr_hour=1 WHERE user_id=? AND email=?", userId, account) > 0; - } - - @Transactional(readOnly = true) - @Override - public List<String> getEmails(Integer userId, boolean active) { - return getJdbcTemplate().queryForList("SELECT email FROM emails WHERE user_id=? " + - (active ? "AND subscr_hour IS NOT NULL" : ""), String.class, userId); - } - @Transactional(readOnly = true) - @Override - public String getEmailByAuthCode(String code) { - try { - return getJdbcTemplate().queryForObject("SELECT account FROM auth WHERE protocol='email' AND authcode=?", String.class, code); - } catch (EmptyResultDataAccessException e) { - return StringUtils.EMPTY; - } - } - - @Transactional - @Override - public void deleteAuthCode(String code) { - getJdbcTemplate().update("DELETE FROM auth WHERE authcode=?", code); - } - -} diff --git a/juick-server/src/main/java/com/juick/service/ImagesServiceImpl.java b/juick-server/src/main/java/com/juick/service/ImagesServiceImpl.java deleted file mode 100644 index 67c8360e..00000000 --- a/juick-server/src/main/java/com/juick/service/ImagesServiceImpl.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.juick.service; - -import com.juick.Attachment; -import com.juick.Message; -import com.juick.Photo; -import com.juick.server.util.ImageUtils; -import org.springframework.util.StringUtils; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Paths; - -public class ImagesServiceImpl implements ImagesService { - private ImageUtils imageUtils; - private String imgDir; - private String tmpDir; - public ImagesServiceImpl(String imgDir, String tmpDir) { - this.imgDir = imgDir; - this.tmpDir = tmpDir; - imageUtils = new ImageUtils(imgDir, tmpDir); - } - @Override - public void setAttachmentMetadata(String baseUrl, Message msg) throws Exception { - if (!StringUtils.isEmpty(msg.getAttachmentType())) { - Photo photo = new Photo(); - if (msg.getRid()> 0) { - photo.setSmall(String.format("%sphotos-512/%d-%d.%s", baseUrl, msg.getMid(), msg.getRid(), msg.getAttachmentType())); - photo.setMedium(String.format("%sphotos-1024/%d-%d.%s", baseUrl, msg.getMid(), msg.getRid(), msg.getAttachmentType())); - photo.setThumbnail(String.format("%sps/%d-%d.%s", baseUrl, msg.getMid(), msg.getRid(), msg.getAttachmentType())); - } else { - photo.setSmall(String.format("%sphotos-512/%d.%s", baseUrl, msg.getMid(), msg.getAttachmentType())); - photo.setMedium(String.format("%sphotos-1024/%d.%s", baseUrl, msg.getMid(), msg.getAttachmentType())); - photo.setThumbnail(String.format("%sps/%d.%s", baseUrl, msg.getMid(), msg.getAttachmentType())); - } - msg.setPhoto(photo); - String imageName = String.format("%s.%s", msg.getMid(), msg.getAttachmentType()); - if (msg.getRid() > 0) { - imageName = String.format("%s-%s.%s", msg.getMid(), msg.getRid(), msg.getAttachmentType()); - } - File fullImage = Paths.get(imgDir, "p", imageName).toFile(); - File mediumImage = Paths.get(imgDir, "photos-1024", imageName).toFile(); - File smallImage = Paths.get(imgDir, "photos-512", imageName).toFile(); - File thumbnailImage = Paths.get(imgDir, "ps", imageName).toFile(); - StringBuilder builder = new StringBuilder(); - builder.append(baseUrl); - builder.append(msg.getAttachmentType().equals("mp4") ? "video" : "p"); - builder.append("/").append(msg.getMid()); - if (msg.getRid() > 0) { - builder.append("-").append(msg.getRid()); - } - builder.append(".").append(msg.getAttachmentType()); - String originalUrl = builder.toString(); - - Attachment original = imageUtils.getAttachment(fullImage); - original.setUrl(originalUrl); - - Attachment medium = imageUtils.getAttachment(mediumImage); - medium.setUrl(photo.getMedium()); - original.setMedium(medium); - - Attachment small = imageUtils.getAttachment(smallImage); - small.setUrl(photo.getSmall()); - original.setSmall(small); - - Attachment thumb = imageUtils.getAttachment(thumbnailImage); - thumb.setUrl(photo.getMedium()); - original.setThumbnail(thumb); - - msg.setAttachment(original); - } - } - - @Override - public void saveImageWithPreviews(String tempFilename, String outputFilename) throws IOException { - imageUtils.saveImageWithPreviews(tempFilename, outputFilename); - } - - @Override - public void saveAvatar(String tempFilename, int uid) throws IOException { - imageUtils.saveAvatar(tempFilename, uid); - } -} diff --git a/juick-server/src/main/java/com/juick/service/MessagesServiceImpl.java b/juick-server/src/main/java/com/juick/service/MessagesServiceImpl.java deleted file mode 100644 index 0b7faf87..00000000 --- a/juick-server/src/main/java/com/juick/service/MessagesServiceImpl.java +++ /dev/null @@ -1,1143 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service; - -import com.juick.*; -import com.juick.model.AnonymousUser; -import com.juick.model.PrivacyOpts; -import com.juick.model.ResponseReply; -import com.juick.server.util.HttpNotFoundException; -import com.juick.util.MessageUtils; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.jdbc.core.ConnectionCallback; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.SqlParameterSource; -import org.springframework.jdbc.core.simple.SimpleJdbcInsert; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import javax.inject.Inject; -import java.net.URI; -import java.sql.*; -import java.time.Instant; -import java.util.*; -import java.util.Date; -import java.util.stream.Collectors; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -public class MessagesServiceImpl extends BaseJdbcService implements MessagesService { - private static final Logger logger = LoggerFactory.getLogger(MessagesServiceImpl.class); - @Inject - private UserService userService; - @Inject - private TagService tagService; - @Inject - private SearchService searchService; - @Inject - private ImagesService imagesService; - @Value("${img_url:https://i.juick.com/}") - private String baseImagesUrl; - - private class MessageMapper implements RowMapper<Message> { - @Override - public Message mapRow(ResultSet rs, int rowNum) throws SQLException { - Message msg = new Message(); - msg.setMid(rs.getInt(1)); - msg.setRid(rs.getInt(2)); - msg.setReplyto(rs.getInt(3)); - User user = new User(); - user.setUid(rs.getInt(4)); - user.setName(Optional.ofNullable(rs.getString(5)).orElse(AnonymousUser.INSTANCE.getName())); - user.setBanned(rs.getBoolean(6)); - user.setUri(URI.create(Optional.ofNullable(rs.getString(22)).orElse(StringUtils.EMPTY))); - msg.setUser(user); - msg.setTimestamp(rs.getTimestamp(7).toInstant()); - msg.ReadOnly = rs.getBoolean(8); - msg.setPrivacy(rs.getInt(9)); - msg.FriendsOnly = msg.getPrivacy() < 0; - msg.setReplies(rs.getInt(10)); - msg.setAttachmentType(rs.getString(11)); - msg.setLikes(rs.getInt(12)); - msg.Hidden = rs.getBoolean(13); - String tagsStr = rs.getString(14); - msg.setTags(MessageUtils.parseTags(tagsStr)); - msg.setRepliesBy(rs.getString(15)); - msg.setText(rs.getString(16)); - msg.setReplyQuote(MessageUtils.formatQuote(rs.getString(17))); - msg.setUpdated(rs.getTimestamp(18).toInstant()); - int quoteUid = rs.getInt(19); - User quoteUser = new User(); - quoteUser.setUid(quoteUid); - quoteUser.setName(rs.getString(20)); - if (quoteUid == 0) { - quoteUser.setName(AnonymousUser.INSTANCE.getName()); - quoteUser.setUri(URI.create(Optional.ofNullable(rs.getString(23)).orElse(StringUtils.EMPTY))); - } - msg.setTo(quoteUser); - msg.setUpdatedAt(rs.getTimestamp(21).toInstant()); - msg.setReplyUri(URI.create(Optional.ofNullable(rs.getString(24)).orElse(StringUtils.EMPTY))); - msg.setHtml(rs.getBoolean(25)); - if (StringUtils.isNotEmpty(msg.getAttachmentType())) { - try { - imagesService.setAttachmentMetadata(baseImagesUrl, msg); - } catch (Exception e) { - logger.warn("exception reading images for mid {} rid {}", msg.getMid(), msg.getRid(), e); - } - } - return msg; - } - } - - - - /** - * @see <a href="https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-type-conversions.html">Java, JDBC and MySQL Types</a> - */ - @Transactional - @Override - public int createMessage(final int uid, final String txt, final String attachment, final Collection<com.juick.Tag> tags) { - SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(getJdbcTemplate()).withTableName("messages") - .usingColumns("user_id", "attach", "ts") - .usingGeneratedKeyColumns("message_id"); - Map<String, Object> insertMap = new HashMap<>(); - insertMap.put("user_id", uid); - Instant now = Instant.now(); - insertMap.put("ts", Timestamp.from(now)); - if (StringUtils.isNotEmpty(attachment)) { - insertMap.put("attach", attachment); - } - int mid = simpleJdbcInsert.executeAndReturnKey(insertMap).intValue(); - if (mid > 0) { - String tagsNames = StringUtils.EMPTY; - - if (CollectionUtils.isNotEmpty(tags)) { - StringBuilder tasNamesBuilder = new StringBuilder(); - List<Object[]> params = new ArrayList<>(tags.size()); - - boolean next = false; - - for (Tag tag : tags) { - if (next) { - tasNamesBuilder.append(" "); - } else - next = true; - - tasNamesBuilder.append(tag.getName()); - params.add(new Object[]{mid, tag.TID}); - } - tagsNames = tasNamesBuilder.toString(); - - getJdbcTemplate().batchUpdate( - "INSERT INTO messages_tags(message_id, tag_id) VALUES (?, ?)", - params, new int[]{Types.INTEGER, Types.INTEGER}); - } - - getJdbcTemplate().update( - "INSERT INTO messages_txt(message_id, tags, txt, updated_at) VALUES (?, ?, ?, ?)", - new Object[]{mid, tagsNames, txt, Timestamp.from(now)}, - new int[]{Types.INTEGER, Types.VARCHAR, Types.VARCHAR, Types.TIMESTAMP}); - getJdbcTemplate().update("UPDATE users SET lastmessage=?, last_seen=? where id=?", Timestamp.from(now), Timestamp.from(now), uid); - } - - return mid; - } - - /** - * @param mid - * @param rid - * @param user - * @param txt - * @param attachment - * @return - * @see <a href="https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-type-conversions.html">Java, JDBC and MySQL Types</a> - */ - @Transactional - @Override - public int createReply(final int mid, final int rid, final User user, final String txt, final String attachment) { - int ridnew = getReplyIDIncrement(mid); - Date ts = Date.from(Instant.now()); - getJdbcTemplate().update("INSERT INTO replies(message_id, reply_id, user_id, replyto, attach, txt, ts, updated_at, user_uri) " + - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", - mid, ridnew, user.getUid(), rid, attachment, txt, ts, ts, user.getUri().toASCIIString()); - - if (ridnew > 0) { - getJdbcTemplate().update( - "UPDATE messages SET replies = replies + 1, updated=? WHERE message_id = ?", - ts, mid); - setLastReadComment(user, mid, ridnew); - getJdbcTemplate().update("UPDATE users SET lastmessage=?, last_seen=? where id=?", ts, ts, user.getUid()); - } - return ridnew; - } - - @Override - public int getReplyIDIncrement(final int mid) { - return getJdbcTemplate().execute((ConnectionCallback<Integer>) conn -> { - conn.setAutoCommit(false); - final int replyNo; - try (PreparedStatement ps = conn.prepareStatement("SELECT maxreplyid+1 FROM messages WHERE message_id=? FOR UPDATE")) { - ps.setInt(1, mid); - try (ResultSet resultSet = ps.executeQuery()) { - if (resultSet.next()) { - replyNo = resultSet.getInt(1); - } else { - throw new IncorrectResultSizeDataAccessException("while getting getReplyIDIncrement, mid=" + mid, 1, 0); - } - } - } - try (PreparedStatement ps = conn.prepareStatement("UPDATE messages SET maxreplyid=? WHERE message_id=?")) { - ps.setInt(1, replyNo); - ps.setInt(2, mid); - if (ps.executeUpdate() != 1) { - throw new IncorrectResultSizeDataAccessException("Cannot find a message to update: " + mid, 1, 0); - } - } - conn.commit(); - return replyNo; - }); - - } - - @Transactional - void updateRepliesBy(int mid) { - List<String> users = getJdbcTemplate().queryForList("SELECT users.nick FROM replies " + - "INNER JOIN users ON replies.user_id=users.id WHERE replies.message_id=? " + - "GROUP BY replies.user_id ORDER BY COUNT(replies.reply_id) DESC LIMIT 5", String.class, mid); - String result = users.stream().map(u -> "@" + u).collect(Collectors.joining(",")); - getJdbcTemplate().update("UPDATE messages_txt SET repliesby=? WHERE message_id=?", result, mid); - } - - @Transactional - @Override - public RecommendStatus recommendMessage(final int mid, final int vuid, final String userUri) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", vuid) - .addValue("uri", userUri) - .addValue("like_id", Reaction.LIKE) - .addValue("mid", mid); - int wasDeleted = getNamedParameterJdbcTemplate() - .update("DELETE FROM favorites WHERE user_id=:uid AND message_id=:mid AND like_id=:like_id AND user_uri=:uri", sqlParameterSource); - if (wasDeleted > 0) { - return RecommendStatus.Deleted; - } else { - boolean wasAdded = getJdbcTemplate() - .update("INSERT INTO favorites(user_id, message_id, ts, like_id, user_uri) VALUES (?, ?, NOW(), ?, ?)", vuid, mid,Reaction.LIKE, userUri) == 1; - if (wasAdded) { - return RecommendStatus.Added; - } - } - return RecommendStatus.Error; - } - - @Override - public RecommendStatus recommendMessage(int mid, int vuid) { - return recommendMessage(mid, vuid, StringUtils.EMPTY); - } - - @Override - public List<Reaction> listReactions() { - return jdbcTemplate.query("SELECT like_id, description FROM reactions", (rs, rowNum) -> { - Reaction reaction = new Reaction(rs.getInt("like_id")); - reaction.setDescription(rs.getString("description")); - return reaction; - }); - } - - @Override - public RecommendStatus likeMessage(int mid, int vuid, int reactionId) { - return likeMessage(mid, vuid, reactionId, StringUtils.EMPTY); - } - - @Transactional - @Override - public RecommendStatus likeMessage(int mid, int vuid, int reactionId, String userUri) throws IllegalArgumentException { - boolean wasAdded = getJdbcTemplate() - .update("INSERT INTO favorites(user_id, message_id, ts, like_id, user_uri) VALUES (?, ?, NOW(), ?, ?)", vuid, mid, reactionId, userUri) == 1; - if (wasAdded) { - return RecommendStatus.Added; - } - - return RecommendStatus.Error; - } - - @Transactional(readOnly = true) - @Override - public boolean canViewThread(final int mid, final int uid) { - List<PrivacyOpts> list = getJdbcTemplate().query( - "SELECT user_id, privacy FROM messages WHERE message_id = ?", - (rs, rowNum) -> { - PrivacyOpts res = new PrivacyOpts(); - - res.setUid(rs.getInt(1)); - res.setPrivacy(rs.getInt(2)); - - return res; - }, - mid); - - PrivacyOpts privacyOpts = list.isEmpty() ? null : list.get(0); - - return privacyOpts == null || - privacyOpts.getPrivacy() >= 0 || - uid == privacyOpts.getUid() || - ((privacyOpts.getPrivacy() == -1 || privacyOpts.getPrivacy() == -2) && - uid > 0 && userService.isInWL(privacyOpts.getUid(), uid)); - } - - @Transactional(readOnly = true) - @Override - public boolean isReadOnly(final int mid) { - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT readonly FROM messages WHERE message_id = ?", - new Object[]{mid}, - Integer.class); - - return !list.isEmpty() && list.get(0) == 1; - } - - @Transactional(readOnly = true) - @Override - public boolean isSubscribed(final int uid, final int mid) { - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT 1 FROM subscr_messages WHERE suser_id = ? AND message_id = ?", - new Object[]{uid, mid}, - Integer.class); - - return !list.isEmpty() && list.get(0) == 1; - } - - @Transactional(readOnly = true) - @Override - public int getMessagePrivacy(final int mid) { - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT privacy FROM messages WHERE message_id = ?", - new Object[]{mid}, - Integer.class); - - return list.isEmpty() ? -4 : list.get(0); - } - - @Transactional(readOnly = true) - @Override - public com.juick.Message getMessage(final int mid) { - - List<com.juick.Message> list = getJdbcTemplate().query( - "SELECT messages.message_id as mid, 0 as rid, 0 as replyto, " - + "messages.user_id as uid, users.nick, users.banned as banned, " - + "" - + "messages.ts," - + "messages.readonly, messages.privacy, messages.replies," - + "messages.attach, COUNT(DISTINCT favorites.user_id) as likes, messages.hidden," - + "txt.tags, txt.repliesby, txt.txt, '' as q, messages.updated as updated, 0 as to_uid, " - + "NULL as to_name, txt.updated_at, '' as user_uri, '' as to_uri, '' as reply_uri, 0 as html FROM messages " - + "INNER JOIN users ON messages.user_id = users.id " - + "INNER JOIN messages_txt AS txt " - + "ON messages.message_id = txt.message_id " - + "LEFT JOIN favorites " - + "ON messages.message_id = favorites.message_id AND favorites.like_id=1 " - + "WHERE messages.message_id = ? " - + "GROUP BY mid, rid, replyto, uid, nick, banned, messages.ts, readonly, " - + "privacy, replies, attach, tags, repliesby, q, updated_at, user_uri, to_uri, reply_uri, html", - new MessageMapper(), - mid); - if (!list.isEmpty()) { - final Message message = list.get(0); - Map<Integer, Set<Reaction>> reactionStats = updateReactionsFor(Collections.singletonList(mid)); - message.setReactions(reactionStats.get(message.getMid())); - return message; - } - return null; - } - - @Transactional(readOnly = true) - @Override - public com.juick.Message getReply(final int mid, final int rid) { - List<com.juick.Message> list = getJdbcTemplate().query( - "SELECT replies.user_id, users.nick," - + "replies.replyto, replies.ts," - + "replies.attach, replies.txt, IFNULL(q.txt,t.txt) as quote, " - + "COALESCE(q.user_id, m.user_id) AS to_uid, COALESCE(qu.nick, mu.nick) AS to_name, " - + "replies.updated_at, replies.user_uri as uri, " - + "q.user_uri AS to_uri, replies.reply_uri AS reply_uri, replies.html, q.reply_uri " - + "FROM replies LEFT JOIN users ON replies.user_id = users.id " - + "LEFT JOIN replies q ON replies.message_id = q.message_id and replies.replyto = q.reply_id " - + "LEFT JOIN messages_txt t ON replies.message_id = t.message_id " - + "LEFT JOIN messages m ON replies.message_id = m.message_id " - + "LEFT JOIN users qu ON q.user_id=qu.id " - + "LEFT JOIN users mu ON m.user_id=mu.id " - + "WHERE replies.message_id = ? AND replies.reply_id = ?", - (rs, num) -> { - Message msg = new Message(); - - msg.setMid(mid); - msg.setRid(rid); - msg.setUser(new User()); - msg.getUser().setUid(rs.getInt(1)); - msg.getUser().setName(rs.getString(2)); - if (msg.getUser().getUid() == 0) { - msg.getUser().setName(AnonymousUser.INSTANCE.getName()); - msg.getUser().setUri( - URI.create(Optional.ofNullable(rs.getString(11)).orElse(StringUtils.EMPTY))); - } - msg.setReplyto(rs.getInt(3)); - msg.setTimestamp(rs.getTimestamp(4).toInstant()); - msg.setAttachmentType(rs.getString(5)); - msg.setText(rs.getString(6)); - String quote = rs.getString(7); - - if (!StringUtils.isEmpty(quote)) { - msg.setReplyQuote(MessageUtils.formatQuote(quote)); - } - int quoteUid = rs.getInt(8); - User quoteUser = new User(); - quoteUser.setUid(quoteUid); - quoteUser.setName(Optional.ofNullable(rs.getString(9)).orElse(AnonymousUser.INSTANCE.getName())); - quoteUser.setUri(URI.create(Optional.ofNullable(rs.getString(12)).orElse(StringUtils.EMPTY))); - msg.setTo(quoteUser); - msg.setUpdatedAt(rs.getTimestamp(10).toInstant()); - msg.setReplyUri(URI.create(Optional.ofNullable(rs.getString(13)).orElse(StringUtils.EMPTY))); - msg.setHtml(rs.getBoolean(14)); - msg.setReplyToUri(URI.create(Optional.ofNullable(rs.getString(15)).orElse(StringUtils.EMPTY))); - if (StringUtils.isNotEmpty(msg.getAttachmentType())) { - try { - imagesService.setAttachmentMetadata(baseImagesUrl, msg); - } catch (Exception e) { - logger.warn("exception reading images for mid {} rid {}", msg.getMid(), msg.getRid(), e); - } - } - return msg; - }, - mid, rid); - - return list.isEmpty() ? null : list.get(0); - } - - @Override - public Message getReplyByUri(String replyUri) { - List<Message> replies = getJdbcTemplate().query("SELECT message_id, reply_id from replies WHERE reply_uri=?", (rs, rowNum) -> getReply(rs.getInt(1), rs.getInt(2)), replyUri); - return replies.isEmpty() ? null : replies.get(0); - } - - @Transactional(readOnly = true) - @Override - public User getMessageAuthor(final int mid) { - List<User> list = getJdbcTemplate().query( - "SELECT messages.user_id, users.nick " - + "FROM messages INNER JOIN users ON messages.user_id = users.id WHERE messages.message_id = ?", - new Object[]{mid}, - (rs, num) -> { - User res = new com.juick.User(); - res.setUid(rs.getInt(1)); - res.setName(rs.getString(2)); - return res; - }); - - return list.isEmpty() ? - null : list.get(0); - } - - @Transactional(readOnly = true) - @Override - public List<String> getMessageRecommendations(final int mid) { - return getJdbcTemplate().queryForList( - "SELECT DISTINCT users.nick FROM favorites " + - "INNER JOIN users ON (favorites.message_id = ? AND favorites.user_id = users.id) " + - "INNER JOIN messages m ON favorites.message_id=m.message_id WHERE favorites.like_id=1 " + - "AND NOT EXISTS (SELECT 1 FROM bl_users WHERE " + - "(user_id = favorites.user_id AND bl_user_id = m.user_id) " + - "OR (user_id = m.user_id AND bl_user_id = favorites.user_id))", - String.class, mid); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getAll(final int visitorUid, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("before", before) - .addValue("visitorUid", visitorUid); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT m.message_id FROM messages m WHERE " + - (before > 0 ? - " m.message_id < :before AND " : StringUtils.EMPTY) + - " m.hidden = 0 AND (m.privacy > 0" + - (visitorUid > 1 ? - " OR m.user_id = :visitorUid) AND NOT EXISTS (" + - " SELECT 1 FROM bl_users b WHERE b.user_id = :visitorUid AND b.bl_user_id = m.user_id)" : - ")") + - " AND NOT EXISTS (SELECT 1 FROM bl_tags bt WHERE bt.tag_id IN " + - "(SELECT tag_id FROM messages_tags WHERE message_id = m.message_id) and :visitorUid = bt.user_id)" + - " AND NOT EXISTS (SELECT 1 from users u WHERE u.banned = 1 and u.id = m.user_id and u.id <> :visitorUid) ORDER BY m.message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getTag(final int tid, final int visitorUid, final int before, final int cnt) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("tid", tid) - .addValue("cnt", cnt) - .addValue("before", before) - .addValue("visitorUid", visitorUid); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT messages.message_id FROM (tags INNER JOIN messages_tags " + - "ON ((tags.synonym_id = :tid OR tags.tag_id = :tid) AND tags.tag_id = messages_tags.tag_id)) " + - "INNER JOIN messages ON messages.message_id = messages_tags.message_id WHERE " + - (before > 0 ? - " messages.message_id < :before AND " : StringUtils.EMPTY) + - "(messages.privacy > 0 OR messages.user_id = :visitorUid) " + - "AND NOT EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :visitorUid and b.bl_user_id = messages.user_id) " + - "ORDER BY messages.message_id DESC LIMIT :cnt", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getTags(final String tids, final int visitorUid, final int before, final int cnt) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("cnt", cnt) - .addValue("before", before) - .addValue("visitorUid", visitorUid); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT messages.message_id FROM messages_tags " + - "INNER JOIN messages USING(message_id) WHERE messages_tags.tag_id IN (" + tids + ") " + - (before > 0 ? - " AND messages.message_id < :before " : StringUtils.EMPTY) + - " AND (messages.privacy > 0 OR messages.user_id = :visitorUid) " + - "ORDER BY messages.message_id DESC LIMIT :cnt", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getPlace(final int placeId, final int visitorUid, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("placeId", placeId) - .addValue("before", before) - .addValue("visitorUid", visitorUid); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT message_id FROM messages WHERE place_id = :placeId " + - (before > 0 ? - " AND message_id < :before " : StringUtils.EMPTY) + - " AND (privacy > 0 OR user_id = :visitorUid) ORDER BY message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getMyFeed(final int uid, final int before, boolean recommended) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("before", before); - - List<Integer> mids = getNamedParameterJdbcTemplate().queryForList( - "(SELECT message_id FROM messages " + - " INNER JOIN subscr_users ON (subscr_users.suser_id = :uid AND subscr_users.user_id = messages.user_id) " + - " WHERE " + - (before > 0 ? - " message_id < :before AND " : StringUtils.EMPTY) + - " (privacy >= 0 OR (privacy >= -2 AND privacy <= -1" + - " AND EXISTS (SELECT 1 FROM wl_users w WHERE w.wl_user_id = :uid and w.user_id = messages.user_id))) " + - " AND NOT EXISTS (SELECT 1 FROM bl_tags bt WHERE bt.tag_id IN " + - "(SELECT tag_id FROM messages_tags WHERE message_id = messages.message_id) and :uid = bt.user_id))" + - " UNION " + - " (SELECT message_id FROM messages WHERE user_id=:uid " + - (before > 0 ? - " AND message_id < :before " : StringUtils.EMPTY) + - (recommended ? - ") UNION " + - " (SELECT f.message_id as message_id FROM favorites f INNER JOIN messages ON " + - "f.message_id=messages.message_id WHERE " + - "EXISTS (SELECT 1 FROM subscr_users s WHERE s.suser_id = :uid and f.user_id = s.user_id)" + - (before > 0 ? - " AND f.message_id < :before " : StringUtils.EMPTY) : StringUtils.EMPTY) + - " AND NOT EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :uid and b.bl_user_id = messages.user_id)" + - " AND NOT EXISTS (SELECT 1 FROM bl_tags bt WHERE bt.tag_id IN " + - "(SELECT tag_id FROM messages_tags WHERE message_id = messages.message_id) and :uid = bt.user_id)) " + - "ORDER BY message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - - return mids; - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getPrivate(final int uid, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("before", before); - - return getNamedParameterJdbcTemplate().queryForList - ("SELECT message_id FROM messages WHERE user_id = :uid AND privacy < 0" + - (before > 0 ? - " AND message_id < :before " : StringUtils.EMPTY) + - "ORDER BY message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getDiscussions(final int uid, final Long to) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("to", new Timestamp(to)); - - if (uid == 0) { - return getNamedParameterJdbcTemplate().query("SELECT message_id FROM messages WHERE " + - (to != 0 ? - " updated < :to AND" : StringUtils.EMPTY) + - " NOT EXISTS (SELECT 1 from users u WHERE u.banned = 1" + - " AND u.id = messages.user_id and u.id <> :uid) " + - " ORDER BY updated DESC, message_id DESC LIMIT 20", - sqlParameterSource, - (rs, rowNum) -> rs.getInt(1)); - } - return getNamedParameterJdbcTemplate().query( - "SELECT messages.message_id, messages.updated FROM subscr_messages " + - "INNER JOIN messages ON messages.message_id=subscr_messages.message_id " + - "WHERE suser_id = :uid " + - (to != 0 ? - "AND updated < :to " : StringUtils.EMPTY) + - " AND NOT EXISTS (SELECT 1 from users u WHERE u.banned = 1" + - " AND u.id = messages.user_id and u.id <> :uid) " + - "ORDER BY updated DESC, message_id DESC LIMIT 20", - sqlParameterSource, - (rs, rowNum) -> rs.getInt(1)); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getRecommended(final int uid, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("before", before); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT f.message_id FROM favorites f WHERE " + - "EXISTS (SELECT 1 FROM subscr_users s WHERE s.suser_id = :uid and f.user_id = s.user_id)" + - (before > 0 ? - " AND f.message_id < :before " : StringUtils.EMPTY) + - "ORDER BY f.message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getPopular(final int visitorUid, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("vid", visitorUid) - .addValue("before", before); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT m.message_id FROM messages m WHERE m.privacy > 0 " + - (before > 0 ? - " AND m.message_id < :before " : StringUtils.EMPTY) + - " AND m.popular > 0 AND NOT EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :vid and b.bl_user_id = m.user_id) " + - " AND NOT EXISTS (SELECT 1 FROM bl_tags bt WHERE bt.tag_id IN " + - "(SELECT tag_id FROM messages_tags WHERE message_id = m.message_id) and :vid = bt.user_id)" + - " ORDER BY m.message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getPhotos(final int visitorUid, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("vid", visitorUid) - .addValue("before", before); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT m.message_id FROM messages m WHERE (m.privacy > 0 OR m.user_id = :vid) " + - (before > 0 ? - " AND m.message_id < :before " : StringUtils.EMPTY) + - " AND m.attach IS NOT NULL " + - " AND NOT EXISTS (SELECT 1 FROM bl_tags bt WHERE bt.tag_id IN " + - "(SELECT tag_id FROM messages_tags WHERE message_id = m.message_id) and :vid = bt.user_id)" + - " AND NOT EXISTS (SELECT 1 from users u WHERE u.banned = 1 and u.id = m.user_id and u.id <> :vid) " + - " AND NOT EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :vid and b.bl_user_id = m.user_id) " + - " ORDER BY m.message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getSearch(final User visitor, final String search, final int page) { - return searchService.searchInAllMessages(visitor, search, page); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getUserBlog(final int uid, final int privacy, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("privacy", privacy) - .addValue("before", before); - - ; - if (userService.getUserByUID(uid).orElseThrow(IllegalStateException::new).isBanned()) { - throw new HttpNotFoundException(); - } - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT message_id FROM messages WHERE user_id = :uid" + - (before > 0 ? - " AND message_id < :before" : StringUtils.EMPTY) + - " AND privacy >= :privacy ORDER BY message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getUserTag(final int uid, final int tid, final int privacy, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("tid", tid) - .addValue("privacy", privacy) - .addValue("before", before); - - if (userService.getUserByUID(uid).orElseThrow(IllegalStateException::new).isBanned()) { - throw new HttpNotFoundException(); - } - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT messages.message_id FROM messages_tags INNER JOIN messages " + - " USING (message_id) WHERE messages.user_id = :uid AND messages_tags.tag_id = :tid " + - (before > 0 ? - " AND messages.message_id < :before " : StringUtils.EMPTY) + - " AND messages.privacy >= :privacy ORDER BY messages.message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getUserBlogAtDay(final int uid, final int privacy, final int daysback) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("privacy", privacy) - .addValue("daysback", daysback); - - if (userService.getUserByUID(uid).orElseThrow(IllegalStateException::new).isBanned()) { - throw new HttpNotFoundException(); - } - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT message_id FROM messages WHERE user_id = :uid" + - (daysback > 0 ? - " AND ts >= date(NOW() - INTERVAL :daysback day)" + - " AND ts < date(NOW() - INTERVAL :daysback day + INTERVAL 1 day)" : StringUtils.EMPTY) + - " AND privacy >= :privacy ORDER BY message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getUserBlogWithRecommendations(final int uid, final int privacy, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("privacy", privacy) - .addValue("before", before); - - if (userService.getUserByUID(uid).orElseThrow(IllegalStateException::new).isBanned()) { - throw new HttpNotFoundException(); - } - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT message_id FROM " + - "(SELECT message_id FROM favorites " + - " WHERE user_id = :uid " + - (before > 0 ? - " AND message_id < :before " : StringUtils.EMPTY) + - " ORDER BY message_id DESC LIMIT 20) as r" + - " UNION ALL " + - "SELECT message_id FROM " + - "(SELECT message_id FROM messages WHERE user_id = :uid" + - (before > 0 ? - " AND message_id < :before" : StringUtils.EMPTY) + - " AND privacy >= :privacy ORDER BY message_id DESC LIMIT 20) as m " + - "ORDER BY message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getUserRecommendations(final int uid, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("before", before); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT message_id FROM favorites " + - " WHERE user_id = :uid " + - (before > 0 ? - " AND message_id < :before " : StringUtils.EMPTY) + - " ORDER BY message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getUserPhotos(final int uid, final int privacy, final int before) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("privacy", privacy) - .addValue("before", before); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT message_id FROM messages WHERE user_id = :uid " + - (before > 0 ? - " AND message_id < :before " : StringUtils.EMPTY) + - " AND privacy >= :privacy AND attach IS NOT NULL ORDER BY message_id DESC LIMIT 20", - sqlParameterSource, - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getUserSearch(final User visitor, final int UID, final String search, final int privacy, final int page) { - return searchService.searchByStringAndUser(visitor, search, UID, page); - } - - @Transactional(readOnly = true) - @Override - public List<com.juick.Message> getMessages(final User visitor, final List<Integer> mids) { - if (CollectionUtils.isNotEmpty(mids)) { - - List<com.juick.Message> msgs = getNamedParameterJdbcTemplate().query( - "WITH RECURSIVE banned(message_id, reply_id) " - + "AS (SELECT message_id, reply_id FROM replies WHERE replies.message_id IN (:ids) " - + "AND (EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :uid AND b.bl_user_id = replies.user_id) " - + "OR EXISTS (SELECT 1 from users u WHERE u.banned = 1 and u.id = replies.user_id and u.id <> :uid)) " - + "UNION ALL SELECT replies.message_id, replies.reply_id FROM replies INNER JOIN banned " - + "ON banned.reply_id = replies.replyto AND banned.message_id=replies.message_id " - + "WHERE replies.message_id IN (:ids)) " - + "SELECT messages.message_id, 0 as rid, 0 as replyto, " - + "messages.user_id,users.nick, 0 as banned, " - + "messages.ts," - + "messages.readonly,messages.privacy, messages.replies-COUNT(DISTINCT banned.reply_id) as replies," - + "messages.attach,COUNT(DISTINCT favorites.user_id) AS likes,messages.hidden," - + "messages_txt.tags,messages_txt.repliesby, messages_txt.txt, '' as q, " - + "messages.updated, 0 as to_uid, NULL as to_name, messages_txt.updated_at, '' as user_uri, " - + "'' as to_uri, '' as reply_uri, 0 as html " - + "FROM (messages INNER JOIN messages_txt " - + "ON messages.message_id=messages_txt.message_id) " - + "INNER JOIN users ON messages.user_id=users.id " - + "LEFT JOIN favorites " - + "ON messages.message_id = favorites.message_id AND favorites.like_id=1 " - + "LEFT JOIN banned " - + "ON messages.message_id = banned.message_id " - + "WHERE messages.message_id IN (:ids) GROUP BY " - + "messages.message_id, rid, replyto, messages.user_id, users.nick, banned, messages.ts, " - + "messages.readonly, messages.privacy, messages.attach, messages.hidden, messages_txt.tags, " - + "messages_txt.repliesby, messages_txt.txt, q, messages.updated, to_uid, to_name, updated_at, " - + "user_uri, reply_uri, html", - new MapSqlParameterSource("ids", mids) - .addValue("uid", visitor.getUid()), - new MessageMapper()); - - - Map<Integer,Set<Reaction>> likes = updateReactionsFor(mids); - - msgs.forEach(i -> i.setReactions(likes.get(i.getMid()))); - - msgs.sort(Comparator.comparing(item -> mids.indexOf(item.getMid()))); - - return msgs; - } - return Collections.emptyList(); - } - - - @Transactional(readOnly = true) - @Override - public Map<Integer,Set<Reaction>> updateReactionsFor(final List<Integer> mids) { - - return getNamedParameterJdbcTemplate().query("select f.message_id as mid, f.like_id as lid," + - " r.description as descr, count(f.like_id) as cnt" + - " from favorites f LEFT JOIN reactions r ON f.like_id = r.like_id " + - " where f.message_id IN (:mids) " + - " group by f.message_id, f.like_id", new MapSqlParameterSource("mids", mids), (ResultSet rs) -> { - Map<Integer,Set<Reaction>> results = new HashMap<>(); - - - while (rs.next()) { - int messageId = rs.getInt("mid"); - int likeId = rs.getInt("lid"); - int count = rs.getInt("cnt"); - String description = rs.getString("descr"); - Reaction reaction = new Reaction(likeId); - reaction.setCount(count); - reaction.setDescription(description); - results.computeIfAbsent(messageId, HashSet::new); - results.get(messageId).add(reaction); - } - - return results; - }); - - } - - - @Transactional - @Override - public List<Message> getReplies(final User user, final int mid) { - List<Message> replies = getNamedParameterJdbcTemplate().query( - "WITH RECURSIVE banned(reply_id, user_id) AS (" + - "SELECT reply_id, user_id FROM replies " + - "WHERE replies.message_id = :mid " + - "AND EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :uid AND b.bl_user_id = replies.user_id) " + - "UNION ALL SELECT replies.reply_id, replies.user_id FROM replies " + - "INNER JOIN banned ON banned.reply_id = replies.replyto " + - "WHERE replies.message_id = :mid) " + - "SELECT replies.message_id as mid, replies.reply_id, replies.replyto, " + - "replies.user_id, users.nick, users.banned, " + - "replies.ts, " + - "0 as readonly, 0 as privacy, 0 as replies, " + - "replies.attach, 0 as likes, 0 as hidden, " + - "NULL as tags, NULL as repliesby, replies.txt, " + - "IFNULL(qw.txt, t.txt) as q, " + - "NOW(), " + - "COALESCE(qw.user_id, m.user_id) as to_uid, COALESCE(qu.nick, mu.nick) as to_name, " + - "replies.updated_at, replies.user_uri as uri, " + - "qw.user_uri as to_uri, replies.reply_uri, replies.html " + - "FROM replies LEFT JOIN users " + - "ON replies.user_id = users.id " + - "LEFT JOIN replies qw ON replies.message_id = qw.message_id and replies.replyto = qw.reply_id " + - "LEFT JOIN messages_txt t on replies.message_id = t.message_id " + - "LEFT JOIN messages m on replies.message_id = m.message_id " + - "LEFT JOIN users qu ON qw.user_id=qu.id " + - "LEFT JOIN users mu ON m.user_id=mu.id " + - "WHERE replies.message_id = :mid " + - "AND NOT EXISTS (SELECT 1 from users u WHERE u.banned = 1 and u.id = replies.user_id and u.id <> :uid)" + - "AND NOT EXISTS (SELECT 1 FROM banned WHERE banned.reply_id = replies.reply_id) " + - "AND NOT EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :uid AND b.bl_user_id = m.user_id) " + - "ORDER BY replies.reply_id ASC", - new MapSqlParameterSource("mid", mid).addValue("uid", user.getUid()), - new MessageMapper()); - if (replies.size() > 0) { - setRead(user, mid); - } - return replies; - } - - @Transactional - @Override - public boolean setMessagePopular(final int mid, final int popular) { - int ret; - MapSqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("mid", mid) - .addValue("popular", popular); - - switch (popular) { - case -2: - ret = getNamedParameterJdbcTemplate().update( - "UPDATE messages SET hidden = 1 WHERE message_id = :mid", - sqlParameterSource); - break; - case -1: - sqlParameterSource.addValue("popular", 0); - default: - ret = getNamedParameterJdbcTemplate().update( - "UPDATE messages SET popular = :popular WHERE message_id = :mid", - sqlParameterSource); - break; - } - - if (popular == -1) - ret = getNamedParameterJdbcTemplate().update( - "INSERT INTO top_ignore_messages VALUES (:mid)", - sqlParameterSource); - - return ret > 0; - } - - @Transactional - @Override - public boolean setMessagePrivacy(final int mid) { - return getJdbcTemplate().update("UPDATE messages SET privacy=1 WHERE message_id=?", mid) > 0; - } - - @Transactional - @Override - public boolean deleteMessage(final int uid, final int mid) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("mid", mid) - .addValue("uid", uid); - - if (getNamedParameterJdbcTemplate().update( - "DELETE FROM messages WHERE message_id = :mid AND user_id = :uid", sqlParameterSource) > 0) { - - getNamedParameterJdbcTemplate().update("DELETE FROM messages_txt WHERE message_id = :mid", sqlParameterSource); - getNamedParameterJdbcTemplate().update("DELETE FROM replies WHERE message_id = :mid", sqlParameterSource); - getNamedParameterJdbcTemplate().update("DELETE FROM subscr_messages WHERE message_id = :mid", sqlParameterSource); - getNamedParameterJdbcTemplate().update("DELETE FROM messages_tags WHERE message_id = :mid", sqlParameterSource); - - return true; - } - return false; - } - @Transactional - @Override - public boolean deleteReply(final int uid, final int mid, final int rid) { - User author = getMessageAuthor(mid); - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("mid", mid) - .addValue("uid", uid) - .addValue("rid", rid); - boolean result; - if (author.getUid() == uid) { - result = getNamedParameterJdbcTemplate() - .update("DELETE FROM replies WHERE message_id=:mid AND reply_id=:rid", sqlParameterSource) > 0; - } else { - result = getNamedParameterJdbcTemplate() - .update("DELETE FROM replies WHERE message_id=:mid AND reply_id=:rid AND user_id=:uid" - , sqlParameterSource) > 0; - } - if (result) { - getNamedParameterJdbcTemplate().update("UPDATE messages SET replies=replies-1 WHERE message_id=:mid", sqlParameterSource); - updateRepliesBy(mid); - return true; - } - return false; - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getLastMessages(int hours) { - return getJdbcTemplate().queryForList("SELECT message_id FROM messages WHERE messages.ts>TIMESTAMPADD(HOUR,?,NOW())", - Integer.class, -hours); - - } - - @Transactional(readOnly = true) - @Override - public List<ResponseReply> getLastReplies(int hours) { - return getJdbcTemplate().query("SELECT users2.nick,replies.message_id,replies.reply_id," + - "users.nick,replies.txt," + - "replies.ts,replies.attach,replies.ts+0, replies.html " + - "FROM ((replies INNER JOIN users ON replies.user_id=users.id) " + - "INNER JOIN messages ON replies.message_id=messages.message_id) " + - "INNER JOIN users AS users2 ON messages.user_id=users2.id " + - "WHERE replies.ts>TIMESTAMPADD(HOUR,?,NOW()) AND messages.privacy>0", (rs, rowNum) -> { - ResponseReply reply = new ResponseReply(); - reply.setMuname(rs.getString(1)); - reply.setMid(rs.getInt(2)); - reply.setRid(rs.getInt(3)); - reply.setUname(rs.getString(4)); - reply.setDescription(rs.getString(5)); - reply.setPubDate(rs.getTimestamp(6)); - reply.setAttachmentType(rs.getString(7)); - reply.setHtml(rs.getBoolean(8)); - return reply; - }, -hours); - } - @Transactional(readOnly = true) - @Override - public List<Integer> getPopularCandidates() { - return getJdbcTemplate().queryForList("SELECT replies.message_id FROM replies " + - "INNER JOIN messages ON replies.message_id = messages.message_id " + - "LEFT JOIN messages_tags ON messages_tags.message_id = messages.message_id " + - "WHERE COALESCE(messages_tags.tag_id, 0) != 2 " + - "AND COALESCE(messages_tags.tag_id, 0) != 805 AND replies.ts > TIMESTAMPADD(HOUR, -2, CURRENT_TIMESTAMP) " + - "AND messages.popular=0 GROUP BY messages.message_id having COUNT(DISTINCT(replies.user_id)) > 5 " + - "UNION ALL SELECT favorites.message_id FROM favorites " + - "INNER JOIN messages ON messages.message_id = favorites.message_id " + - "LEFT JOIN messages_tags ON messages_tags.message_id = messages.message_id " + - "WHERE COALESCE(messages_tags.tag_id, 0) != 2 AND favorites.ts > TIMESTAMPADD(HOUR, -2, CURRENT_TIMESTAMP) " + - "AND messages.popular=0 GROUP BY messages.message_id HAVING COUNT(DISTINCT favorites.user_id) > 1;", Integer.class); - } - @Transactional - @Override - public void setLastReadComment(User user, Integer mid, Integer rid) { - jdbcTemplate.update("UPDATE subscr_messages SET last_read_rid=GREATEST(?, last_read_rid) WHERE message_id=? AND suser_id=?", - rid, mid, user.getUid()); - } - @Transactional - @Override - public void setRead(User user, Integer mid) { - jdbcTemplate.update("UPDATE subscr_messages SET last_read_rid=(select replies from messages " + - "where messages.message_id=subscr_messages.message_id) WHERE message_id=? AND suser_id=?", - mid, user.getUid()); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getUnread(User user) { - return jdbcTemplate.queryForList( - "select subscr_messages.message_id " + - "from subscr_messages inner join messages on subscr_messages.message_id=messages.message_id " + - "where subscr_messages.suser_id=? and " + - "messages.replies>subscr_messages.last_read_rid", - Integer.class, user.getUid()); - } - - @Transactional - @Override - public boolean updateMessage(Integer mid, Integer rid, String body) { - Instant now = Instant.now(); - if (rid == 0) { - return jdbcTemplate.update("UPDATE messages_txt SET txt=?, updated_at=? WHERE message_id=?", body, Timestamp.from(now), mid) > 0; - } else { - return jdbcTemplate.update("UPDATE replies SET txt=?, updated_at=? WHERE message_id=? and reply_id=?", - body, Timestamp.from(now), mid, rid) > 0; - } - } - - @Override - public boolean updateReplyUri(Message reply, URI replyUri) { - return jdbcTemplate.update("UPDATE replies SET reply_uri=?, html=1 WHERE message_id=? AND reply_id=?", - replyUri.toASCIIString(), reply.getMid(), reply.getRid()) > 0; - } - - @Override - public boolean replyExists(URI replyUri) { - return jdbcTemplate.queryForList("SELECT reply_id FROM replies WHERE reply_uri=?", - Integer.class, replyUri.toASCIIString()).size() > 0; - } - - @Override - public boolean deleteReply(URI userUri, URI replyUri) { - return jdbcTemplate.update("DELETE FROM replies WHERE user_uri=? AND reply_uri=?", - userUri.toASCIIString(), replyUri.toASCIIString()) > 0; - } -} diff --git a/juick-server/src/main/java/com/juick/service/MessengerServiceImpl.java b/juick-server/src/main/java/com/juick/service/MessengerServiceImpl.java deleted file mode 100644 index 57101ffe..00000000 --- a/juick-server/src/main/java/com/juick/service/MessengerServiceImpl.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.juick.service; - -import com.juick.User; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.Optional; -import java.util.UUID; - -@Repository -public class MessengerServiceImpl extends BaseJdbcService implements MessengerService { - - @Transactional(readOnly = true) - @Override - public Integer getUserId(String senderId) { - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT id FROM users INNER JOIN messenger " + - "ON messenger.user_id = users.id WHERE messenger.sender_id=?", Integer.class, senderId); - - return list.isEmpty() ? 0 : list.get(0); - } - @Transactional(readOnly = true) - @Override - public Optional<String> getSenderId(User user) { - List<String> list = getJdbcTemplate().queryForList( - "SELECT sender_id FROM messenger " + - "WHERE user_id=?", String.class, user.getUid()); - - return list.isEmpty() ? Optional.empty() : Optional.of(list.get(0)); - } - - @Transactional - @Override - public boolean createMessengerUser(String senderId, String displayName) { - return getJdbcTemplate().update( - "INSERT INTO messenger(sender_id, display_name, loginhash) VALUES(?,?,?)", - senderId, displayName, UUID.randomUUID().toString()) > 0; - } - @Transactional(readOnly = true) - @Override - public String getDisplayName(String hash) { - try { - return getJdbcTemplate().queryForObject("SELECT display_name FROM messenger WHERE loginhash=?", String.class, hash); - } catch (EmptyResultDataAccessException e) { - return null; - } - } - @Transactional - @Override - public String getSignUpHash(final String senderId, final String username) { - List<String> list = getJdbcTemplate().queryForList( - "SELECT loginhash FROM messenger WHERE sender_id = ? AND user_id IS NULL", - String.class, - senderId); - - if (list.isEmpty()) { - String hash = UUID.randomUUID().toString(); - getJdbcTemplate().update( - "INSERT INTO messenger(sender_id, loginhash, display_name) VALUES (?, ?, ?)", senderId, hash, username); - return hash; - } - return list.get(0); - } - @Transactional - @Override - public boolean linkMessengerUser(String hash, int uid) { - return getJdbcTemplate().update("UPDATE messenger SET user_id=?, loginhash=NULL WHERE loginhash=?", uid, hash) > 0; - } -} diff --git a/juick-server/src/main/java/com/juick/service/PMQueriesServiceImpl.java b/juick-server/src/main/java/com/juick/service/PMQueriesServiceImpl.java deleted file mode 100644 index 712e4b0e..00000000 --- a/juick-server/src/main/java/com/juick/service/PMQueriesServiceImpl.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service; - -import com.juick.Chat; -import com.juick.User; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.SqlParameterSource; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -public class PMQueriesServiceImpl extends BaseJdbcService implements PMQueriesService { - - @Transactional - @Override - public boolean createPM(final int uidFrom, final int uid_to, final String body) { - return getJdbcTemplate().update( - "INSERT INTO pm(user_id, user_id_to, txt) VALUES (?, ?, ?)", - uidFrom, uid_to, body) > 0; - } - - @Transactional - @Override - public boolean addPMinRoster(final int uid, final String jid) { - return getJdbcTemplate().update( - "INSERT INTO pm_inroster(user_id, jid) VALUES (?, ?) ON DUPLICATE KEY UPDATE user_id=user_id", uid, jid) > 0; - } - - @Transactional - @Override - public boolean removePMinRoster(final int uid, final String jid) { - return getJdbcTemplate().update( - "DELETE FROM pm_inroster WHERE user_id = ? AND jid = ?", uid, jid) > 0; - } - - @Transactional - @Override - public boolean havePMinRoster(final int uid, final String jid) { - List<Integer> res = getJdbcTemplate().queryForList( - "SELECT 1 FROM pm_inroster WHERE user_id = ? AND jid = ?", - Integer.class, - uid, jid); - return res.size() > 0; - } - - @Transactional(readOnly = true) - @Override - public List<Chat> getLastChats(final User user) { - return getJdbcTemplate().query( - "SELECT l.user_id, users.nick, l.last, pm.txt FROM pm " - + "INNER JOIN users ON users.id = pm.user_id " - + "" - + "INNER JOIN (SELECT user_id, MAX(ts) AS last FROM pm " - + "WHERE user_id_to=? GROUP BY user_id) l ON l.last = pm.ts " - + "WHERE pm.user_id_to=? " - + "ORDER BY l.last DESC", - (rs, rowNum) -> { - com.juick.Chat u = new com.juick.Chat(); - u.setUid(rs.getInt(1)); - u.setName(rs.getString(2)); - u.setLastMessageTimestamp(rs.getTimestamp(3).toInstant()); - u.setLastMessageText(rs.getString(4).trim()); - return u; - }, - user.getUid(), user.getUid()); - } - - @Transactional - @Override - public List<com.juick.Message> getPMMessages(final int uid, final int uidTo) { - SqlParameterSource sqlParameterSource = new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("uidTo", uidTo); - - return getNamedParameterJdbcTemplate().query( - "SELECT pm.user_id, pm.txt, pm.ts, users.nick FROM pm INNER JOIN users ON users.id=pm.user_id WHERE (user_id = :uid AND user_id_to = :uidTo) " - + "OR (user_id_to = :uid AND user_id = :uidTo) ORDER BY ts DESC LIMIT 20", - sqlParameterSource, - (rs, rowNum) -> { - com.juick.Message msg = new com.juick.Message(); - int uuid = rs.getInt(1); - User user = new User(); - user.setUid(uuid); - user.setName(rs.getString(4)); - msg.setUser(user); - msg.setText(rs.getString(2).trim()); - msg.setTimestamp(rs.getTimestamp(3).toInstant()); - return msg; - }); - } - - @Transactional(readOnly = true) - @Override - public List<com.juick.Message> getLastPMInbox(final int uid) { - return getJdbcTemplate().query( - "SELECT pm.user_id, users.nick, pm.txt, pm.ts " + - "FROM pm INNER JOIN users ON pm.user_id=users.id WHERE pm.user_id_to=? ORDER BY pm.ts DESC LIMIT 20", - (rs, num) -> { - com.juick.Message msg = new com.juick.Message(); - msg.setUser(new User()); - msg.getUser().setUid(rs.getInt(1)); - msg.getUser().setName(rs.getString(2)); - msg.setText(rs.getString(3).trim()); - msg.setTimestamp(rs.getTimestamp(4).toInstant()); - return msg; - }, - uid); - } - - @Transactional(readOnly = true) - @Override - public List<com.juick.Message> getLastPMSent(final int uid) { - return getJdbcTemplate().query( - "SELECT pm.user_id_to, users.nick, pm.txt, " + - "pm.ts FROM pm INNER JOIN users ON pm.user_id_to=users.id " + - "WHERE pm.user_id=? ORDER BY pm.ts DESC LIMIT 20", - (rs, num) -> { - com.juick.Message msg = new com.juick.Message(); - msg.setUser(new User()); - msg.getUser().setUid(rs.getInt(1)); - msg.getUser().setName(rs.getString(2)); - msg.setText(rs.getString(3).trim()); - msg.setTimestamp(rs.getTimestamp(4).toInstant()); - return msg; - }, - uid); - } -} diff --git a/juick-server/src/main/java/com/juick/service/PrivacyQueriesServiceImpl.java b/juick-server/src/main/java/com/juick/service/PrivacyQueriesServiceImpl.java deleted file mode 100644 index 9f9cda1d..00000000 --- a/juick-server/src/main/java/com/juick/service/PrivacyQueriesServiceImpl.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service; - -import com.juick.Tag; -import com.juick.User; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -@Transactional -public class PrivacyQueriesServiceImpl extends BaseJdbcService implements PrivacyQueriesService { - - @Override - public PrivacyResult blacklistUser(final User user, final User target) { - int result = getJdbcTemplate().update( - "DELETE FROM bl_users WHERE user_id = ? AND bl_user_id = ?", - user.getUid(), target.getUid()); - - if (result > 0) - return PrivacyResult.Removed; - - getJdbcTemplate().update( - "INSERT INTO bl_users(user_id, bl_user_id) VALUES (?, ?)", - user.getUid(), target.getUid()); - - return PrivacyResult.Added; - } - - @Override - public PrivacyResult blacklistTag(final User user, final Tag tag) { - int result = getJdbcTemplate().update( - "DELETE FROM bl_tags WHERE user_id = ? AND tag_id = ?", - user.getUid(), tag.TID); - - if (result > 0) - return PrivacyResult.Removed; - - getJdbcTemplate().update( - "INSERT INTO bl_tags(user_id, tag_id) VALUES (?, ?)", - user.getUid(), tag.TID); - - return PrivacyResult.Added; - } -} diff --git a/juick-server/src/main/java/com/juick/service/PushQueriesServiceImpl.java b/juick-server/src/main/java/com/juick/service/PushQueriesServiceImpl.java deleted file mode 100644 index 7f97956c..00000000 --- a/juick-server/src/main/java/com/juick/service/PushQueriesServiceImpl.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service; - -import org.apache.commons.collections4.CollectionUtils; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import javax.inject.Inject; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -public class PushQueriesServiceImpl extends BaseJdbcService implements PushQueriesService { - - @Transactional(readOnly = true) - @Override - public List<String> getGCMRegID(final int uid) { - return getJdbcTemplate().queryForList( - "SELECT regid FROM android WHERE user_id=?", - String.class, - uid); - } - - @Transactional(readOnly = true) - @Override - public List<String> getGCMTokens(final Collection<Integer> uids) { - if (CollectionUtils.isEmpty(uids)) - return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT regid FROM android INNER JOIN users ON (users.id = android.user_id) WHERE users.id IN (:ids)", - new MapSqlParameterSource("ids", uids), - String.class); - } - - @Transactional - @Override - public boolean addGCMToken(Integer uid, String token) { - return getJdbcTemplate().update("INSERT IGNORE INTO android(user_id,regid) VALUES (?, ?)", - uid, token) > 0; - } - - @Transactional - @Override - public boolean deleteGCMToken(String token) { - return getJdbcTemplate().update("DELETE FROM android WHERE regid=?", token) > 0; - } - - @Transactional(readOnly = true) - @Override - public List<String> getMPNSURL(final int uid) { - return getJdbcTemplate().queryForList( - "SELECT url FROM winphone WHERE user_id=?", - String.class, - uid); - } - - @Transactional(readOnly = true) - @Override - public List<String> getMPNSTokens(final Collection<Integer> uids) { - if (CollectionUtils.isEmpty(uids)) - return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT url FROM winphone INNER JOIN users ON (users.id=winphone.user_id) WHERE users.id IN (:ids)", - new MapSqlParameterSource("ids", uids), - String.class); - } - - @Transactional - @Override - public boolean addMPNSToken(Integer uid, String token) { - return getJdbcTemplate().update("INSERT IGNORE INTO winphone(user_id,url) VALUES (?, ?)", - uid, token) > 0; - } - - @Transactional - @Override - public boolean deleteMPNSToken(String token) { - return getJdbcTemplate().update("DELETE FROM winphone WHERE url=?", token) > 0; - } - - @Transactional(readOnly = true) - @Override - public List<String> getAPNSToken(final int uid) { - return getJdbcTemplate().queryForList( - "SELECT token from ios WHERE user_id=?", - String.class, - uid); - } - - @Transactional - @Override - public boolean deleteAPNSToken(String token) { - return getJdbcTemplate().update("DELETE FROM ios WHERE token=?", token) > 0; - } - - @Transactional(readOnly = true) - @Override - public List<String> getAPNSTokens(final Collection<Integer> uids) { - if (CollectionUtils.isEmpty(uids)) - return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT token FROM ios INNER JOIN users ON (users.id = ios.user_id) WHERE users.id IN (:ids)", - new MapSqlParameterSource("ids", uids), - String.class); - } - - @Transactional - @Override - public boolean addAPNSToken(Integer uid, String token) { - try { - return getJdbcTemplate().update("INSERT INTO ios(user_id,token) VALUES (?, ?)", - uid, token) > 0; - } catch (DuplicateKeyException e) { - return true; - } - } -} diff --git a/juick-server/src/main/java/com/juick/service/ShowQueriesServiceImpl.java b/juick-server/src/main/java/com/juick/service/ShowQueriesServiceImpl.java deleted file mode 100644 index 0fba35f1..00000000 --- a/juick-server/src/main/java/com/juick/service/ShowQueriesServiceImpl.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service; - -import com.juick.User; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Collections; -import java.util.List; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -@Transactional(readOnly = true) -public class ShowQueriesServiceImpl extends BaseJdbcService implements ShowQueriesService { - - @Override - public List<String> getRecommendedUsers(final User forUser) { - if (forUser == null) - return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT u.nick FROM subscr_users su1 INNER JOIN users u " + - "ON su1.user_id = u.id " + - "WHERE NOT EXISTS (SELECT 1 FROM subscr_users su2 WHERE su2.suser_id = :uid and su1.user_id = su2.user_id) " + - "AND EXISTS (SELECT 1 FROM subscr_users su3 WHERE su3.suser_id = :uid and su3.user_id = su1.suser_id ) " + - "AND NOT EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :uid and su1.user_id = b.bl_user_id) " + - "AND su1.user_id != :uid AND u.lastmessage > UNIX_TIMESTAMP() - 259200 " + - "GROUP BY su1.user_id ORDER BY count(*) DESC LIMIT 10", - new MapSqlParameterSource("uid", forUser.getUid()), - String.class); - } - - @Override - public List<String> getTopUsers() { - return getJdbcTemplate().query( - "SELECT users.nick,COUNT(subscr_users.suser_id) AS cnt " + - "FROM (subscr_users INNER JOIN users ON subscr_users.user_id=users.id) " + - "INNER JOIN useroptions ON users.id=useroptions.user_id " + - "WHERE useroptions.privacy_view>0 AND users.lastmessage > UNIX_TIMESTAMP() - 259200 " + - "AND users.id!=2 GROUP BY subscr_users.user_id ORDER BY cnt DESC LIMIT 10", - (rs, rowNum) -> rs.getString(1)); - } -} diff --git a/juick-server/src/main/java/com/juick/service/SphinxSearchService.java b/juick-server/src/main/java/com/juick/service/SphinxSearchService.java deleted file mode 100644 index de7bd7f2..00000000 --- a/juick-server/src/main/java/com/juick/service/SphinxSearchService.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service; - -import com.juick.User; -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import javax.inject.Inject; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * Created by aalexeev on 11/18/16. - */ - -@Repository -@Transactional(readOnly = true) -public class SphinxSearchService extends BaseJdbcService implements SearchService { - private static final int DEFAULT_MAX_RESULT = 20; - - private int maxResult = DEFAULT_MAX_RESULT; - - @Inject - UserService userService; - - public String sortHint(String searchString) { - boolean isOneWord = searchString.split("[^\\S\\+]+").length == 1; - return isOneWord ? "extended:@id desc" : "extended:@weight desc, @id desc"; - } - - @Override - public List<Integer> searchInAllMessages(User visitor, final String searchString, final int page) { - if (StringUtils.isBlank(searchString)) - return Collections.emptyList(); - - Map<String, String> sphinxQuery = new HashMap<>(); - sphinxQuery.put("limit", String.valueOf(maxResult)); - sphinxQuery.put("mode", "any"); - sphinxQuery.put("sort", sortHint(searchString)); - String usersFilter = userService.getUserBLUsers(visitor.getUid()).stream().map(u -> String.valueOf(u.getUid())).collect(Collectors.joining(",")); - sphinxQuery.put("!filter", "user_id," + usersFilter); - if (page > 0) { - sphinxQuery.put("offset", String.valueOf(page * maxResult)); - } - - return getJdbcTemplate().queryForList( - String.format("SELECT id FROM search WHERE query = '%s;%s'", searchString, - sphinxQuery.entrySet().stream().map(Object::toString) - .collect(Collectors.joining(";"))), Integer.class); - } - - @Override - public List<Integer> searchByStringAndUser(User visitor, final String searchString, final int userId, int page) { - if (StringUtils.isBlank(searchString)) - return Collections.emptyList(); - - Map<String, String> sphinxQuery = new HashMap<>(); - sphinxQuery.put("limit", String.valueOf(maxResult)); - sphinxQuery.put("mode", "any"); - sphinxQuery.put("sort", sortHint(searchString)); - if (page > 0) { - sphinxQuery.put("offset", String.valueOf(page * maxResult)); - } - return getJdbcTemplate().queryForList( - String.format("SELECT id FROM search WHERE query = '%s;%s;filter=user_id,%d'", searchString, - sphinxQuery.entrySet().stream().map(Object::toString) - .collect(Collectors.joining(";")), userId), Integer.class); - } - - @Override - public void setMaxResult(int maxResult) { - if (maxResult <= 0) - throw new IllegalArgumentException("maxResult value (" + maxResult + ") must be greater then 0"); - - this.maxResult = maxResult; - } -}
\ No newline at end of file diff --git a/juick-server/src/main/java/com/juick/service/SubscriptionServiceImpl.java b/juick-server/src/main/java/com/juick/service/SubscriptionServiceImpl.java deleted file mode 100644 index 5ce3593b..00000000 --- a/juick-server/src/main/java/com/juick/service/SubscriptionServiceImpl.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service; - -import com.juick.Message; -import com.juick.Tag; -import com.juick.User; -import com.juick.model.NotifyOpts; -import com.juick.util.MessageUtils; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.collections4.IteratorUtils; -import org.apache.commons.collections4.ListUtils; -import org.apache.commons.lang3.StringUtils; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import javax.annotation.Nonnull; -import javax.inject.Inject; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -public class SubscriptionServiceImpl extends BaseJdbcService implements SubscriptionService { - @Inject - private UserService userService; - @Inject - private MessagesService messagesService; - @Inject - private TagService tagService; - - @Transactional(readOnly = true) - @Override - public List<User> getSubscribedUsers(final int uid, final Message msg) { - int mid = msg.getMid(); - User author = messagesService.getMessageAuthor(mid); - - List<User> subscribers = userService.getUserReaders(uid); - List<User> mentionedUsers = userService.getUsersByName(MessageUtils.getMentions(msg).stream() - .map(u -> u.substring(1)).collect(Collectors.toList())); - List<User> users = ListUtils.union(subscribers, mentionedUsers); - List<Integer> tags = tagService.getMessageTagsIDs(mid); - List<String> tagsStr = tagService.getMessageTags(mid).stream().map(t -> t.getTag().getName()).collect(Collectors.toList()); - - Set<Integer> set = new HashSet<>(); - set.addAll( - users.stream() - .map(User::getUid).filter(u -> Collections.disjoint(tagService.getUserBLTags(u), tagsStr)) - .collect(Collectors.toList())); - - - if (!tags.isEmpty()) { - List<Integer> tagUsers = getNamedParameterJdbcTemplate().queryForList( - "SELECT st.suser_id FROM subscr_tags st " + - "WHERE st.tag_id IN (:ids) AND st.suser_id != :uid " + - " AND NOT EXISTS (SELECT 1 FROM bl_users bu WHERE bu.bl_user_id = :authorUid and st.suser_id = bu.user_id)" + - " AND NOT EXISTS (SELECT 1 FROM bl_tags bt WHERE bt.tag_id IN (:ids) and st.suser_id = bt.user_id)", - new MapSqlParameterSource() - .addValue("ids", tags) - .addValue("uid", uid) - .addValue("authorUid", author.getUid()), - Integer.class); - set.addAll(tagUsers); - } - return userService.getUsersByID(set); - } - @Override - public List<User> getUsersSubscribedToComments(@Nonnull final Message msg, @Nonnull final Message reply) { - return getUsersSubscribedToComments(msg, reply, false); - } - - @Transactional(readOnly = true) - @Override - public List<User> getUsersSubscribedToComments(@Nonnull final Message msg, @Nonnull final Message reply, - boolean blacklisted) { - List<User> subscribers = userService.getUsersByID(getJdbcTemplate().queryForList( - "SELECT suser_id FROM subscr_messages WHERE message_id=? AND suser_id!=?", - Integer.class, - msg.getMid(), reply.getUser().getUid())); - List<User> mentionedUsers = userService.getUsersByName(MessageUtils.getMentions(reply).stream() - .map(u -> u.substring(1)).collect(Collectors.toList())); - List<User> users = IteratorUtils.toList(CollectionUtils.union(subscribers, mentionedUsers).iterator()); - if (!users.isEmpty()) { - return users.stream() - .filter(u -> blacklisted || !userService.isReplyToBL(u, reply)) - .collect(Collectors.toList()); - } - return Collections.emptyList(); - } - - @Override - public List<User> getUsersSubscribedToUserRecommendations(final int uid, final Message msg) { - List<String> msgTags = tagService.getMessageTags(msg.getMid()).stream().map(t -> t.getTag().getName()).collect(Collectors.toList()); - if (msg.getLikes() == 1) { - return userService.getUserReaders(uid).stream() - .filter(u -> !u.equals(msg.getUser())) - .filter(u -> !userService.isInBLAny(u.getUid(), msg.getUser().getUid())) - .filter(u -> Collections.disjoint(tagService.getUserBLTags(u.getUid()), msgTags)) - .collect(Collectors.toList()); - } - return Collections.emptyList(); - } - - @Transactional - @Override - public boolean subscribeMessage(final Message message, final User user) { - try { - boolean result = getJdbcTemplate().update( - "INSERT INTO subscr_messages(suser_id, message_id) VALUES (?, ?)", user.getUid(), message.getMid()) == 1; - messagesService.setLastReadComment(user, message.getMid(), message.getReplies()); - return result; - } catch (DuplicateKeyException e) { - return true; - } - } - - @Transactional - @Override - public boolean unSubscribeMessage(final int mid, final int vuid) { - return getJdbcTemplate().update( - "DELETE FROM subscr_messages WHERE message_id=? AND suser_id=?", mid, vuid) > 0; - } - - @Transactional - @Override - public boolean subscribeUser(final User user, final User toUser) { - try { - return getJdbcTemplate().update( - "INSERT INTO subscr_users(user_id,suser_id) VALUES (?,?)", toUser.getUid(), user.getUid()) == 1; - } catch (DuplicateKeyException e) { - return true; - } - } - - @Transactional - @Override - public boolean unSubscribeUser(final User user, final User fromUser) { - return getJdbcTemplate().update( - "DELETE FROM subscr_users WHERE suser_id=? AND user_id=?", user.getUid(), fromUser.getUid()) > 0; - } - - @Transactional - @Override - public boolean subscribeTag(final User user, final Tag toTag) { - try { - - return getJdbcTemplate().update( - "INSERT INTO subscr_tags(tag_id,suser_id) VALUES (?,?)", toTag.TID, user.getUid()) == 1; - } catch (DuplicateKeyException e) { - return true; - } - } - - @Transactional - @Override - public boolean unSubscribeTag(final User user, final Tag toTag) { - return getJdbcTemplate().update( - "DELETE FROM subscr_tags WHERE tag_id=? AND suser_id=?", toTag.TID, user.getUid()) > 0; - } - - @Transactional(readOnly = true) - @Override - public List<String> getSubscribedTags(User user) { - return getJdbcTemplate().queryForList("SELECT tags.name FROM subscr_tags INNER JOIN tags " + - "ON(tags.tag_id = subscr_tags.tag_id) " + - "WHERE subscr_tags.suser_id=? ORDER BY tags.name", String.class, user.getUid()); - } - - @Transactional(readOnly = true) - @Override - public NotifyOpts getNotifyOptions(final User user) { - List<NotifyOpts> list = getJdbcTemplate().query( - "SELECT jnotify,subscr_notify,recommendations FROM useroptions WHERE user_id=?", - (rs, num) -> { - NotifyOpts options = new NotifyOpts(); - options.setRepliesEnabled(rs.getInt(1) > 0); - options.setSubscriptionsEnabled(rs.getInt(2) > 0); - options.setRecommendationsEnabled(rs.getInt(3) > 0); - return options; - }, - user.getUid()); - - return list.isEmpty() ? - new NotifyOpts() : list.get(0); - } - - @Transactional - @Override - public boolean setNotifyOptions(final User user, final NotifyOpts options) { - int jnotify = getJdbcTemplate().update( - "UPDATE useroptions SET jnotify=? WHERE user_id=?", - options.isRepliesEnabled() ? 1 : 0, - user.getUid()); - - int subscr_notify = getJdbcTemplate().update( - "UPDATE useroptions SET subscr_notify=? WHERE user_id=?", - options.isSubscriptionsEnabled() ? 1 : 0, - user.getUid()); - - int recommendations = getJdbcTemplate().update( - "UPDATE useroptions SET recommendations=? WHERE user_id=?", - options.isRecommendationsEnabled() ? 1 : 0, - user.getUid()); - - return jnotify > 0 && subscr_notify > 0 && recommendations > 0; - } -} diff --git a/juick-server/src/main/java/com/juick/service/TagServiceImpl.java b/juick-server/src/main/java/com/juick/service/TagServiceImpl.java deleted file mode 100644 index 42159d3b..00000000 --- a/juick-server/src/main/java/com/juick/service/TagServiceImpl.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service; - -import com.juick.Tag; -import com.juick.User; -import com.juick.model.TagStats; -import com.juick.util.StreamUtils; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -public class TagServiceImpl extends BaseJdbcService implements TagService { - - @Transactional(readOnly = true) - @Override - public com.juick.Tag getTag(final int tid) { - List<Tag> list = getJdbcTemplate().query( - "SELECT synonym_id,name FROM tags WHERE tag_id=?", - (rs, num) -> { - Tag ret = new Tag(rs.getString(2)); - ret.TID = tid; - ret.SynonymID = rs.getInt(1); - return ret; - }, - tid); - - return list.isEmpty() ? - null : list.get(0); - } - - @Transactional - @Override - public com.juick.Tag getTag(final String tag, final boolean autoCreate) { - if (StringUtils.isBlank(tag)) - return null; - - List<Tag> list = getJdbcTemplate().query( - "SELECT tag_id, synonym_id, name FROM tags WHERE name = ?", - (rs, rowNum) -> { - Tag ret1 = new Tag(rs.getString(3)); - ret1.TID = rs.getInt(1); - ret1.SynonymID = rs.getInt(2); - return ret1; - }, - tag); - - Tag ret = list.isEmpty() ? - null : list.get(0); - - if (ret == null && autoCreate) { - ret = new com.juick.Tag(tag); - ret.TID = createTag(tag); - } - - return ret; - } - - @Override - public List<Tag> getTags(Stream<String> tags, final boolean autoCreate) { - return tags.filter(StringUtils::isNotBlank).map(tag -> getTag(tag, autoCreate)).filter(Objects::nonNull).distinct() - .collect(Collectors.toList()); - } - - @Transactional(readOnly = true) - @Override - public boolean getTagNoIndex(final int tagId) { - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT noindex FROM tags WHERE tag_id=?", Integer.class, tagId); - - return !list.isEmpty() && list.get(0) == 1; - } - - @Transactional - @Override - public int createTag(final String name) { - KeyHolder holder = new GeneratedKeyHolder(); - getJdbcTemplate().update( - con -> { - PreparedStatement stmt = con.prepareStatement( - "INSERT INTO tags(name) VALUES (?)", - Statement.RETURN_GENERATED_KEYS); - stmt.setString(1, name); - return stmt; - }, - holder); - - return holder.getKey().intValue(); - } - - private class TagStatsMapper implements RowMapper<TagStats> { - - @Override - public TagStats mapRow(ResultSet rs, int rowNum) throws SQLException { - Tag t = new Tag(rs.getString(1)); - TagStats s = new TagStats(); - s.setTag(t); - s.setUsageCount(rs.getInt(2)); - return s; - } - } - - @Transactional(readOnly = true) - @Override - public List<TagStats> getUserTagStats(final int uid) { - return getJdbcTemplate().query( - "SELECT tags.name,COUNT(messages.message_id) " + - "FROM (messages INNER JOIN messages_tags ON (messages.user_id=? " + - "AND messages.message_id=messages_tags.message_id)) " + - "INNER JOIN tags ON messages_tags.tag_id=tags.tag_id GROUP BY tags.tag_id ORDER BY tags.name ASC", - new TagStatsMapper(), - uid); - } - - @Transactional(readOnly = true) - @Override - public List<String> getUserBLTags(final int uid) { - return getJdbcTemplate().queryForList( - "SELECT tags.name FROM tags INNER JOIN bl_tags " + - "ON (bl_tags.user_id = ? AND bl_tags.tag_id = tags.tag_id) ORDER BY tags.name", - String.class, uid); - } - - @Transactional(readOnly = true) - @Override - public List<String> getPopularTags() { - return getJdbcTemplate().queryForList( - "select name from tags where noindex=0 order by stat_messages desc limit 20", String.class); - } - - @Transactional(readOnly = true) - @Override - public List<TagStats> getTagStats() { - return getJdbcTemplate().query( - "SELECT tags.name,COUNT(DISTINCT messages.user_id) AS cnt " + - "FROM (messages INNER JOIN messages_tags ON (messages.ts>TIMESTAMPADD(DAY,-3,NOW()) " + - "AND messages.message_id=messages_tags.message_id)) " + - "INNER JOIN tags ON messages_tags.tag_id=tags.tag_id " + - "WHERE tags.tag_id NOT IN (SELECT tag_id FROM tags_ignore) " + - "GROUP BY tags.tag_id ORDER BY cnt DESC LIMIT 20", new TagStatsMapper()); - } - - @Transactional - @Override - public List<Tag> updateTags(final int mid, final Collection<Tag> newTags) { - List<Tag> currentTags = getMessageTags(mid).stream() - .map(TagStats::getTag).collect(Collectors.toList()); - - if (CollectionUtils.isEmpty(newTags)) - return currentTags; - - List<Integer> idsForDelete = newTags.stream() - .filter(currentTags::contains) - .map(tag -> tag.TID) - .collect(Collectors.toList()); - if (newTags.size() - idsForDelete.size() >= 5) { - return currentTags; - } - - if (!idsForDelete.isEmpty()) - getNamedParameterJdbcTemplate().update( - "DELETE FROM messages_tags WHERE message_id = :mid AND tag_id in (:ids)", - new MapSqlParameterSource().addValue("ids", idsForDelete).addValue("mid", mid)); - - newTags.stream().filter(t -> !currentTags.contains(t)) - .forEach(t -> getJdbcTemplate().update("INSERT INTO messages_tags(message_id,tag_id) VALUES (?,?)", mid, t.TID)); - - List<Tag> result = getMessageTags(mid).stream() - .map(TagStats::getTag).collect(Collectors.toList()); - jdbcTemplate.update("UPDATE messages_txt SET tags=? WHERE message_id=?", result.stream() - .map(Tag::getName).collect(Collectors.joining(" ")), mid); - return result; - } - - @Override - public Pair<String, List<Tag>> fromString(final String txt) { - String firstLine = txt.split("\\n", 2)[0]; - Supplier<Stream<String>> tagsStream = () -> StreamUtils.takeWhile(Arrays.stream(firstLine.split("\\ ")), - t -> !t.equals("*") && t.startsWith("*")); - int tagsLength = tagsStream.get().collect(Collectors.joining(" ")).length(); - String body = txt.substring(tagsLength); - List<Tag> tags = tagsStream.get().map(t -> getTag(t.substring(1), true)) - .distinct().collect(Collectors.toList()); - return Pair.of(body, tags); - } - - @Transactional(readOnly = true) - @Override - public List<TagStats> getMessageTags(final int mid) { - return getJdbcTemplate().query( - "SELECT tags.tag_id,synonym_id,name,stat_messages FROM tags " + - "INNER JOIN messages_tags ON (messages_tags.message_id = ? AND messages_tags.tag_id = tags.tag_id)", - (rs, num) -> { - com.juick.Tag t = new com.juick.Tag(rs.getString(3)); - t.TID = rs.getInt(1); - t.SynonymID = rs.getInt(2); - TagStats s = new TagStats(); - s.setTag(t); - s.setUsageCount(rs.getInt(4)); - return s; - }, mid); - } - - @Transactional(readOnly = true) - @Override - public List<Integer> getMessageTagsIDs(final int mid) { - return getJdbcTemplate().queryForList( - "SELECT tag_id FROM messages_tags WHERE message_id = ?", - Integer.class, mid); - } - - @Override - public boolean blacklistTag(User user, Tag tag) { - int rowcount = getNamedParameterJdbcTemplate().update("DELETE FROM bl_tags WHERE tag_id = :tid AND user_id = :uid", - new MapSqlParameterSource().addValue("tid", tag.TID).addValue("uid", user.getUid())); - return rowcount <= 0 && getNamedParameterJdbcTemplate() - .update("INSERT INTO bl_tags(user_id, tag_id) VALUES(:uid,:tid)", - new MapSqlParameterSource().addValue("tid", tag.TID) - .addValue("uid", user.getUid())) > 0; - } - - @Transactional(readOnly = true) - @Override - public boolean isInBL(User user, Tag tag) { - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT 1 FROM bl_tags WHERE user_id = ? AND tag_id = ?", - Integer.class, user.getUid(), tag.TID); - return !list.isEmpty() && list.get(0) == 1; - } - - @Transactional(readOnly = true) - @Override - public boolean isSubscribed(User user, Tag tag) { - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT 1 FROM subscr_tags WHERE suser_id = ? AND tag_id = ?", - Integer.class, user.getUid(), tag.TID); - return !list.isEmpty() && list.get(0) == 1; - } - -} diff --git a/juick-server/src/main/java/com/juick/service/TelegramServiceImpl.java b/juick-server/src/main/java/com/juick/service/TelegramServiceImpl.java deleted file mode 100644 index 99cbabf6..00000000 --- a/juick-server/src/main/java/com/juick/service/TelegramServiceImpl.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service; - -import com.juick.User; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Collections; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; - -/** - * Created by vt on 24/11/2016. - */ -@Repository -public class TelegramServiceImpl extends BaseJdbcService implements TelegramService { - - @Transactional - @Override - public boolean deleteAnonymous(Long id) { - return getJdbcTemplate().update("DELETE FROM telegram WHERE tg_id=? AND user_id IS NULL", id) > 0; - } - - @Transactional(readOnly = true) - @Override - public List<Long> getAnonymous() { - return getJdbcTemplate().queryForList("SELECT tg_id FROM telegram WHERE user_id IS NULL", Long.class); - } - - @Transactional(readOnly = true) - @Override - public int getUser(final long tgId) { - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT id FROM users INNER JOIN telegram " + - "ON telegram.user_id = users.id WHERE telegram.tg_id=?", Integer.class, tgId); - - return list.isEmpty() ? 0 : list.get(0); - } - - @Transactional - @Override - public boolean createTelegramUser(final long tgID, final String tgName) { - return getJdbcTemplate().update( - "INSERT INTO telegram(tg_id, tg_name, loginhash) VALUES(?,?,?)", - tgID, tgName, UUID.randomUUID().toString()) > 0; - } - - @Transactional - @Override - public boolean deleteTelegramUser(Integer uid) { - return getJdbcTemplate().update("DELETE FROM telegram WHERE user_id=?", uid) > 0; - } - - @Transactional(readOnly = true) - @Override - public List<Long> getTelegramIdentifiers(List<User> users) { - List<Integer> uids = users.stream().map(User::getUid).collect(Collectors.toList()); - if (uids.isEmpty()) { - return Collections.emptyList(); - } - return getNamedParameterJdbcTemplate().queryForList("" + - "SELECT tg_id FROM telegram WHERE user_id IN(:uids)", new MapSqlParameterSource() - .addValue("uids", uids), Long.class); - } -} diff --git a/juick-server/src/main/java/com/juick/service/UserServiceImpl.java b/juick-server/src/main/java/com/juick/service/UserServiceImpl.java deleted file mode 100644 index fdc4f28c..00000000 --- a/juick-server/src/main/java/com/juick/service/UserServiceImpl.java +++ /dev/null @@ -1,668 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service; - -import com.juick.Message; -import com.juick.User; -import com.juick.model.AnonymousUser; -import com.juick.model.Auth; -import com.juick.model.UserInfo; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.StringUtils; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -import javax.annotation.Nonnull; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.sql.Timestamp; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; - -/** - * Created by aalexeev on 11/13/16. - */ -@Repository -public class UserServiceImpl extends BaseJdbcService implements UserService { - - private class UserMapper implements RowMapper<User> { - @Override - public User mapRow(ResultSet rs, int rowNum) throws SQLException { - User user = new User(); - - user.setUid(rs.getInt(1)); - user.setName(rs.getString(2)); - user.setCredentials(rs.getString(3)); - user.setBanned(rs.getBoolean(4)); - Timestamp seen = rs.getTimestamp(5); - if (seen != null) { - user.setSeen(seen.toInstant()); - } - return user; - } - } - - @Transactional - @Override - public String getSignUpHashByJID(final String jid) { - List<String> list = getJdbcTemplate().queryForList( - "SELECT loginhash FROM jids WHERE jid = ? AND user_id IS NULL", String.class, jid); - - if (list.isEmpty()) { - String hash = UUID.randomUUID().toString(); - getJdbcTemplate().update("INSERT INTO jids(jid, loginhash) VALUES (?, ?)", jid, hash); - return hash; - } - return list.get(0); - } - - @Transactional - @Override - public String getSignUpHashByTelegramID(final Long telegramId, final String username) { - List<String> list = getJdbcTemplate().queryForList( - "SELECT loginhash FROM telegram WHERE tg_id = ? AND user_id IS NULL", - String.class, - telegramId); - - if (list.isEmpty()) { - String hash = UUID.randomUUID().toString(); - getJdbcTemplate().update( - "INSERT INTO telegram(tg_id, loginhash, tg_name) VALUES (?, ?, ?)", telegramId, hash, username); - return hash; - } - return list.get(0); - } - - @Transactional - @Override - public int createUser(final String username, final String password) { - KeyHolder holder = new GeneratedKeyHolder(); - try { - getJdbcTemplate().update( - con -> { - PreparedStatement stmt = con.prepareStatement( - "INSERT INTO users(nick,passw) VALUES (?,?)", - Statement.RETURN_GENERATED_KEYS); - stmt.setString(1, username); - stmt.setString(2, password); - return stmt; - }, - holder); - } catch (DuplicateKeyException e) { - return -1; - } - - int uid = holder.getKey().intValue(); - - getJdbcTemplate().update("INSERT INTO useroptions(user_id) VALUES (?)", uid); - getJdbcTemplate().update("INSERT INTO subscr_users(user_id, suser_id) VALUES (2, ?)", uid); - - return uid; - } - - @Transactional(readOnly = true) - @Override - public Optional<User> getUserByUID(final int uid) { - List<User> list = getJdbcTemplate().query( - "SELECT id, nick, passw, banned, last_seen FROM users WHERE id = ?", new UserMapper(), uid); - - return list.isEmpty() ? Optional.empty() : Optional.of(list.get(0)); - } - - @Transactional(readOnly = true) - @Nonnull - @Override - public User getUserByName(final String username) { - if (StringUtils.isNotBlank(username)) { - List<User> list = getJdbcTemplate().query( - "SELECT id, nick, passw, banned, last_seen FROM users WHERE nick = ?", new UserMapper(), username); - - if (!list.isEmpty()) - return list.get(0); - } - return AnonymousUser.INSTANCE; - } - - @Override - @Transactional(readOnly = true) - @Nonnull - public User getUserByEmail(String email) { - if (StringUtils.isNotBlank(email)) { - List<User> list = getJdbcTemplate().query( - "SELECT id, nick, passw, banned, last_seen FROM users WHERE id = (SELECT DISTINCT user_id FROM emails WHERE email = ?)", - new UserMapper(), - email); - - if (!list.isEmpty()) - return list.get(0); - } - return AnonymousUser.INSTANCE; - } - - @Transactional(readOnly = true) - @Override - public User getUserByJID(final String jid) { - User result = null; - - if (StringUtils.isNotBlank(jid)) { - List<User> list = getJdbcTemplate().query( - "SELECT id, nick, passw, banned, last_seen FROM users WHERE id = (SELECT user_id FROM jids WHERE jid = ?)", - new UserMapper(), - jid); - - if (!list.isEmpty()) - result = list.get(0); - } - return result; - } - - @Transactional(readOnly = true) - @Override - public List<User> getUsersByName(final Collection<String> unames) { - if (CollectionUtils.isEmpty(unames)) - return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().query( - "SELECT id, nick, passw, banned, last_seen FROM users WHERE nick IN (:unames)", - new MapSqlParameterSource("unames", unames), - new UserMapper()); - } - - @Transactional(readOnly = true) - @Override - public List<User> getUsersByID(final Collection<Integer> uids) { - if (CollectionUtils.isEmpty(uids)) - return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().query( - "SELECT id, nick, passw, banned, last_seen FROM users WHERE id IN (:ids)", - new MapSqlParameterSource("ids", uids), - new UserMapper()); - } - - @Transactional(readOnly = true) - @Override - public List<String> getJIDsbyUID(final int uid) { - return getJdbcTemplate().queryForList("SELECT jid FROM jids WHERE user_id = ? AND active = 1", String.class, uid); - } - - @Transactional(readOnly = true) - @Override - public int getUIDbyJID(final String jid) { - if (StringUtils.isNotBlank(jid)) { - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT user_id FROM jids WHERE jid = ?", Integer.class, jid); - - if (!list.isEmpty()) - return list.get(0); - } - return 0; - } - - @Transactional(readOnly = true) - @Override - public int getUIDbyName(final String uname) { - if (StringUtils.isNotBlank(uname)) { - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT id FROM users WHERE nick = ?", Integer.class, uname); - - if (!list.isEmpty()) - return list.get(0); - } - return 0; - } - - @Transactional(readOnly = true) - @Override - public int getUIDbyHash(final String hash) { - if (StringUtils.isNotBlank(hash)) { - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT user_id FROM logins WHERE hash = ?", Integer.class, hash); - - if (!list.isEmpty()) - return list.get(0); - } - return 0; - } - - @Transactional(readOnly = true) - @Override - public com.juick.User getUserByHash(final String hash) { - if (StringUtils.isNotBlank(hash)) { - List<User> list = getJdbcTemplate().query( - "SELECT logins.user_id, users.nick, users.passw, users.banned, last_seen FROM logins " + - "INNER JOIN users ON logins.user_id = users.id WHERE logins.hash = ?", - new UserMapper(), - hash); - - if (!list.isEmpty()) { - User user = list.get(0); - user.setAuthHash(hash); - return user; - } - } - return AnonymousUser.INSTANCE; - } - - @Transactional - @Override - public String getHashByUID(final int uid) { - List<String> list = getJdbcTemplate().queryForList( - "SELECT hash FROM logins WHERE user_id = ?", String.class, uid); - - if (list.isEmpty()) { - String hash = RandomStringUtils.randomAlphanumeric(16).toUpperCase(); - getJdbcTemplate().update("INSERT INTO logins(user_id, hash) VALUES (?, ?)", uid, hash); - return hash; - } - return list.get(0); - } - - @Transactional(readOnly = true) - @Override - public int checkPassword(final String username, final String password) { - if (StringUtils.isNotBlank(username)) { - List<User> list = getJdbcTemplate().query( - "SELECT id, nick, passw, banned, last_seen FROM users WHERE nick = ?", - new UserMapper(), - username); - - if (!list.isEmpty()) { - User user = list.get(0); - if (Objects.equals(password, user.getCredentials())) - return user.getUid(); - } - } - return -1; - } - - @Transactional - @Override - public boolean updatePassword(final User user, final String newPassword) { - return user != null && - user.getUid() > 0 && - getJdbcTemplate().update("UPDATE users SET passw = ? WHERE id = ?", newPassword, user.getUid()) > 0; - } - - @Transactional(readOnly = true) - @Override - public int getUserOptionInt(final int uid, final String option, final int defaultValue) { - if (StringUtils.isBlank(option)) - return defaultValue; - - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT " + option + " FROM useroptions WHERE user_id = ?", Integer.class, uid); - - return list.isEmpty() ? defaultValue : list.get(0); - } - - @Transactional - @Override - public int setUserOptionInt(final int uid, final String option, final int value) { - if (StringUtils.isBlank(option)) - return 0; - - return getJdbcTemplate().update("UPDATE useroptions SET " + option + "= ? WHERE user_id = ?", value, uid); - } - - @Transactional(readOnly = true) - @Override - public UserInfo getUserInfo(final User user) { - List<UserInfo> list = getJdbcTemplate().query( - "SELECT fullname, country, url, descr FROM usersinfo WHERE user_id = ?", - ((rs, rowNum) -> { - UserInfo info = new UserInfo(); - info.setFullName(rs.getString(1)); - info.setCountry(rs.getString(2)); - info.setUrl(rs.getString(3)); - info.setDescription(rs.getString(4)); - return info; - }), - user.getUid()); - - return list.isEmpty() ? new UserInfo() : list.get(0); - } - - @Transactional - @Override - public boolean updateUserInfo(final User user, final UserInfo info) { - return getJdbcTemplate().update( - "INSERT INTO usersinfo(user_id, fullname, country, url, descr) VALUES (?, ?, ?, ?, ?) " + - "ON DUPLICATE KEY UPDATE fullname = ?, country = ?, url = ?, descr = ?", - user.getUid(), - info.getFullName(), - info.getCountry(), - info.getUrl(), - info.getDescription(), - info.getFullName(), - info.getCountry(), - info.getUrl(), - info.getDescription()) > 0; - } - - @Transactional(readOnly = true) - @Override - public boolean getCanMedia(final int uid) { - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT users.lastphoto - UNIX_TIMESTAMP() FROM users WHERE id = ?", - Integer.class, - uid); - - return !list.isEmpty() && list.get(0) < 3600; - } - - @Transactional(readOnly = true) - @Override - public boolean isInWL(final int uid, final int check) { - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT 1 FROM wl_users WHERE user_id = ? AND wl_user_id = ?", - Integer.class, uid, check); - - return !list.isEmpty() && list.get(0) == 1; - } - - @Transactional(readOnly = true) - @Override - public boolean isInBL(final int uid, final int check) { - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT 1 FROM bl_users WHERE user_id = ? AND bl_user_id = ?", Integer.class, uid, check); - - return !list.isEmpty() && list.get(0) == 1; - } - - @Transactional(readOnly = true) - @Override - public boolean isInBLAny(final int uid, final int uid2) { - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT 1 FROM bl_users WHERE (user_id = ? AND bl_user_id = ?) " - + "OR (user_id = ? AND bl_user_id = ?)", - new Object[]{uid, uid2, uid2, uid}, - Integer.class); - - return !list.isEmpty() && list.get(0) == 1; - } - - @Transactional(readOnly = true) - @Override - public boolean isReplyToBL(final User user, final Message reply) { - return getNamedParameterJdbcTemplate().queryForObject("WITH RECURSIVE banned(reply_id, user_id) AS (" + - "SELECT reply_id, user_id FROM replies " + - "WHERE replies.message_id = :mid " + - "AND EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :uid AND b.bl_user_id = replies.user_id) " + - "UNION ALL SELECT replies.reply_id, replies.user_id FROM replies " + - "INNER JOIN banned ON banned.reply_id = replies.replyto " + - "WHERE replies.message_id = :mid) " + - "SELECT COUNT(reply_id) from replies " + - "INNER JOIN messages m ON m.message_id = replies.message_id " + - "WHERE replies.message_id = :mid " + - "AND replies.reply_id = :rid " + - "AND (EXISTS (SELECT 1 FROM banned WHERE banned.reply_id = replies.reply_id) " + - "OR EXISTS (SELECT 1 FROM bl_users b WHERE b.user_id = :uid AND b.bl_user_id = m.user_id)" + - "OR EXISTS (SELECT 1 FROM bl_users b WHERE b.bl_user_id = :uid AND b.user_id = m.user_id))", - new MapSqlParameterSource("uid", user.getUid()) - .addValue("mid", reply.getMid()) - .addValue("rid", reply.getRid()), - Integer.class) > 0; - } - - @Transactional(readOnly = true) - @Override - public List<Integer> checkBL(final int visitor, final Collection<Integer> uids) { - if (CollectionUtils.isEmpty(uids)) - return Collections.emptyList(); - - return getNamedParameterJdbcTemplate().queryForList( - "SELECT user_id FROM bl_users WHERE bl_user_id = :visitor and user_id IN (:ids)", - new MapSqlParameterSource() - .addValue("visitor", visitor) - .addValue("ids", uids), - Integer.class); - } - - @Transactional(readOnly = true) - @Override - public boolean isSubscribed(final int uid, final int check) { - List<Integer> list = getJdbcTemplate().queryForList( - "SELECT 1 FROM subscr_users WHERE suser_id = ? AND user_id = ?", - Integer.class, uid, check); - - return !list.isEmpty() && list.get(0) == 1; - } - - @Transactional(readOnly = true) - @Override - public List<com.juick.User> getUserReadLeastPopular(final int uid, final int cnt) { - return getJdbcTemplate().query( - "SELECT users.id,users.nick FROM (subscr_users " + - "INNER JOIN users_subscr ON (subscr_users.suser_id=? " + - "AND subscr_users.user_id=users_subscr.user_id)) INNER JOIN users " + - "ON subscr_users.user_id=users.id ORDER BY cnt LIMIT ?", - (rs, num) -> { - com.juick.User u = new com.juick.User(); - u.setUid(rs.getInt(1)); - u.setName(rs.getString(2)); - return u; - }, - uid, - cnt); - } - - @Transactional(readOnly = true) - @Override - public List<User> getUserReaders(final int uid) { - return getJdbcTemplate().query( - "SELECT users.id, users.nick FROM subscr_users " + - "INNER JOIN users ON subscr_users.suser_id=users.id " + - "WHERE subscr_users.user_id=? ORDER BY users.nick", - (rs, num) -> { - com.juick.User u = new com.juick.User(); - u.setUid(rs.getInt(1)); - u.setName(rs.getString(2)); - return u; - }, - uid); - } - - @Transactional(readOnly = true) - @Override - public List<User> getUserFriends(final int uid) { - return getJdbcTemplate().query( - "SELECT users.id,users.nick FROM subscr_users " + - "INNER JOIN users ON subscr_users.user_id=users.id " + - "WHERE subscr_users.suser_id=? AND users.id!=? " + - "ORDER BY users.nick", - (rs, num) -> { - com.juick.User u = new com.juick.User(); - u.setUid(rs.getInt(1)); - u.setName(rs.getString(2)); - return u; - }, - uid, - uid); - } - - @Transactional(readOnly = true) - @Override - public Integer getUserRecommendations(User user) { - try { - return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM favorites WHERE user_id=?", Integer.class, user.getUid()); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - - @Transactional(readOnly = true) - @Override - public List<com.juick.User> getUserBLUsers(final int uid) { - return getJdbcTemplate().query("SELECT users.id,users.nick FROM users INNER JOIN bl_users " + - "ON(bl_users.bl_user_id=users.id) WHERE bl_users.user_id=? ORDER BY users.nick", - (rs, num) -> { - com.juick.User u = new com.juick.User(); - u.setUid(rs.getInt(1)); - u.setName(rs.getString(2)); - return u; - }, uid); - } - - @Transactional - @Override - public boolean linkTwitterAccount( - final User user, final String accessToken, final String accessTokenSecret, final String screenName) { - return getJdbcTemplate().update("INSERT INTO twitter(user_id,access_token,access_token_secret,uname) " + - "VALUES (?,?,?,?)" + - " ON DUPLICATE KEY UPDATE access_token=?,access_token_secret=?,uname=?", - user.getUid(), accessToken, accessTokenSecret, screenName, accessToken, accessTokenSecret, screenName) > 0; - } - - @Transactional(readOnly = true) - @Override - public int getStatsMyReaders(final int uid) { - List<Integer> list = getJdbcTemplate().queryForList("SELECT COUNT(*) FROM subscr_users WHERE user_id = ?", Integer.class, uid); - return list.isEmpty() ? 0 : list.get(0); - } - - @Transactional(readOnly = true) - @Override - public int getStatsMessages(final int uid) { - List<Integer> list = getJdbcTemplate().queryForList("SELECT COUNT(*) FROM messages WHERE user_id = ?", Integer.class, uid); - return list.isEmpty() ? 0 : list.get(0); - } - - @Transactional(readOnly = true) - @Override - public int getStatsReplies(final int uid) { - List<Integer> list = getJdbcTemplate().queryForList("SELECT COUNT(*) FROM replies WHERE user_id = ?", Integer.class, uid); - return list.isEmpty() ? 0 : list.get(0); - } - - @Transactional - @Override - public boolean setActiveStatusForJID(final String JID, final UserService.ActiveStatus jidStatus) { - User user = getUserByJID(JID); - if (user != null) { - int newStatus = jidStatus == UserService.ActiveStatus.Active ? 1 : 0; - return getJdbcTemplate().update( - "UPDATE jids SET active = ? WHERE user_id = ? AND jid = ?", - newStatus, user.getUid(), JID) >= 0; - } - return false; - } - - @Transactional(readOnly = true) - @Override - public List<String> getAllJIDs(final User user) { - return getJdbcTemplate().queryForList( - "SELECT jid FROM jids WHERE user_id=?", String.class, user.getUid()); - } - - @Transactional(readOnly = true) - @Override - public List<Auth> getAuthCodes(final User user) { - return getJdbcTemplate().query( - "SELECT account,authcode FROM auth WHERE user_id=? AND protocol='xmpp'", - (rs, num) -> new Auth(rs.getString(1), rs.getString(2)), - user.getUid()); - } - - @Transactional(readOnly = true) - @Override - public List<String> getEmails(final User user) { - return getJdbcTemplate().queryForList("SELECT email FROM emails WHERE user_id=?", String.class, user.getUid()); - } - - @Transactional(readOnly = true) - @Override - public String getEmailHash(final User user) { - List<String> list = getJdbcTemplate().queryForList( - "SELECT hash FROM mail WHERE user_id = ?", - String.class, - user.getUid()); - return list.isEmpty() ? StringUtils.EMPTY : list.get(0) + "@mail.juick.com"; - } - - @Transactional - @Override - public int deleteLoginForUser(final String name) { - if (StringUtils.isBlank(name)) - return 0; - - return getJdbcTemplate().update( - "delete from logins where user_id in (select id from users where nick = ?)", name); - } - - @Transactional - @Override - public int setLoginForUser(final int uid, final String loginHash) { - if (StringUtils.isEmpty(loginHash)) - return 0; - - return getNamedParameterJdbcTemplate().update( - "INSERT INTO logins (user_id, hash) VALUES(:uid, :hash) ON DUPLICATE KEY UPDATE hash = :hash", - new MapSqlParameterSource() - .addValue("hash", loginHash) - .addValue("uid", uid)); - } - - @Transactional - @Override - public void logout(int uid) { - getJdbcTemplate().update("DELETE FROM logins WHERE user_id=?", uid); - } - - @Transactional - @Override - public boolean deleteJID(int uid, String jid) { - return getNamedParameterJdbcTemplate().update("DELETE FROM jids " + - "WHERE (SELECT COUNT(*) cnt FROM (select user_id, jid FROM jids j) c WHERE user_id=:uid) > 1 " + - "AND user_id=:uid AND jid=:jid", - new MapSqlParameterSource() - .addValue("uid", uid) - .addValue("jid", jid)) > 0; - } - - @Transactional - @Override - public boolean unauthJID(int uid, String jid) { - return getJdbcTemplate() - .update("DELETE FROM auth WHERE user_id=? AND protocol='xmpp' AND account=?", uid, jid) > 0; - } - - @Transactional(readOnly = true) - @Override - public List<String> getActiveJIDs() { - return getJdbcTemplate().queryForList("SELECT jid FROM jids WHERE active=1 AND loginhash IS NULL", String.class); - } - - @Override - public void updateLastSeen(User user) { - getJdbcTemplate().update("UPDATE users SET last_seen=now() WHERE id=?", user.getUid()); - } -} diff --git a/juick-server/src/main/java/com/juick/service/activities/ActivityListener.java b/juick-server/src/main/java/com/juick/service/activities/ActivityListener.java deleted file mode 100644 index 863bda04..00000000 --- a/juick-server/src/main/java/com/juick/service/activities/ActivityListener.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.juick.service.activities; - -import org.springframework.context.event.EventListener; -import org.springframework.scheduling.annotation.Async; - -public interface ActivityListener { - @Async - @EventListener - void processFollowEvent(FollowEvent event); - @Async - @EventListener - void undoFollowEvent(UndoFollowEvent event); - @Async - @EventListener - void deleteUserEvent(DeleteUserEvent event); - @Async - @EventListener - void deleteMessageEvent(DeleteMessageEvent event); -} diff --git a/juick-server/src/main/java/com/juick/service/activities/DeleteMessageEvent.java b/juick-server/src/main/java/com/juick/service/activities/DeleteMessageEvent.java deleted file mode 100644 index 67e40f44..00000000 --- a/juick-server/src/main/java/com/juick/service/activities/DeleteMessageEvent.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.juick.service.activities; - -import com.juick.Message; -import org.springframework.context.ApplicationEvent; - -public class DeleteMessageEvent extends ApplicationEvent { - private Message message; - /** - * Create a new ApplicationEvent. - * - * @param source the object on which the event initially occurred (never {@code null}) - */ - public DeleteMessageEvent(Object source, Message message) { - super(source); - this.message = message; - } - - public Message getMessage() { - return message; - } -} diff --git a/juick-server/src/main/java/com/juick/service/activities/DeleteUserEvent.java b/juick-server/src/main/java/com/juick/service/activities/DeleteUserEvent.java deleted file mode 100644 index 8b51da9d..00000000 --- a/juick-server/src/main/java/com/juick/service/activities/DeleteUserEvent.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.juick.service.activities; - -import org.springframework.context.ApplicationEvent; - -public class DeleteUserEvent extends ApplicationEvent { - private String userUri; - /** - * Create a new ApplicationEvent. - * - * @param source the object on which the event initially occurred (never {@code null}) - */ - public DeleteUserEvent(Object source, String userUri) { - super(source); - this.userUri = userUri; - } - - public String getUserUri() { - return userUri; - } -} diff --git a/juick-server/src/main/java/com/juick/service/activities/FollowEvent.java b/juick-server/src/main/java/com/juick/service/activities/FollowEvent.java deleted file mode 100644 index c96613ba..00000000 --- a/juick-server/src/main/java/com/juick/service/activities/FollowEvent.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.juick.service.activities; - -import com.juick.server.api.activity.model.activities.Follow; -import org.springframework.context.ApplicationEvent; - -public class FollowEvent extends ApplicationEvent { - private Follow request; - /** - * Create a new ApplicationEvent. - * - * @param source the object on which the event initially occurred (never {@code null}) - */ - public FollowEvent(Object source, Follow followRequest) { - super(source); - this.request = followRequest; - } - - public Follow getRequest() { - return request; - } -} diff --git a/juick-server/src/main/java/com/juick/service/activities/UndoFollowEvent.java b/juick-server/src/main/java/com/juick/service/activities/UndoFollowEvent.java deleted file mode 100644 index 2b48e6f6..00000000 --- a/juick-server/src/main/java/com/juick/service/activities/UndoFollowEvent.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.juick.service.activities; - -import org.springframework.context.ApplicationEvent; - -public class UndoFollowEvent extends ApplicationEvent { - private String actor; - private String object; - /** - * Create a new ApplicationEvent. - * - * @param source the object on which the event initially occurred (never {@code null}) - */ - public UndoFollowEvent(Object source, String actor, String object) { - super(source); - this.actor = actor; - this.object = object; - } - - public String getActor() { - return actor; - } - - public String getObject() { - return object; - } -} diff --git a/juick-server/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java b/juick-server/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java deleted file mode 100644 index 9215d09a..00000000 --- a/juick-server/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service.security; - -import com.juick.User; -import com.juick.service.security.entities.JuickUser; -import com.juick.service.UserService; -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.authentication.RememberMeAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.RememberMeServices; -import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices; -import org.springframework.util.Assert; -import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.web.util.WebUtils; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * Created by aalexeev on 4/5/17. - */ -public class HashParamAuthenticationFilter extends OncePerRequestFilter { - public static final String PARAM_NAME = "hash"; - - private final UserService userService; - private final RememberMeServices rememberMeServices; - - - public HashParamAuthenticationFilter( - final UserService userService, - final RememberMeServices rememberMeServices) { - Assert.notNull(userService, "userService should not be null"); - Assert.notNull(rememberMeServices, "rememberMeServices should not be null"); - - this.userService = userService; - this.rememberMeServices = rememberMeServices; - } - - @Override - protected void doFilterInternal( - HttpServletRequest request, - HttpServletResponse response, - FilterChain filterChain) throws ServletException, IOException { - - String hash = getHashFromRequest(request); - - if (hash != null && authenticationIsRequired()) { - User user = userService.getUserByHash(hash); - - if (!user.isAnonymous()) { - User userWithPassword = userService.getUserByName(user.getName()); - userWithPassword.setAuthHash(userService.getHashByUID(userWithPassword.getUid())); - Authentication authentication = new RememberMeAuthenticationToken( - ((AbstractRememberMeServices)rememberMeServices).getKey(), new JuickUser(userWithPassword), JuickUser.USER_AUTHORITY); - - SecurityContextHolder.getContext().setAuthentication(authentication); - - rememberMeServices.loginSuccess(request, response, authentication); - } - } - - filterChain.doFilter(request, response); - } - - private boolean authenticationIsRequired() { - Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); - - return existingAuth == null || - !existingAuth.isAuthenticated() || - existingAuth instanceof AnonymousAuthenticationToken; - } - - private String getHashFromRequest(HttpServletRequest request) { - String paramHash = request.getParameter(PARAM_NAME); - Cookie cookieHash = WebUtils.getCookie(request, PARAM_NAME); - - if (paramHash == null && cookieHash != null) { - return cookieHash.getValue(); - } - return paramHash; - } -} diff --git a/juick-server/src/main/java/com/juick/service/security/JuickUserDetailsService.java b/juick-server/src/main/java/com/juick/service/security/JuickUserDetailsService.java deleted file mode 100644 index 59425fab..00000000 --- a/juick-server/src/main/java/com/juick/service/security/JuickUserDetailsService.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service.security; - -import com.juick.service.UserService; -import com.juick.service.security.entities.JuickUser; -import org.apache.commons.lang3.StringUtils; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.util.Assert; - -/** - * Created by aalexeev on 11/28/16. - */ -public class JuickUserDetailsService implements UserDetailsService { - private final UserService userService; - - public JuickUserDetailsService(final UserService userService) { - Assert.notNull(userService, "UserService must be initialized"); - this.userService = userService; - } - - @Override - public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException { - if (StringUtils.isBlank(username)) - throw new UsernameNotFoundException("Invalid user name " + username); - - com.juick.User user = userService.getUserByName(username); - - if (!user.isAnonymous()) { - user.setAuthHash(userService.getHashByUID(user.getUid())); - return new JuickUser(user); - } - - throw new UsernameNotFoundException("The username " + username + " is not found"); - } -} diff --git a/juick-server/src/main/java/com/juick/service/security/NullUserDetailsService.java b/juick-server/src/main/java/com/juick/service/security/NullUserDetailsService.java deleted file mode 100644 index 91acefa3..00000000 --- a/juick-server/src/main/java/com/juick/service/security/NullUserDetailsService.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service.security; - -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; - -/** - * Created by aalexeev on 11/28/16. - */ -public class NullUserDetailsService implements UserDetailsService { - @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - throw new UsernameNotFoundException( - "loadUserByUsername called for NullUserDetailsService, user " + username + "can not be found"); - } -} diff --git a/juick-server/src/main/java/com/juick/service/security/deprecated/CookieSimpleHashRememberMeServices.java b/juick-server/src/main/java/com/juick/service/security/deprecated/CookieSimpleHashRememberMeServices.java deleted file mode 100644 index e385d7dd..00000000 --- a/juick-server/src/main/java/com/juick/service/security/deprecated/CookieSimpleHashRememberMeServices.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service.security.deprecated; - -import com.juick.User; -import com.juick.service.security.entities.JuickUser; -import com.juick.service.UserService; -import com.juick.service.security.NullUserDetailsService; -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.env.Environment; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.web.authentication.RememberMeServices; -import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices; -import org.springframework.security.web.authentication.rememberme.InvalidCookieException; -import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationException; -import org.springframework.util.Assert; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.Optional; - -/** - * Created by aalexeev on 11/28/16. - * - * @deprecated not recommended use for secure reasons - */ -@Deprecated -public class CookieSimpleHashRememberMeServices extends AbstractRememberMeServices implements RememberMeServices { - private static final Logger logger = LoggerFactory.getLogger(CookieSimpleHashRememberMeServices.class); - - private static final String COOKIE_PARAM_NAME = "hash"; - - private final UserService userService; - - public CookieSimpleHashRememberMeServices( - final String key, final UserService userService, final Environment environment) { - super(key, new NullUserDetailsService()); - - Assert.notNull(userService); - Assert.notNull(environment); - - this.userService = userService; - - setCookieName(COOKIE_PARAM_NAME); - setCookieDomain(environment.getProperty("web_domain", "localhost")); - setAlwaysRemember(true); - } - - @Override - public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { - super.logout(request, response, authentication); - userService.deleteLoginForUser(authentication.getName()); - } - - @Override - protected void onLoginSuccess( - HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) { - String username = successfulAuthentication.getName(); - - logger.debug("Creating new persistent login for user {}", username); - - try { - int uid = userService.getUIDbyName(username); - - Assert.isTrue(uid > 0); - - String hash = RandomStringUtils.randomAlphanumeric(16).toUpperCase(); - - userService.setLoginForUser(uid, hash); - - setCookie(new String[]{hash}, getTokenValiditySeconds(), request, response); - } catch (Exception e) { - logger.error("Failed to save cookies", e); - } - } - - @Override - protected UserDetails processAutoLoginCookie( - String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) - throws RememberMeAuthenticationException, UsernameNotFoundException { - String hash = cookieTokens[0]; - - if (StringUtils.isBlank(hash)) { - hash = request.getParameter("hash"); - } - if (StringUtils.isBlank(hash)) { - throw new InvalidCookieException("Cookie is invalid and hash parameter not found"); - } - - int uid = userService.getUIDbyHash(hash); - if (uid <= 0) - throw new UsernameNotFoundException("User not found by hash, cookies" + cookieTokens); - - Optional<User> userOptional = userService.getUserByUID(uid); - - Assert.isTrue(userOptional.isPresent()); - - return new JuickUser(userService.getUserByName(userOptional.get().getName())); - } - - @Override - protected String[] decodeCookie(String cookieValue) throws InvalidCookieException { - return new String[]{cookieValue}; - } - - @Override - protected String encodeCookie(String[] cookieTokens) { - return cookieTokens != null && cookieTokens.length > 0 ? cookieTokens[0] : StringUtils.EMPTY; - } -} diff --git a/juick-server/src/main/java/com/juick/service/security/deprecated/RequestParamHashRememberMeServices.java b/juick-server/src/main/java/com/juick/service/security/deprecated/RequestParamHashRememberMeServices.java deleted file mode 100644 index 3631e5a4..00000000 --- a/juick-server/src/main/java/com/juick/service/security/deprecated/RequestParamHashRememberMeServices.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service.security.deprecated; - -import com.juick.User; -import com.juick.service.security.entities.JuickUser; -import com.juick.service.UserService; -import com.juick.service.security.NullUserDetailsService; -import org.apache.commons.lang3.StringUtils; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.web.authentication.RememberMeServices; -import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices; -import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationException; -import org.springframework.util.Assert; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * Created by aalexeev on 11/30/16. - * - * @deprecated for security reasons - */ -@Deprecated -public class RequestParamHashRememberMeServices extends AbstractRememberMeServices implements RememberMeServices { - private static final String PARAM_NAME = "hash"; - - private final UserService userService; - - public RequestParamHashRememberMeServices(String key, UserService userService) { - super(key, new NullUserDetailsService()); - - Assert.notNull(userService); - this.userService = userService; - setAlwaysRemember(false); - } - - @Override - protected void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) { - // do nothing - } - - @Override - protected boolean rememberMeRequested(HttpServletRequest request, String parameter) { - return false; // always false - } - - @Override - protected void cancelCookie(HttpServletRequest request, HttpServletResponse response) { - // do nothing - } - - @Override - protected String extractRememberMeCookie(HttpServletRequest request) { - return PARAM_NAME; // return any not blank value - } - - @Override - protected UserDetails processAutoLoginCookie( - String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) - throws RememberMeAuthenticationException, UsernameNotFoundException { - String hash = request.getParameter(PARAM_NAME); - - if (StringUtils.isNotBlank(hash)) { - User user = userService.getUserByHash(hash); - if (!user.isAnonymous()) - return new JuickUser(userService.getUserByName(user.getName())); - } - throw new UsernameNotFoundException("User not found by hash " + hash); - } -} diff --git a/juick-server/src/main/java/com/juick/service/security/entities/JuickUser.java b/juick-server/src/main/java/com/juick/service/security/entities/JuickUser.java deleted file mode 100644 index c43f112f..00000000 --- a/juick-server/src/main/java/com/juick/service/security/entities/JuickUser.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.service.security.entities; - -import com.juick.User; -import com.juick.model.AnonymousUser; -import org.apache.commons.lang3.StringUtils; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * Created by aalexeev on 11/21/16. - */ -public class JuickUser implements UserDetails { - static final GrantedAuthority ROLE_USER = new SimpleGrantedAuthority("ROLE_USER"); - static final GrantedAuthority ROLE_ANONYMOUS = new SimpleGrantedAuthority("ROLE_ANONYMOUS"); - - public static final List<GrantedAuthority> USER_AUTHORITY = Collections.singletonList(ROLE_USER); - public static final List<GrantedAuthority> ANONYMOUS_AUTHORITY = Collections.singletonList(ROLE_ANONYMOUS); - - public static final JuickUser ANONYMOUS_USER = new JuickUser(AnonymousUser.INSTANCE, ANONYMOUS_AUTHORITY); - - private final com.juick.User user; - private final Collection<? extends GrantedAuthority> authorities; - - public JuickUser(com.juick.User user) { - this(user, USER_AUTHORITY); - } - - public JuickUser(com.juick.User user, Collection<? extends GrantedAuthority> authorities) { - this.user = user; - this.authorities = authorities; - } - - @Override - public Collection<? extends GrantedAuthority> getAuthorities() { - return authorities; - } - - @Override - public String getPassword() { - return "{noop}" + user.getCredentials(); - } - - @Override - public String getUsername() { - return user.getName(); - } - - @Override - public boolean isAccountNonExpired() { - return true; - } - - @Override - public boolean isAccountNonLocked() { - return StringUtils.isNotBlank(user.getCredentials()); - } - - @Override - public boolean isCredentialsNonExpired() { - return isAccountNonLocked(); - } - - @Override - public boolean isEnabled() { - return !user.isBanned() && isCredentialsNonExpired(); - } - - public User getUser() { - return user; - } -} diff --git a/juick-server/src/main/java/com/mitchellbosecke/pebble/extension/FormatterExtension.java b/juick-server/src/main/java/com/mitchellbosecke/pebble/extension/FormatterExtension.java deleted file mode 100644 index 9189c2be..00000000 --- a/juick-server/src/main/java/com/mitchellbosecke/pebble/extension/FormatterExtension.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.mitchellbosecke.pebble.extension; - -import com.mitchellbosecke.pebble.extension.filters.*; - -import java.util.HashMap; -import java.util.Map; - -/** - * Created by vitalyster on 04.05.2017. - */ -public class FormatterExtension extends AbstractExtension { - @Override - public Map<String, Filter> getFilters() { - Map<String, Filter> filters = new HashMap<>(); - filters.put("formatMessage", new FormatMessageFilter()); - filters.put("prettyTime", new PrettyTimeFilter()); - filters.put("timestamp", new TimestampFilter()); - filters.put("tagsList", new TagsListFilter()); - return filters; - } -} diff --git a/juick-server/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatMessageFilter.java b/juick-server/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatMessageFilter.java deleted file mode 100644 index 1b75727e..00000000 --- a/juick-server/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatMessageFilter.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.mitchellbosecke.pebble.extension.filters; - -import com.juick.Message; -import com.juick.model.AnonymousUser; -import com.juick.util.MessageUtils; -import com.mitchellbosecke.pebble.extension.Filter; -import com.mitchellbosecke.pebble.extension.escaper.SafeString; -import com.mitchellbosecke.pebble.template.EvaluationContext; -import com.mitchellbosecke.pebble.template.PebbleTemplate; -import org.apache.commons.lang3.StringUtils; - -import java.util.List; -import java.util.Map; -import java.util.Optional; - -/** - * Created by vitalyster on 04.05.2017. - */ -public class FormatMessageFilter implements Filter { - @Override - public Object apply(Object input, Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) { - if (input instanceof Message) { - Message msg = (Message) input; - if (msg.isHtml()) { - return new SafeString(msg.getText()); - } - boolean isCode = msg.getTags().stream().anyMatch(t -> t.getName().equals("code")); - String formattedMessage = isCode ? MessageUtils.formatMessageCode(StringUtils.defaultString(msg.getText())) - : MessageUtils.formatMessage(StringUtils.defaultString(msg.getText())); - if (MessageUtils.isPM(msg)) { - return new SafeString(formattedMessage); - } else { - String toUserString = msg.getTo().getUid() == 0 ? String.format("<a href=\"%s\" data-user-uri=\"1\">@%s</a>", - msg.getTo().getUri().toASCIIString(), msg.getTo().getName()) : String.format("<a href=\"https://juick.com/%s/\">@%s</a>", msg.getTo().getName(), msg.getTo().getName()); - String formatString = MessageUtils.replyStartsWithQuote(msg) ? "%s,\n%s" : "%s, %s"; - String msgTxt = msg.getRid() > 0 ? String.format(formatString, toUserString, formattedMessage) - : formattedMessage; - return new SafeString(msgTxt); - } - } - throw new IllegalArgumentException("invalid input"); - } - - @Override - public List<String> getArgumentNames() { - return null; - } -} diff --git a/juick-server/src/main/java/com/mitchellbosecke/pebble/extension/filters/PrettyTimeFilter.java b/juick-server/src/main/java/com/mitchellbosecke/pebble/extension/filters/PrettyTimeFilter.java deleted file mode 100644 index 72dab20d..00000000 --- a/juick-server/src/main/java/com/mitchellbosecke/pebble/extension/filters/PrettyTimeFilter.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.mitchellbosecke.pebble.extension.filters; - -import com.juick.util.PrettyTimeFormatter; -import com.mitchellbosecke.pebble.extension.Filter; -import com.mitchellbosecke.pebble.template.EvaluationContext; -import com.mitchellbosecke.pebble.template.PebbleTemplate; - -import java.time.Instant; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -/** - * Created by vitalyster on 04.05.2017. - */ -public class PrettyTimeFilter implements Filter { - - PrettyTimeFormatter formatter = new PrettyTimeFormatter(); - - @Override - public Object apply(Object input, Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) { - if (input instanceof Instant) { - Locale locale = context.getLocale(); - return formatter.format(locale, Date.from((Instant)input)); - } - throw new IllegalArgumentException("invalid input"); - } - - @Override - public List<String> getArgumentNames() { - return null; - } -} diff --git a/juick-server/src/main/java/com/mitchellbosecke/pebble/extension/filters/TagsListFilter.java b/juick-server/src/main/java/com/mitchellbosecke/pebble/extension/filters/TagsListFilter.java deleted file mode 100644 index c7b00ea3..00000000 --- a/juick-server/src/main/java/com/mitchellbosecke/pebble/extension/filters/TagsListFilter.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.mitchellbosecke.pebble.extension.filters; - -import com.juick.Tag; -import com.mitchellbosecke.pebble.extension.Filter; -import com.mitchellbosecke.pebble.template.EvaluationContext; -import com.mitchellbosecke.pebble.template.PebbleTemplate; - -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * Created by vitalyster on 23.05.2017. - */ -public class TagsListFilter implements Filter { - @SuppressWarnings("unchecked") - @Override - public Object apply(Object input, Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) { - return ((List<Tag>) input).stream().map(Tag::getName).collect(Collectors.toList()); - } - - @Override - public List<String> getArgumentNames() { - return null; - } -} diff --git a/juick-server/src/main/java/com/mitchellbosecke/pebble/extension/filters/TimestampFilter.java b/juick-server/src/main/java/com/mitchellbosecke/pebble/extension/filters/TimestampFilter.java deleted file mode 100644 index 5f98c167..00000000 --- a/juick-server/src/main/java/com/mitchellbosecke/pebble/extension/filters/TimestampFilter.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.mitchellbosecke.pebble.extension.filters; - -import com.mitchellbosecke.pebble.extension.Filter; -import com.mitchellbosecke.pebble.template.EvaluationContext; -import com.mitchellbosecke.pebble.template.PebbleTemplate; - -import java.time.Instant; -import java.util.Date; -import java.util.List; -import java.util.Map; - -public class TimestampFilter implements Filter { - @Override - public Object apply(Object input, Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) { - if (input instanceof Instant) { - return Date.from((Instant)input); - } - throw new IllegalArgumentException("invalid input"); - } - - @Override - public List<String> getArgumentNames() { - return null; - } -} diff --git a/juick-server/src/main/java/rocks/xmpp/core/session/debug/LogbackDebugger.java b/juick-server/src/main/java/rocks/xmpp/core/session/debug/LogbackDebugger.java deleted file mode 100644 index aae49bc7..00000000 --- a/juick-server/src/main/java/rocks/xmpp/core/session/debug/LogbackDebugger.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package rocks.xmpp.core.session.debug; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import rocks.xmpp.core.session.XmppSession; - -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Created by vitalyster on 17.11.2016. - */ -public class LogbackDebugger implements XmppDebugger { - private Logger logger; - - @Override - public void initialize(XmppSession xmppSession) { - logger = LoggerFactory.getLogger("com.juick.server.xmpp"); - } - - @Override - public void writeStanza(String s, Object o) { - logger.info("OUT: {}", s); - } - - @Override - public void readStanza(String s, Object o) { - logger.info("IN: {}", s); - } - - @Override - public OutputStream createOutputStream(OutputStream outputStream) { - return outputStream; - } - - @Override - public InputStream createInputStream(InputStream inputStream) { - return inputStream; - } -} diff --git a/juick-server/src/main/java/ru/sape/Sape.java b/juick-server/src/main/java/ru/sape/Sape.java deleted file mode 100644 index 38577c45..00000000 --- a/juick-server/src/main/java/ru/sape/Sape.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * http://code.google.com/p/javasape/ - */ -package ru.sape; - -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, String cookie) { - return new SapePageLinks(sapePageLinkConnection, sapeUser, requestUri, cookie, debug); - } -} diff --git a/juick-server/src/main/java/ru/sape/SapeConnection.java b/juick-server/src/main/java/ru/sape/SapeConnection.java deleted file mode 100644 index a15658fa..00000000 --- a/juick-server/src/main/java/ru/sape/SapeConnection.java +++ /dev/null @@ -1,108 +0,0 @@ -package ru.sape; - -import com.github.ooxi.phparser.SerializedPhpParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -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.*; - -public class SapeConnection { - private static final Logger logger = LoggerFactory.getLogger(SapeConnection.class); - private final String version = "1.0.3"; - private final List<String> 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<String, Object> cached; - long cacheUpdated; - - @SuppressWarnings("unchecked") - public Map<String, Object> 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:")) { - logger.error("Sape responded with error: {}", data); - - continue; - } - - try { - cached = (Map<String, Object>) new SerializedPhpParser(data).parse(); - } catch (Exception e) { - logger.error("Can't parse Sape data", e); - continue; - } - - cacheUpdated = System.currentTimeMillis(); - - return cached; - } - - logger.error("Unable to fetch Sape data"); - - return Collections.emptyMap(); - } - - return cached; - } -} diff --git a/juick-server/src/main/java/ru/sape/SapePageLinks.java b/juick-server/src/main/java/ru/sape/SapePageLinks.java deleted file mode 100644 index e89b4e71..00000000 --- a/juick-server/src/main/java/ru/sape/SapePageLinks.java +++ /dev/null @@ -1,76 +0,0 @@ -package ru.sape; - -import java.util.*; - -public class SapePageLinks { - - private boolean showCode; - - public SapePageLinks(SapeConnection sapeConnection, String sapeUser, String requestUri, String sapeCookie) { - this(sapeConnection, sapeUser, requestUri, sapeCookie, false); - } - - @SuppressWarnings("unchecked") - public SapePageLinks(SapeConnection sapeConnection, String sapeUser, String requestUri, String sapeCookie, boolean showCode) { - if (sapeUser.equals(sapeCookie)) { - showCode = true; - } - - Map<String, Object> data = sapeConnection.getData(); - - if (data.containsKey("__sape_delimiter__")) { - linkDelimiter = (String) data.get("__sape_delimiter__"); - } - - if (data.containsKey(requestUri)) { - pageLinks = new ArrayList<>(((Map<Object, String>) 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<Object, String>) newUrl).values()); - } else { - pageLinks = new ArrayList<>(Collections.singletonList((String) newUrl)); - } - } - } - - this.showCode = showCode; - } - private String linkDelimiter = "."; - private List<String> 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<String> 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, "<sape_noindex>"); - s.append("</sape_noindex>"); - } - - return s.toString(); - } -} diff --git a/juick-server/src/main/resources/1x1.png b/juick-server/src/main/resources/1x1.png Binary files differdeleted file mode 100644 index 1914264c..00000000 --- a/juick-server/src/main/resources/1x1.png +++ /dev/null diff --git a/juick-server/src/main/resources/Transparent.gif b/juick-server/src/main/resources/Transparent.gif Binary files differdeleted file mode 100644 index f191b280..00000000 --- a/juick-server/src/main/resources/Transparent.gif +++ /dev/null diff --git a/juick-server/src/main/resources/db/migration/V1.10__favorites_user_uri.sql b/juick-server/src/main/resources/db/migration/V1.10__favorites_user_uri.sql deleted file mode 100644 index 8f382398..00000000 --- a/juick-server/src/main/resources/db/migration/V1.10__favorites_user_uri.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE favorites ADD COLUMN user_uri char(255) DEFAULT NULL; -UPDATE favorites SET user_uri='' WHERE user_uri IS NULL; -ALTER TABLE favorites MODIFY COLUMN user_uri char(255) NOT NULL DEFAULT '';
\ No newline at end of file diff --git a/juick-server/src/main/resources/db/migration/V1.11__increase pm timestamp precision.sql b/juick-server/src/main/resources/db/migration/V1.11__increase pm timestamp precision.sql deleted file mode 100644 index e83eb621..00000000 --- a/juick-server/src/main/resources/db/migration/V1.11__increase pm timestamp precision.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE pm MODIFY COLUMN ts timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP;
\ No newline at end of file diff --git a/juick-server/src/main/resources/db/migration/V1.12__drop unused tables.sql b/juick-server/src/main/resources/db/migration/V1.12__drop unused tables.sql deleted file mode 100644 index 1599f5f6..00000000 --- a/juick-server/src/main/resources/db/migration/V1.12__drop unused tables.sql +++ /dev/null @@ -1,5 +0,0 @@ -DROP TABLE IF EXISTS messages_votes; -DROP TABLE IF EXISTS reader_links; -DROP TABLE IF EXISTS reader_rss; -DROP TABLE IF EXISTS captcha; -DROP TABLE IF EXISTS captchaimg;
\ No newline at end of file diff --git a/juick-server/src/main/resources/db/migration/V1.13__drop unused tables.sql b/juick-server/src/main/resources/db/migration/V1.13__drop unused tables.sql deleted file mode 100644 index c35fc92c..00000000 --- a/juick-server/src/main/resources/db/migration/V1.13__drop unused tables.sql +++ /dev/null @@ -1,5 +0,0 @@ -DROP TABLE IF EXISTS ads_messages; -DROP TABLE IF EXISTS ads_messages_log; -DROP TABLE IF EXISTS presence; -DROP TABLE IF EXISTS friends_facebook; -DROP TABLE IF EXISTS users_refs;
\ No newline at end of file diff --git a/juick-server/src/main/resources/db/migration/V1.14__drop broken pm_streams.sql b/juick-server/src/main/resources/db/migration/V1.14__drop broken pm_streams.sql deleted file mode 100644 index 448c5ce2..00000000 --- a/juick-server/src/main/resources/db/migration/V1.14__drop broken pm_streams.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE IF EXISTS pm_streams;
\ No newline at end of file diff --git a/juick-server/src/main/resources/db/migration/V1.15__drop unused columns add ts for some tables.sql b/juick-server/src/main/resources/db/migration/V1.15__drop unused columns add ts for some tables.sql deleted file mode 100644 index 6b3ab388..00000000 --- a/juick-server/src/main/resources/db/migration/V1.15__drop unused columns add ts for some tables.sql +++ /dev/null @@ -1,4 +0,0 @@ -ALTER TABLE subscr_users DROP COLUMN `jid`; -ALTER TABLE subscr_users DROP COLUMN `active`; -ALTER TABLE auth ADD COLUMN ts timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP; -ALTER TABLE mail ADD COLUMN ts timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP;
\ No newline at end of file diff --git a/juick-server/src/main/resources/db/migration/V1.16__last seen.sql b/juick-server/src/main/resources/db/migration/V1.16__last seen.sql deleted file mode 100644 index 52ca4e90..00000000 --- a/juick-server/src/main/resources/db/migration/V1.16__last seen.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE users ADD COLUMN last_seen timestamp(6) NULL; -UPDATE users SET last_seen=lastmessage;
\ No newline at end of file diff --git a/juick-server/src/main/resources/db/migration/V1.1__Add updated_at field.sql b/juick-server/src/main/resources/db/migration/V1.1__Add updated_at field.sql deleted file mode 100644 index dac179b1..00000000 --- a/juick-server/src/main/resources/db/migration/V1.1__Add updated_at field.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE messages_txt ADD COLUMN updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP; -ALTER TABLE replies ADD COLUMN updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP; diff --git a/juick-server/src/main/resources/db/migration/V1.2__Drop telegram_chats.sql b/juick-server/src/main/resources/db/migration/V1.2__Drop telegram_chats.sql deleted file mode 100644 index c8faee0d..00000000 --- a/juick-server/src/main/resources/db/migration/V1.2__Drop telegram_chats.sql +++ /dev/null @@ -1,2 +0,0 @@ -INSERT INTO telegram(tg_id, tg_name, loginhash) SELECT chat_id AS tg_id, 'Anonymous', UUID() FROM telegram_chats; -DROP TABLE telegram_chats;
\ No newline at end of file diff --git a/juick-server/src/main/resources/db/migration/V1.3__Nullable user_id column in auth table.sql b/juick-server/src/main/resources/db/migration/V1.3__Nullable user_id column in auth table.sql deleted file mode 100644 index ced85ade..00000000 --- a/juick-server/src/main/resources/db/migration/V1.3__Nullable user_id column in auth table.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE auth MODIFY COLUMN user_id int(10) unsigned NULL;
\ No newline at end of file diff --git a/juick-server/src/main/resources/db/migration/V1.4__ActivityPub followers.sql b/juick-server/src/main/resources/db/migration/V1.4__ActivityPub followers.sql deleted file mode 100644 index 16b39f62..00000000 --- a/juick-server/src/main/resources/db/migration/V1.4__ActivityPub followers.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE IF NOT EXISTS `followers` ( - `user_id` int(10) unsigned DEFAULT NULL, - `acct` char(64) NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY `acct` (`acct`), - foreign key (user_id) references users(id) -);
\ No newline at end of file diff --git a/juick-server/src/main/resources/db/migration/V1.5__Drop acct index.sql b/juick-server/src/main/resources/db/migration/V1.5__Drop acct index.sql deleted file mode 100644 index 58757d88..00000000 --- a/juick-server/src/main/resources/db/migration/V1.5__Drop acct index.sql +++ /dev/null @@ -1,6 +0,0 @@ -ALTER TABLE followers ADD COLUMN `acct_migr` char(64) NOT NULL; -UPDATE followers SET `acct_migr` = `acct`; -ALTER TABLE followers DROP COLUMN `acct`; -ALTER TABLE followers ADD COLUMN `acct` char(64) NOT NULL; -UPDATE followers SET `acct` = `acct_migr`; -ALTER TABLE followers DROP COLUMN `acct_migr`;
\ No newline at end of file diff --git a/juick-server/src/main/resources/db/migration/V1.6__user_uri.sql b/juick-server/src/main/resources/db/migration/V1.6__user_uri.sql deleted file mode 100644 index c302907c..00000000 --- a/juick-server/src/main/resources/db/migration/V1.6__user_uri.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE replies ADD COLUMN user_uri char(255) DEFAULT NULL;
\ No newline at end of file diff --git a/juick-server/src/main/resources/db/migration/V1.7__reply_uri.sql b/juick-server/src/main/resources/db/migration/V1.7__reply_uri.sql deleted file mode 100644 index 9ec35485..00000000 --- a/juick-server/src/main/resources/db/migration/V1.7__reply_uri.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE replies ADD COLUMN reply_uri char(255) DEFAULT NULL;
\ No newline at end of file diff --git a/juick-server/src/main/resources/db/migration/V1.8__html reply.sql b/juick-server/src/main/resources/db/migration/V1.8__html reply.sql deleted file mode 100644 index 9f939971..00000000 --- a/juick-server/src/main/resources/db/migration/V1.8__html reply.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE replies ADD COLUMN html tinyint(4) NOT NULL DEFAULT '0';
\ No newline at end of file diff --git a/juick-server/src/main/resources/db/migration/V1.9__reply_uri_index.sql b/juick-server/src/main/resources/db/migration/V1.9__reply_uri_index.sql deleted file mode 100644 index 0ee3c77f..00000000 --- a/juick-server/src/main/resources/db/migration/V1.9__reply_uri_index.sql +++ /dev/null @@ -1 +0,0 @@ -create index reply_uri_index on replies(reply_uri)
\ No newline at end of file diff --git a/juick-server/src/main/resources/errors.properties b/juick-server/src/main/resources/errors.properties deleted file mode 100644 index 7ec8fbfd..00000000 --- a/juick-server/src/main/resources/errors.properties +++ /dev/null @@ -1,3 +0,0 @@ -error.title = Error page - -error.login=Wrong user or password
\ No newline at end of file diff --git a/juick-server/src/main/resources/errors_ru.properties b/juick-server/src/main/resources/errors_ru.properties deleted file mode 100644 index ca13b926..00000000 --- a/juick-server/src/main/resources/errors_ru.properties +++ /dev/null @@ -1,3 +0,0 @@ -error.title = Произошла ошибка - -error.login=Произошла ошибка, проверьте имя пользователя и пароль
\ No newline at end of file diff --git a/juick-server/src/main/resources/help b/juick-server/src/main/resources/help deleted file mode 160000 -Subproject ce103cd9a2a8a200c6ebb3b41525e7c8f639d98 diff --git a/juick-server/src/main/resources/juick.png b/juick-server/src/main/resources/juick.png Binary files differdeleted file mode 100644 index a7b0e901..00000000 --- a/juick-server/src/main/resources/juick.png +++ /dev/null diff --git a/juick-server/src/main/resources/juick.sql b/juick-server/src/main/resources/juick.sql deleted file mode 100644 index a6fb76cd..00000000 --- a/juick-server/src/main/resources/juick.sql +++ /dev/null @@ -1,947 +0,0 @@ --- MySQL dump 10.16 Distrib 10.1.26-MariaDB, for debian-linux-gnu (x86_64) --- --- Host: localhost Database: juick --- ------------------------------------------------------ -use juick; --- Server version 10.1.26-MariaDB-0+deb9u1 -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8mb4 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `ads_messages` --- - -DROP TABLE IF EXISTS `ads_messages`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `ads_messages` ( - `message_id` int(10) unsigned NOT NULL -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `ads_messages_log` --- - -DROP TABLE IF EXISTS `ads_messages_log`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `ads_messages_log` ( - `user_id` int(10) unsigned NOT NULL, - `message_id` int(10) unsigned NOT NULL, - `ts` int(10) unsigned NOT NULL DEFAULT '0' -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `android` --- - -DROP TABLE IF EXISTS `android`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `android` ( - `user_id` int(10) unsigned NOT NULL, - `regid` char(255) NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY `regid` (`regid`), - KEY `user_id` (`user_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `auth` --- - -DROP TABLE IF EXISTS `auth`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `auth` ( - `user_id` int(10) unsigned NOT NULL, - `protocol` enum('xmpp','email','sms') NOT NULL, - `account` char(64) NOT NULL, - `authcode` char(8) NOT NULL -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `bl_tags` --- - -DROP TABLE IF EXISTS `bl_tags`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `bl_tags` ( - `user_id` int(10) unsigned NOT NULL, - `tag_id` int(10) unsigned NOT NULL, - KEY `tag_id` (`tag_id`), - KEY `user_id` (`user_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `bl_users` --- - -DROP TABLE IF EXISTS `bl_users`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `bl_users` ( - `user_id` int(10) unsigned NOT NULL, - `bl_user_id` int(10) unsigned NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`user_id`,`bl_user_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `captcha` --- - -DROP TABLE IF EXISTS `captcha`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `captcha` ( - `jid` char(64) NOT NULL, - `hash` char(16) NOT NULL, - `confirmed` tinyint(4) NOT NULL -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `captchaimg` --- - -DROP TABLE IF EXISTS `captchaimg`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `captchaimg` ( - `id` char(16) NOT NULL, - `txt` char(6) NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `ip` char(16) NOT NULL -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `emails` --- - -DROP TABLE IF EXISTS `emails`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `emails` ( - `user_id` int(10) unsigned NOT NULL, - `email` char(64) NOT NULL, - `subscr_hour` tinyint(4) DEFAULT NULL, - KEY `email` (`email`) USING HASH -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `facebook` --- - -DROP TABLE IF EXISTS `facebook`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `facebook` ( - `user_id` int(10) unsigned DEFAULT NULL, - `fb_id` bigint(20) unsigned DEFAULT NULL, - `loginhash` char(36) DEFAULT NULL, - `access_token` char(255) DEFAULT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `fb_name` char(64) DEFAULT NULL, - `fb_link` char(255) DEFAULT NULL, - `crosspost` tinyint(1) unsigned NOT NULL DEFAULT '1', - KEY `user_id` (`user_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `favorites` --- - -DROP TABLE IF EXISTS `favorites`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `favorites` ( - `user_id` int(10) unsigned NOT NULL, - `message_id` int(10) unsigned NOT NULL, - `ts` datetime NOT NULL, - `like_id` int(10) unsigned NOT NULL DEFAULT '1', - KEY `user_id` (`user_id`), - KEY `message_id` (`message_id`), - KEY `like_id` (`like_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `friends_facebook` --- - -DROP TABLE IF EXISTS `friends_facebook`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `friends_facebook` ( - `user_id` int(10) unsigned NOT NULL, - `friend_id` bigint(20) unsigned NOT NULL, - UNIQUE KEY `user_id` (`user_id`,`friend_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `images` --- - -DROP TABLE IF EXISTS `images`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `images` ( - `mid` int(10) unsigned NOT NULL, - `rid` int(10) unsigned NOT NULL, - `thumb` int(10) unsigned NOT NULL, - `small` int(10) unsigned NOT NULL, - `medium` int(10) unsigned NOT NULL, - `height` int(10) unsigned NOT NULL, - `width` int(10) unsigned NOT NULL, - PRIMARY KEY (`mid`,`rid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `ios` --- - -DROP TABLE IF EXISTS `ios`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `ios` ( - `user_id` int(10) unsigned NOT NULL, - `token` char(64) COLLATE utf8mb4_unicode_ci NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY `token` (`token`), - KEY `user_id` (`user_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `jids` --- - -DROP TABLE IF EXISTS `jids`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `jids` ( - `user_id` int(10) unsigned DEFAULT NULL, - `jid` char(64) NOT NULL, - `active` tinyint(1) NOT NULL DEFAULT '1', - `loginhash` char(36) DEFAULT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY `jid` (`jid`), - KEY `user_id` (`user_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `logins` --- - -DROP TABLE IF EXISTS `logins`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `logins` ( - `user_id` int(10) unsigned NOT NULL, - `hash` char(16) NOT NULL, - UNIQUE KEY `user_id` (`user_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `mail` --- - -DROP TABLE IF EXISTS `mail`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `mail` ( - `user_id` int(10) unsigned NOT NULL, - `hash` char(16) NOT NULL, - PRIMARY KEY (`user_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `meon` --- - -DROP TABLE IF EXISTS `meon`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `meon` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `user_id` int(10) unsigned NOT NULL, - `link` char(255) NOT NULL, - `name` char(32) NOT NULL, - `ico` smallint(5) unsigned DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `messages` --- - -DROP TABLE IF EXISTS `messages`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `messages` ( - `message_id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `user_id` int(10) unsigned NOT NULL, - `lang` enum('en','ru','fr','fa','__') NOT NULL DEFAULT '__', - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `replies` smallint(5) unsigned NOT NULL DEFAULT '0', - `maxreplyid` smallint(5) unsigned NOT NULL DEFAULT '0', - `privacy` tinyint(4) NOT NULL DEFAULT '1', - `readonly` tinyint(1) NOT NULL DEFAULT '0', - `attach` enum('jpg','mp4','png') DEFAULT NULL, - `place_id` int(10) unsigned DEFAULT NULL, - `lat` decimal(10,7) DEFAULT NULL, - `lon` decimal(10,7) DEFAULT NULL, - `popular` tinyint(4) NOT NULL DEFAULT '0', - `hidden` tinyint(3) unsigned NOT NULL DEFAULT '0', - `likes` smallint(6) NOT NULL DEFAULT '0', - `updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`message_id`), - KEY `user_id` (`user_id`), - KEY `ts` (`ts`), - KEY `attach` (`attach`), - KEY `place_id` (`place_id`), - KEY `popular` (`popular`), - KEY `hidden` (`hidden`), - KEY `updated_indx` (`updated`,`message_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `messages_access` --- - -DROP TABLE IF EXISTS `messages_access`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `messages_access` ( - `message_id` int(10) unsigned NOT NULL, - `user_id` int(10) unsigned NOT NULL, - KEY `message_id` (`message_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `messages_tags` --- - -DROP TABLE IF EXISTS `messages_tags`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `messages_tags` ( - `message_id` int(10) unsigned NOT NULL, - `tag_id` int(10) unsigned NOT NULL, - UNIQUE KEY `message_id_2` (`message_id`,`tag_id`), - KEY `message_id` (`message_id`), - KEY `tag_id` (`tag_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `messages_txt` --- - -DROP TABLE IF EXISTS `messages_txt`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `messages_txt` ( - `message_id` int(10) unsigned NOT NULL, - `tags` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `repliesby` varchar(96) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `txt` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, - PRIMARY KEY (`message_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `messages_votes` --- - -DROP TABLE IF EXISTS `messages_votes`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `messages_votes` ( - `message_id` int(10) unsigned NOT NULL, - `user_id` int(10) unsigned NOT NULL, - `vote` tinyint(4) NOT NULL DEFAULT '1', - UNIQUE KEY `message_id` (`message_id`,`user_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `messenger` --- - -DROP TABLE IF EXISTS `messenger`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `messenger` ( - `user_id` int(10) unsigned DEFAULT NULL, - `sender_id` bigint(20) NOT NULL, - `display_name` char(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `loginhash` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `places` --- - -DROP TABLE IF EXISTS `places`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `places` ( - `place_id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `lat` decimal(10,7) NOT NULL, - `lon` decimal(10,7) NOT NULL, - `name` char(64) NOT NULL, - `descr` char(255) DEFAULT NULL, - `url` char(128) DEFAULT NULL, - `user_id` int(10) unsigned NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`place_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `places_tags` --- - -DROP TABLE IF EXISTS `places_tags`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `places_tags` ( - `place_id` int(10) unsigned NOT NULL, - `tag_id` int(10) unsigned NOT NULL -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `pm` --- - -DROP TABLE IF EXISTS `pm`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `pm` ( - `user_id` int(10) unsigned NOT NULL, - `user_id_to` int(10) unsigned NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `txt` text NOT NULL -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `pm_inroster` --- - -DROP TABLE IF EXISTS `pm_inroster`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `pm_inroster` ( - `user_id` int(10) unsigned NOT NULL, - `jid` char(64) NOT NULL, - UNIQUE KEY `user_id_2` (`user_id`,`jid`), - KEY `user_id` (`user_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `pm_streams` --- - -DROP TABLE IF EXISTS `pm_streams`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `pm_streams` ( - `user_id` int(10) unsigned NOT NULL, - `user_id_to` int(10) unsigned NOT NULL, - `lastmessage` datetime NOT NULL, - `lastview` datetime DEFAULT NULL, - `unread` smallint(5) unsigned NOT NULL DEFAULT '0', - UNIQUE KEY `user_id` (`user_id`,`user_id_to`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `presence` --- - -DROP TABLE IF EXISTS `presence`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `presence` ( - `user_id` int(10) unsigned NOT NULL, - `jid` char(64) DEFAULT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - UNIQUE KEY `jid` (`jid`) -) ENGINE=MEMORY DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `reactions` --- - -DROP TABLE IF EXISTS `reactions`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `reactions` ( - `like_id` int(10) unsigned NOT NULL, - `description` varchar(100) NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `reader_links` --- - -DROP TABLE IF EXISTS `reader_links`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `reader_links` ( - `link_id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `rss_id` int(10) unsigned NOT NULL, - `url` char(255) NOT NULL, - `title` char(255) NOT NULL, - `ts` datetime NOT NULL, - PRIMARY KEY (`link_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `reader_rss` --- - -DROP TABLE IF EXISTS `reader_rss`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `reader_rss` ( - `rss_id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `url` char(255) NOT NULL, - `lastcheck` datetime NOT NULL, - PRIMARY KEY (`rss_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `replies` --- - -DROP TABLE IF EXISTS `replies`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `replies` ( - `message_id` int(10) unsigned NOT NULL, - `reply_id` smallint(5) unsigned NOT NULL, - `user_id` int(10) unsigned NOT NULL, - `replyto` smallint(5) unsigned NOT NULL DEFAULT '0', - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `attach` enum('jpg','mp4','png') COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `txt` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, - KEY `ts` (`ts`), - KEY `message_id` (`message_id`), - KEY `uid` (`user_id`), - KEY `reply_indx` (`message_id`,`reply_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `search` --- - -DROP TABLE IF EXISTS `search`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `search` ( - `id` bigint(20) unsigned NOT NULL, - `weight` int(11) NOT NULL, - `query` varchar(3072) NOT NULL, - `group_id` int(11) DEFAULT NULL, - KEY `query` (`query`(768)) -) ENGINE=SPHINX DEFAULT CHARSET=utf8mb4 CONNECTION='sphinx://127.0.0.1:3312/messages'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `sphinx` --- - -DROP TABLE IF EXISTS `sphinx`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `sphinx` ( - `counter_id` tinyint(3) unsigned NOT NULL, - `max_id` int(10) unsigned NOT NULL, - PRIMARY KEY (`counter_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `subscr_messages` --- - -DROP TABLE IF EXISTS `subscr_messages`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `subscr_messages` ( - `message_id` int(10) unsigned NOT NULL, - `suser_id` int(10) unsigned NOT NULL, - `last_read_rid` smallint(5) NOT NULL DEFAULT '0', - UNIQUE KEY `message_id` (`message_id`,`suser_id`), - KEY `last_read_indx` (`suser_id`,`last_read_rid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `subscr_tags` --- - -DROP TABLE IF EXISTS `subscr_tags`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `subscr_tags` ( - `tag_id` int(10) unsigned NOT NULL, - `suser_id` int(10) unsigned NOT NULL, - UNIQUE KEY `tag_id` (`tag_id`,`suser_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `subscr_users` --- - -DROP TABLE IF EXISTS `subscr_users`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `subscr_users` ( - `user_id` int(10) unsigned NOT NULL, - `suser_id` int(10) unsigned NOT NULL, - `jid` char(64) DEFAULT NULL, - `active` bit(1) NOT NULL DEFAULT b'1', - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY `user_id` (`user_id`,`suser_id`), - KEY `suser_id` (`suser_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `tags` --- - -DROP TABLE IF EXISTS `tags`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `tags` ( - `tag_id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `synonym_id` int(10) unsigned DEFAULT NULL, - `name` char(70) CHARACTER SET utf8mb4 DEFAULT NULL, - `top` tinyint(1) unsigned NOT NULL DEFAULT '0', - `noindex` tinyint(1) unsigned NOT NULL DEFAULT '0', - `stat_messages` int(10) unsigned NOT NULL DEFAULT '0', - `stat_users` smallint(5) unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`tag_id`), - KEY `synonym_id` (`synonym_id`), - KEY `stat_msg_indx` (`name`,`stat_messages`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `tags_ignore` --- - -DROP TABLE IF EXISTS `tags_ignore`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `tags_ignore` ( - `tag_id` int(10) unsigned NOT NULL -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `tags_synonyms` --- - -DROP TABLE IF EXISTS `tags_synonyms`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `tags_synonyms` ( - `name` char(64) NOT NULL, - `changeto` char(64) NOT NULL -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `telegram` --- - -DROP TABLE IF EXISTS `telegram`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `telegram` ( - `user_id` int(10) unsigned DEFAULT NULL, - `tg_id` bigint(20) NOT NULL, - `tg_name` char(64) COLLATE utf8mb4_unicode_ci NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `loginhash` char(36) COLLATE utf8mb4_unicode_ci DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `telegram_chats` --- - -DROP TABLE IF EXISTS `telegram_chats`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `telegram_chats` ( - `chat_id` bigint(20) DEFAULT NULL, - UNIQUE KEY `chat_id` (`chat_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `top_ignore_messages` --- - -DROP TABLE IF EXISTS `top_ignore_messages`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `top_ignore_messages` ( - `message_id` int(10) unsigned NOT NULL -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `top_ignore_tags` --- - -DROP TABLE IF EXISTS `top_ignore_tags`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `top_ignore_tags` ( - `tag_id` int(10) unsigned NOT NULL, - PRIMARY KEY (`tag_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `top_ignore_users` --- - -DROP TABLE IF EXISTS `top_ignore_users`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `top_ignore_users` ( - `user_id` int(10) unsigned NOT NULL, - PRIMARY KEY (`user_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `twitter` --- - -DROP TABLE IF EXISTS `twitter`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `twitter` ( - `user_id` int(10) unsigned NOT NULL, - `access_token` char(64) NOT NULL, - `access_token_secret` char(64) NOT NULL, - `uname` char(64) NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `crosspost` tinyint(1) unsigned NOT NULL DEFAULT '1', - PRIMARY KEY (`user_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `useroptions` --- - -DROP TABLE IF EXISTS `useroptions`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `useroptions` ( - `user_id` int(10) unsigned NOT NULL, - `jnotify` tinyint(1) NOT NULL DEFAULT '1', - `subscr_active` tinyint(1) NOT NULL DEFAULT '1', - `off_ts` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `xmppxhtml` tinyint(1) NOT NULL DEFAULT '0', - `subscr_notify` tinyint(1) NOT NULL DEFAULT '1', - `recommendations` tinyint(1) NOT NULL DEFAULT '1', - `privacy_view` tinyint(1) NOT NULL DEFAULT '1', - `privacy_reply` tinyint(1) NOT NULL DEFAULT '1', - `privacy_pm` tinyint(1) NOT NULL DEFAULT '1', - `repliesview` tinyint(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`user_id`), - KEY `recommendations` (`recommendations`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `users` --- - -DROP TABLE IF EXISTS `users`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `users` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `nick` char(64) NOT NULL, - `passw` char(32) NOT NULL, - `lang` enum('en','ru','fr','fa','__') NOT NULL DEFAULT '__', - `banned` tinyint(3) unsigned NOT NULL DEFAULT '0', - `lastmessage` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `lastpm` int(11) NOT NULL DEFAULT '0', - `lastphoto` int(11) NOT NULL DEFAULT '0', - `karma` smallint(6) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `nick` (`nick`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `users_refs` --- - -DROP TABLE IF EXISTS `users_refs`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `users_refs` ( - `user_id` int(10) unsigned NOT NULL, - `ref` int(10) unsigned NOT NULL, - KEY `ref` (`ref`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `users_subscr` --- - -DROP TABLE IF EXISTS `users_subscr`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `users_subscr` ( - `user_id` int(10) unsigned NOT NULL, - `cnt` smallint(5) unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`user_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `usersinfo` --- - -DROP TABLE IF EXISTS `usersinfo`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `usersinfo` ( - `user_id` int(10) unsigned NOT NULL, - `jid` char(32) DEFAULT NULL, - `fullname` char(32) DEFAULT NULL, - `country` char(32) DEFAULT NULL, - `url` char(64) DEFAULT NULL, - `gender` char(32) DEFAULT NULL, - `bday` char(10) DEFAULT NULL, - `descr` varchar(255) DEFAULT NULL, - PRIMARY KEY (`user_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `version` --- - -DROP TABLE IF EXISTS `version`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `version` ( - `version` bigint(20) NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `vk` --- - -DROP TABLE IF EXISTS `vk`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `vk` ( - `user_id` int(10) unsigned DEFAULT NULL, - `vk_id` bigint(20) unsigned DEFAULT NULL, - `loginhash` char(36) DEFAULT NULL, - `access_token` char(128) DEFAULT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `vk_name` char(64) DEFAULT NULL, - `vk_link` char(64) NOT NULL, - `crosspost` tinyint(3) unsigned NOT NULL DEFAULT '1', - KEY `user_id` (`user_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `winphone` --- - -DROP TABLE IF EXISTS `winphone`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `winphone` ( - `user_id` int(10) unsigned NOT NULL, - `url` char(255) NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY `url` (`url`), - KEY `user_id` (`user_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `wl_users` --- - -DROP TABLE IF EXISTS `wl_users`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `wl_users` ( - `user_id` int(10) unsigned NOT NULL, - `wl_user_id` int(10) unsigned NOT NULL, - PRIMARY KEY (`user_id`,`wl_user_id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2018-06-22 7:38:17 diff --git a/juick-server/src/main/resources/messages.properties b/juick-server/src/main/resources/messages.properties deleted file mode 100644 index cfd8a826..00000000 --- a/juick-server/src/main/resources/messages.properties +++ /dev/null @@ -1,80 +0,0 @@ -date.format=MM/dd/yyyy - -link.settings=Settings -link.returnToMain=Back to Home Page -link.contacts=Contacts -link.tos=TOS -link.adv=Advertisement - -link.popular=Popular -link.allMessages=Discover -link.withPhotos=Photos -link.trends=Trends -link.my=My feed -link.privateMessages=PM -link.discuss=Discuss -link.recommended=Recommended -link.postMessage=Post -link.Login=Login -link.logout=Logout - -link.settings.main=Main -link.settings.password=Password -link.settings.about=About - -label.sponsor=Sponsor -label.sponsors=Sponsors -label.search=Search -label.register=Register -label.username=User name -label.password=Password - -postForm.newMessage=New message... -postForm.imageLink=Link to image -postForm.imageFormats=JPG/PNG, up to 10 MB -postForm.or=or -postForm.upload=Upload -postForm.tags=Tags (space separated) -postForm.submit=Send - -message.recommend=Recommend -message.recommendedBy=♡ recommended by -message.recommendedOthers=and {0} others -message.comment=Comment -message.writeComment=Write a comment... -message.share=Share -message.subscribe=Subscribe -message.subscribed=Subscribed -message.delete=Delete -message.loginForSending=<a href="{0}" class="a-login">Login</a> to post messages and comments -message.sendLoginToXmpp=Send <b>LOGIN</b> to <a href="xmpp:juick@juick.com?message;body=LOGIN">juick@juick.com</a> - -messages.next=Next - -reply.reply=Reply -reply.inReplyTo=in reply to -reply.replies=Replies - -replies.showAsList=Show as list -replies.showAsTree=Show as tree -replies.unfoldAll=Unfold all - -question.areRegistered=Already registered? - -title.help=Help -title.loginOrSignup=Juick - Log In or Sign Up -title.index.anonym=Juick microblogs: popular posts -title.index.user=Popular - -error.pageNotFound=Page not found -error.pageNotFound.description=User probably deleted this post, or this page never existed. - -blog.blog=Blog -blog.recommendations=Recommendations -blog.photos=Photos -blog.iread=I read -blog.readers=My readers -blog.bl=My blacklist -blog.messages=Messages -blog.comments=Comments -blog.allPostsWithTag=All posts tagged
\ No newline at end of file diff --git a/juick-server/src/main/resources/messages_ru.properties b/juick-server/src/main/resources/messages_ru.properties deleted file mode 100644 index 2a2269ae..00000000 --- a/juick-server/src/main/resources/messages_ru.properties +++ /dev/null @@ -1,78 +0,0 @@ -date.format=dd.MM.yyyy - -link.settings=Настройки -link.returnToMain=Вернуться на главную -link.contacts=Контакты -link.tos=TOS - -link.popular=Популярные -link.allMessages=Обзор -link.withPhotos=Фото -link.trends=Темы -link.my=Моя лента -link.privateMessages=Приватные -link.discuss=Диалоги -link.recommended=Рекомендации -link.postMessage=Написать -link.Login=Войти -link.logout=Выйти - -link.settings.main=Главная -link.settings.password=Пароль -link.settings.about=О пользователе - -label.sponsor=Спонсор -label.sponsors=Спонсоры -label.search=Поиск -label.register=Зарегистрироваться -label.username=Имя пользователя -label.password=Пароль - -postForm.newMessage=Новое сообщение... -postForm.imageLink=Ссылка на изображение -postForm.imageFormats=JPG/PNG, до 10Мб -postForm.or=или -postForm.upload=загрузить -postForm.tags=Теги (через пробел) -postForm.submit=Отправить - -message.recommend=Рекомендовать -message.recommendedBy=♡ рекомендовали -message.recommendedOthers=и еще {0} -message.comment=Комментировать -message.writeComment=Написать комментарий... -message.share=Поделиться -message.subscribe=Подписаться -message.subscribed=Подписан -message.delete=Удалить -message.loginForSending=Чтобы добавлять сообщения и комментарии, <a href="{0}" class="a-login">представьтесь</a> -message.sendLoginToXmpp=Отправьте <b>LOGIN</b> на <a href="xmpp:juick@juick.com?message;body=LOGIN">juick@juick.com</a> - -messages.next=Читать дальше - -reply.reply=Ответить -reply.inReplyTo=в ответ на -reply.replies=Ответы -replies.showAsList=Показать списком -replies.showAsTree=Показать деревом -replies.unfoldAll=Раскрыть все - -question.areRegistered=Уже зарегистрированы? - -title.help=Справка -title.loginOrSignup=Juick - Войдите в систему или зарегистрируйтесь -title.index.anonym=Микроблоги Juick: популярные записи -title.index.user=Популярные - -error.pageNotFound=Страница не найдена -error.pageNotFound.description=Сожалеем, но страницу с этим адресом удалил её автор, либо её никогда не существовало. - -blog.blog=Блог -blog.recommendations=Рекомендации -blog.photos=Фотографии -blog.iread=Я читаю -blog.readers=Мои подписчики -blog.bl=Черный список -blog.messages=Сообщения -blog.comments=Комментарии -blog.allPostsWithTag=Все записи с тегом
\ No newline at end of file diff --git a/juick-server/src/main/resources/pg_schema_wip b/juick-server/src/main/resources/pg_schema_wip deleted file mode 100644 index 61178495..00000000 --- a/juick-server/src/main/resources/pg_schema_wip +++ /dev/null @@ -1,1539 +0,0 @@ --- --- PostgreSQL database dump --- - -SET statement_timeout = 0; -SET lock_timeout = 0; -SET client_encoding = 'UTF8'; -SET standard_conforming_strings = off; -SET check_function_bodies = false; -SET client_min_messages = warning; -SET escape_string_warning = off; - --- --- Name: juick; Type: SCHEMA; Schema: -; Owner: juick --- - -CREATE SCHEMA juick; - - -ALTER SCHEMA juick OWNER TO juick; - --- --- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: --- - -CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; - - --- --- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: --- - -COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; - - -SET search_path = public, pg_catalog; - --- --- Name: auth_protocol; Type: TYPE; Schema: public; Owner: juick --- - -CREATE TYPE auth_protocol AS ENUM ( - 'xmpp', - 'email', - 'sms' -); - - -ALTER TYPE auth_protocol OWNER TO juick; - --- --- Name: messages_attach; Type: TYPE; Schema: public; Owner: juick --- - -CREATE TYPE messages_attach AS ENUM ( - 'jpg', - 'mp4', - 'png' -); - - -ALTER TYPE messages_attach OWNER TO juick; - --- --- Name: messages_lang; Type: TYPE; Schema: public; Owner: juick --- - -CREATE TYPE messages_lang AS ENUM ( - 'en', - 'ru', - 'fr', - 'fa', - '__' -); - - -ALTER TYPE messages_lang OWNER TO juick; - --- --- Name: replies_attach; Type: TYPE; Schema: public; Owner: juick --- - -CREATE TYPE replies_attach AS ENUM ( - 'jpg', - 'mp4', - 'png' -); - - -ALTER TYPE replies_attach OWNER TO juick; - --- --- Name: users_lang; Type: TYPE; Schema: public; Owner: juick --- - -CREATE TYPE users_lang AS ENUM ( - 'en', - 'ru', - 'fr', - 'fa', - '__' -); - - -ALTER TYPE users_lang OWNER TO juick; - -SET default_tablespace = ''; - -SET default_with_oids = false; - --- --- Name: ads_messages; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE ads_messages ( - message_id bigint NOT NULL -); - - -ALTER TABLE ads_messages OWNER TO juick; - --- --- Name: ads_messages_log; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE ads_messages_log ( - user_id bigint NOT NULL, - message_id bigint NOT NULL, - ts bigint DEFAULT 0::bigint NOT NULL -); - - -ALTER TABLE ads_messages_log OWNER TO juick; - --- --- Name: android; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE android ( - user_id bigint NOT NULL, - regid character varying(255) NOT NULL, - ts timestamp with time zone DEFAULT now() NOT NULL -); - - -ALTER TABLE android OWNER TO juick; - --- --- Name: auth; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE auth ( - user_id bigint NOT NULL, - protocol auth_protocol NOT NULL, - account character varying(64) NOT NULL, - authcode character varying(8) NOT NULL -); - - -ALTER TABLE auth OWNER TO juick; - --- --- Name: bl_tags; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE bl_tags ( - user_id bigint NOT NULL, - tag_id bigint NOT NULL -); - - -ALTER TABLE bl_tags OWNER TO juick; - --- --- Name: bl_users; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE bl_users ( - user_id bigint NOT NULL, - bl_user_id bigint NOT NULL, - ts timestamp with time zone DEFAULT now() NOT NULL -); - - -ALTER TABLE bl_users OWNER TO juick; - --- --- Name: captcha; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE captcha ( - jid character varying(64) NOT NULL, - hash character varying(16) NOT NULL, - confirmed smallint NOT NULL -); - - -ALTER TABLE captcha OWNER TO juick; - --- --- Name: captchaimg; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE captchaimg ( - id character varying(16) NOT NULL, - txt character varying(6) NOT NULL, - ts timestamp with time zone DEFAULT now() NOT NULL, - ip character varying(16) NOT NULL -); - - -ALTER TABLE captchaimg OWNER TO juick; - --- --- Name: emails; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE emails ( - user_id bigint NOT NULL, - email character varying(64) NOT NULL, - subscr_hour smallint -); - - -ALTER TABLE emails OWNER TO juick; - --- --- Name: facebook; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE facebook ( - user_id bigint, - fb_id numeric NOT NULL, - loginhash character varying(36), - access_token character varying(255), - ts timestamp with time zone DEFAULT now() NOT NULL, - fb_name character varying(64) NOT NULL, - fb_link character varying(255) NOT NULL, - crosspost boolean DEFAULT true NOT NULL -); - - -ALTER TABLE facebook OWNER TO juick; - --- --- Name: favorites; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE favorites ( - user_id bigint NOT NULL, - message_id bigint NOT NULL, - ts timestamp with time zone -); - - -ALTER TABLE favorites OWNER TO juick; - --- --- Name: friends_facebook; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE friends_facebook ( - user_id bigint NOT NULL, - friend_id numeric NOT NULL -); - - -ALTER TABLE friends_facebook OWNER TO juick; - --- --- Name: images; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE images ( - mid bigint NOT NULL, - rid bigint NOT NULL, - thumb bigint NOT NULL, - small bigint NOT NULL, - medium bigint NOT NULL, - height bigint NOT NULL, - width bigint NOT NULL -); - - -ALTER TABLE images OWNER TO juick; - --- --- Name: ios; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE ios ( - user_id bigint NOT NULL, - token character varying(64) NOT NULL, - ts timestamp with time zone DEFAULT now() NOT NULL -); - - -ALTER TABLE ios OWNER TO juick; - --- --- Name: jids; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE jids ( - user_id bigint, - jid character varying(64) NOT NULL, - active smallint DEFAULT 0 NOT NULL, - loginhash character varying(36), - ts timestamp with time zone DEFAULT now() NOT NULL -); - - -ALTER TABLE jids OWNER TO juick; - --- --- Name: logins; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE logins ( - user_id bigint NOT NULL, - hash character varying(16) NOT NULL -); - - -ALTER TABLE logins OWNER TO juick; - --- --- Name: mail; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE mail ( - user_id bigint NOT NULL, - hash character varying(16) NOT NULL -); - - -ALTER TABLE mail OWNER TO juick; - --- --- Name: meon; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE meon ( - id bigint NOT NULL, - user_id bigint NOT NULL, - link character varying(255) NOT NULL, - name character varying(32) NOT NULL, - ico smallint -); - - -ALTER TABLE meon OWNER TO juick; - --- --- Name: meon_id_seq; Type: SEQUENCE; Schema: public; Owner: juick --- - -CREATE SEQUENCE meon_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - -ALTER TABLE meon_id_seq OWNER TO juick; - --- --- Name: meon_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: juick --- - -ALTER SEQUENCE meon_id_seq OWNED BY meon.id; - - --- --- Name: messages; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE messages ( - message_id bigint NOT NULL, - user_id bigint NOT NULL, - lang messages_lang DEFAULT '__'::messages_lang NOT NULL, - ts timestamp with time zone DEFAULT now() NOT NULL, - replies smallint DEFAULT 0::smallint NOT NULL, - maxreplyid smallint DEFAULT 0::smallint NOT NULL, - privacy smallint DEFAULT 1::smallint NOT NULL, - readonly boolean DEFAULT false NOT NULL, - attach messages_attach, - place_id bigint, - lat numeric(10,7), - lon numeric(10,7), - popular smallint DEFAULT 0::smallint NOT NULL, - hidden smallint DEFAULT 0::smallint NOT NULL, - likes smallint DEFAULT 0::smallint NOT NULL -); - - -ALTER TABLE messages OWNER TO juick; - --- --- Name: messages_access; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE messages_access ( - message_id bigint NOT NULL, - user_id bigint NOT NULL -); - - -ALTER TABLE messages_access OWNER TO juick; - --- --- Name: messages_message_id_seq; Type: SEQUENCE; Schema: public; Owner: juick --- - -CREATE SEQUENCE messages_message_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - -ALTER TABLE messages_message_id_seq OWNER TO juick; - --- --- Name: messages_message_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: juick --- - -ALTER SEQUENCE messages_message_id_seq OWNED BY messages.message_id; - - --- --- Name: messages_tags; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE messages_tags ( - message_id bigint NOT NULL, - tag_id bigint NOT NULL -); - - -ALTER TABLE messages_tags OWNER TO juick; - --- --- Name: messages_txt; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE messages_txt ( - message_id bigint NOT NULL, - tags text, - repliesby text, - txt text NOT NULL -); - - -ALTER TABLE messages_txt OWNER TO juick; - --- --- Name: messages_votes; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE messages_votes ( - message_id bigint NOT NULL, - user_id bigint NOT NULL, - vote smallint DEFAULT 1::smallint NOT NULL -); - - -ALTER TABLE messages_votes OWNER TO juick; - --- --- Name: places; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE places ( - place_id bigint NOT NULL, - lat numeric(10,7) NOT NULL, - lon numeric(10,7) NOT NULL, - name character varying(64) NOT NULL, - descr character varying(255), - url character varying(128), - user_id bigint NOT NULL, - ts timestamp with time zone DEFAULT now() NOT NULL -); - - -ALTER TABLE places OWNER TO juick; - --- --- Name: places_place_id_seq; Type: SEQUENCE; Schema: public; Owner: juick --- - -CREATE SEQUENCE places_place_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - -ALTER TABLE places_place_id_seq OWNER TO juick; - --- --- Name: places_place_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: juick --- - -ALTER SEQUENCE places_place_id_seq OWNED BY places.place_id; - - --- --- Name: places_tags; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE places_tags ( - place_id bigint NOT NULL, - tag_id bigint NOT NULL -); - - -ALTER TABLE places_tags OWNER TO juick; - --- --- Name: pm; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE pm ( - user_id bigint NOT NULL, - user_id_to bigint NOT NULL, - ts timestamp with time zone DEFAULT now() NOT NULL, - txt text NOT NULL -); - - -ALTER TABLE pm OWNER TO juick; - --- --- Name: pm_inroster; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE pm_inroster ( - user_id bigint NOT NULL, - jid character varying(64) NOT NULL -); - - -ALTER TABLE pm_inroster OWNER TO juick; - --- --- Name: pm_streams; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE pm_streams ( - user_id bigint NOT NULL, - user_id_to bigint NOT NULL, - lastmessage timestamp with time zone NOT NULL, - lastview timestamp with time zone, - unread smallint DEFAULT 0::smallint NOT NULL -); - - -ALTER TABLE pm_streams OWNER TO juick; - --- --- Name: presence; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE presence ( - user_id bigint NOT NULL, - jid character varying(64), - ts timestamp with time zone DEFAULT now() NOT NULL -); - - -ALTER TABLE presence OWNER TO juick; - --- --- Name: reader_links; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE reader_links ( - link_id bigint NOT NULL, - rss_id bigint NOT NULL, - url character varying(255) NOT NULL, - title character varying(255) NOT NULL, - ts timestamp with time zone NOT NULL -); - - -ALTER TABLE reader_links OWNER TO juick; - --- --- Name: reader_links_link_id_seq; Type: SEQUENCE; Schema: public; Owner: juick --- - -CREATE SEQUENCE reader_links_link_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - -ALTER TABLE reader_links_link_id_seq OWNER TO juick; - --- --- Name: reader_links_link_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: juick --- - -ALTER SEQUENCE reader_links_link_id_seq OWNED BY reader_links.link_id; - - --- --- Name: reader_rss; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE reader_rss ( - rss_id bigint NOT NULL, - url character varying(255) NOT NULL, - lastcheck timestamp with time zone NOT NULL -); - - -ALTER TABLE reader_rss OWNER TO juick; - --- --- Name: reader_rss_rss_id_seq; Type: SEQUENCE; Schema: public; Owner: juick --- - -CREATE SEQUENCE reader_rss_rss_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - -ALTER TABLE reader_rss_rss_id_seq OWNER TO juick; - --- --- Name: reader_rss_rss_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: juick --- - -ALTER SEQUENCE reader_rss_rss_id_seq OWNED BY reader_rss.rss_id; - - --- --- Name: replies; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE replies ( - message_id bigint NOT NULL, - reply_id smallint NOT NULL, - user_id bigint NOT NULL, - replyto smallint DEFAULT 0::smallint NOT NULL, - ts timestamp with time zone DEFAULT now() NOT NULL, - attach replies_attach, - txt text NOT NULL -); - - -ALTER TABLE replies OWNER TO juick; - --- --- Name: sphinx; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE sphinx ( - counter_id smallint NOT NULL, - max_id bigint NOT NULL -); - - -ALTER TABLE sphinx OWNER TO juick; - --- --- Name: subscr_messages; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE subscr_messages ( - message_id bigint NOT NULL, - suser_id bigint NOT NULL -); - - -ALTER TABLE subscr_messages OWNER TO juick; - --- --- Name: subscr_tags; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE subscr_tags ( - tag_id bigint NOT NULL, - suser_id bigint NOT NULL, - jid character varying(64) NOT NULL, - active boolean NOT NULL -); - - -ALTER TABLE subscr_tags OWNER TO juick; - --- --- Name: subscr_users; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE subscr_users ( - user_id bigint NOT NULL, - suser_id bigint NOT NULL, - jid character varying(64), - active boolean NOT NULL, - ts timestamp with time zone DEFAULT now() NOT NULL -); - - -ALTER TABLE subscr_users OWNER TO juick; - --- --- Name: tags; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE tags ( - tag_id bigint NOT NULL, - synonym_id bigint, - name character varying(70), - top boolean DEFAULT false NOT NULL, - noindex boolean DEFAULT false NOT NULL, - stat_messages bigint DEFAULT 0::bigint NOT NULL, - stat_users smallint DEFAULT 0::smallint NOT NULL -); - - -ALTER TABLE tags OWNER TO juick; - --- --- Name: tags_ignore; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE tags_ignore ( - tag_id bigint NOT NULL -); - - -ALTER TABLE tags_ignore OWNER TO juick; - --- --- Name: tags_synonyms; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE tags_synonyms ( - name character varying(64) NOT NULL, - changeto character varying(64) NOT NULL -); - - -ALTER TABLE tags_synonyms OWNER TO juick; - --- --- Name: tags_tag_id_seq; Type: SEQUENCE; Schema: public; Owner: juick --- - -CREATE SEQUENCE tags_tag_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - -ALTER TABLE tags_tag_id_seq OWNER TO juick; - --- --- Name: tags_tag_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: juick --- - -ALTER SEQUENCE tags_tag_id_seq OWNED BY tags.tag_id; - - --- --- Name: telegram; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE telegram ( - user_id bigint, - tg_id numeric NOT NULL, - tg_name character varying(64) NOT NULL, - ts timestamp with time zone DEFAULT now() NOT NULL, - loginhash character varying(36) -); - - -ALTER TABLE telegram OWNER TO juick; - --- --- Name: telegram_chats; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE telegram_chats ( - chat_id numeric -); - - -ALTER TABLE telegram_chats OWNER TO juick; - --- --- Name: top_ignore_messages; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE top_ignore_messages ( - message_id bigint NOT NULL -); - - -ALTER TABLE top_ignore_messages OWNER TO juick; - --- --- Name: top_ignore_tags; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE top_ignore_tags ( - tag_id bigint NOT NULL -); - - -ALTER TABLE top_ignore_tags OWNER TO juick; - --- --- Name: top_ignore_users; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE top_ignore_users ( - user_id bigint NOT NULL -); - - -ALTER TABLE top_ignore_users OWNER TO juick; - --- --- Name: twitter; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE twitter ( - user_id bigint NOT NULL, - access_token character varying(64) NOT NULL, - access_token_secret character varying(64) NOT NULL, - uname character varying(64) NOT NULL, - ts timestamp with time zone DEFAULT now() NOT NULL, - crosspost boolean DEFAULT true NOT NULL -); - - -ALTER TABLE twitter OWNER TO juick; - --- --- Name: useroptions; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE useroptions ( - user_id bigint NOT NULL, - jnotify boolean DEFAULT true NOT NULL, - subscr_active boolean DEFAULT true NOT NULL, - off_ts timestamp with time zone, - xmppxhtml boolean DEFAULT false NOT NULL, - subscr_notify boolean DEFAULT true NOT NULL, - recommendations boolean DEFAULT true NOT NULL, - privacy_view boolean DEFAULT true NOT NULL, - privacy_reply boolean DEFAULT true NOT NULL, - privacy_pm boolean DEFAULT true NOT NULL, - repliesview boolean DEFAULT false NOT NULL -); - - -ALTER TABLE useroptions OWNER TO juick; - --- --- Name: users; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE users ( - id bigint NOT NULL, - nick character varying(64) NOT NULL, - passw character varying(32) NOT NULL, - lang users_lang DEFAULT '__'::users_lang NOT NULL, - banned smallint DEFAULT 0::smallint NOT NULL, - lastmessage bigint DEFAULT 0::bigint NOT NULL, - lastpm bigint DEFAULT 0::bigint NOT NULL, - lastphoto bigint DEFAULT 0::bigint NOT NULL, - karma smallint DEFAULT 0::smallint NOT NULL -); - - -ALTER TABLE users OWNER TO juick; - --- --- Name: users_id_seq; Type: SEQUENCE; Schema: public; Owner: juick --- - -CREATE SEQUENCE users_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - -ALTER TABLE users_id_seq OWNER TO juick; - --- --- Name: users_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: juick --- - -ALTER SEQUENCE users_id_seq OWNED BY users.id; - - --- --- Name: users_refs; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE users_refs ( - user_id bigint NOT NULL, - ref bigint NOT NULL -); - - -ALTER TABLE users_refs OWNER TO juick; - --- --- Name: users_subscr; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE users_subscr ( - user_id bigint NOT NULL, - cnt smallint DEFAULT 0::smallint NOT NULL -); - - -ALTER TABLE users_subscr OWNER TO juick; - --- --- Name: usersinfo; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE usersinfo ( - user_id bigint NOT NULL, - jid character varying(32), - fullname character varying(32), - country character varying(32), - url character varying(64), - gender character varying(32), - bday character varying(10), - descr text -); - - -ALTER TABLE usersinfo OWNER TO juick; - --- --- Name: version; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE version ( - version numeric NOT NULL -); - - -ALTER TABLE version OWNER TO juick; - --- --- Name: vk; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE vk ( - user_id bigint, - vk_id numeric NOT NULL, - loginhash character varying(36), - access_token character varying(128) NOT NULL, - ts timestamp with time zone DEFAULT now() NOT NULL, - vk_name character varying(64) NOT NULL, - vk_link character varying(64) NOT NULL, - crosspost smallint DEFAULT 1::smallint NOT NULL -); - - -ALTER TABLE vk OWNER TO juick; - --- --- Name: winphone; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE winphone ( - user_id bigint NOT NULL, - url character varying(255) NOT NULL, - ts timestamp with time zone DEFAULT now() NOT NULL -); - - -ALTER TABLE winphone OWNER TO juick; - --- --- Name: wl_users; Type: TABLE; Schema: public; Owner: juick; Tablespace: --- - -CREATE TABLE wl_users ( - user_id bigint NOT NULL, - wl_user_id bigint NOT NULL -); - - -ALTER TABLE wl_users OWNER TO juick; - --- --- Name: id; Type: DEFAULT; Schema: public; Owner: juick --- - -ALTER TABLE ONLY meon ALTER COLUMN id SET DEFAULT nextval('meon_id_seq'::regclass); - - --- --- Name: message_id; Type: DEFAULT; Schema: public; Owner: juick --- - -ALTER TABLE ONLY messages ALTER COLUMN message_id SET DEFAULT nextval('messages_message_id_seq'::regclass); - - --- --- Name: place_id; Type: DEFAULT; Schema: public; Owner: juick --- - -ALTER TABLE ONLY places ALTER COLUMN place_id SET DEFAULT nextval('places_place_id_seq'::regclass); - - --- --- Name: link_id; Type: DEFAULT; Schema: public; Owner: juick --- - -ALTER TABLE ONLY reader_links ALTER COLUMN link_id SET DEFAULT nextval('reader_links_link_id_seq'::regclass); - - --- --- Name: rss_id; Type: DEFAULT; Schema: public; Owner: juick --- - -ALTER TABLE ONLY reader_rss ALTER COLUMN rss_id SET DEFAULT nextval('reader_rss_rss_id_seq'::regclass); - - --- --- Name: tag_id; Type: DEFAULT; Schema: public; Owner: juick --- - -ALTER TABLE ONLY tags ALTER COLUMN tag_id SET DEFAULT nextval('tags_tag_id_seq'::regclass); - - --- --- Name: id; Type: DEFAULT; Schema: public; Owner: juick --- - -ALTER TABLE ONLY users ALTER COLUMN id SET DEFAULT nextval('users_id_seq'::regclass); - - --- --- Name: idx_20438_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY images - ADD CONSTRAINT idx_20438_primary PRIMARY KEY (mid, rid); - - --- --- Name: idx_20453_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY mail - ADD CONSTRAINT idx_20453_primary PRIMARY KEY (user_id); - - --- --- Name: idx_20458_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY meon - ADD CONSTRAINT idx_20458_primary PRIMARY KEY (id); - - --- --- Name: idx_20483_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY messages - ADD CONSTRAINT idx_20483_primary PRIMARY KEY (message_id); - - --- --- Name: idx_20502_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY messages_txt - ADD CONSTRAINT idx_20502_primary PRIMARY KEY (message_id); - - --- --- Name: idx_20514_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY places - ADD CONSTRAINT idx_20514_primary PRIMARY KEY (place_id); - - --- --- Name: idx_20542_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY reader_links - ADD CONSTRAINT idx_20542_primary PRIMARY KEY (link_id); - - --- --- Name: idx_20551_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY reader_rss - ADD CONSTRAINT idx_20551_primary PRIMARY KEY (rss_id); - - --- --- Name: idx_20571_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY sphinx - ADD CONSTRAINT idx_20571_primary PRIMARY KEY (counter_id); - - --- --- Name: idx_20586_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY tags - ADD CONSTRAINT idx_20586_primary PRIMARY KEY (tag_id); - - --- --- Name: idx_20616_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY top_ignore_tags - ADD CONSTRAINT idx_20616_primary PRIMARY KEY (tag_id); - - --- --- Name: idx_20619_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY top_ignore_users - ADD CONSTRAINT idx_20619_primary PRIMARY KEY (user_id); - - --- --- Name: idx_20622_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY twitter - ADD CONSTRAINT idx_20622_primary PRIMARY KEY (user_id); - - --- --- Name: idx_20627_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY useroptions - ADD CONSTRAINT idx_20627_primary PRIMARY KEY (user_id); - - --- --- Name: idx_20653_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY users - ADD CONSTRAINT idx_20653_primary PRIMARY KEY (id); - - --- --- Name: idx_20663_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY usersinfo - ADD CONSTRAINT idx_20663_primary PRIMARY KEY (user_id); - - --- --- Name: idx_20672_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY users_subscr - ADD CONSTRAINT idx_20672_primary PRIMARY KEY (user_id); - - --- --- Name: idx_20694_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY wl_users - ADD CONSTRAINT idx_20694_primary PRIMARY KEY (user_id, wl_user_id); - - --- --- Name: idx_29418_primary; Type: CONSTRAINT; Schema: public; Owner: juick; Tablespace: --- - -ALTER TABLE ONLY bl_users - ADD CONSTRAINT idx_29418_primary PRIMARY KEY (user_id, bl_user_id); - - --- --- Name: idx_20390_regid; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_20390_regid ON android USING btree (regid); - - --- --- Name: idx_20390_user_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20390_user_id ON android USING btree (user_id); - - --- --- Name: idx_20404_tag_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20404_tag_id ON bl_tags USING btree (tag_id); - - --- --- Name: idx_20404_user_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20404_user_id ON bl_tags USING btree (user_id); - - --- --- Name: idx_20418_email; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20418_email ON emails USING btree (email); - - --- --- Name: idx_20421_user_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20421_user_id ON facebook USING btree (user_id); - - --- --- Name: idx_20432_user_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_20432_user_id ON friends_facebook USING btree (user_id, friend_id); - - --- --- Name: idx_20441_token; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_20441_token ON ios USING btree (token); - - --- --- Name: idx_20441_user_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20441_user_id ON ios USING btree (user_id); - - --- --- Name: idx_20445_jid; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_20445_jid ON jids USING btree (jid); - - --- --- Name: idx_20445_user_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20445_user_id ON jids USING btree (user_id); - - --- --- Name: idx_20450_user_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_20450_user_id ON logins USING btree (user_id); - - --- --- Name: idx_20483_attach; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20483_attach ON messages USING btree (attach); - - --- --- Name: idx_20483_hidden; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20483_hidden ON messages USING btree (hidden); - - --- --- Name: idx_20483_place_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20483_place_id ON messages USING btree (place_id); - - --- --- Name: idx_20483_popular; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20483_popular ON messages USING btree (popular); - - --- --- Name: idx_20483_ts; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20483_ts ON messages USING btree (ts); - - --- --- Name: idx_20483_user_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20483_user_id ON messages USING btree (user_id); - - --- --- Name: idx_20496_message_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20496_message_id ON messages_access USING btree (message_id); - - --- --- Name: idx_20499_message_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20499_message_id ON messages_tags USING btree (message_id); - - --- --- Name: idx_20499_message_id_2; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_20499_message_id_2 ON messages_tags USING btree (message_id, tag_id); - - --- --- Name: idx_20499_tag_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20499_tag_id ON messages_tags USING btree (tag_id); - - --- --- Name: idx_20508_message_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_20508_message_id ON messages_votes USING btree (message_id, user_id); - - --- --- Name: idx_20529_user_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20529_user_id ON pm_inroster USING btree (user_id); - - --- --- Name: idx_20529_user_id_2; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_20529_user_id_2 ON pm_inroster USING btree (user_id, jid); - - --- --- Name: idx_20532_user_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_20532_user_id ON pm_streams USING btree (user_id, user_id_to); - - --- --- Name: idx_20536_jid; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_20536_jid ON presence USING btree (jid); - - --- --- Name: idx_20563_message_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20563_message_id ON replies USING btree (message_id); - - --- --- Name: idx_20563_ts; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20563_ts ON replies USING btree (ts); - - --- --- Name: idx_20563_user_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20563_user_id ON replies USING btree (user_id); - - --- --- Name: idx_20574_message_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_20574_message_id ON subscr_messages USING btree (message_id, suser_id); - - --- --- Name: idx_20577_tag_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_20577_tag_id ON subscr_tags USING btree (tag_id, suser_id); - - --- --- Name: idx_20580_suser_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20580_suser_id ON subscr_users USING btree (suser_id); - - --- --- Name: idx_20580_user_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_20580_user_id ON subscr_users USING btree (user_id, suser_id); - - --- --- Name: idx_20586_synonym_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20586_synonym_id ON tags USING btree (synonym_id); - - --- --- Name: idx_20607_chat_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_20607_chat_id ON telegram_chats USING btree (chat_id); - - --- --- Name: idx_20627_recommendations; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20627_recommendations ON useroptions USING btree (recommendations); - - --- --- Name: idx_20653_nick; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_20653_nick ON users USING btree (nick); - - --- --- Name: idx_20669_ref; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20669_ref ON users_refs USING btree (ref); - - --- --- Name: idx_20682_user_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20682_user_id ON vk USING btree (user_id); - - --- --- Name: idx_20690_url; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_20690_url ON winphone USING btree (url); - - --- --- Name: idx_20690_user_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_20690_user_id ON winphone USING btree (user_id); - - --- --- Name: idx_29422_message_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_29422_message_id ON favorites USING btree (message_id); - - --- --- Name: idx_29422_user_id; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE INDEX idx_29422_user_id ON favorites USING btree (user_id); - - --- --- Name: idx_29422_user_id_2; Type: INDEX; Schema: public; Owner: juick; Tablespace: --- - -CREATE UNIQUE INDEX idx_29422_user_id_2 ON favorites USING btree (user_id, message_id); - - --- --- Name: public; Type: ACL; Schema: -; Owner: postgres --- - -REVOKE ALL ON SCHEMA public FROM PUBLIC; -REVOKE ALL ON SCHEMA public FROM postgres; -GRANT ALL ON SCHEMA public TO postgres; -GRANT ALL ON SCHEMA public TO PUBLIC; - - --- --- PostgreSQL database dump complete --- - diff --git a/juick-server/src/main/resources/rome.properties b/juick-server/src/main/resources/rome.properties deleted file mode 100644 index fdb9aaa2..00000000 --- a/juick-server/src/main/resources/rome.properties +++ /dev/null @@ -1,2 +0,0 @@ -rss_2.0.item.ModuleParser.classes=com.juick.server.api.rss.extension.JuickModuleParser -rss_2.0.item.ModuleGenerator.classes=com.juick.server.api.rss.extension.JuickModuleGenerator
\ No newline at end of file diff --git a/juick-server/src/main/resources/schema.sql b/juick-server/src/main/resources/schema.sql deleted file mode 100644 index 2e8fad9b..00000000 --- a/juick-server/src/main/resources/schema.sql +++ /dev/null @@ -1,396 +0,0 @@ -SET DB_CLOSE_ON_EXIT TRUE; - -CREATE TABLE IF NOT EXISTS `android` ( - `user_id` int(10) unsigned NOT NULL, - `regid` char(255) NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, -); -CREATE TABLE IF NOT EXISTS `auth` ( - `user_id` int(10) unsigned NOT NULL, - `protocol` enum('xmpp','email','sms') NOT NULL, - `account` char(64) NOT NULL, - `authcode` char(8) NOT NULL -); -CREATE TABLE IF NOT EXISTS `bl_tags` ( - `user_id` int(10) unsigned NOT NULL, - `tag_id` int(10) unsigned NOT NULL, -); -CREATE TABLE IF NOT EXISTS `bl_users` ( - `user_id` int(10) unsigned NOT NULL, - `bl_user_id` int(10) unsigned NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`user_id`,`bl_user_id`) -); -CREATE TABLE IF NOT EXISTS `facebook` ( - `user_id` int(10) unsigned DEFAULT NULL, - `fb_id` bigint(20) unsigned NULL, - `loginhash` char(36) DEFAULT NULL, - `access_token` char(255) DEFAULT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `fb_name` char(64) NULL, - `fb_link` char(255) NULL, - `crosspost` tinyint(1) unsigned NOT NULL DEFAULT '1' -); - -CREATE TABLE IF NOT EXISTS `reactions` ( - `like_id` int(10) unsigned NOT NULL, - `description` varchar (100) NOT NULL -); -CREATE TABLE IF NOT EXISTS `favorites` ( - `user_id` int(10) unsigned NOT NULL, - `message_id` int(10) unsigned NOT NULL, - `ts` datetime NOT NULL, - `like_id` int(10), - foreign key (like_id) references reactions(like_id) -); - - - -CREATE TABLE IF NOT EXISTS `friends_facebook` ( - `user_id` int(10) unsigned NOT NULL, - `friend_id` bigint(20) unsigned NOT NULL, - UNIQUE KEY `user_id` (`user_id`,`friend_id`) -); -CREATE TABLE IF NOT EXISTS `images` ( - `mid` int(10) unsigned NOT NULL, - `rid` int(10) unsigned NOT NULL, - `thumb` int(10) unsigned NOT NULL, - `small` int(10) unsigned NOT NULL, - `medium` int(10) unsigned NOT NULL, - `height` int(10) unsigned NOT NULL, - `width` int(10) unsigned NOT NULL, - PRIMARY KEY (`mid`,`rid`) -); - -CREATE TABLE IF NOT EXISTS `mail` ( - `user_id` int(10) unsigned NOT NULL, - `hash` char(16) NOT NULL, - PRIMARY KEY (`user_id`) -); - -CREATE TABLE IF NOT EXISTS `meon` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `user_id` int(10) unsigned NOT NULL, - `link` char(255) NOT NULL, - `name` char(32) NOT NULL, - `ico` smallint(5) unsigned DEFAULT NULL, - PRIMARY KEY (`id`) -); - - - -CREATE TABLE IF NOT EXISTS `messages_access` ( - `message_id` int(10) unsigned NOT NULL, - `user_id` int(10) unsigned NOT NULL -); - -CREATE TABLE IF NOT EXISTS `messages_tags` ( - `message_id` int(10) unsigned NOT NULL, - `tag_id` int(10) unsigned NOT NULL, - UNIQUE KEY `message_id_2` (`message_id`,`tag_id`) -); - -CREATE TABLE IF NOT EXISTS `messages_txt` ( - `message_id` int(10) unsigned NOT NULL, - `tags` varchar(255) DEFAULT NULL, - `repliesby` varchar(96) DEFAULT NULL, - `txt` mediumtext NOT NULL, - PRIMARY KEY (`message_id`) -); - -CREATE TABLE IF NOT EXISTS `messages_votes` ( - `message_id` int(10) unsigned NOT NULL, - `user_id` int(10) unsigned NOT NULL, - `vote` tinyint(4) NOT NULL DEFAULT '1', - UNIQUE KEY `message_id` (`message_id`,`user_id`) -); - -CREATE TABLE IF NOT EXISTS `messenger` ( - `user_id` int(10) unsigned DEFAULT NULL, - `sender_id` bigint(20) NOT NULL, - `display_name` char(64) NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `loginhash` char(36) DEFAULT NULL -); - -CREATE TABLE IF NOT EXISTS `places` ( - `place_id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `lat` decimal(10,7) NOT NULL, - `lon` decimal(10,7) NOT NULL, - `name` char(64) NOT NULL, - `descr` char(255) DEFAULT NULL, - `url` char(128) DEFAULT NULL, - `user_id` int(10) unsigned NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`place_id`) -); - -CREATE TABLE IF NOT EXISTS `places_tags` ( - `place_id` int(10) unsigned NOT NULL, - `tag_id` int(10) unsigned NOT NULL -); - -CREATE TABLE IF NOT EXISTS `pm` ( - `user_id` int(10) unsigned NOT NULL, - `user_id_to` int(10) unsigned NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `txt` text NOT NULL -); - -CREATE TABLE IF NOT EXISTS `pm_streams` ( - `user_id` int(10) unsigned NOT NULL, - `user_id_to` int(10) unsigned NOT NULL, - `lastmessage` datetime NOT NULL, - `lastview` datetime DEFAULT NULL, - `unread` smallint(5) unsigned NOT NULL DEFAULT '0', - UNIQUE KEY (`user_id`,`user_id_to`) -); - -CREATE TABLE IF NOT EXISTS `presence` ( - `user_id` int(10) unsigned NOT NULL, - `jid` char(64) DEFAULT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY (`jid`) -); - -CREATE TABLE IF NOT EXISTS `replies` ( - `message_id` int(10) unsigned NOT NULL, - `reply_id` smallint(5) unsigned NOT NULL, - `user_id` int(10) unsigned NOT NULL, - `replyto` smallint(5) unsigned NOT NULL DEFAULT '0', - `ts` timestamp(9) NOT NULL DEFAULT CURRENT_TIMESTAMP, - `attach` nchar(3) check (attach in ('jpg', 'mp4', 'png')), - `txt` mediumtext NOT NULL -); - -CREATE TABLE IF NOT EXISTS `subscr_messages` ( - `message_id` int(10) unsigned NOT NULL, - `suser_id` int(10) unsigned NOT NULL, - `last_read_rid` smallint(5) unsigned NOT NULL DEFAULT '0', - UNIQUE KEY (`message_id`,`suser_id`) -); - -CREATE TABLE IF NOT EXISTS `subscr_tags` ( - `tag_id` int(10) unsigned NOT NULL, - `suser_id` int(10) unsigned NOT NULL, - UNIQUE KEY (`tag_id`,`suser_id`) -); - -CREATE TABLE IF NOT EXISTS `subscr_users` ( - `user_id` int(10) unsigned NOT NULL, - `suser_id` int(10) unsigned NOT NULL, - `jid` char(64) DEFAULT NULL, - `active` bit(1) NOT NULL DEFAULT TRUE, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY (`user_id`,`suser_id`) -); - -CREATE TABLE IF NOT EXISTS `tags` ( - `tag_id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `synonym_id` int(10) unsigned DEFAULT NULL, - `name` varchar_ignorecase(70) DEFAULT NULL, - `top` tinyint(1) unsigned NOT NULL DEFAULT '0', - `noindex` tinyint(1) unsigned NOT NULL DEFAULT '0', - `stat_messages` int(10) unsigned NOT NULL DEFAULT '0', - `stat_users` smallint(5) unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`tag_id`) -); - -CREATE TABLE IF NOT EXISTS `tags_ignore` ( - `tag_id` int(10) unsigned NOT NULL -); - -CREATE TABLE IF NOT EXISTS `tags_synonyms` ( - `name` char(64) NOT NULL, - `changeto` char(64) NOT NULL -); - -CREATE TABLE IF NOT EXISTS `telegram` ( - `user_id` int(10) unsigned DEFAULT NULL, - `tg_id` bigint(20) NOT NULL, - `tg_name` char(64) DEFAULT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `loginhash` char(36) DEFAULT NULL -); - -CREATE TABLE IF NOT EXISTS `telegram_chats` ( - `chat_id` bigint(20) DEFAULT NULL, - UNIQUE KEY `chat_id` (`chat_id`) -); - -CREATE TABLE IF NOT EXISTS `top_ignore_messages` ( - `message_id` int(10) unsigned NOT NULL -); - -CREATE TABLE IF NOT EXISTS `top_ignore_tags` ( - `tag_id` int(10) unsigned NOT NULL, - PRIMARY KEY (`tag_id`) -); - -CREATE TABLE IF NOT EXISTS `top_ignore_users` ( - `user_id` int(10) unsigned NOT NULL, - PRIMARY KEY (`user_id`) -); - -CREATE TABLE IF NOT EXISTS `twitter` ( - `user_id` int(10) unsigned NOT NULL, - `access_token` char(64) NOT NULL, - `access_token_secret` char(64) NOT NULL, - `uname` char(64) NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `crosspost` tinyint(1) unsigned NOT NULL DEFAULT '1', - PRIMARY KEY (`user_id`) -); - -CREATE TABLE IF NOT EXISTS `useroptions` ( - `user_id` int(10) unsigned NOT NULL, - `jnotify` tinyint(1) NOT NULL DEFAULT '1', - `subscr_active` tinyint(1) NOT NULL DEFAULT '1', - `off_ts` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `xmppxhtml` tinyint(1) NOT NULL DEFAULT '0', - `subscr_notify` tinyint(1) NOT NULL DEFAULT '1', - `recommendations` tinyint(1) NOT NULL DEFAULT '1', - `privacy_view` tinyint(1) NOT NULL DEFAULT '1', - `privacy_reply` tinyint(1) NOT NULL DEFAULT '1', - `privacy_pm` tinyint(1) NOT NULL DEFAULT '1', - `repliesview` tinyint(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`user_id`) -); - -CREATE TABLE IF NOT EXISTS `users` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `nick` char(64) NOT NULL, - `passw` char(32) NOT NULL, - `lang` enum('en','ru','fr','fa','__') NOT NULL DEFAULT '__', - `banned` tinyint(3) unsigned NOT NULL DEFAULT '0', - `lastmessage` timestamp(9) NOT NULL DEFAULT CURRENT_TIMESTAMP, - `lastpm` int(11) NOT NULL DEFAULT '0', - `lastphoto` int(11) NOT NULL DEFAULT '0', - `karma` smallint(6) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `nick` (`nick`) -); - -CREATE TABLE IF NOT EXISTS `users_refs` ( - `user_id` int(10) unsigned NOT NULL, - `ref` int(10) unsigned NOT NULL -); - -CREATE TABLE IF NOT EXISTS `users_subscr` ( - `user_id` int(10) unsigned NOT NULL, - `cnt` smallint(5) unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`user_id`) -); - -CREATE TABLE IF NOT EXISTS `usersinfo` ( - `user_id` int(10) unsigned NOT NULL, - `jid` char(32) DEFAULT NULL, - `fullname` char(32) DEFAULT NULL, - `country` char(32) DEFAULT NULL, - `url` char(64) DEFAULT NULL, - `gender` char(32) DEFAULT NULL, - `bday` char(10) DEFAULT NULL, - `descr` varchar(255) DEFAULT NULL, - PRIMARY KEY (`user_id`) -); -CREATE TABLE IF NOT EXISTS `emails` ( - `user_id` int(10) unsigned NOT NULL, - `email` char(64) NOT NULL PRIMARY KEY, - `subscr_hour` tinyint(4) DEFAULT NULL, - foreign key (user_id) references users(id) -); -CREATE TABLE IF NOT EXISTS `ios` ( - `user_id` int(10) unsigned NOT NULL, - `token` char(64) NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY `token` (`token`), - foreign key (user_id) references users(id) -); - -CREATE TABLE IF NOT EXISTS `jids` ( - `user_id` int(10) unsigned DEFAULT NULL, - `jid` char(64) NOT NULL, - `active` tinyint(1) NOT NULL DEFAULT '1', - `loginhash` char(36) DEFAULT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY `jid` (`jid`), - foreign key (user_id) references users(id) -); - -CREATE TABLE IF NOT EXISTS `logins` ( - `user_id` int(10) unsigned NOT NULL, - `hash` char(16) NOT NULL, - UNIQUE KEY (`user_id`) -); -CREATE TABLE IF NOT EXISTS `messages` ( - `message_id` int(10) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY, - `user_id` int(10) unsigned NOT NULL, - `lang` enum('en','ru','fr','fa','__') NOT NULL DEFAULT '__', - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `replies` smallint(5) unsigned NOT NULL DEFAULT '0', - `maxreplyid` smallint(5) unsigned NOT NULL DEFAULT '0', - `privacy` tinyint(4) NOT NULL DEFAULT '1', - `readonly` tinyint(1) NOT NULL DEFAULT '0', - `attach` nchar(3) check (attach in ('jpg', 'mp4', 'png')), - `place_id` int(10) unsigned DEFAULT NULL, - `lat` decimal(10,7) DEFAULT NULL, - `lon` decimal(10,7) DEFAULT NULL, - `popular` tinyint(4) NOT NULL DEFAULT '0', - `hidden` tinyint(3) unsigned NOT NULL DEFAULT '0', - `likes` smallint(6) NOT NULL DEFAULT '0', - `updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (`user_id`) references users(id) -); -CREATE TABLE IF NOT EXISTS `pm_inroster` ( - `user_id` int(10) unsigned NOT NULL, - `jid` char(64) NOT NULL, - UNIQUE KEY (`user_id`,`jid`), - FOREIGN KEY (`user_id`) references users(id) -); - -CREATE TABLE IF NOT EXISTS `version` ( - `version` bigint(20) NOT NULL -); - -CREATE TABLE IF NOT EXISTS `vk` ( - `user_id` int(10) unsigned DEFAULT NULL, - `vk_id` bigint(20) NOT NULL, - `loginhash` char(36) DEFAULT NULL, - `access_token` char(128) NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `vk_name` char(64) NOT NULL, - `vk_link` char(64) NOT NULL, - `crosspost` bit(1) unsigned NOT NULL DEFAULT FALSE, - FOREIGN KEY (`user_id`) references users(id) -); - -CREATE TABLE IF NOT EXISTS `winphone` ( - `user_id` int(10) unsigned NOT NULL, - `url` char(255) NOT NULL, - `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY (`url`), - FOREIGN KEY (`user_id`) references users(id) -); - -CREATE TABLE IF NOT EXISTS `wl_users` ( - `user_id` int(10) unsigned NOT NULL, - `wl_user_id` int(10) unsigned NOT NULL, - PRIMARY KEY (`user_id`,`wl_user_id`) -); - -CREATE CACHED TABLE PUBLIC."flyway_schema_history" ( - "installed_rank" INT NOT NULL, - "version" VARCHAR(50), - "description" VARCHAR(200) NOT NULL, - "type" VARCHAR(20) NOT NULL, - "script" VARCHAR(1000) NOT NULL, - "checksum" INT, - "installed_by" VARCHAR(100) NOT NULL, - "installed_on" TIMESTAMP DEFAULT CURRENT_TIMESTAMP() NOT NULL, - "execution_time" INT NOT NULL, - "success" BOOLEAN NOT NULL -); -ALTER TABLE PUBLIC."flyway_schema_history" ADD CONSTRAINT PUBLIC."flyway_schema_history_pk" PRIMARY KEY("installed_rank"); --- 1 +/- SELECT COUNT(*) FROM PUBLIC."flyway_schema_history"; -INSERT INTO PUBLIC."flyway_schema_history"("installed_rank", "version", "description", "type", "script", "checksum", "installed_by", "installed_on", "execution_time", "success") VALUES -(1, '1', '<< Flyway Baseline >>', 'BASELINE', '<< Flyway Baseline >>', NULL, 'SA', TIMESTAMP '2018-08-14 13:05:13.724', 0, TRUE);
\ No newline at end of file diff --git a/juick-server/src/main/resources/static/favicon.png b/juick-server/src/main/resources/static/favicon.png Binary files differdeleted file mode 100644 index bc7161e2..00000000 --- a/juick-server/src/main/resources/static/favicon.png +++ /dev/null diff --git a/juick-server/src/main/resources/static/logo.png b/juick-server/src/main/resources/static/logo.png Binary files differdeleted file mode 100644 index 933f6099..00000000 --- a/juick-server/src/main/resources/static/logo.png +++ /dev/null diff --git a/juick-server/src/main/resources/static/tagscloud.png b/juick-server/src/main/resources/static/tagscloud.png Binary files differdeleted file mode 100644 index 3e1bf169..00000000 --- a/juick-server/src/main/resources/static/tagscloud.png +++ /dev/null diff --git a/juick-server/src/main/resources/templates/layouts/content.html b/juick-server/src/main/resources/templates/layouts/content.html deleted file mode 100644 index d2d29c4e..00000000 --- a/juick-server/src/main/resources/templates/layouts/content.html +++ /dev/null @@ -1,38 +0,0 @@ -<!DOCTYPE html> -<html prefix="og: http://ogp.me/ns#"> -<head> - <meta charset="utf-8"/> - <meta http-equiv="X-UA-Compatible" content="IE=edge"/> - <script type="text/javascript" src="{{ beans.webApp.scriptsUrl }}"></script> - <link rel="stylesheet" type="text/css" href="{{ beans.webApp.styleUrl }}"/> - {% block headers %} - {{ headers | default('') | raw }} - {% endblock %} - <title>{{ title | default('Juick') }}</title> - <meta property="og:type" content="{{ ogtype | default('website') }}" /> - <meta property="fb:app_id" content="130568668304" /> - <meta name="viewport" content="width=device-width,initial-scale=1"/> - <meta name="msapplication-config" content="//i.juick.com/browserconfig.xml"/> - <meta name="msapplication-TileColor" content="#ffffff"/> - <meta name="msapplication-TileImage" content="//i.juick.com/ms-icon-144x144.png"/> - <meta name="theme-color" content="#ffffff"/> - <meta name="apple-mobile-web-app-capable" content="yes" /> - <link rel="apple-touch-icon" sizes="57x57" href="//i.juick.com/apple-icon-57x57.png"/> - <link rel="apple-touch-icon" sizes="60x60" href="//i.juick.com/apple-icon-60x60.png"/> - <link rel="apple-touch-icon" sizes="72x72" href="//i.juick.com/apple-icon-72x72.png"/> - <link rel="apple-touch-icon" sizes="76x76" href="//i.juick.com/apple-icon-76x76.png"/> - <link rel="apple-touch-icon" sizes="114x114" href="//i.juick.com/apple-icon-114x114.png"/> - <link rel="apple-touch-icon" sizes="120x120" href="//i.juick.com/apple-icon-120x120.png"/> - <link rel="apple-touch-icon" sizes="144x144" href="//i.juick.com/apple-icon-144x144.png"/> - <link rel="apple-touch-icon" sizes="152x152" href="//i.juick.com/apple-icon-152x152.png"/> - <link rel="apple-touch-icon" sizes="180x180" href="//i.juick.com/apple-icon-180x180.png"/> - <link rel="icon" type="image/png" sizes="32x32" href="//i.juick.com/favicon-32x32.png"/> - <link rel="icon" type="image/png" sizes="96x96" href="//i.juick.com/favicon-96x96.png"/> - <link rel="icon" type="image/png" sizes="16x16" href="//i.juick.com/favicon-16x16.png"/> - <link rel="manifest" href="//i.juick.com/manifest.json"/> -</head> -<body id="body" {% if visitor.uid > 0 %}data-hash="{{visitor.authHash}}"{% endif %}> -{% block body %} -{% endblock %} -</body> -</html> diff --git a/juick-server/src/main/resources/templates/layouts/default.html b/juick-server/src/main/resources/templates/layouts/default.html deleted file mode 100644 index 343885c4..00000000 --- a/juick-server/src/main/resources/templates/layouts/default.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "layouts/content" %} -{% block body %} -{% include "views/partial/navigation" %} -<div id="wrapper"> - <section id="content" - {% if msg | default('') is not empty %}data-mid="{{ msg.mid }}"{% endif %}> - {% block content %} - {% endblock %} - </section> - <aside id="column"> - {% block column %} - {% endblock %} - </aside> -</div> -{% include "views/partial/footer" %} -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/layouts/minimal.html b/juick-server/src/main/resources/templates/layouts/minimal.html deleted file mode 100644 index 15924521..00000000 --- a/juick-server/src/main/resources/templates/layouts/minimal.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "layouts/content" %} -{% block body %} -<div id="wrapper"> - <section id="minimal_content"> - {% block content %} - {% endblock %} - </section> -</div> -{% include "views/partial/footer" %} -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/layouts/note.html b/juick-server/src/main/resources/templates/layouts/note.html deleted file mode 100644 index 42b939c0..00000000 --- a/juick-server/src/main/resources/templates/layouts/note.html +++ /dev/null @@ -1,5 +0,0 @@ -{% import "views/macros/tags" %} -<p>{{ msg | formatMessage }}</p> -{% if msg.tags.size > 0 %} -<div class="msg-tags">{{ allTags(baseUri, msg.tags | tagsList) }}</div> -{% endif %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/404.html b/juick-server/src/main/resources/templates/views/404.html deleted file mode 100644 index 02a790e6..00000000 --- a/juick-server/src/main/resources/templates/views/404.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "layouts/default" %} -{% block content %} - <article> - <h1>Страница не найдена</h1> - <p>Сожалеем, но страницу с этим адресом удалил её автор, либо её никогда не существовало.</p> - </article> -{% endblock %} - -{% block "column" %} -{% include "views/partial/homecolumn" %} -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/blog.html b/juick-server/src/main/resources/templates/views/blog.html deleted file mode 100644 index 91decad6..00000000 --- a/juick-server/src/main/resources/templates/views/blog.html +++ /dev/null @@ -1,24 +0,0 @@ -{% extends "layouts/default" %} -{% import "views/macros/tags" %} -{% block content %} -{% if noindex %} -<!--noindex--> -{% endif %} -{% if paramTag | default('') is not empty %} -<p class="page"><a href="/tag/{{ paramTag.name | urlencode }}">← {{ i18n("messages","blog.allPostsWithTag") }} <b>{{ paramTag.name | escape }}</b></a></p> -{% endif %} -<div> -{% for msg in msgs %} -{% include "views/partial/message" %} -{% endfor %} -</div> -{% if nextpage | default('') is not empty %} -<p class="page"><a href="{{ nextpage | raw }}" rel="prev">{{ i18n("messages","messages.next") }} →</a></p> -{% endif %} -{% endblock %} -{% block "column" %} -{% include "views/partial/usercolumn" %} -{% if noindex %} -<!--/noindex--> -{% endif %} -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/blog_tags.html b/juick-server/src/main/resources/templates/views/blog_tags.html deleted file mode 100644 index 48e517eb..00000000 --- a/juick-server/src/main/resources/templates/views/blog_tags.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "layouts/default" %} -{% import "views/macros/tags" %} -{% block content %} -<p> - {{ tags(user.name, tags) }} -</p> -{% endblock %} -{% block "column" %} -{% include "views/partial/usercolumn" %} -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/help.html b/juick-server/src/main/resources/templates/views/help.html deleted file mode 100644 index 3a022497..00000000 --- a/juick-server/src/main/resources/templates/views/help.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "layouts/default" %} -{% block content %} -<article> - {{ content | raw }} -</article> -{% endblock %} - -{% block "column" %} -{{ navigation | raw }} -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/index.html b/juick-server/src/main/resources/templates/views/index.html deleted file mode 100644 index 97d726de..00000000 --- a/juick-server/src/main/resources/templates/views/index.html +++ /dev/null @@ -1,29 +0,0 @@ -{% extends "layouts/default" %} -{% import "views/macros/tags" %} -{% block content %} -{% if noindex %} -<!--noindex--> -{% endif %} -{% for msg in msgs %} -{% include "views/partial/message" %} -{% endfor %} -{% if nextpage | default('') is not empty %} -<p class="page"><a href="{{ nextpage | raw }}" rel="prev">{{ i18n("messages","messages.next") }} →</a></p> -{% endif %} -{% endblock %} -{% block "column" %} -{% if tag | default('') is not empty %} -{% include "views/partial/tagcolumn" %} -{% elseif visitor.uid > 0 %} -{% if discover %} -{% include "views/partial/homecolumn" %} -{% else %} -{% include "views/partial/usercolumn" %} -{% endif %} -{% else %} -{% include "views/partial/homecolumn" %} -{% endif %} -{% if noindex %} -<!--/noindex--> -{% endif %} -{% endblock %} diff --git a/juick-server/src/main/resources/templates/views/login.html b/juick-server/src/main/resources/templates/views/login.html deleted file mode 100644 index a538cb26..00000000 --- a/juick-server/src/main/resources/templates/views/login.html +++ /dev/null @@ -1,144 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <title>Juick</title> - <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js" defer="defer"></script> - <style> - * { margin: 0; padding: 0; } - html { font-family: sans-serif; font-size: 12pt; } - html { background: #f8f8f8; } - body { margin: 100px auto 0 auto; width: 1000px; } - a { color: #069; } - ul { float: left; width: 700px; height: 350px; list-style-type: none; background: url(/tagscloud.png) no-repeat; position: relative; box-shadow: 0 0 3px rgba(0,0,0,.16); } - ul a { position: absolute; display: block; text-indent: 100%; white-space: nowrap; overflow: hidden; } - - #bottom1 { position: absolute; left: 0px; bottom: 10px; width: 100%; text-align: center; color: #555; } - #bottom2 { position: absolute; left: 0px; bottom: -50px; width: 100%; padding-bottom: 20px; text-align: center; font-size: small; color: #777; } - - #signup,#signin { margin-left: 730px; width: 250px; } - #signup { padding-top: 25px; } - #signup>div { width: 100%; margin: 15px 0; } - #signup>div>a { display: block; width: 100%; height: 32px; line-height: 32px; text-indent: 37px; text-decoration: none; overflow: hidden; } - - #facebook a { color: #FFF; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAADNQTFRFO1edX3ewl6bLnKrOoK3QrrrYvMXe2N7r3OLu3+Tv5urz7O/29vf6+Pn7+vv9/Pz9////ykQjsQAAAEZJREFUOMtjYBgFuAATO68ADxdOaUYuATDAqYBbAL8CFgECCjiBcqz4XMiPz3oQEKCtAgEkwEdIAQchBWyEFDAPkDdHsAIAhZkIwz/VK/UAAAAASUVORK5CYII=") no-repeat #3A569C; } - #vk a { color: #FFF; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAHJQTFRFbY+zbo+zbpCzb5C0cpO1c5O2dZW3dpa4e5m6gJ29gZ69lq/In7bNo7jPrcDUs8XXvs3dv87dy9fkztnlz9rm0Nrm093o1N7o1+Dq3OTt3ubu4Ofv5Orw7fH27vL28PP38vX49Pb5+vv8+/z9/Pz9////2jSYlQAAAG5JREFUOMvtkEcOgDAMBE3vvXdIyP+/iMMRKfYHmMtcRtE6AD8f1Is8pyKgAs0RGYO2HSWqMQaoBHVRgYsS3AsrtyFlrqgdJlCLb95gxQO6IkZCqL+KCjz0TQU5ejOf2a3aJXPF7BOB2PvMhp8PDzGRFgEe7xvEAAAAAElFTkSuQmCC") no-repeat #6d8fb3; } - #xmpp>a { color: #333; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAt9QTFRFBj5rCUFoFz5mDEFuDUNqGUJkGUNgAEprEkVtGkRhHURnHEZjAE+BIElmEEx/HEx0PUdTHE5wMEtfO0laJk5sFlN6Nk1cKFBuIlJ1R0pYRExTGF2KMVh1OFhxDGSQT1deNlx6TVhkIGKPUlphKWOFU1tiOmB+Vl1kmlAuNmaDQGpaIG6ba2Q2SGeBN2uUNW+LF3mGRmyLV3BAL3xWgmVJ2VAa2lEb0VYX11UafWlmam9mam5xy1km3lUea29y2VccvF8obHBz21gd4VcXS3ebPoVG1lwl5FkaU3iYYHaH2F0ejHFIx2Iv4V0aP4o+02As218g4l4bPYtFX351KZFaymU43mIrVYoz5mEfT4w0xGsrSo82eXqDw2s+z2k1OZVAT4SoPZU5RZM4NJVYbYc8VoSiWY43WItbuXBK52gYOI20TpM6YYSYfX98T5Q7foB9aYSZSZc8Ro21cYxH7GwdT46e0HFGeopO2HA8V5ZEf4s8528l1HM7UJs5UI+xXoyrWZhGY4ylyHdMwXhXQZS7XpdNU5tH4XJBYJZZcIuhbpJk0HdPU588kohqOZ2pVZS30XpX6XcxSaFrz4FIUqVWWqVCYZm23YBGxYRrZZ2QXpy/5YFC7IA+6oBRb6ZUcKZn44ZSgKJu54hHmJqXbbBNfKh3cq5nYqrMpJyVdrBjx5WCo56diamAjaWdyZeE8JFV8ZJWnqOlaK/Sd7Zh7pVdgbN6x52N8JdfwqCU8ZhgZLbXb7a5grtTmK+UxKKWtqahq6qhd7bHqauop6yvqqyp755pbbzRrK6rwamgib53qq+xra+sobSg86Jza8PJsLKujMVctrGwqLapv7Crr7Wr8ad1srSxj8R9uLOys7WyubSztLazkcZ/sre6tbe0r7m0tri1t7m2vri3uLq3v7m4ubu4nc9mpNBvos2UptJxtNWXtteFuNmbuNyQud2Rut6SwN+aweCbwuGdw+KeweKkF4OfHQAAAadJREFUOMtjeEoAMAwfBQ9vYJO68RCu4BYDq1bXmr2XHoMlnlzet2qGpTgX8y2EFWwensEdrW2FbUAlJ2zDKsqaYwo6eZHcIGzoor/s5IdPSfefPt3nf3Xn3HZpPU8xJAXWIvU88VOffcm78vTptrijJe4OfAmy1kgKzjMlaIfUPvvQcObp03U120ucTZQaGc8je1NZrd8g8cL7eUufPp0wfX2mvVydripKONzkTG1JXvL6TuXTR/Zb5gR6F+Vw30QNqNUss/pqn3/MvrTD7Wy1V9Y0jtXoIZnLv2nSnrfzF3bP3hDRu1wwHzOo1VWOTXn5yi/q7MTJuxTUscTFQyHNc5vfbU1btOCgjNBDbJH1UF5i9/XP6VVrJRUfYo/Nx3YcpS/euOo4Pn6MqeDx44f3LoY2SYnOXBlUfPPug8cwRQxQ6Qf39sda+HqZaQiwGydFlx+4eQ+qAqrg4b3bPqbmNhkrNm5cbGUUHply7d6Dh8hWPH744Oa186eOHDl06NDh46fP33zwEMUEiCMe3L13GwjuPbj38PHTx9jT5OPHj5G9MFQyDgA8riWAv9eLFAAAAABJRU5ErkJggg==") no-repeat #BBB; } - #xmppinfo { background: #FFF; padding: 10px; display: none; } - - #signin { text-align: center; font-size: small; } - #signinform { background: #FFF; padding: 10px 15px; margin-top: 15px; display: none; } - input.txt { width: 212px; border: 1px solid #CCC; margin: 3px 0; padding: 3px; } - input.submit { width: 70px; border: 1px solid #CCC; margin: 3px 0; padding: 3px; } - </style> - <link rel="icon" href="//i.juick.com/favicon.png"/> - </head> - -<body> - -<ul id="tags"> - <li><a href="/tag/juick" style="left: 359px; top: 120px; width: 311px; height: 99px">juick</a></li> - <li><a href="/tag/linux" style="left: 201px; top: 100px; width: 98px; height: 35px">linux</a></li> - <li><a href="/tag/android" style="left: 314px; top: 42px; width: 45px; height: 158px">android</a></li> - <li><a href="/tag/работа" style="left: 149px; top: 138px; width: 165px; height: 41px">работа</a></li> - <li><a href="/tag/music" style="left: 119px; top: 249px; width: 124px; height: 32px">music</a></li> - <li><a href="/tag/windows" style="left: 448px; top: 234px; width: 186px; height: 32px">windows</a></li> - <li><a href="/tag/google" style="left: 244px; top: 252px; width: 134px; height: 41px">google</a></li> - <li><a href="/tag/кино" style="left: 68px; top: 83px; width: 97px; height: 28px">кино</a></li> - <li><a href="/tag/фото" style="left: 400px; top: 266px; width: 101px; height: 29px">фото</a></li> - <li><a href="/tag/жизнь" style="left: 554px; top: 266px; width: 125px; height: 27px">жизнь</a></li> - <li><a href="/tag/еда" style="left: 46px; top: 196px; width: 71px; height: 32px">еда</a></li> - <li><a href="/tag/музыка" style="left: 61px; top: 111px; width: 139px; height: 27px">музыка</a></li> - <li><a href="/tag/прекрасное" style="left: 152px; top: 200px; width: 205px; height: 32px">прекрасное</a></li> - <li><a href="/tag/книги" style="left: 148px; top: 293px; width: 103px; height: 25px">книги</a></li> - <li><a href="/tag/цитата" style="left: 325px; top: 301px; width: 126px; height: 27px">цитата</a></li> <li><a href="/tag/games" style="left: 117px; top: 142px; width: 30px; height: 104px">games</a></li> - <li><a href="/tag/ubuntu" style="left: 503px; top: 2px; width: 28px; height: 102px">ubuntu</a></li> - <li><a href="/tag/котэ" style="left: 534px; top: 27px; width: 76px; height: 28px">котэ</a></li> - <li><a href="/tag/ВНЕЗАПНО" style="left: 501px; top: 293px; width: 146px; height: 23px">ВНЕЗАПНО</a></li> - <li><a href="/tag/юмор" style="left: 73px; top: 53px; width: 84px; height: 28px">юмор</a></li> - <li><a href="/tag/мысли" style="left: 202px; top: 179px; width: 102px; height: 21px">мысли</a></li> - <li><a href="/tag/pic" style="left: 400px; top: 78px; width: 33px; height: 38px">pic</a></li> - <li><a href="/tag/политота" style="left: 531px; top: 60px; width: 130px; height: 24px">политота</a></li> - <li><a href="/tag/WOT" style="left: 159px; top: 63px; width: 48px; height: 20px">WOT</a></li> - <li><a href="/tag/fail" style="left: 8px; top: 170px; width: 34px; height: 27px">fail</a></li> - <li><a href="/tag/погода" style="left: 670px; top: 126px; width: 24px; height: 93px">погода</a></li> - <li><a href="/tag/apple" style="left: 42px; top: 167px; width: 64px; height: 29px">apple</a></li> - <li><a href="/tag/jabber" style="left: 436px; top: 43px; width: 25px; height: 75px">jabber</a></li> - <li><a href="/tag/тян" style="left: 532px; top: 94px; width: 47px; height: 21px">тян</a></li> - <li><a href="/tag/work" style="left: 359px; top: 55px; width: 58px; height: 23px">work</a></li> - <li><a href="/tag/Python" style="left: 240px; top: 63px; width: 74px; height: 23px">Python</a></li> - <li><a href="/tag/Видео" style="left: 266px; top: 232px; width: 76px; height: 20px">Видео</a></li> - <li><a href="/tag/авто" style="left: 359px; top: 30px; width: 58px; height: 24px">авто</a></li> - <li><a href="/tag/Anime" style="left: 360px; top: 328px; width: 66px; height: 21px">Anime</a></li> - <li><a href="/tag/игры" style="left: 378px; top: 242px; width: 22px; height: 58px">игры</a></li> - <li><a href="/tag/вело" style="left: 176px; top: 9px; width: 18px; height: 54px">вело</a></li> - <li><a href="/tag/web" style="left: 661px; top: 219px; width: 22px; height: 47px">web</a></li> - <li><a href="/tag/YouTube" style="left: 498px; top: 316px; width: 81px; height: 24px">YouTube</a></li> - <li><a href="/tag/Вопрос" style="left: 208px; top: 18px; width: 22px; height: 72px">Вопрос</a></li> - <li><a href="/tag/железо" style="left: 159px; top: 318px; width: 75px; height: 16px">железо</a></li> - <li><a href="/tag/Microsoft" style="left: 20px; top: 146px; width: 86px; height: 21px">Microsoft</a></li> - <li><a href="/tag/video" style="left: 616px; top: 101px; width: 51px; height: 19px">video</a></li> - <li><a href="/tag/Россия" style="left: 32px; top: 242px; width: 68px; height: 16px">Россия</a></li> - <li><a href="/tag/java" style="left: 409px; top: 226px; width: 39px; height: 22px">java</a></li> - <li><a href="/tag/новости" style="left: 39px; top: 67px; width: 21px; height: 79px">новости</a></li> - <li><a href="/tag/интернет" style="left: 100px; top: 233px; width: 17px; height: 85px">интернет</a></li> - <li><a href="/tag/steam" style="left: 14px; top: 228px; width: 52px; height: 13px">steam</a></li> - <li><a href="/tag/слова" style="left: 501px; top: 272px; width: 51px; height: 18px">слова</a></li> - <li><a href="/tag/почта" style="left: 477px; top: 27px; width: 17px; height: 56px">почта</a></li> - <li><a href="/tag/help" style="left: 123px; top: 281px; width: 21px; height: 35px">help</a></li> - <li><a href="/tag/skype" style="left: 110px; top: 320px; width: 49px; height: 20px">skype</a></li> - <li><a href="/tag/debian" style="left: 461px; top: 47px; width: 16px; height: 51px">debian</a></li> - <li><a href="/tag/win" style="left: 505px; top: 104px; width: 27px; height: 16px">win</a></li> - <li><a href="/tag/Религия" style="left: 33px; top: 281px; width: 67px; height: 17px">Религия</a></li> - <li><a href="/tag/soft" style="left: 286px; top: 86px; width: 28px; height: 14px">soft</a></li> - <li><a href="/tag/Политика" style="left: 144px; top: 281px; width: 75px; height: 12px">Политика</a></li> - <li><a href="/tag/сны" style="left: 426px; top: 328px; width: 33px; height: 13px">сны</a></li> - <li><a href="/tag/Питер" style="left: 146px; top: 233px; width: 50px; height: 16px">Питер</a></li> - <li><a href="/tag/bash" style="left: 451px; top: 311px; width: 38px; height: 16px">bash</a></li> - <li><a href="/tag/code" style="left: 279px; top: 310px; width: 39px; height: 16px">code</a></li> - <li><a href="/tag/yandex" style="left: 19px; top: 263px; width: 56px; height: 18px">yandex</a></li> - <li><a href="/tag/firefox" style="left: 452px; top: 295px; width: 48px; height: 16px">firefox</a></li> - <li><a href="/tag/hardware" style="left: 230px; top: 40px; width: 67px; height: 18px">hardware</a></li> - <li><a href="/tag/git" style="left: 78px; top: 258px; width: 20px; height: 19px">git</a></li> - <li><a href="/tag/dev" style="left: 165px; top: 88px; width: 31px; height: 19px">dev</a></li> - <li><a href="/tag/mobile" style="left: 421px; top: 24px; width: 15px; height: 47px">mobile</a></li> - <li><a href="/tag/люди" style="left: 151px; top: 184px; width: 43px; height: 15px">люди</a></li> - <li><a href="/tag/php" style="left: 149px; top: 24px; width: 27px; height: 18px">php</a></li> - <li><a href="/tag/haskell" style="left: 271px; top: 293px; width: 48px; height: 16px">haskell</a></li> - <li><a href="/tag/стихи" style="left: 135px; top: 42px; width: 41px; height: 11px">стихи</a></li> - <li><a href="/tag/photo" style="left: 639px; top: 219px; width: 20px; height: 39px">photo</a></li> - <li><a href="/tag/чай" style="left: 448px; top: 220px; width: 27px; height: 14px">чай</a></li> - <li><a href="/tag/Опрос" style="left: 297px; top: 22px; width: 14px; height: 41px">Опрос</a></li> - <li><a href="/tag/Chrome" style="left: 311px; top: 25px; width: 48px; height: 17px">Chrome</a></li> - <li><a href="/tag/life" style="left: 255px; top: 311px; width: 23px; height: 16px">life</a></li> - <li><a href="/tag/opera" style="left: 226px; top: 232px; width: 38px; height: 14px">opera</a></li> - <li><a href="/tag/programming" style="left: 234px; top: 327px; width: 81px; height: 14px">programming</a></li> - <li><a href="/tag/дети" style="left: 15px; top: 197px; width: 31px; height: 13px">дети</a></li> - <li><a href="/tag/сериалы" style="left: 575px; top: 219px; width: 61px; height: 13px">сериалы</a></li> - <li><a href="/tag/учеба" style="left: 616px; top: 84px; width: 43px; height: 17px">учеба</a></li> - </ul> - -<div id="bottom1">juick.com © 2008-2018 <a href="/help/ru/contacts" rel="nofollow">Контакты</a> · <a href="/help/" rel="nofollow">Помощь</a></div> - -<div id="signup"> - {{ i18n("messages","label.register") }}: - <div id="facebook"><a href="/_fblogin" rel="nofollow">Facebook</a></div> - <div id="vk"><a href="/_vklogin" rel="nofollow">ВКонтакте</a></div> - <div id="tg"> - <script async src="https://telegram.org/js/telegram-widget.js?3" - data-telegram-login="Juick_bot" data-size="medium" data-radius="0" - data-auth-url="https://juick.com/_tglogin" data-request-access="write"></script> - </div> - </div> -<div id="signin"> - <a href="#" onclick="$('#signinform').toggle(); $('#nickinput').focus(); return false"> - {{ i18n("messages","question.areRegistered") }} - </a> - <div id="signinform"><form action="/login" method="POST"> - <input class="txt" type="text" name="username" placeholder='{{ i18n("messages","label.username") }}' id="nickinput"/> - <input class="txt" type="password" name="password" placeholder='{{ i18n("messages","label.password") }}'/> - <input class="submit" type="submit" value="OK"/> - </form></div> - </div> - -</body> -</html> diff --git a/juick-server/src/main/resources/templates/views/login_success.html b/juick-server/src/main/resources/templates/views/login_success.html deleted file mode 100644 index ee71f12f..00000000 --- a/juick-server/src/main/resources/templates/views/login_success.html +++ /dev/null @@ -1,13 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="UTF-8"> - <title>Blank window</title> -</head> -<body> - <script type="text/javascript"> - window.opener.postMessage("{{ hash }}", "*"); - window.close(); - </script> -</body> -</html> diff --git a/juick-server/src/main/resources/templates/views/macros/tags.html b/juick-server/src/main/resources/templates/views/macros/tags.html deleted file mode 100644 index defed8e6..00000000 --- a/juick-server/src/main/resources/templates/views/macros/tags.html +++ /dev/null @@ -1,11 +0,0 @@ -{% macro tags(uname="", tagsList) %} -{% for tag in tagsList %} -<a href="/{{ uname }}/?tag={{ tag | urlencode }}">{{ tag | raw }}</a> -{% endfor %} -{% endmacro %} - -{% macro allTags(baseUri, tagsList) %} -{% for tag in tagsList %} -<a href="{{ baseUri }}tag/{{ tag | urlencode }}">#{{ tag | raw }}</a> -{% endfor %} -{% endmacro %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/partial/footer.html b/juick-server/src/main/resources/templates/views/partial/footer.html deleted file mode 100644 index 35972254..00000000 --- a/juick-server/src/main/resources/templates/views/partial/footer.html +++ /dev/null @@ -1,16 +0,0 @@ -<div id="footer"> - <div id="footer-right"> · - <a href="/help/contacts" rel="nofollow">{{ i18n("messages","link.contacts") }}</a> · - <a href="/help/tos" rel="nofollow">{{ i18n("messages","link.tos") }}</a> - </div> - <div id="footer-social"> - <a href="https://twitter.com/Juick" rel="nofollow"><i data-icon="ei-sc-twitter" data-size="m"></i></a> - <a href="https://vk.com/juick" rel="nofollow"><i data-icon="ei-sc-vk" data-size="m"></i></a> - <a href="https://www.facebook.com/JuickCom" rel="nofollow"><i data-icon="ei-sc-facebook" data-size="m"></i></a> - </div> - <div id="footer-left">juick.com © 2008-2018 - {% if links | default ('') is not empty %} - <br/>{{ i18n("messages","label.sponsors") }}: {{ links | raw }} - {% endif %} - </div> -</div> diff --git a/juick-server/src/main/resources/templates/views/partial/homecolumn.html b/juick-server/src/main/resources/templates/views/partial/homecolumn.html deleted file mode 100644 index 01448bca..00000000 --- a/juick-server/src/main/resources/templates/views/partial/homecolumn.html +++ /dev/null @@ -1,25 +0,0 @@ -<ul class="toolbar"> - <li> - <a href="/?show=top" title="Top"> - <i data-icon="ei-heart" data-size="s"></i>Top - </a> - </li> - <li> - <a href="/?show=all" title="{{ i18n("messages","link.allMessages") }}"> - <i data-icon="ei-search" data-size="s"></i>{{ i18n("messages","link.allMessages") }} - </a> - </li> - <li> - <a href="/?show=photos" title="{{ i18n("messages","link.withPhotos") }}"> - <i data-icon="ei-camera" data-size="s"></i>{{ i18n("messages","link.withPhotos") }} - </a> - </li> -</ul> -<div class="tags"> - <h4>{{ i18n("messages","link.trends") }}</h4> - {% include "views/partial/tags" %} - {% if showAdv | default(false) %} - <h4>Наши друзья</h4> - <a href="https://ru.wix.com/">конструктор сайтов</a> - {% endif %} -</div>
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/partial/message.html b/juick-server/src/main/resources/templates/views/partial/message.html deleted file mode 100644 index 00ca048c..00000000 --- a/juick-server/src/main/resources/templates/views/partial/message.html +++ /dev/null @@ -1,76 +0,0 @@ -<article data-mid="{{ msg.mid }}"> - <header class="h"> - <span> - <a href="/{{ msg.user.name }}/"><span>{{ msg.user.name }}</span></a> - </span> - <div class="msg-avatar"><a href="/{{ msg.user.name }}/"> - <img src="//i.juick.com/a/{{ msg.user.uid }}.png" alt="{{ msg.user.name }}"/></a> - </div> - <div class="msg-ts"> - <a href="/{{ msg.user.name }}/{{ msg.mid }}"> - <time datetime="{{ msg.timestamp | timestamp | date('yyyy-MM-dd HH:mm:ss') }}Z" - title="{{ msg.timestamp | timestamp | date('yyyy-MM-dd HH:mm:ss') }} GMT"> - {{ msg.timestamp | prettyTime }} - </time> - </a> - </div> - <div class="msg-tags"> - {{ tags(msg.user.name, msg.tags | tagsList) }} - </div> - </header> - <p>{{ msg | formatMessage }}</p> - {% if msg.AttachmentType is not empty %} - <p class="ir"><a href="//i.juick.com/p/{{ msg.mid }}.{{ msg.AttachmentType }}" data-fname="{{ msg.mid }}.{{ msg.AttachmentType }}"> - <img src="//i.juick.com/photos-512/{{ msg.mid }}.{{ msg.AttachmentType }}" alt=""/></a> - </p> - {% endif %} - <nav class="l"> - {% if visitor.uid == msg.user.uid %} - <a href="/{{ msg.mid }}" class="a-like msg-button"> - <span class="msg-button-icon"> - <i data-icon="ei-heart" data-size="s"></i> - {% if msg.likes > 0 %} {{ msg.likes }}{% endif %} - </span> - <span> {{ i18n("messages","message.recommend") }}</span> - </a> - {% elseif visitor.uid > 0 %} - <a href="/post?body=!+%23{{ msg.mid }}" class="a-like msg-button"> - <span class="msg-button-icon"> - <i data-icon="ei-heart" data-size="s"></i> - {% if msg.likes > 0 %} {{ msg.likes }}{% endif %} - </span> - <span> {{ i18n("messages","message.recommend") }}</span> - </a> - {% else %} - <a href="/login" class="a-login msg-button"> - <span class="msg-button-icon"> - <i data-icon="ei-heart" data-size="s"></i> - {% if msg.likes > 0 %} {{ msg.likes }}{% endif %} - </span> - <span> {{ i18n("messages","message.recommend") }}</span> - </a> - {% endif %} - {% if (not msg.ReadOnly) or (visitor.uid == msg.user.uid) %} - <a href="/{{ msg.mid }}" class="a-comment msg-button"> - <span class="msg-button-icon"> - <i data-icon="ei-comment" data-size="s"></i> - {% if msg.Replies > 0 %} - {% if msg.unread %} - <span class="badge">{{ msg.Replies }}</span> - {% else %} - {{ msg.Replies }} - {% endif %} - {% endif %} - </span> - <span> {{ i18n("messages","message.comment") }}</span> - </a> - <a href="#" class="msg-menu msg-button"> - <i data-icon="ei-link" data-size="s"></i> - <span> {{ i18n("messages","message.share") }}</span> - </a> - {% endif %} - {% if msg.FriendsOnly %} - <a href="#" class="a-privacy">Открыть доступ</a> - {% endif %} - </nav> -</article>
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/partial/navigation.html b/juick-server/src/main/resources/templates/views/partial/navigation.html deleted file mode 100644 index 03b6c56d..00000000 --- a/juick-server/src/main/resources/templates/views/partial/navigation.html +++ /dev/null @@ -1,36 +0,0 @@ -<header> - <div id="header_wrapper"> - {% if visitor.uid > 0 %} - <div id="ctitle"> - <a href="/{{ visitor.name }}"> - <img src="//i.juick.com/a/{{ visitor.uid }}.png" alt=""/>{{ visitor.name }} - </a> - </div> - {% else %} - <div id="logo"><a href="/{% if visitor.uid > 0 %}?show=my{% endif %}">Juick</a></div> - {% endif %} - <div id="search"> - <form action="/"> - <input name="search" class="text" - placeholder="{{ i18n('messages','label.search') }}" value="{{ search | default('') }}"/> - </form> - </div> - <nav id="global"> - <ul> - <li><a href="/"><i data-icon="ei-comment" data-size="s"></i>{{ i18n("messages","link.discuss") }}{% if visitor.unreadCount > 0 %}<span class="badge">{{ visitor.unreadCount }}</span>{% endif %}</a></li> - <li><a href="/?show=all" rel="nofollow"><i data-icon="ei-search" data-size="s"></i>{{ i18n("messages","link.allMessages") }}</a></li> - {% if visitor.uid > 0 %} - <li><a id="post" href="/post"> - <i data-icon="ei-pencil" data-size="s"></i>{{ i18n("messages","link.postMessage") }}</a> - </li> - {% else %} - <li> - <a class="a-login" href="/login" rel="nofollow"> - <i data-icon="ei-user" data-size="s"></i>{{ i18n("messages", "link.Login") }} - </a> - </li> - {% endif %} - </ul> - </nav> - </div> -</header> diff --git a/juick-server/src/main/resources/templates/views/partial/settings_tabs.html b/juick-server/src/main/resources/templates/views/partial/settings_tabs.html deleted file mode 100644 index 4715253e..00000000 --- a/juick-server/src/main/resources/templates/views/partial/settings_tabs.html +++ /dev/null @@ -1,6 +0,0 @@ -<div id="pagetabs"><ul> - <li><a href="/settings">{{ i18n("messages","link.settings.main") }}</a></li> - <li><a href="/settings?page=password">{{ i18n("messages","link.settings.password") }}</a></li> - <li><a href="/settings?page=about">{{ i18n("messages","link.settings.about") }}</a></li> - <li><a href="/logout"><i data-icon="ei-user" data-size="s"></i>{{ i18n("messages","link.logout") }}</a></li> -</ul></div>
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/partial/tagcolumn.html b/juick-server/src/main/resources/templates/views/partial/tagcolumn.html deleted file mode 100644 index 3e61d3d3..00000000 --- a/juick-server/src/main/resources/templates/views/partial/tagcolumn.html +++ /dev/null @@ -1,33 +0,0 @@ -<div id="ctitle"> - <h2>*{{ tag.name }}</h2> -</div> -{% if visitor is not empty and visitor.uid > 0 %} -<ul class="toolbar"> - {% if isSubscribed %} - <li> - <a href="/post?body=U+%2A{{ tag.name }}" title="Подписан"> - <i data-icon="ei-check" data-size="s"></i>Subscribed - </a> - </li> - {% else %} - <li> - <a href="/post?body=S+%2A{{ tag.name }}" title="Подписаться"> - <i data-icon="ei-plus" data-size="s"></i>Subscribe - </a> - </li> - {% endif %} - {% if isInBL %} - <li> - <a href="/post?body=BL+%2A{{ tag.name }}" title="Разблокировать"> - <i data-icon="ei-close-o" data-size="s"></i>Unblock - </a> - </li> - {% else %} - <li> - <a href="/post?body=BL+%2A{{ tag.name }}" title="Заблокировать"> - <i data-icon="ei-close" data-size="s"></i>Block - </a> - </li> - {% endif %} -</ul> -{% endif %} diff --git a/juick-server/src/main/resources/templates/views/partial/tags.html b/juick-server/src/main/resources/templates/views/partial/tags.html deleted file mode 100644 index 3235213e..00000000 --- a/juick-server/src/main/resources/templates/views/partial/tags.html +++ /dev/null @@ -1,3 +0,0 @@ -{% for tag in tags %} - <a href="/tag/{{ tag | urlencode }}" title="{{ tag }}">{{ tag | raw }}</a> -{% endfor %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/partial/usercolumn.html b/juick-server/src/main/resources/templates/views/partial/usercolumn.html deleted file mode 100644 index 2b1963e3..00000000 --- a/juick-server/src/main/resources/templates/views/partial/usercolumn.html +++ /dev/null @@ -1,89 +0,0 @@ -{% if visitor is not empty and visitor.uid > 0 and visitor.uid != user.uid %} -<div id="ctitle"> - <a href="/{{ user.name }}"> - <img src="//i.juick.com/a/{{ user.uid }}.png" alt=""/>{{ user.name }} - </a> -</div> -<ul class="toolbar"> - {% if isSubscribed %} - <li> - <a href="/post?body=U+%40{{ user.name }}" title="Подписан"> - <i data-icon="ei-check" data-size="s"></i>Subscribed - </a> - </li> - {% else %} - <li> - <a href="/post?body=S+%40{{ user.name }}" title="Подписаться"> - <i data-icon="ei-plus" data-size="s"></i>Subscribe - </a> - </li> - {% endif %} - {% if isInBL %} - <li> - <a href="/post?body=BL+%40{{ user.name }}" title="Разблокировать"> - <i data-icon="ei-close-o" data-size="s"></i>Unblock - </a> - </li> - {% else %} - <li> - <a href="/post?body=BL+%40{{ user.name }}" title="Заблокировать"> - <i data-icon="ei-close" data-size="s"></i>Block - </a> - </li> - {% endif %} - {% if not isInBLAny %} - <li> - <a href="/pm/sent?uname={{ user.name }}" title="Написать приватное сообщение"> - <i data-icon="ei-envelope" data-size="s"></i>PM - </a> - </li> - {% endif %} -</ul> -{% else %} -<hr/> -{% endif %} -<ul> - {% if visitor is not empty and visitor.uid == user.uid %} - <li><a href="/?show=my"><i data-icon="ei-clock" data-size="s"></i>{{ i18n("messages","link.my") }}</a></li> - <li><a href="/pm/inbox"><i data-icon="ei-envelope" data-size="s"></i>{{ i18n("messages","link.privateMessages") }}</a></li> - <li><a href="/?show=discuss"><i data-icon="ei-comment" data-size="s"></i>{{ i18n("messages","link.discuss") }}</a></li> - {% endif %} - <li><a href="/{{ user.name }}/?show=recomm" rel="nofollow"><i data-icon="ei-heart" data-size="s"></i>{{ i18n("messages","blog.recommendations") }}</a></li> - <li><a href="/{{ user.name }}/?show=photos" rel="nofollow"><i data-icon="ei-camera" data-size="s"></i>{{ i18n("messages","blog.photos") }}</a></li> - {% if visitor is not empty and visitor.uid == user.uid and false %} - <li><a href="/?show=mycomments" rel="nofollow">{{ i18n("messages","blog.comments") }}</a></li> - <li><a href="/?show=unanswered" rel="nofollow">Неотвеченные</a></li> - {% endif %} - {% if visitor is not empty and visitor.uid == user.uid %} - <li><a href="/settings" rel="nofollow"><i data-icon="ei-gear" data-size="s"></i>{{ i18n("messages","link.settings") }}</a></li> - {% endif %} -</ul> -<hr/> -<form action="/{{ user.name }}/"> - <p><input type="text" name="search" class="inp" placeholder="{{ i18n('messages','label.search') }}"/></p> -</form> -{% include "views/partial/usertags" %} -<hr/> -<div id="ustats"> - <ul> - <li><a href="/{{ user.name }}/friends">{{ i18n("messages","blog.iread") }}: {{ statsIRead }}</a></li> - <li><a href="/{{ user.name }}/readers">{{ i18n("messages","blog.readers") }}: {{ statsMyReaders }}</a></li> - {% if statsMyBL > 0 and visitor.uid == user.uid %} - <li><a href="/{{ user.name }}/bl">{{ i18n("messages","blog.bl") }}: {{ statsMyBL }}</a></li> - {% endif %} - <li>{{ i18n("messages","blog.messages") }}: {{ statsMessages }}</li> - <li>{{ i18n("messages","blog.comments") }}: {{ statsReplies }}</li> - </ul> - {% if iread is not empty %} - <div class="iread"> - {% for u in iread %} - <span> - <a href="/{{ u.name }}/"> - <img src="//i.juick.com/as/{{ u.uid }}.png" alt="{{ u.name }}"/> - </a> - </span> - {% endfor %} - </div> - {% endif %} - -</div> diff --git a/juick-server/src/main/resources/templates/views/partial/usertags.html b/juick-server/src/main/resources/templates/views/partial/usertags.html deleted file mode 100644 index 71d1303e..00000000 --- a/juick-server/src/main/resources/templates/views/partial/usertags.html +++ /dev/null @@ -1,3 +0,0 @@ -{% import "views/macros/tags" %} -{{ tags(user.name, tagStats) }} -<a href="/{{ user.name }}/tags" rel="nofollow">...</a>
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/pm_inbox.html b/juick-server/src/main/resources/templates/views/pm_inbox.html deleted file mode 100644 index e82e120e..00000000 --- a/juick-server/src/main/resources/templates/views/pm_inbox.html +++ /dev/null @@ -1,35 +0,0 @@ -{% extends "layouts/default" %} -{% block content %} -{% if not msgs.isEmpty() %} -<ul id="private-messages"> - {% for msg in msgs %} - <li class="msg"> - <div class="msg-cont"> - <div class="msg-header"> - @<a href="/{{ msg.user.name }}/">{{ msg.user.name }}</a>: - <div class="msg-avatar"> - <a href="/{{ msg.user.name }}/"> - <img src="//i.juick.com/a/{{ msg.user.uid }}.png" alt="{{ msg.user.name }}"/> - </a> - </div> - <div class="msg-ts">{{ msg.timestamp | prettyTime }}</div> - </div> - - <div class="msg-txt">{{ msg | formatMessage }}</div> - <form class="pmmsg"> - <input type="hidden" name="uname" value="{{ msg.user.name }}"/> - <div class="msg-comment"> - <div class="ta-wrapper"> - <textarea name="body" rows="1" class="replypm" placeholder="Написать ответ"></textarea> - </div> - </div> - </form> - </div> - </li> - {% endfor %} -</ul> -{% endif %} -{% endblock %} -{% block "column" %} -{% include "views/partial/usercolumn" %} -{% endblock %} diff --git a/juick-server/src/main/resources/templates/views/pm_sent.html b/juick-server/src/main/resources/templates/views/pm_sent.html deleted file mode 100644 index dcda64d8..00000000 --- a/juick-server/src/main/resources/templates/views/pm_sent.html +++ /dev/null @@ -1,33 +0,0 @@ -{% extends "layouts/default" %} -{% block content %} -<form class="pmmsg"> - <div class="newpm"> - <div class="newpm-to">To: <input type="text" name="uname" placeholder="username" value="{{ uname }}"/></div> - <div class="newpm-body"><textarea name="body" rows="2"></textarea></div> - <div class="newpm-send"><input type="submit" value="OK"/></div> - </div> -</form> -{% if not msgs.isEmpty() %} -<ul id="private-messages"> - {% for msg in msgs %} - <li class="msg"> - <div class="msg-cont"> - <div class="msg-header"> - @<a href="/{{ msg.user.name }}/">{{ msg.user.name }}</a>: - <div class="msg-avatar"> - <a href="/{{ msg.user.name }}/"> - <img src="//i.juick.com/a/{{ msg.user.uid }}.png" alt="{{ msg.user.name }}"/> - </a> - </div> - <div class="msg-ts">{{ msg.timestamp | prettyTime }}</div> - </div> - <div class="msg-txt">{{ msg | formatMessage }}</div> - </div> - </li> - {% endfor %} -</ul> -{% endif %} -{% endblock %} -{% block "column" %} -{% include "views/partial/usercolumn" %} -{% endblock %} diff --git a/juick-server/src/main/resources/templates/views/post.html b/juick-server/src/main/resources/templates/views/post.html deleted file mode 100644 index 3753b36c..00000000 --- a/juick-server/src/main/resources/templates/views/post.html +++ /dev/null @@ -1,19 +0,0 @@ -{% extends "layouts/minimal" %} -{% import "views/macros/tags" %} -{% block content %} -<article> -<form id="postmsg"> - <p style="text-align: left"> - <b>Фото:</b> <span id="attachmentfile"> - <input style="width: 100%;" type="file" name="attach"/> <i>({{ i18n("messages","postForm.imageFormats") }})</i></span> - </p> - <p> - <textarea name="body" class="newmessage" rows="7" cols="10" placeholder="*weather It's very cold today!">{{ body }}</textarea> - <br/> - <input type="submit" class="subm" value=" {{ i18n("messages","postForm.submit") }} "/> - </p> -</form> -</article> -<p style="text-align: left"><b>Теги:</b></p> -{{ tags(visitor.name, tags) }} -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/post_success.html b/juick-server/src/main/resources/templates/views/post_success.html deleted file mode 100644 index 2106f3cb..00000000 --- a/juick-server/src/main/resources/templates/views/post_success.html +++ /dev/null @@ -1,19 +0,0 @@ -{% extends "layouts/minimal" %} -{% block content %} -<h1>Сообщение опубликовано</h1> -<p>Поделитесь своим новым постом в социальных сетях:</p> -{% if sharetwi | default('') is not empty %} -<p class="social"> - <a href="https://twitter.com/intent/tweet?text={{ sharetwi }}" - class="sharenew"><i data-icon="ei-sc-twitter" data-size="m"></i>Отправить в Twitter</a></p> -{% endif %} -<p class="social"> - <a href="https://vk.com/share.php?url={{ url | urlencode }}" - class="sharenew"><i data-icon="ei-sc-vk" data-size="m"></i>Отправить в ВКонтакте</a></p> -{% if facebook | default('') is not empty %} -<p class="social"> - <a href="https://www.facebook.com/sharer/sharer.php?u={{ url | urlencode }}" - class="sharenew"><i data-icon="ei-sc-facebook" data-size="m"></i>Отправить в Facebook</a></p> -{% endif %} -<p>Ссылка на сообщение: <a href="{{ url | raw }}">{{ url }}</a></p> -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/settings_about.html b/juick-server/src/main/resources/templates/views/settings_about.html deleted file mode 100644 index bbf9e772..00000000 --- a/juick-server/src/main/resources/templates/views/settings_about.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "layouts/default" %} -{% block content %} -<article> - <form action="/settings" method="POST" enctype="multipart/form-data"> - <p>Full name: <input type="text" name="fullname" value="{{ userinfo.fullName }}"/></p> - <p>Country: <input type="text" name="country" value="{{ userinfo.country }}"/></p> - <p>URL: <input type="text" name="url" value="{{ userinfo.url }}" size="32"/><br/> - <small>Please, start with "http://"</small></p> - <p>About:<br/> - <input type="text" name="descr" value="{{ userinfo.description }}" style="width: 100%"/><br/> - <small>Max. 255 symbols</small></p> - <p>Avatar: <input type="file" name="avatar"/><br/> - <small>Recommendations: PNG, 96x96, <50Kb. Also, JPG and GIF supported.</small></p> - <p><input type="hidden" name="page" value="about"/><input type="submit" value=" OK "/></p> - </form> -</article> -{% endblock %} -{% block "column" %} -{% include "views/partial/settings_tabs" %} -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/settings_auth-email.html b/juick-server/src/main/resources/templates/views/settings_auth-email.html deleted file mode 100644 index e906d704..00000000 --- a/juick-server/src/main/resources/templates/views/settings_auth-email.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "layouts/default" %} -{% block content %} -<article> - <p>{{ result }}</p><p><a href="/settings">Settings</a>.</p> -</article> -{% endblock %} -{% block "column" %} -{% include "views/partial/settings_tabs" %} -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/settings_main.html b/juick-server/src/main/resources/templates/views/settings_main.html deleted file mode 100644 index 65fbc984..00000000 --- a/juick-server/src/main/resources/templates/views/settings_main.html +++ /dev/null @@ -1,151 +0,0 @@ -{% extends "layouts/default" %} -{% block content %} -<article> - <h1>Настройки</h1> - <form action="/settings" method="POST" enctype="multipart/form-data"> - <fieldset> - <legend>Notification options</legend> - <p><input type="checkbox" name="jnotify" value="1" {% if notify_options.repliesEnabled %} - checked="checked" {% endif %}/> Reply notifications ("Message posted")</p> - <p><input type="checkbox" name="subscr_notify" value="1" {% if notify_options.subscriptionsEnabled %} - checked="checked" {% endif %}/> Subscriptions notifications ("@user subscribed...")</p> - <p><input type="checkbox" name="recomm" value="1" {% if notify_options.recommendationsEnabled %} - checked="checked" {% endif %}/> Posts recommendations ("Recommended by @user")</p> - <p><input type="hidden" name="page" value="main"/><input type="submit" value=" OK "/></p> - </fieldset> - </form> - <fieldset> - <legend style="background: url(//telegram.org/favicon.ico?3) no-repeat; padding-left: 58px; line-height: 48px;"> - Telegram</legend> - {% if telegram_name is not empty %} - <form action="/settings" method="post"> - <div>Telegram: <b>{{ telegram_name }}</b> — - <input type="hidden" name="page" value="telegram-del"/> - <input type="submit" value=" Disable "/> - </div> - </form> - {% else %} - <p>To connect Telegram account: send any text message to <a href="https://telegram.me/Juick_bot">@Juick_bot</a> - </p> - {% endif %} - </fieldset> - {% if jids | length > 0 %} - <form action="/settings" method="POST" enctype="multipart/form-data"> - <fieldset> - <legend style="background: url(//static.juick.com/settings/xmpp.png) no-repeat; padding-left: 58px; line-height: 48px;"> - XMPP accounts - </legend> - <p>Your accounts:</p> - <p> - {% for jid in jids %} - <label><input type="radio" name="delete" value="xmpp;{{ jid }}">{{ jid }}</label><br/> - {% endfor %} - {% for auth in auths %} - <label><input type="radio" name="delete" - value="xmpp-unauth;{{ auth.account }}">{{ auth.account }}</label> - — <a href="#" - onclick="alert(\'To confirm, please send "AUTH {{ auth.getAuthCode() }}" (without quotes) from this account to "juick@juick.com".\'); return false;">Confirm</a><br/> - {% endfor %} - </p> - {% if jids | length > 1 %} - <p><input type="hidden" name="page" value="jid-del"/><input type="submit" value=" Delete "/></p> - {% endif %} - <p>To add new jabber account: send any text message to <a href="xmpp:juick@juick.com?message;body=login">juick@juick.com</a> - </p> - </fieldset> - </form> - {% endif %} - <fieldset> - <legend style="background: url(//static.juick.com/settings/email.png) no-repeat; padding-left: 58px; line-height: 48px;"> - E-mail - </legend> - <form action="/settings" method="POST" enctype="multipart/form-data"> - <p>Add account:<br/> - <input type="text" name="account"/> - <input type="hidden" name="page" value="email-add"/> - <input type="submit" value=" Add "/> - </p> - </form> - <form action="/settings" method="POST" enctype="multipart/form-data"> - <p>Your accounts:</p> - <p> - {% for email in emails %} - <label><input type="radio" name="account" value="{{ email }}">{{ email }}</label><br/> - {% endfor %} - {% if emails is empty %} - - </p> - {% else %} - </p> - {% if jids | length > 1 %} - <p><input type="hidden" name="page" value="email-del"/><input type="submit" value=" Delete "/></p> - {% endif %} - {% endif %} - </form> - {% if emails is not empty %} - <!--email_off--> - <form action="/settings" method="POST" enctype="multipart/form-data"> - <p>You can receive notifications to email:<br/> - Sent to <select name="account"> - <option value="">Disabled</option> - {% for email in emails %} - <option value="{{ email }}" {% if email_active == email %} selected="selected" {% endif %}> - {{ email }} - </option> - {% endfor %} - </select> - <input type="hidden" name="page" value="email-subscr"/> - <input type="submit" value="OK"/></p> - </form> - <!--/email_off--> - {% endif %} - <p> </p> - <p>You can post to Juick via e-mail. Send your <span style="text-decoration: underline">plain text</span> - messages to <span><a href="mailto:juick@juick.com">juick@juick.com</a></span>. You can attach one photo or video file.</p> - </fieldset> - <fieldset> - <legend style="background: url(//static.juick.com/settings/facebook.png) no-repeat; padding-left: 58px; line-height: 48px;"> - Facebook - </legend> - {% if fbstatus.connected %} - {% if fbstatus.crosspostEnabled %} - <form action="/settings" method="post"> - <div> - Facebook: <b>Enabled</b> — - <input type="hidden" name="page" value="facebook-disable"/> - <input type="submit" value=" Disable "/> - </div> - </form> - {% else %} - <form action="/settings" method="post"> - <div> - Facebook: <b>Disabled</b> — - <input type="hidden" name="page" value="facebook-enable"/> - <input type="submit" value=" Enable "/> - </div> - </form> - {% endif %} - {% else %} - <p>Cross-posting to Facebook: <a href="/_fblogin"><img src="//static.juick.com/facebook-connect.png" alt="Connect to Facebook"/></a></p> - {% endif %} - </fieldset> - <fieldset> - <legend style="background: url(//static.juick.com/settings/twitter.png) no-repeat; padding-left: 58px; line-height: 48px;"> - Twitter</legend> - {% if twitter_name is not empty %} - <form action="/settings" method="post"> - <div>Twitter: <b>{{ twitter_name }}</b> — - <input type="hidden" name="page" value="twitter-del"/> - <input type="submit" value=" Disable "/> - </div> - </form> - {% else %} - <p>Cross-posting to Twitter: <a href="/_twitter"><img src="//static.juick.com/twitter-connect.png" - alt="Connect to Twitter"/></a></p> - {% endif %} - </fieldset> - -</article> -{% endblock %} -{% block "column" %} -{% include "views/partial/settings_tabs" %} -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/settings_password.html b/juick-server/src/main/resources/templates/views/settings_password.html deleted file mode 100644 index aba0b139..00000000 --- a/juick-server/src/main/resources/templates/views/settings_password.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "layouts/default" %} -{% block content %} -<article> - <fieldset> - <legend>Changing your password</legend> - <form action="/settings" method="post"> - <input type="hidden" name="page" value="password"/> - <p>Change password: <input type="password" name="password" size="8"/> <input type="submit" - value=" Update "/><br/> - <i>(max. length - 16 symbols)</i></p> - </form> - </fieldset> -</article> -{% endblock %} -{% block "column" %} -{% include "views/partial/settings_tabs" %} -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/settings_privacy.html b/juick-server/src/main/resources/templates/views/settings_privacy.html deleted file mode 100644 index 83b87b93..00000000 --- a/juick-server/src/main/resources/templates/views/settings_privacy.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "layouts/default" %} -{% block content %} -<article> - <p>Privacy</p> -</article> -{% endblock %} -{% block "column" %} -{% include "views/partial/settings_tabs" %} -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/settings_result.html b/juick-server/src/main/resources/templates/views/settings_result.html deleted file mode 100644 index d87a5ea6..00000000 --- a/juick-server/src/main/resources/templates/views/settings_result.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "layouts/default" %} -{% block content %} -<article> - <p>{{ result | raw }}</p> -</article> -{% endblock %} -{% block "column" %} -{% include "views/partial/settings_tabs" %} -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/signup.html b/juick-server/src/main/resources/templates/views/signup.html deleted file mode 100644 index d6eb921f..00000000 --- a/juick-server/src/main/resources/templates/views/signup.html +++ /dev/null @@ -1,43 +0,0 @@ -{% extends "layouts/default" %} -{% block content %} -<h1 class="signup-h1"> - {% if type | slice(0, 1) == 'f' %} - <img src="//static.juick.com/settings/facebook.png" alt="Facebook"/> - {% elseif type | slice(0, 1) == 'v' %} - <img src="//static.juick.com/settings/vk.png" alt="VKontakte"/> - {% elseif type | slice(0, 1) == 'e' %} - <img src="//static.juick.com/settings/email.png" alt="Email"/> - {% elseif type | slice(0, 1) == 'd' %} - <img src="//telegram.org/favicon.ico?3" alt="Telegram"/> - {% endif %} - {{ account | raw }}</h1> - -<h2 class="signup-h2">Связать с существующим аккаунтом Juick</h2> -<form action="/signup" method="post"> - <input type="hidden" name="action" value="link"/> - <input type="hidden" name="type" value="{{ type }}"/> - <input type="hidden" name="hash" value="{{ hash }}"/> - {% if visitor.getUID() > 0 %} - <input type="submit" value="Связать с этим аккаунтом"/> - {% else %} - <p>Имя пользователя: <input type="text" name="username"/></p> - <p>Пароль: <input type="password" name="password"/></p> - <p><input type="submit" value=" OK "/></p> - {% endif %} -</form> - -{% if type != "xmpp" %} -<hr class="signup-hr"/> - -<h2 class="signup-h2">Создать новый аккаунт Juick</h2> -<form action="/signup" method="post"> - <input type="hidden" name="action" value="new"/> - <input type="hidden" name="type" value="{{ type }}"/> - <input type="hidden" name="hash" value="{{ hash }}"/> - <p>Имя пользователя: <input type="text" name="username" id="username"/><br/><i>(От 2-х до 16-и латинских символов - и/или цифр, дефис)</i></p> - <p>Пароль: <input type="password" name="password"/><br/><i>(от 6-и до 32-х символов)</i></p> - <p><input type="submit" value=" OK "/></p> -</form> -{% endif %} -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/thread.html b/juick-server/src/main/resources/templates/views/thread.html deleted file mode 100644 index 478258cf..00000000 --- a/juick-server/src/main/resources/templates/views/thread.html +++ /dev/null @@ -1,175 +0,0 @@ -{% extends "layouts/default" %} -{% import "views/macros/tags" %} -{% block content %} -<ul id="0"> - <li id="msg-{{ msg.mid }}" class="msg msgthread"> - <div class="msg-cont"> - <div class="msg-header"> - <div class="msg-avatar"> - <a href="/{{ msg.user.name }}/"><img src="//i.juick.com/a/{{ msg.user.uid }}.png" alt="{{ msg.user.name }}"/></a> - </div> - <span> - <a href="/{{ msg.user.name }}/"><span>{{ msg.user.name }}</span></a> - </span> - <div class="msg-ts"> - <a href="/{{ msg.user.name }}/{{ msg.mid }}"> - <time datetime="{{ msg.timestamp | timestamp | date('yyyy-MM-dd HH:mm:ss') }}Z" - title="{{ msg.timestamp | timestamp | date('yyyy-MM-dd HH:mm:ss') }} GMT"> - {{ msg.timestamp | prettyTime }} - </time> - </a> - </div> - <div class="msg-tags"> - {{ tags(msg.user.name, msg.tags | tagsList) }} - </div> - </div> - <div class="msg-txt">{{ msg | formatMessage }}</div> - {% if msg.AttachmentType is not empty %} - <div class="msg-media"> - <a href="//i.juick.com/p/{{ msg.mid }}.{{ msg.AttachmentType }}" data-fname="{{ msg.mid }}.{{ msg.AttachmentType }}"> - <img src="//i.juick.com/photos-512/{{ msg.mid }}.{{ msg.AttachmentType }}" alt=""/> - </a> - </div> - {% endif %} - <nav class="l"> - {% if visitor.uid == msg.user.uid %} - <a href="/{{ msg.mid }}" class="a-like msg-button"> - <span class="msg-button-icon"> - <i data-icon="ei-heart" data-size="s"></i> - {% if msg.Likes > 0 %} {{ msg.Likes }}{% endif %} - </span> - <span> {{ i18n("messages","message.recommend") }}</span> - </a> - {% elseif visitor.uid > 0 %} - <a href="/post?body=!+%23{{ msg.mid }}" class="a-like msg-button"> - <span class="msg-button-icon"> - <i data-icon="ei-heart" data-size="s"></i> - {% if msg.Likes > 0 %} {{ msg.Likes }}{% endif %} - </span> - <span> {{ i18n("messages","message.recommend") }}</span> - </a> - {% else %} - <a href="/login" class="a-login msg-button"> - <span class="msg-button-icon"> - <i data-icon="ei-heart" data-size="s"></i> - {% if msg.Likes > 0 %} {{ msg.Likes }}{% endif %} - </span> - <span> {{ i18n("messages","message.recommend") }}</span> - - </a> - {% endif %} - <a href="#" class="msg-menu msg-button"> - <i data-icon="ei-link" data-size="s"></i> - <span> {{ i18n("messages","message.share") }}</span> - </a> - {% if visitor.uid > 0 %} - {% if visitor.uid != msg.user.uid %} - {% if visitorSubscribed %} - <a href="/post?body=U+%23{{ msg.mid }}" class="msg-button"> - <i data-icon="ei-check" data-size="s"></i> - <span> {{ i18n("messages","message.subscribed") }}</span> - </a> - {% else %} - <a href="/post?body=S+%23{{ msg.mid }}" class="a-sub msg-button"> - <i data-icon="ei-eye" data-size="s"></i> - <span> {{ i18n("messages","message.subscribe") }}</span> - </a> - {% endif %} - {% else %} - <a href="/post?body=D+%23{{ msg.mid }}" class="msg-button"> - <i data-icon="ei-close" data-size="s"></i> - <span> {{ i18n("messages","message.delete") }}</span> - </a> - {% endif %} - {% endif %} - {% if msg.FriendsOnly %} - <a href="#" class="a-privacy">Открыть доступ</a> - {% endif %} - </nav> - {% if msg.VisitorCanComment %} - <form class="msg-comment-target"> - <input type="hidden" name="mid" value="{{ msg.mid }}"/> - <div class="msg-comment"> - <div class="ta-wrapper"> - <textarea name="body" rows="1" class="reply" placeholder="{{ i18n("messages","message.writeComment") }}"></textarea> - </div> - </div> - </form> - {% endif %} - {% if recomm is not empty %} - <div class="msg-recomms">{{ i18n("messages","message.recommendedBy") }} - {% for rec in recomm %} - <a href="/{{ rec }}/">@{{ rec }}</a>{% if loop.index < (loop.length - 1) %}, {% endif %} - {% endfor %} - {% if msg.likes > recomm.size() %} - {{ i18n("messages","message.recommendedOthers", msg.likes - recomm.size()) }} - {% endif %} - </div> - {% endif %} - </div> - </li> -</ul> -<div class="title2"> - {% if visitor.uid > 0 %} - <img src="/api/thread/mark_read/{{ msg.mid }}-{{ msg.rid }}.gif?hash={{visitor.authHash}}" /> - {% endif %} - <h2>{{ i18n("messages","reply.replies") }} ({{ replies.size() }})</h2> -</div> - -<ul id="replies"> - {% for msg in replies %} - <li id="{{ msg.rid }}" class="msg"> - <div class="msg-cont"> - <div class="msg-header" data-uri="{{ msg.user.uri }}"> - {% if not msg.user.banned %} - <a class="a-username" href="/{{ msg.user.name }}/">{{ msg.user.name }}</a> - <div class="msg-avatar"> - <a class="a-username" href="/{{ msg.user.name }}/"> - <img src="//i.juick.com/a/{{ msg.user.uid }}.png" alt="{{ msg.user.name }}"/> - </a> - </div> - {% else %} - [удалено]: - <div class="msg-avatar"> - <img src="//i.juick.com/av-96.png"/> - </div> - {% endif %} - <div class="msg-ts"> - <a href="/{{ msg.mid }}#{{ msg.rid }}"> - <time datetime="{{ msg.timestamp | timestamp | date('yyyy-MM-dd HH:mm:ss') }}Z" - title="{{ msg.timestamp | timestamp | date('yyyy-MM-dd HH:mm:ss') }} GMT"> - {{ msg.timestamp | prettyTime }} - </time> - </a> - </div> - </div> - <div class="msg-txt">{{ msg | formatMessage }}</div> - {% if msg.AttachmentType is not empty %} - <div class="msg-media"> - <a href="//i.juick.com/p/{{ msg.mid }}-{{ msg.rid }}.{{ msg.AttachmentType }}" data-fname="{{ msg.mid }}-{{ msg.rid }}.{{ msg.AttachmentType }}"> - <img src="//i.juick.com/photos-512/{{ msg.mid }}-{{ msg.rid }}.{{ msg.AttachmentType }}" alt=""/> - </a> - </div> - {% endif %} - <div class="msg-links">/{{ msg.rid }} - {% if msg.replyto > 0 %} - {{ i18n("messages","reply.inReplyTo") }} <a href="#{{ msg.replyto }}">/{{ msg.replyto }}</a> - {% endif %} - {% if msg.VisitorCanComment %} - · <a href="/post?body=%23{{ msg.mid }}/{{ msg.rid }}%20" class="a-thread-comment">{{ i18n("messages","reply.reply") }}</a> - </div> - <div class="msg-comment-target msg-comment-hidden"></div> - {% elseif visitor.uid == 0 %} - · <a href="#" class="a-login">{{ i18n("messages","reply.reply") }}</a> - </div> - {% else %} - </div> - {% endif %} - </div> - </li> - {% endfor %} -</ul> -{% endblock %} -{% block "column" %} -{% include "views/partial/usercolumn" %} -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/main/resources/templates/views/users.html b/juick-server/src/main/resources/templates/views/users.html deleted file mode 100644 index 702ba6b9..00000000 --- a/juick-server/src/main/resources/templates/views/users.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "layouts/default" %} -{% import "views/macros/tags" %} -{% block content %} -<div class="users"> - {% for u in users %} - <span> - <a href="/{{ u.name }}/"> - <img src="//i.juick.com/as/{{ u.uid }}.png" alt="{{ u.name }}"/> - {{ u.name }} - </a> - </span> - {% endfor %} -</div> -{% endblock %} -{% block "column" %} -{% include "views/partial/usercolumn" %} -{% endblock %}
\ No newline at end of file diff --git a/juick-server/src/test/java/com/juick/server/configuration/SwaggerConfiguration.java b/juick-server/src/test/java/com/juick/server/configuration/SwaggerConfiguration.java deleted file mode 100644 index 7c03f393..00000000 --- a/juick-server/src/test/java/com/juick/server/configuration/SwaggerConfiguration.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.juick.server.configuration; - -import com.google.common.base.Predicates; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import springfox.documentation.builders.PathSelectors; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.service.ApiInfo; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2; - -import java.util.Collections; - -@Configuration -@EnableSwagger2 -public class SwaggerConfiguration { - @Bean - public Docket api() { - return new Docket(DocumentationType.SWAGGER_2) - .host("api.juick.com") - .select() - .apis(Predicates.not(Predicates.or(RequestHandlerSelectors.basePackage("org.springframework.boot"), RequestHandlerSelectors.basePackage("com.juick.server.www")))) - .paths(PathSelectors.any()).build().apiInfo(new ApiInfo("Juick API", "Juick REST API Documentation", - "2.0", "https://juick.com/help/tos", null, - "AGPLv3", "https://www.gnu.org/licenses/agpl-3.0.html", Collections.emptyList())); - } -} diff --git a/juick-server/src/test/java/com/juick/server/tests/ServerTests.java b/juick-server/src/test/java/com/juick/server/tests/ServerTests.java deleted file mode 100644 index 1c643d86..00000000 --- a/juick-server/src/test/java/com/juick/server/tests/ServerTests.java +++ /dev/null @@ -1,1795 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package com.juick.server.tests; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.gargoylesoftware.htmlunit.CookieManager; -import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.css.StyleElement; -import com.gargoylesoftware.htmlunit.html.DomElement; -import com.gargoylesoftware.htmlunit.html.HtmlPage; -import com.jayway.jsonpath.JsonPath; -import com.juick.*; -import com.juick.model.AnonymousUser; -import com.juick.model.CommandResult; -import com.juick.model.PrivateChats; -import com.juick.model.TagStats; -import com.juick.server.*; -import com.juick.server.api.activity.model.Context; -import com.juick.server.api.activity.model.activities.Create; -import com.juick.server.api.activity.model.activities.Delete; -import com.juick.server.api.activity.model.activities.Follow; -import com.juick.server.api.activity.model.activities.Undo; -import com.juick.server.api.activity.model.objects.Note; -import com.juick.server.api.activity.model.objects.Person; -import com.juick.server.api.webfinger.model.Account; -import com.juick.server.api.xnodeinfo2.model.NodeInfo; -import com.juick.server.util.HttpUtils; -import com.juick.server.util.ImageUtils; -import com.juick.server.xmpp.helpers.XMPPStatus; -import com.juick.server.xmpp.s2s.ConnectionIn; -import com.juick.service.*; -import com.juick.service.component.MessageEvent; -import com.juick.util.DateFormattersHolder; -import com.juick.util.MessageUtils; -import com.mitchellbosecke.pebble.PebbleEngine; -import com.mitchellbosecke.pebble.error.PebbleException; -import com.mitchellbosecke.pebble.template.PebbleTemplate; -import org.apache.commons.codec.CharEncoding; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.collections4.IteratorUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.text.StringEscapeUtils; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.core.io.ClassPathResource; -import org.springframework.http.*; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.util.FileSystemUtils; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.xml.sax.SAXException; -import rocks.xmpp.addr.Jid; -import rocks.xmpp.core.session.Extension; -import rocks.xmpp.core.session.XmppSession; -import rocks.xmpp.core.session.XmppSessionConfiguration; -import rocks.xmpp.core.stanza.model.StanzaError; -import rocks.xmpp.core.stanza.model.client.ClientMessage; -import rocks.xmpp.core.stanza.model.errors.Condition; - -import javax.inject.Inject; -import javax.servlet.http.Cookie; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Marshaller; -import javax.xml.bind.Unmarshaller; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import java.io.*; -import java.net.Socket; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.sql.Timestamp; -import java.time.Instant; -import java.util.*; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.StreamSupport; - -import static junit.framework.TestCase.assertTrue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -/** - * Created by vitalyster on 25.11.2016. - */ -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) -@TestPropertySource(properties = { - "broken_ssl_hosts=localhost,serverstorageisfull.tld", - "ios_app_id=12345678.com.juick.ExampleApp", - "xmppbot_jid=juick@localhost/Juick", - "hostname=localhost", - "componentname=localhost", - "spring.jackson.default-property-inclusion=non_default" -}) -@AutoConfigureMockMvc -public class ServerTests { - - @Inject - private MockMvc mockMvc; - @Inject - private WebClient webClient; - @Inject - private TestRestTemplate restTemplate; - @Inject - private MessagesService messagesService; - @Inject - private UserService userService; - @Inject - private TagService tagService; - @Inject - private ObjectMapper jsonMapper; - @Inject - private XMPPServer server; - @Inject - private CommandsManager commandsManager; - @Inject - private XMPPConnection router; - @Inject - private SubscriptionService subscriptionService; - @Inject - private PrivacyQueriesService privacyQueriesService; - @Inject - private JdbcTemplate jdbcTemplate; - @Inject - private EmailService emailService; - @Inject - private PMQueriesService pmQueriesService; - @Inject - private TelegramService telegramService; - @Inject - private CrosspostService crosspostService; - @Inject - private ImagesService imagesService; - @Inject - private ServerManager serverManager; - @Inject - private KeystoreManager keystoreManager; - @Value("${hostname:localhost}") - private Jid jid; - @Value("${xmppbot_jid:juick@localhost}") - private Jid botJid; - @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String tmpDir; - @Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}") - private String imgDir; - @Inject - private PebbleEngine pebbleEngine; - @Value("${ios_app_id:}") - private String appId; - @Inject - private SignatureManager signatureManager; - @Inject - private ActivityPubManager activityPubManager; - - private static User ugnich, freefd, juick; - static String ugnichName, ugnichPassword, freefdName, freefdPassword, juickName, juickPassword; - URI emptyUri = URI.create(StringUtils.EMPTY); - - private static boolean isSetUp = false; - - @Before - public void setUp() throws Exception { - FileSystemUtils.deleteRecursively(Paths.get(imgDir, "p")); - FileSystemUtils.deleteRecursively(Paths.get(imgDir, "photos-1024")); - FileSystemUtils.deleteRecursively(Paths.get(imgDir, "photos-512")); - FileSystemUtils.deleteRecursively(Paths.get(imgDir, "ps")); - Files.createDirectory(Paths.get(imgDir, "p")); - Files.createDirectory(Paths.get(imgDir, "photos-1024")); - Files.createDirectory(Paths.get(imgDir, "photos-512")); - Files.createDirectory(Paths.get(imgDir, "ps")); - if (!isSetUp) { - ugnichName = "ugnich"; - ugnichPassword = "secret"; - freefdName = "freefd"; - freefdPassword = "MyPassw0rd!"; - juickName = "juick"; - juickPassword = "demo"; - int ugnichId = userService.createUser(ugnichName, ugnichPassword); - ugnich = userService.getUserByUID(ugnichId).orElseThrow(IllegalStateException::new); - int freefdId = userService.createUser(freefdName, freefdPassword); - freefd = userService.getUserByUID(freefdId).orElseThrow(IllegalStateException::new); - int juickId = userService.createUser(juickName, juickPassword); - juick = userService.getUserByUID(juickId).orElseThrow(IllegalStateException::new); - subscriptionService.subscribeUser(freefd, ugnich); - webClient.getOptions().setJavaScriptEnabled(false); - isSetUp = true; - } - } - @After - public void teardown() throws IOException { - FileSystemUtils.deleteRecursively(Paths.get(imgDir, "p")); - FileSystemUtils.deleteRecursively(Paths.get(imgDir, "photos-1024")); - FileSystemUtils.deleteRecursively(Paths.get(imgDir, "photos-512")); - FileSystemUtils.deleteRecursively(Paths.get(imgDir, "ps")); - } - @Test - public void getMyFeed() { - int mid0 = messagesService.createMessage(ugnich.getUid(), "test", null, null); - int mid2 = messagesService.createMessage(ugnich.getUid(), "test2", null, null); - List<Integer> freefdFeed = messagesService.getMyFeed(freefd.getUid(), 0, false); - assertThat(freefdFeed.get(0), equalTo(mid2)); - int tonyaid = userService.createUser("Tonya", "secret"); - int mid3 = messagesService.createMessage(tonyaid, "test3", null, null); - messagesService.recommendMessage(mid3, ugnich.getUid()); - assertThat(messagesService.getMyFeed(freefd.getUid(), 0, false).get(0), equalTo(mid2)); - assertThat(messagesService.getMyFeed(freefd.getUid(), 0, true).get(0), equalTo(mid3)); - assertThat(messagesService.getMyFeed(freefd.getUid(), mid2, true).get(0), equalTo(mid0)); - assertThat(messagesService.recommendMessage(mid0, ugnich.getUid()), equalTo(MessagesService.RecommendStatus.Added)); - assertThat(messagesService.getMessage(mid0).getLikes(), equalTo(1)); - assertThat(messagesService.recommendMessage(mid0, ugnich.getUid()), equalTo(MessagesService.RecommendStatus.Deleted)); - assertThat(messagesService.getMessage(mid0).getLikes(), equalTo(0)); - assertThat(messagesService.getAll(ugnich.getUid(), 0).get(0), equalTo(mid3)); - Tag yoTag = tagService.getTag("yoyo", true); - assertThat(tagService.getTag("YOYO", false), equalTo(yoTag)); - int mid = messagesService.createMessage(ugnich.getUid(), "yo", null, Collections.singletonList(yoTag)); - Message msg = messagesService.getMessage(mid); - List<User> subscribers = subscriptionService.getSubscribedUsers(ugnich.getUid(), msg); - - telegramService.createTelegramUser(12345, "freefd"); - String loginhash = jdbcTemplate.queryForObject("SELECT loginhash FROM telegram where tg_id=?", - String.class, 12345); - crosspostService.setTelegramUser(loginhash, freefd.getUid()); - - List<Long> telegramSubscribers = telegramService.getTelegramIdentifiers(subscribers); - assertThat(subscribers.size(), equalTo(1)); - assertThat(subscribers.size(), equalTo(telegramSubscribers.size())); - assertThat(subscribers.get(0).getUid(), equalTo(freefd.getUid())); - tagService.blacklistTag(freefd, yoTag); - List<User> subscribers2 = subscriptionService.getSubscribedUsers(ugnich.getUid(), msg); - assertThat(subscribers2.size(), equalTo(0)); - assertThat(telegramService.getTelegramIdentifiers(subscribers2).size(), equalTo(0)); - tagService.blacklistTag(freefd, yoTag); - assertThat(subscriptionService.getSubscribedUsers(ugnich.getUid(), msg).size(), equalTo(1)); - subscriptionService.unSubscribeUser(freefd, ugnich); - assertThat(subscriptionService.getSubscribedUsers(ugnich.getUid(), msg).size(), equalTo(0)); - Message mentionMessage = new Message(); - mentionMessage.setText("@freefd - dick"); - assertThat(subscriptionService.getSubscribedUsers(ugnich.getUid(), mentionMessage).size(), equalTo(1)); - subscriptionService.subscribeUser(freefd, ugnich); - assertThat(subscriptionService.getSubscribedUsers(ugnich.getUid(), mentionMessage).size(), equalTo(1)); - } - @Test - public void pmTests() { - pmQueriesService.createPM(freefd.getUid(), ugnich.getUid(), "hello"); - Message pm = pmQueriesService.getPMMessages(ugnich.getUid(), freefd.getUid()).get(0); - assertThat(pm.getText(), equalTo("hello")); - assertThat(pm.getUser().getUid(), equalTo(freefd.getUid())); - } - - @Test - public void messageTests() { - int user_id = userService.createUser("mmmme", "secret"); - User user = userService.getUserByUID(user_id).orElse(AnonymousUser.INSTANCE); - assertEquals("it should be me", "mmmme", user.getName()); - int mid = messagesService.createMessage(user_id, "yo", null, new ArrayList<>()); - Message msg = messagesService.getMessage(mid); - assertEquals("yo", msg.getText()); - User me = msg.getUser(); - assertEquals("mmmme", me.getName()); - assertEquals("mmmme", messagesService.getMessageAuthor(mid).getName()); - int tagID = tagService.createTag("weather"); - Tag tag = tagService.getTag(tagID); - List<Tag> tagList = new ArrayList<>(); - tagList.add(tag); - int mid2 = messagesService.createMessage(user_id, "yo2", null, tagList); - Message msg2 = messagesService.getMessage(mid2); - assertEquals(1, msg2.getTags().size()); - assertEquals("we already have ugnich", -1, userService.createUser("ugnich", "x")); - int ugnich_id = userService.createUser("hugnich", "x"); - User ugnich = userService.getUserByUID(ugnich_id).orElse(AnonymousUser.INSTANCE); - int rid = messagesService.createReply(msg2.getMid(), 0, ugnich, "bla-bla", null); - assertEquals(1, rid); - assertThat(msg2.getTo(), equalTo(AnonymousUser.INSTANCE)); - Message reply = messagesService.getReply(msg2.getMid(), rid); - assertThat(reply.getTo().getName(), equalTo(user.getName())); - List<Message> replies = messagesService.getReplies(user, msg2.getMid()); - assertThat(replies.size(), equalTo(1)); - assertThat(replies.get(0), equalTo(reply)); - int ridToReply = messagesService.createReply(msg2.getMid(), 1, ugnich, "blax2", null); - Message reply2 = messagesService.getReply(msg2.getMid(), ridToReply); - assertThat(reply.getTo().getName(), equalTo(user.getName())); - List<Message> replies2 = messagesService.getReplies(user, msg2.getMid()); - assertThat(replies2.size(), equalTo(2)); - assertThat(replies2.get(1), equalTo(reply2)); - - Message msg3 = messagesService.getMessage(mid2); - - assertEquals(2, msg3.getReplies()); - assertEquals("weather", msg3.getTags().get(0).getName()); - assertEquals(ugnich.getUid(), userService.checkPassword(ugnich.getName(), "x")); - assertEquals(-1, userService.checkPassword(ugnich.getName(), "xy")); - - subscriptionService.subscribeMessage(msg, user); - subscriptionService.subscribeMessage(msg, ugnich); - int reply_id = messagesService.createReply(msg.getMid(), 0, ugnich, "comment", null); - assertEquals(1, subscriptionService.getUsersSubscribedToComments(msg, - messagesService.getReply(msg.getMid(), reply_id)).size()); - assertThat(messagesService.getDiscussions(ugnich.getUid(), 0L).get(0), - equalTo(msg.getMid())); - messagesService.deleteMessage(user_id, mid); - messagesService.deleteMessage(user_id, mid2); - String htmlTagName = ">_<"; - Tag htmlTag = tagService.getTag(htmlTagName, true); - TagStats htmlTagStats = new TagStats(); - htmlTagStats.setTag(htmlTag); - String dbTagName = jdbcTemplate.queryForObject("select name from tags where name=?", String.class, htmlTagName); - assertEquals("db tags should not be escaped", dbTagName, htmlTag.getName()); - int mid4 = messagesService.createMessage(user_id, "yoyoyo", null, null); - Message msg4 = messagesService.getMessage(mid4); - assertEquals("tags string should be empty", StringUtils.EMPTY, MessageUtils.getTagsString(msg4)); - messagesService.deleteMessage(user_id, mid4); - } - public ExpectedException exception = ExpectedException.none(); - - @Test - public void likeTypeStatsTests(){ - int user_id = userService.createUser("dsdss", "secret"); - final int freefdId = freefd.getUid(); - int mid = messagesService.createMessage(user_id, "yo", null, new ArrayList<>()); - messagesService.likeMessage(mid, freefdId , 2); - messagesService.likeMessage(mid, freefdId,2); - messagesService.likeMessage(mid, freefdId,3); - messagesService.likeMessage(mid, freefdId,1); - - Message msg4 = messagesService.getMessage(mid); - assertThat(msg4.getLikes(), equalTo(1)); - - Assert.assertEquals(2, msg4.getReactions().stream().filter(r -> r.getId() == 2) - .findFirst().orElseThrow(IllegalStateException::new).getCount()); - Assert.assertEquals(1,msg4.getReactions().stream().filter(r -> r.getId() == 3) - .findFirst().orElseThrow(IllegalStateException::new).getCount()); - } - @Test - public void lastJidShouldNotBeDeleted() { - int ugnich_id = userService.createUser("hugnich2", "x"); - jdbcTemplate.update("INSERT INTO jids(user_id,jid,active) VALUES(?,?,?)", ugnich_id, "firstjid@localhost", 1); - jdbcTemplate.update("INSERT INTO jids(user_id,jid,active) VALUES(?,?,?)", ugnich_id, "secondjid@localhost", 1); - assertThat(userService.deleteJID(ugnich_id, "secondjid@localhost"), equalTo(true)); - assertThat(userService.deleteJID(ugnich_id, "firstjid@localhost"), equalTo(false)); - } - @Test - public void lastEmailShouldNotBeDeleted() { - int ugnich_id = userService.createUser("hugnich3", "x"); - jdbcTemplate.update("INSERT INTO emails(user_id,email) VALUES(?,?)", ugnich_id, "first@localhost"); - jdbcTemplate.update("INSERT INTO emails(user_id,email) VALUES(?,?)", ugnich_id, "second@localhost"); - assertThat(emailService.deleteEmail(ugnich_id, "second@localhost"), equalTo(true)); - assertThat(emailService.deleteEmail(ugnich_id, "first@localhost"), equalTo(false)); - } - @Test - public void messageUpdatedTimeShouldMatchLastReplyTime() throws InterruptedException { - int ugnich_id = userService.createUser("hugnich4", "x"); - int mid = messagesService.createMessage(ugnich_id, "yo", null, null); - Instant ts = jdbcTemplate.queryForObject("SELECT updated FROM messages WHERE message_id=?", - Timestamp.class, mid).toInstant(); - Thread.sleep(1000); - int rid = messagesService.createReply(mid, 0, ugnich, "people", null); - Instant rts = jdbcTemplate.queryForObject("SELECT updated FROM messages WHERE message_id=?", - Timestamp.class, mid).toInstant(); - assertThat(rts, greaterThan(ts)); - Message msg = messagesService.getReply(mid, rid); - assertThat(rts, equalTo(msg.getTimestamp())); - messagesService.deleteMessage(ugnich_id, mid); - } - - @Test - public void testAllUnAuthorized() throws Exception { - mockMvc.perform(get("/api/")) - .andExpect(status().isMovedPermanently()); - - mockMvc.perform(get("/api/auth")) - .andExpect(status().isUnauthorized()); - - mockMvc.perform(get("/api/home")) - .andExpect(status().isUnauthorized()); - - mockMvc.perform(get("/api/messages/recommended")) - .andExpect(status().isUnauthorized()); - - mockMvc.perform(get("/api/messages/set_privacy")) - .andExpect(status().isUnauthorized()); - } - - @Test - public void homeTestWithMessages() throws Exception { - String msgText = "Привет, я - Угнич"; - CommandResult result = commandsManager.processCommand(ugnich, msgText, URI.create("http://static.juick.com/settings/facebook.png")); - int mid = result.getNewMessage().get().getMid(); - Message msg = messagesService.getMessage(mid); - tagService.createTag("тест"); - mockMvc.perform( - get("/api/home") - .with(httpBasic(ugnichName, ugnichPassword))) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)) - .andExpect(jsonPath("$[0].mid", is(msg.getMid()))) - .andExpect(jsonPath("$[0].timestamp", - is(DateFormattersHolder.getMessageFormatterInstance().format(msg.getTimestamp())))) - .andExpect(jsonPath("$[0].body", is(msg.getText()))) - .andExpect(jsonPath("$[0].attachment.url", - is(String.format("https://i.juick.com/p/%d.png", msg.getMid())))) - .andExpect(jsonPath("$[0].attachment.small.url", - is(String.format("https://i.juick.com/photos-512/%d.png", msg.getMid())))) - .andExpect(jsonPath("$[0].user.avatar").doesNotExist()); - } - - @Test - public void homeTestWithMessagesAndRememberMe() throws Exception { - String ugnichHash = userService.getHashByUID(ugnich.getUid()); - mockMvc.perform( - get("/api/home") - .with(httpBasic(ugnichName, ugnichPassword))) - .andExpect(status().isOk()) - .andReturn(); - - mockMvc.perform(get("/api/home") - .param("hash", ugnichHash)) - .andExpect(status().isOk()); - } - - @Test - public void homeTestWithMessagesAndSimpleCors() throws Exception { - mockMvc.perform( - get("/api/home") - .with(httpBasic(ugnichName, ugnichPassword)) - .header("Origin", "http://api.example.net")) - .andExpect(status().isOk()) - .andExpect(header().string("Access-Control-Allow-Origin", "*")); - } - - @Test - public void homeTestWithPreflightCors() throws Exception { - mockMvc.perform( - options("/api/home") - .with(httpBasic(ugnichName, ugnichPassword)) - .header("Origin", "http://api.example.net") - .header("Access-Control-Request-Method", "POST") - .header("Access-Control-Request-Headers", "X-PINGOTHER, Content-Type")) - .andExpect(status().isOk()) - .andExpect(header().string("Access-Control-Allow-Origin", "*")) - .andExpect(header().string("Access-Control-Allow-Methods", "POST,GET,PUT,OPTIONS,DELETE")) - .andExpect(header().string("Access-Control-Allow-Headers", "X-PINGOTHER, Content-Type")); - } - - @Test - public void anonymousApis() throws Exception { - - - mockMvc.perform(get("/api/messages")) - .andExpect(status().isOk()); - - mockMvc.perform(get("/api/users") - .param("uname", "ugnich") - .param("uname", "freefd")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)) - .andExpect(jsonPath("$", hasSize(2))); - } - - @Test - public void messagesUrlTest() throws Exception { - int user_id = userService.createUser("dsds4345", "secret"); - - String freefdHash = userService.getHashByUID(freefd.getUid()); - System.out.println("user_id"+ user_id); - String userIdHash = userService.getHashByUID(user_id); - final int freefdId = freefd.getUid(); - int mid = messagesService.createMessage(user_id, "yo", null, new ArrayList<>()); - messagesService.likeMessage(mid, freefdId, 2 ); - messagesService.likeMessage(mid, freefdId, 2 ); - messagesService.likeMessage(mid, freefdId, 3 ); - - mockMvc.perform(get("/api/messages?"+ "hash=" + userIdHash)) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)) - .andExpect((jsonPath("$[0].reactions[?(@.id == 3)].count", - is(Collections.singletonList(1))))) - .andExpect((jsonPath("$[0].reactions[?(@.id == 2)].count", - is(Collections.singletonList(2))))); - - mockMvc.perform(get("/api/reactions?hash=" + userIdHash)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)) - .andExpect(jsonPath("$.length()", is(7))); - } - - @Test - public void tags() throws Exception { - Tag weather = tagService.getTag("weather", true); - Tag yo = tagService.getTag("yo", true); - messagesService.createMessage(ugnich.getUid(), "text", null, Arrays.asList(yo, weather)); - messagesService.createMessage(freefd.getUid(), "text2", null, Collections.singletonList(yo)); - MvcResult result = mockMvc.perform(get("/api/tags")) - .andExpect(status().isOk()) - .andReturn(); - List<TagStats> tagsFromApi = jsonMapper.readValue(result.getResponse().getContentAsString(), - new TypeReference<List<TagStats>>(){}); - TagStats yoStats = tagsFromApi.stream().filter(t -> t.getTag().getName().equals("yo")).findFirst().get(); - assertThat(yoStats.getUsageCount(), is(2)); - MvcResult result2 = mockMvc.perform(get("/api/tags") - .param("user_id", String.valueOf(ugnich.getUid()))) - .andExpect(status().isOk()) - .andReturn(); - List<TagStats> ugnichTagsFromApi = jsonMapper.readValue(result2.getResponse().getContentAsString(), - new TypeReference<List<TagStats>>(){}); - TagStats yoUgnichStats = ugnichTagsFromApi.stream().filter(t -> t.getTag().getName().equals("yo")).findFirst().get(); - assertThat(yoUgnichStats.getUsageCount(), is(1)); - } - - @Test - public void postWithReferer() throws Exception { - mockMvc.perform(post("/api/post") - .param("body", "yo") - .with(httpBasic(ugnichName, ugnichPassword))) - .andExpect(status().isOk()); - } - @Test - public void threadWithEphemeralNumberShouldReturn404() throws Exception { - mockMvc.perform(get("/api/thread").param("mid", "999999999") - .with(httpBasic(ugnichName, ugnichPassword))).andExpect(status().is4xxClientError()); - } - @Test - public void performRequestsWithIssuedToken() throws Exception { - String ugnichHash = userService.getHashByUID(ugnich.getUid()); - mockMvc.perform(get("/api/home")).andExpect(status().isUnauthorized()); - mockMvc.perform(get("/api/auth")) - .andExpect(status().isUnauthorized()); - mockMvc.perform(get("/api/auth").with(httpBasic(ugnichName, "wrongpassword"))) - .andExpect(status().isUnauthorized()); - MvcResult result = mockMvc.perform(get("/api/auth").with(httpBasic(ugnichName, ugnichPassword))) - .andExpect(status().isOk()) - .andReturn(); - String authHash = result.getResponse().getContentAsString(); - assertThat(authHash, equalTo(ugnichHash)); - mockMvc.perform(get("/api/home").param("hash", ugnichHash)).andExpect(status().isOk()); - } - @Test - public void registerForNotificationsTests() throws Exception { - String token = "123456"; - ExternalToken registration = new ExternalToken(null, "apns", token, null); - mockMvc.perform(put("/api/notifications").with(httpBasic(ugnichName, ugnichPassword)) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .content(jsonMapper.writeValueAsBytes(Collections.singletonList(registration)))) - .andExpect(status().isOk()); - MvcResult result = mockMvc.perform(get("/api/notifications") - .param("uid", String.valueOf(ugnich.getUid())) - .with(httpBasic(juickName, juickPassword))) - .andExpect(status().isOk()) - .andReturn(); - List<User> user = jsonMapper.readValue(result.getResponse().getContentAsString(), - new TypeReference<List<User>>() { - }); - assertThat(user.get(0).getTokens().get(0).getToken(), equalTo(token)); - } - @Test - public void tg2juickLinks() { - UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://juick.com/m/123456#23").build(); - assertThat(uriComponents.getPath().substring(3), is("123456")); - assertThat(uriComponents.getFragment(), is("23")); - } - @Test - public void notificationsTokensTest() throws Exception { - List<ExternalToken> tokens = Collections.singletonList(new ExternalToken(null, "gcm", "123456", null)); - mockMvc.perform(delete("/api/notifications").with(httpBasic(ugnichName, ugnichPassword)) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .content(jsonMapper.writeValueAsBytes(tokens))).andExpect(status().isForbidden()); - mockMvc.perform(delete("/api/notifications").with(httpBasic(juickName, juickPassword)) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .content(jsonMapper.writeValueAsBytes(tokens))).andExpect(status().isOk()); - } - @Test - public void notificationsSettingsAllowedOnlyForServiceUser() throws Exception { - CommandResult result = commandsManager.processCommand(ugnich, "yo", emptyUri); - String stringValueOfMid = String.valueOf(result.getNewMessage().get().getMid()); - mockMvc.perform(get("/api/notifications").with(httpBasic(juickName, juickPassword)) - .param("mid", stringValueOfMid).param("uid", String.valueOf(ugnich.getUid()))).andExpect(status().isOk()); - mockMvc.perform(get("/api/notifications") - .param("mid", stringValueOfMid).param("uid", String.valueOf(ugnich.getUid()))).andExpect(status().isUnauthorized()); - } - @Test - public void topTest() { - int topmid = messagesService.createMessage(ugnich.getUid(), "top message", null, null); - IntStream.rangeClosed(6, 12).forEach(i -> { - User next = new User(); - next.setUid(i); - messagesService.createReply(topmid, 0, next, "yo", null); - }); - - List<Integer> topCandidates = messagesService.getPopularCandidates(); - assertThat(topCandidates.size(), is(1)); - assertThat(topCandidates.get(0), is(topmid)); - Tag juickTag = tagService.getTag("juick", false); - assertThat(juickTag.TID, is(2)); - tagService.updateTags(topmid, Collections.singletonList(juickTag)); - assertThat(messagesService.getPopularCandidates().isEmpty(), is(true)); - tagService.updateTags(topmid, Collections.singletonList(juickTag)); - assertThat(messagesService.getPopularCandidates().isEmpty(), is(false)); - jdbcTemplate.update("INSERT INTO tags(tag_id, name) VALUES(805, 'NSFW')"); - Tag nsfw = tagService.getTag("NSFW", false); - assertThat(nsfw.TID, equalTo(805)); - tagService.updateTags(topmid, Collections.singletonList(nsfw)); - assertThat(messagesService.getPopularCandidates().isEmpty(), is(true)); - } - @Test - public void inReplyToScannerTest() { - String header = "<123456.56@juick.com>"; - Scanner headerScanner = new Scanner(header).useDelimiter(EmailManager.MSGID_PATTERN); - int mid = Integer.parseInt(headerScanner.next()); - int rid = Integer.parseInt(headerScanner.next()); - assertThat(mid, equalTo(123456)); - assertThat(rid, equalTo(56)); - } - @Test - public void lastMessagesTest() throws Exception { - mockMvc.perform( - get("/rss/")) - .andExpect(status().isOk()) - .andExpect(content().contentType("application/rss+xml;charset=UTF-8")) - .andExpect(xpath("/rss/channel/description").string("The latest messages at Juick")); - } - @Test - public void statusPageIsUp() throws Exception { - mockMvc.perform(get("/api/status").with(httpBasic(ugnichName, ugnichPassword))).andExpect(status().isOk()); - assertThat(server.getJid(), equalTo(jid)); - } - @Test - public void botIsUpAndProcessingResourceConstraints() throws Exception { - jdbcTemplate.execute("DELETE FROM users WHERE nick='renha'"); - int renhaId = userService.createUser("renha", "umnnbt"); - Jid from = Jid.of("renha@serverstorageisfull.tld"); - jdbcTemplate.update("INSERT INTO jids(user_id,jid,active) VALUES(?,?,?)", - renhaId, from.toEscapedString(), 1); - rocks.xmpp.core.stanza.model.Message xmppMessage = new rocks.xmpp.core.stanza.model.Message(); - xmppMessage.setType(rocks.xmpp.core.stanza.model.Message.Type.ERROR); - xmppMessage.setFrom(from); - xmppMessage.setTo(botJid); - StanzaError err = new StanzaError(StanzaError.Type.CANCEL, Condition.RESOURCE_CONSTRAINT); - xmppMessage.setError(err); - Function<Integer, Boolean> isActive = f -> jdbcTemplate.queryForObject("SELECT active FROM jids WHERE user_id=?", Integer.class, f) == 1; - assertThat(isActive.apply(renhaId), equalTo(true)); - ClientMessage result = router.incomingMessage(xmppMessage); - assertNull(result); - assertThat(isActive.apply(renhaId), equalTo(false)); - xmppMessage.setError(null); - xmppMessage.setType(rocks.xmpp.core.stanza.model.Message.Type.CHAT); - xmppMessage.setBody("On"); - result = router.incomingMessage(xmppMessage); - assertThat(result.getBody(), equalTo("XMPP notifications are activated")); - assertTrue(isActive.apply(renhaId)); - xmppMessage.setBody("*test test"); - result = router.incomingMessage(xmppMessage); - assertThat(result.getBody(), startsWith("New message posted")); - xmppMessage.setFrom(from); - xmppMessage.setBody("PING"); - result = router.incomingMessage(xmppMessage); - assertThat(result.getBody(), equalTo("PONG")); - int secretlySadId = userService.createUser("secretlysad", "bbk"); - xmppMessage.setTo(botJid.withLocal("secretlysad")); - xmppMessage.setBody("What's up?"); - result = router.incomingMessage(xmppMessage); - assertThat(result.getBody(), startsWith("Private message sent")); - String xml = "<message xmlns=\"jabber:server\" from=\"" + botJid + "\" to=\"renha@serverstorageisfull.tld\" type=\"chat\"><body>@yo:\n" + - "343432434\n" + - "\n" + - "#2 http://juick.com/2</body><thread>juick-2</thread><juick xmlns=\"http://juick.com/message\" mid=\"2\" privacy=\"1\" quote=\"\" replyto=\"0\" rid=\"0\" ts=\"2018-04-10 06:58:23\"><body>343432434</body><updated></updated><user xmlns=\"http://juick.com/user\" uname=\"yo\" uid=\"2\"></user></juick><nick xmlns=\"http://jabber.org/protocol/nick\">@yo</nick></message>"; - result = router.incomingMessage((rocks.xmpp.core.stanza.model.Message)server.parse(xml)); - String active = "<message xmlns=\"jabber:server\" from=\"renha@serverstorageisfull.tld/work\" to=\"" - + botJid.asBareJid().toEscapedString() + "\" type=\"chat\" id=\"purple553384c6\"><active xmlns=\"http://jabber.org/protocol/chatstates\"></active></message>"; - result = router.incomingMessage((rocks.xmpp.core.stanza.model.Message)server.parse(active)); - xmppMessage.setFrom(botJid); - // TODO: assert events - } - @Test - public void botCommandsTests() throws Exception { - assertThat(commandsManager.processCommand(AnonymousUser.INSTANCE, "PING", emptyUri).getText(), is("PONG")); - // subscription commands have two lines, others have 1 - assertThat(commandsManager.processCommand(AnonymousUser.INSTANCE, "help", emptyUri).getText().split("\n").length, is(32)); - } - - @Test - public void protocolTests() throws Exception { - int uid = userService.createUser("me", "secret"); - User user = userService.getUserByUID(uid).orElse(AnonymousUser.INSTANCE); - Tag yo = tagService.getTag("yo", true); - Message msg = commandsManager.processCommand(user, "*yo yoyo", URI.create("http://static.juick.com/settings/facebook.png")).getNewMessage().get(); - assertThat(msg.getAttachmentType(), is("png")); - Message msgreply = commandsManager.processCommand(user, "#" + msg.getMid() + " yyy", HttpUtils.downloadImage(URI.create("http://static.juick.com/settings/xmpp.png").toURL(), tmpDir)).getNewMessage().get(); - assertThat(msgreply.getAttachmentType(), equalTo("png")); - assertEquals("text should match", "yoyo", - messagesService.getMessage(msg.getMid()).getText()); - assertEquals("tag should match", "yo", - tagService.getMessageTags(msg.getMid()).get(0).getTag().getName()); - CommandResult yoyoMsg = commandsManager.processCommand(user, "*yo", URI.create("http://static.juick.com/settings/facebook.png")); - assertTrue(yoyoMsg.getNewMessage().isPresent()); - assertThat(yoyoMsg.getNewMessage().get().getTags().get(0), is(yo)); - Message msg2 = yoyoMsg.getNewMessage().get(); - int mid = msg2.getMid(); - Timestamp last = jdbcTemplate.queryForObject("SELECT lastmessage FROM users WHERE id=?", Timestamp.class, user.getUid()); - assertThat(last.toInstant(), equalTo(yoyoMsg.getNewMessage().get().getTimestamp())); - assertEquals("should be message", true, - commandsManager.processCommand(user, String.format("#%d", mid), emptyUri).getText().startsWith("@me")); - int readerUid = userService.createUser("dummyReader", "dummySecret"); - User readerUser = userService.getUserByUID(readerUid).orElse(AnonymousUser.INSTANCE); - assertThat(commandsManager.processCommand(readerUser, "s", emptyUri).getText().startsWith("You are subscribed to"), is(true)); - assertThat(commandsManager.processCommand(readerUser, "S", emptyUri).getText().startsWith("You are subscribed to"), is(true)); - assertEquals("should be subscribed", "Subscribed", - commandsManager.processCommand(readerUser, "S #" + mid, emptyUri).getText()); - assertEquals("should be favorited", "Message is added to your recommendations", - commandsManager.processCommand(readerUser, "! #" + mid, emptyUri).getText()); - int rid = messagesService.createReply(mid, 0, user, "comment", null); - assertEquals("number of subscribed users should match", 1, - subscriptionService.getUsersSubscribedToComments( - messagesService.getMessage(mid), - messagesService.getReply(mid, rid)).size()); - privacyQueriesService.blacklistUser(user, readerUser); - assertEquals("number of subscribed users should match", 0, - subscriptionService.getUsersSubscribedToComments( - messagesService.getMessage(mid), - messagesService.getReply(mid, rid)).size()); - assertEquals("number of subscribed users should match", 1, - subscriptionService.getUsersSubscribedToComments( - messagesService.getMessage(mid), - messagesService.getReply(mid, rid), true).size()); - assertEquals("should be subscribed", "Subscribed to @" + user.getName(), - commandsManager.processCommand(readerUser, "S @" + user.getName(), emptyUri) - .getText()); - List<User> friends = userService.getUserFriends(readerUid); - assertEquals("number of friend users should match", 2, - friends.size()); - assertEquals("number of reader users should match", 1, - userService.getUserReaders(uid).size()); - String expectedSecondReply = "Reply posted.\n#" + mid + "/2 " - + "https://juick.com/m/" + mid + "#2"; - String expectedThirdReply = "Reply posted.\n#" + mid + "/3 " - + "https://juick.com/m/" + mid + "#3"; - assertEquals("should be second reply", expectedSecondReply, - commandsManager.processCommand(user, "#" + mid + " yoyo", URI.create("http://static.juick.com/settings/facebook.png")).getText()); - assertEquals("should be third reply", expectedThirdReply, - commandsManager.processCommand(user, " \t\n #" + mid + "/2 ", - URI.create("http://static.juick.com/settings/facebook.png")).getText()); - Message reply = messagesService.getReplies(user, mid).stream().filter(m -> m.getRid() == 3).findFirst() - .orElse(new Message()); - Timestamp lastreply = jdbcTemplate.queryForObject("SELECT lastmessage FROM users WHERE id=?", Timestamp.class, user.getUid()); - assertThat(lastreply.toInstant(), equalTo(reply.getTimestamp())); - assertEquals("should be reply to second comment", 2, reply.getReplyto()); - assertEquals("tags should NOT be updated", "It is not your message", - commandsManager.processCommand(readerUser, "#" + mid + " *yo *there", emptyUri) - .getText()); - assertEquals("tags should be updated", "Tags are updated", - commandsManager.processCommand(user, "#" + mid + " *there", emptyUri).getText()); - assertEquals("number of tags should match", 2, - tagService.getMessageTags(mid).size()); - assertThat(messagesService.getMessage(mid).getTags().size(), is(2)); - assertEquals("should be blacklisted", "Tag added to your blacklist", - commandsManager.processCommand(readerUser, "BL *there", emptyUri).getText()); - assertEquals("number of subscribed users should match", 0, - subscriptionService.getSubscribedUsers(uid, msg2).size()); - assertEquals("tags should be updated", "Tags are updated", - commandsManager.processCommand(user, "#" + mid + " *there", emptyUri).getText()); - assertEquals("number of tags should match", 1, - tagService.getMessageTags(mid).size()); - int taggerUid = userService.createUser("dummyTagger", "dummySecret"); - User taggerUser = userService.getUserByUID(taggerUid).orElse(AnonymousUser.INSTANCE); - assertEquals("should be subscribed", "Subscribed", - commandsManager.processCommand(taggerUser, "S *yo", emptyUri).getText()); - assertEquals("number of subscribed users should match", 2, - subscriptionService.getSubscribedUsers(uid, msg2).size()); - assertEquals("should be unsubscribed", "Unsubscribed from yo", - commandsManager.processCommand(taggerUser, "U *yo", emptyUri).getText()); - assertEquals("number of subscribed users should match", 1, - subscriptionService.getSubscribedUsers(uid, msg2).size()); - assertEquals("number of readers should match", 1, - userService.getUserReaders(uid).size()); - String readerFeed = commandsManager.processCommand(readerUser, "#", emptyUri).getText(); - assertTrue("description should match", readerFeed.startsWith("Your feed")); - assertEquals("should be unsubscribed", "Unsubscribed from @" + user.getName(), - commandsManager.processCommand(readerUser, "U @" + user.getName(), emptyUri) - .getText()); - assertEquals("number of readers should match", 0, - userService.getUserReaders(uid).size()); - assertEquals("number of friends should match", 1, - userService.getUserFriends(uid).size()); - assertEquals("should be unsubscribed", "Unsubscribed from #" + mid, - commandsManager.processCommand(readerUser, "u #" + mid, emptyUri).getText()); - assertEquals("number of subscribed users should match", 0, - subscriptionService.getUsersSubscribedToComments(messagesService.getMessage(mid), - messagesService.getReply(mid, rid)).size()); - assertNotEquals("should NOT be deleted", String.format("Message %s deleted", mid), - commandsManager.processCommand(readerUser, "D #" + mid, emptyUri).getText()); - assertEquals("should be deleted", "Message deleted", - commandsManager.processCommand(user, "D #" + mid, emptyUri).getText()); - assertEquals("should be not found", "Message not found", - commandsManager.processCommand(user, "#" + mid, emptyUri).getText()); - - String expectedCodeMessage = "some smelly code goes here\n" + - "> void main(void** args) {\n" + - "> }"; - String codeAndTags = "*code\n" + expectedCodeMessage; - Message codeAndTagsMessage = commandsManager.processCommand(user, codeAndTags, emptyUri).getNewMessage().get(); - List<Tag> codeAndTagsTags = codeAndTagsMessage.getTags(); - assertEquals("expected single tag", 1, - codeAndTagsTags.size()); - assertEquals("the single tag should be the 'code'", "code", - codeAndTagsTags.get(0).getName()); - assertEquals("and the message should be with a C-code and without tags", expectedCodeMessage, - codeAndTagsMessage.getText()); - CommandResult result = commandsManager.processCommand(user, "*one *two *three *four *five *six test", emptyUri); - assertThat(result.getNewMessage(), is(Optional.empty())); - assertThat(result.getText(), is("Sorry, 5 tags maximum.")); - result = commandsManager.processCommand(user, String.format("#%d *one *two *three *four *five *six", msg.getMid()), emptyUri); - assertThat(result.getNewMessage(), is(Optional.empty())); - assertThat(result.getText(), is("Tags are NOT updated (5 tags maximum?)")); - result = commandsManager.processCommand(user, "I'm very smart to post my login url there" + - "<https://juick.com/settings?hash=VTYZkKV8FWkmu6g1>", emptyUri); - assertThat(result.getNewMessage().isPresent(), is(true)); - assertFalse(result.getNewMessage().get().getText().contains("VTYZkKV8FWkmu6g1")); - result = commandsManager.processCommand(user, "*корм *juick_ppl *рационализм *? *мюсли а сколько микроморт в дневной порции сверхмюслей?", emptyUri); - assertTrue(result.getNewMessage().isPresent()); - String tags = "*Juick *Google *Google Play"; - String data = "Вчера отправлял *NSFW постинг в топ :)"; - result = commandsManager.processCommand(user, String.format("%s %s", tags, data), emptyUri); - assertThat(result.getNewMessage().get().getTags().size(), equalTo(3)); - assertThat(result.getNewMessage().get().getText(), equalTo(data)); - tags = "*\u041a\u0438\u0435\u0432 *\u044d\u043a\u043e\u043b\u043e\u0433\u0438\u044f"; - data = "* \u043c\u0443\u0441\u043e\u0440\\n\u0423 \u043c\u0435\u043d\u044f \u043a\u0430\u0436\u0434\u0443\u044e \u043d\u0435\u0434\u0435\u043b\u044e \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044f \u043f\u043e 4-5 \u0431\u0443\u0442\u044b\u043b\u043e\u043a 1,5\u043b \u041f\u0415\u0422. \u041c\u043d\u0435 \u0433\u0435\u043c\u043e\u0440\u043d\u043e \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u043f\u043e \u043a\u0438\u043b\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0438\u043b\u0438 \u043f\u043e 5\u043a\u0433 \u044d\u0442\u043e\u0433\u043e \u043c\u0443\u0441\u043e\u0440\u0430, \u0447\u0442\u043e\u0431\u044b \u0432\u0435\u0437\u0442\u0438 \u0435\u0433\u043e \u0435\u0449\u0435 \u043a\u0443\u0434\u0430-\u0442\u043e.\\n\u041d\u0435, \u043d\u0443 \u0435\u0441\u0442\u044c \u043b\u044e\u0434\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043e\u0431\u0438\u0440\u0430\u044e\u0442 \u0432\u0442\u043e\u0440\u0441\u044b\u0440\u044c\u0435 \u043f\u043e \u043c\u0443\u0441\u043e\u0440\u043a\u0430\u043c, \u0441\u0432\u0430\u043b\u043a\u0430\u043c, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0434\u0435\u043d\u044c\u0433\u0438 \u043d\u0443\u0436\u043d\u044b. \u0418 \u0431\u044b\u0432\u0430\u044e\u0442 \u0441\u0442\u043e\u044f\u0442 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b-\u043a\u043b\u0435\u0442\u043a\u0438 \u0434\u043b\u044f \u043f\u043b\u0430\u0441\u0442\u0438\u043a\u0430, \u043d\u043e \u0442\u0430\u043a \u043a\u0430\u043a \u043c\u0443\u0441\u043e\u0440 \u0432\u044b\u0432\u043e\u0437\u044f\u0442 \u043d\u0435 \u0447\u0430\u0441\u0442\u043e \u0438\u043b\u0438 \u043b\u044e\u0434\u0438 \u0432\u043d\u0435\u0437\u0430\u043f\u043d\u043e \u0432\u044b\u043a\u0438\u0434\u044b\u0432\u0430\u044e\u0442 \u043c\u043d\u043e\u0433\u043e \u043c\u0443\u0441\u043e\u0440\u0430, \u0442\u043e \u0432 \u0442\u043e\u0439 \u043a\u043b\u0435\u0442\u043a\u0435 \u0442\u043e\u0442 \u043c\u0443\u0441\u043e\u0440, \u0447\u0442\u043e \u043d\u0435 \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u043b\u0441\u044f \u0432 \u043e\u0431\u044b\u0447\u043d\u044b\u0445 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430\u0445."; - result = commandsManager.processCommand(user, String.format("%s %s", tags, data), emptyUri); - assertThat(result.getNewMessage().get().getTags().size(), equalTo(2)); - assertThat(result.getNewMessage().get().getTags().get(0).getName(), equalTo("Киев")); - assertThat(result.getNewMessage().get().getText(), equalTo(data)); - result = commandsManager.processCommand(user, "S @unknown-user", emptyUri); - assertThat(result.getNewMessage(), is(Optional.empty())); - assertThat(result.getText(), is("User not found")); - } - @Test - public void mailParserTest() throws Exception { - String mail = "MIME-Version: 1.0\n" + - "Received: by 10.176.0.242 with HTTP; Fri, 16 Mar 2018 05:31:50 -0700 (PDT)\n" + - "In-Reply-To: <2891710.100@juick.com>\n" + - "References: <2891710.0@juick.com> <2891710.100@juick.com>\n" + - "Date: Fri, 16 Mar 2018 15:31:50 +0300\n" + - "Delivered-To: vitalyster@gmail.com\n" + - "Message-ID: <CAF+0zPD_YLVgYovajLqUFwkRAgJT+FzyQ2EzikQsPKsrnfKv-Q@mail.gmail.com>\n" + - "Subject: Re: New reply to TJ\n" + - "From: Vitaly Takmazov <vitalyster@gmail.com>\n" + - "To: Juick <juick@juick.com>\n" + - "Content-Type: multipart/alternative; boundary=\"001a11454886e42be5056786ca70\"\n" + - "\n" + - "--001a11454886e42be5056786ca70\n" + - "Content-Type: text/plain; charset=\"UTF-8\"\n" + - "\n" + - "s2313334\n" + - "\n" + - "--001a11454886e42be5056786ca70\n" + - "Content-Type: text/html; charset=\"UTF-8\"\n" + - "\n" + - "<div dir=\"ltr\">s2313334</div>\n" + - "\n" + - "--001a11454886e42be5056786ca70--"; - mockMvc.perform(post("/api/mail").with(httpBasic(juickName, juickPassword)).content(mail)) - .andExpect(status().isOk()); - } - - @Test - public void recommendTests() throws Exception { - - int mid = messagesService.createMessage(ugnich.getUid(), "to be liked", null, null); - String freefdHash = userService.getHashByUID(freefd.getUid()); - int freefdMid = messagesService.createMessage(freefd.getUid(), "to be not liked", null, null); - - mockMvc.perform(post("/api/like?mid=" + mid + "&hash=" + freefdHash)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.status", is("Message is added to your recommendations"))); - mockMvc.perform(get("/api/thread?mid=" + mid + "&hash=" + freefdHash)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$[0].recommendations.length()", is(1))) - .andExpect(jsonPath("$[0].recommendations[0]", is(freefdName))); - mockMvc.perform(post("/api/like?mid=" + freefdMid + "&hash=" + freefdHash)) - .andExpect(status().isForbidden()); - } - - @Test - public void likesTests() throws Exception{ - int user_id = userService.createUser("dsds", "secret"); - String freefdHash = userService.getHashByUID(freefd.getUid()); - int mid1 = messagesService.createMessage(user_id, "yo", null, new ArrayList<>()); - - mockMvc.perform(post("/api/react?mid=" + mid1 + "&hash=" + freefdHash+ "&reactionId=2")) - .andExpect(status().isOk()); - - Message msg4 = messagesService.getMessage(mid1); - assertThat(msg4.getLikes(), is(0)); - assertThat(messagesService.getMessages(AnonymousUser.INSTANCE, Collections.singletonList(mid1)).get(0).getLikes(), is(0)); - Assert.assertEquals(1, msg4.getReactions().stream().filter(r -> r.getId() == 2) - .findFirst().orElseThrow(IllegalStateException::new).getCount()); - mockMvc.perform(post("/api/react?mid=" + mid1 + "&hash=" + freefdHash+ "&reactionId=1")) - .andExpect(status().isOk()); - mockMvc.perform(post("/api/react?mid=" + mid1 + "&hash=" + freefdHash+ "&reactionId=1")) - .andExpect(status().isOk()); - assertThat(messagesService.getMessage(mid1).getLikes(), is(1)); - } - - @Test - public void lastReadTests() throws Exception { - jdbcTemplate.execute("DELETE FROM bl_users"); - assertThat(userService.isInBLAny(ugnich.getUid(), freefd.getUid()), is(false)); - int mid = messagesService.createMessage(ugnich.getUid(), "to be watched", null, null); - subscriptionService.subscribeMessage(messagesService.getMessage(mid), ugnich); - messagesService.createReply(mid, 0, freefd, "new reply", null); - BiFunction<User, Integer, Integer> lastRead = (user, m) -> jdbcTemplate.queryForObject( - "SELECT last_read_rid FROM subscr_messages WHERE suser_id=? AND message_id=?", - Integer.class, user.getUid(), m); - assertThat(lastRead.apply(ugnich, mid), is(0)); - assertThat(messagesService.getUnread(ugnich).size(), is(1)); - assertThat(messagesService.getUnread(ugnich).get(0), is(mid)); - messagesService.getReplies(ugnich, mid); - assertThat(lastRead.apply(ugnich, mid), is(1)); - assertThat(messagesService.getUnread(ugnich).size(), is(0)); - messagesService.setLastReadComment(ugnich, mid, 0); - assertThat(lastRead.apply(ugnich, mid), is(1)); - String ugnichHash = userService.getHashByUID(ugnich.getUid()); - int freefdrid = messagesService.createReply(mid, 0, freefd, "again", null); - mockMvc.perform(get(String.format("/api/thread/mark_read/%d-%d.gif?hash=%s", mid, freefdrid, ugnichHash))) - .andExpect(status().isOk()) - .andExpect(content().bytes(IOUtils.toByteArray( - Objects.requireNonNull(getClass().getClassLoader().getResource("Transparent.gif"))))); - assertThat(lastRead.apply(ugnich, mid), is(freefdrid)); - privacyQueriesService.blacklistUser(ugnich, freefd); - int newfreefdrid = messagesService.createReply(mid, 0, freefd, "from ban", null); - serverManager.processMessageEvent(new MessageEvent(this, messagesService.getReply(mid, newfreefdrid), - Collections.emptyList())); - assertThat(userService.isReplyToBL(ugnich, messagesService.getReply(mid, newfreefdrid)), is(true)); - // TODO: test event listeners correctly - Thread.sleep(2000L); - assertThat(lastRead.apply(ugnich, mid), is(newfreefdrid)); - privacyQueriesService.blacklistUser(ugnich, freefd); - newfreefdrid = messagesService.createReply(mid, 0, freefd, "after ban", null); - assertThat(lastRead.apply(ugnich, mid), lessThan(newfreefdrid)); - mockMvc.perform(get(String.format("/api/thread?mid=%d&hash=%s", mid, ugnichHash))) - .andExpect(status().isOk()); - assertThat(lastRead.apply(ugnich, mid), is(newfreefdrid)); - } - @Test - public void feedsShouldNotContainMessagesWithBannedTags() { - Tag banned = tagService.getTag("banned", true); - int mid = messagesService.createMessage(ugnich.getUid(), "yo", "jpg", - Collections.singletonList(banned)); - privacyQueriesService.blacklistTag(freefd, banned); - assertTrue(messagesService.getMessages(AnonymousUser.INSTANCE, messagesService.getAll(freefd.getUid(), 0)) - .stream().noneMatch(m -> m.getTags().contains(banned))); - assertFalse(messagesService.getMessages(AnonymousUser.INSTANCE, messagesService.getAll(ugnich.getUid(), 0)) - .stream().noneMatch(m -> m.getTags().contains(banned))); - assertTrue(messagesService.getMessages(AnonymousUser.INSTANCE, messagesService.getPhotos(freefd.getUid(), 0)) - .stream().noneMatch(m -> m.getTags().contains(banned))); - assertFalse(messagesService.getMessages(AnonymousUser.INSTANCE, messagesService.getPhotos(ugnich.getUid(), 0)) - .stream().noneMatch(m -> m.getTags().contains(banned))); - jdbcTemplate.update("UPDATE messages SET popular=1 WHERE message_id=?", mid); - assertTrue(messagesService.getMessages(AnonymousUser.INSTANCE, messagesService.getPopular(freefd.getUid(), 0)) - .stream().noneMatch(m -> m.getTags().contains(banned))); - assertFalse(messagesService.getMessages(AnonymousUser.INSTANCE, messagesService.getPopular(ugnich.getUid(), 0)) - .stream().noneMatch(m -> m.getTags().contains(banned))); - assertTrue(messagesService.getMessages(AnonymousUser.INSTANCE, messagesService.getMyFeed(freefd.getUid(), 0, true)) - .stream().noneMatch(m -> m.getTags().contains(banned))); - int newUid = userService.createUser("newUser", "12345"); - int newMid = messagesService.createMessage(newUid, "people", null, Collections.singletonList(banned)); - messagesService.recommendMessage(newMid, ugnich.getUid()); - assertTrue(messagesService.getMessages(AnonymousUser.INSTANCE, messagesService.getMyFeed(freefd.getUid(), 0, true)) - .stream().noneMatch(m -> m.getTags().contains(banned))); - tagService.updateTags(newMid, Collections.singletonList(banned)); - assertThat(messagesService.getMessage(newMid).getTags().size(), is(0)); - privacyQueriesService.blacklistUser(freefd, userService.getUserByUID(newUid).orElse(AnonymousUser.INSTANCE)); - assertTrue(messagesService.getMessages(AnonymousUser.INSTANCE, messagesService.getMyFeed(freefd.getUid(), 0, true)) - .stream().noneMatch(m -> m.getMid() == newMid)); - } - @Test - public void tagsShouldBeDeserializedFromXml() throws JAXBException { - XmppSessionConfiguration configuration = XmppSessionConfiguration.builder() - .extensions(Extension.of(com.juick.Message.class)) - .build(); - XmppSession xmpp = new XmppSession("juick.com", configuration) { - @Override - public void connect(Jid from) { - - } - - @Override - public Jid getConnectedResource() { - return null; - } - }; - String tag = "<tag xmlns='http://juick.com/message'>yo</tag>"; - String xml = "<message xmlns='jabber:client' from='juick@juick.com' type='chat'><body>yo</body><juick mid='1' ts='2017-09-14' uid='1' uname='ugnich' xmlns='http://juick.com/message'><body>yo</body><user uid='1' uname='ugnich' xmlns='http://juick.com/user'/><tag>yo</tag><tag>people</tag></juick></message>"; - Unmarshaller unmarshaller = xmpp.createUnmarshaller(); - rocks.xmpp.core.stanza.model.Message xmppMessage = (rocks.xmpp.core.stanza.model.Message) unmarshaller.unmarshal(new StringReader(xml)); - Tag xmlTag = (Tag) unmarshaller.unmarshal(new StringReader(tag)); - assertThat(xmlTag.getName(), equalTo("yo")); - Message juickMessage = xmppMessage.getExtension(Message.class); - assertThat(juickMessage.getTags().get(0).getName(), equalTo("yo")); - } - @Test - public void messageParserSerializer() throws ParserConfigurationException, - IOException, SAXException, JAXBException { - Message msg = new Message(); - msg.setTags(MessageUtils.parseTags("test test" + (char) 0xA0 + "2 test3")); - assertEquals("First tag must be", "test", msg.getTags().get(0).getName()); - assertEquals("Third tag must be", "test3", msg.getTags().get(2).getName()); - assertEquals("Count of tags must be", 3, msg.getTags().size()); - Instant currentDate = Instant.now(); - msg.setTimestamp(currentDate); - String jsonMessage = jsonMapper.writeValueAsString(msg); - assertEquals("date should be in timestamp field", DateFormattersHolder.getMessageFormatterInstance().format(currentDate), - JsonPath.read(jsonMessage, "$.timestamp")); - - - JAXBContext context = JAXBContext - .newInstance(Message.class); - Marshaller m = context.createMarshaller(); - - StringWriter sw = new StringWriter(); - m.marshal(msg, sw); - - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - DocumentBuilder db = dbf.newDocumentBuilder(); - Document doc = db.parse(new ByteArrayInputStream(sw.toString().getBytes(CharEncoding.UTF_8))); - Node juickNode = doc.getElementsByTagName("juick").item(0); - NamedNodeMap attrs = juickNode.getAttributes(); - assertEquals("date should be in ts field", DateFormattersHolder.getMessageFormatterInstance().format(currentDate), - attrs.getNamedItem("ts").getNodeValue()); - } - @Test - public void restTemplateTests() { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - MultiValueMap<String, String> map= new LinkedMultiValueMap<>(); - HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers); - - map.add("body", "yo"); - map.add("hash", userService.getHashByUID(ugnich.getUid())); - ResponseEntity<CommandResult> result = restTemplate.postForEntity( - "/api/post", - request, CommandResult.class); - assertThat(result.getStatusCode(), is(HttpStatus.OK)); - } - @Test - public void emptyAuthenticatedPostShouldThrowBadRequest() throws Exception { - mockMvc.perform(post("/api/post") - .with(httpBasic(juickName, juickPassword))) - .andExpect(status().isBadRequest()); - } - @Test - public void attachmentSizeTests() throws URISyntaxException, IOException { - ImageUtils imageUtils = new ImageUtils(StringUtils.EMPTY, StringUtils.EMPTY); - Attachment attachment = imageUtils.getAttachment(new File(getClass().getClassLoader().getResource("Transparent.gif").toURI())); - assertThat(attachment.getHeight(), is(1)); - assertThat(attachment.getWidth(), is(1)); - } - @Test - public void meContainsAllInfo() throws Exception { - jdbcTemplate.update("DELETE FROM subscr_users"); - assertThat(userService.getUserReaders(ugnich.getUid()).size(), is(0)); - assertThat(userService.getUserFriends(ugnich.getUid()).size(), is(0)); - commandsManager.processCommand(freefd, "S @ugnich", emptyUri); - commandsManager.processCommand(ugnich, "S @freefd", emptyUri); - assertThat(userService.getUserReaders(ugnich.getUid()).size(), is(1)); - String hash = userService.getHashByUID(ugnich.getUid()); - mockMvc.perform(get("/api/me") - .with(httpBasic(ugnichName, ugnichPassword))) - .andExpect(jsonPath("$.hash", is(hash))) - .andExpect(jsonPath("$.readers.length()", is(1))) - .andExpect(jsonPath("$.read.length()", is(1))); - } - @Test - public void feedsShouldNotContainBannedUsers() throws Exception { - commandsManager.processCommand(ugnich, "BL @freefd", emptyUri); - CommandResult result = commandsManager.processCommand(ugnich, "freefd - dick", emptyUri); - int mid = result.getNewMessage().get().getMid(); - commandsManager.processCommand(freefd, String.format("#%d ugnich - dick too", mid), emptyUri); - commandsManager.processCommand(juick, String.format("#%d/1 ban for a hour!", mid), emptyUri); - commandsManager.processCommand(juick, String.format("#%d freefd is here but it is hidden from you", mid), emptyUri); - assertThat(messagesService.getMessage(mid).getReplies(), is(3)); - Message reply = messagesService.getReply(mid, 3); - assertThat(userService.isReplyToBL(ugnich, reply), is(false)); - List<Message> replies = messagesService.getReplies(ugnich, mid); - assertThat(replies.size(), is(1)); - commandsManager.processCommand(freefd, String.format("#%d/3 hahaha!", mid), emptyUri); - assertThat(messagesService.getMessage(mid).getReplies(), is(4)); - replies = messagesService.getReplies(ugnich, mid); - assertThat(replies.size(), is(1)); - commandsManager.processCommand(juick, String.format("#%d/4 mmm?!", mid), emptyUri); - assertThat(messagesService.getMessage(mid).getReplies(), is(5)); - replies = messagesService.getReplies(ugnich, mid); - reply = messagesService.getReply(mid, 5); - assertThat(userService.isReplyToBL(ugnich, reply), is(true)); - assertThat(replies.size(), is(1)); - List<Message> msgs = messagesService.getMessages(ugnich, Collections.singletonList(mid)); - assertThat(msgs.get(0).getReplies(), is(1)); - commandsManager.processCommand(ugnich, "BL @freefd", emptyUri); - messagesService.setRead(ugnich, mid); - assertThat(messagesService.getReplies(ugnich, mid).size(), is(5)); - List<Message> nonblmsgs = messagesService.getMessages(ugnich, Collections.singletonList(mid)); - assertThat(nonblmsgs.get(0).getReplies(), is(5)); - commandsManager.processCommand(ugnich, "BL @freefd", emptyUri); - Tag tag = tagService.getTag("linux", true); - messagesService.createMessage(freefd.getUid(), "sux", null, Collections.singletonList(tag)); - assertThat(messagesService.getTag(tag.TID, freefd.getUid(), 0, 10).size(), is(1)); - assertThat(messagesService.getTag(tag.TID, ugnich.getUid(), 0, 10).size(), is(0)); - commandsManager.processCommand(ugnich, "BL @freefd", emptyUri); - } - @Test - public void cmykJpegShouldBeProcessedCorrectly() throws Exception { - CommandResult postJpgCmyk = commandsManager.processCommand(ugnich, "YO", new ClassPathResource("cmyk.jpg").getURI()); - assertThat(postJpgCmyk.getNewMessage().isPresent(), is(true)); - int mid = postJpgCmyk.getNewMessage().get().getMid(); - File originalFile = Paths.get(imgDir, "p", String.format("%d.jpg", mid)).toFile(); - assertThat(originalFile.exists(), is(true)); - File mediumFile = Paths.get(imgDir, "photos-1024", String.format("%d.jpg", mid)).toFile(); - assertThat(mediumFile.exists(), is(true)); - assertThat(postJpgCmyk.getNewMessage().get().getAttachment().getWidth(), is(2585)); - assertThat(postJpgCmyk.getNewMessage().get().getAttachment().getHeight(), is(3335)); - assertThat(postJpgCmyk.getNewMessage().get().getAttachment().getMedium().getHeight(), is(1024)); - assertThat(postJpgCmyk.getNewMessage().get().getAttachment().getSmall().getHeight(), is(512)); - } - @Test - public void JpegWithoutJfifShouldBeProcessedCorrectly() throws Exception { - CommandResult postJpgCmyk = commandsManager.processCommand(ugnich, "YO", new ClassPathResource("nojfif.jpg").getURI()); - assertThat(postJpgCmyk.getNewMessage().isPresent(), is(true)); - int mid = postJpgCmyk.getNewMessage().get().getMid(); - File originalFile = Paths.get(imgDir, "p", String.format("%d.jpg", mid)).toFile(); - assertThat(originalFile.exists(), is(true)); - File mediumFile = Paths.get(imgDir, "photos-1024", String.format("%d.jpg", mid)).toFile(); - assertThat(mediumFile.exists(), is(true)); - assertThat(postJpgCmyk.getNewMessage().get().getAttachment().getWidth(), is(3264)); - assertThat(postJpgCmyk.getNewMessage().get().getAttachment().getHeight(), is(2448)); - assertThat(postJpgCmyk.getNewMessage().get().getAttachment().getMedium().getHeight(), is(768)); - assertThat(postJpgCmyk.getNewMessage().get().getAttachment().getSmall().getHeight(), is(384)); - } - @Test - public void JpegFromJuickUriShouldBeProcessedCorrectly() throws Exception { - Path tmpFile = Paths.get(tmpDir, "2915104.jpg"); - Files.copy(Paths.get(ClassLoader.getSystemResource("2915104.jpg").toURI()), tmpFile, StandardCopyOption.REPLACE_EXISTING); - assertThat(tmpFile.toFile().exists(), is(true)); - CommandResult postJpgiPhone = commandsManager.processCommand(ugnich, "YO", URI.create("juick://2915104.jpg")); - assertThat(postJpgiPhone.getNewMessage().isPresent(), is(true)); - int mid = postJpgiPhone.getNewMessage().get().getMid(); - File originalFile = Paths.get(imgDir, "p", String.format("%d.jpg", mid)).toFile(); - assertThat(originalFile.exists(), is(true)); - File mediumFile = Paths.get(imgDir, "photos-1024", String.format("%d.jpg", mid)).toFile(); - assertThat(mediumFile.exists(), is(true)); - assertThat(postJpgiPhone.getNewMessage().get().getAttachment().getWidth(), is(1280)); - assertThat(postJpgiPhone.getNewMessage().get().getAttachment().getHeight(), is(1280)); - assertThat(postJpgiPhone.getNewMessage().get().getAttachment().getMedium().getHeight(), is(1024)); - assertThat(postJpgiPhone.getNewMessage().get().getAttachment().getSmall().getHeight(), is(512)); - } - @Test - public void changeExtensionWhenReceiveFileWithWrongContentType() throws Exception { - Path pngOutput = Paths.get(tmpDir, "cmyk.png"); - Files.deleteIfExists(pngOutput); - Files.copy(getClass().getClassLoader().getResourceAsStream("cmyk.jpg"), pngOutput); - assertThat(pngOutput.toFile().exists(), is(true)); - CommandResult postJpgCmyk = commandsManager.processCommand(ugnich, "YO", pngOutput.toUri()); - assertThat(postJpgCmyk.getNewMessage().isPresent(), is(true)); - assertThat(postJpgCmyk.getNewMessage().get().getAttachmentType(), is("jpg")); - CommandResult replyJpgCmyk = commandsManager.processCommand(ugnich, String.format("#%d YO", postJpgCmyk.getNewMessage().get().getMid()), pngOutput.toUri()); - assertThat(replyJpgCmyk.getNewMessage().isPresent(), is(true)); - assertThat(replyJpgCmyk.getNewMessage().get().getAttachmentType(), is("jpg")); - } - @Test - public void messageEditingSpec() throws Exception { - MvcResult result = mockMvc.perform(post("/api/post").with(httpBasic(ugnichName, ugnichPassword)) - .param("body", "YO")).andExpect(status().is2xxSuccessful()).andReturn(); - Message original = jsonMapper.readValue(result.getResponse().getContentAsString(), CommandResult.class) - .getNewMessage().get(); - assertThat(original.getText(), equalTo("YO")); - assertThat(original.getUpdatedAt(), equalTo(original.getTimestamp())); - // to have updated_at greater than ts - Thread.sleep(1000); - result = mockMvc.perform(post("/api/update").with(httpBasic(ugnichName, ugnichPassword)) - .param("mid", String.valueOf(original.getMid())) - .param("body", "PEOPLE")).andExpect(status().is2xxSuccessful()).andReturn(); - Message edited = jsonMapper.readValue(result.getResponse().getContentAsString(), CommandResult.class) - .getNewMessage().get(); - assertThat(edited.getText(), equalTo("PEOPLE")); - assertThat(edited.getUpdatedAt(), greaterThan(edited.getTimestamp())); - mockMvc.perform(post("/api/update").with(httpBasic(freefdName, freefdPassword)) - .param("mid", String.valueOf(original.getMid())) - .param("body", "PEOPLE")).andExpect(status().is(403)); - result = mockMvc.perform(post("/api/comment").with(httpBasic(freefdName, freefdPassword)) - .param("mid", String.valueOf(original.getMid())) - .param("body", "HEY")).andExpect(status().is2xxSuccessful()).andReturn(); - CommandResult comment = jsonMapper.readValue(result.getResponse().getContentAsString(), CommandResult.class); - assertThat(comment.getNewMessage().get().getText(), is("HEY")); - assertThat(comment.getNewMessage().get().getUpdatedAt(), is(comment.getNewMessage().get().getTimestamp())); - // to have updated_at greater than ts - Thread.sleep(1000); - result = mockMvc.perform(post("/api/update").with(httpBasic(freefdName, freefdPassword)) - .param("mid", String.valueOf(comment.getNewMessage().get().getMid())) - .param("rid", String.valueOf(comment.getNewMessage().get().getRid())) - .param("body", "HEY, JOE")).andExpect(status().is2xxSuccessful()).andReturn(); - Message editedComment = jsonMapper.readValue(result.getResponse().getContentAsString(), CommandResult.class) - .getNewMessage().get(); - assertThat(editedComment.getText(), is("HEY, JOE")); - assertThat(editedComment.getUpdatedAt(), greaterThan(editedComment.getTimestamp())); - messagesService.deleteMessage(ugnich.getUid(), original.getMid()); - } - @Test - public void subscribersToRecommendations() { - int readerId = userService.createUser("reader", "123456"); - int recommenderId = userService.createUser("recommender", "123456"); - int lateRecommenderId = userService.createUser("lateRecommender", "123456"); - int posterId = userService.createUser("poster", "123456"); - User reader = userService.getUserByName("reader"); - User recommender = userService.getUserByName("recommender"); - User lateRecommender = userService.getUserByName("lateRecommender"); - User poster = userService.getUserByName("poster"); - subscriptionService.subscribeUser(reader, recommender); - subscriptionService.subscribeUser(reader, lateRecommender); - Tag sampleTag = tagService.getTag("banned", true); - int posterMid = messagesService.createMessage(posterId, "YO", null, Collections.singletonList(sampleTag)); - messagesService.recommendMessage(posterMid, recommenderId); - BiFunction<Integer, Message, List<User>> subscribers = (recommId, msg) -> - subscriptionService.getUsersSubscribedToUserRecommendations(recommId, msg); - List<User> recommendSubscribers = subscribers.apply(recommenderId, messagesService.getMessage(posterMid)); - assertThat(recommendSubscribers.size(), is(1)); - assertThat(recommendSubscribers.get(0).getUid(), is(readerId)); - privacyQueriesService.blacklistUser(reader, poster); - assertThat(subscribers.apply(recommenderId, messagesService.getMessage(posterMid)).size(), is(0)); - privacyQueriesService.blacklistUser(reader, poster); - assertThat(subscribers.apply(recommenderId, messagesService.getMessage(posterMid)).size(), is(1)); - tagService.blacklistTag(reader, sampleTag); - assertThat(subscribers.apply(recommenderId, messagesService.getMessage(posterMid)).size(), is(0)); - tagService.blacklistTag(reader, sampleTag); - assertThat(subscribers.apply(recommenderId, messagesService.getMessage(posterMid)).size(), is(1)); - messagesService.recommendMessage(posterMid, lateRecommenderId); - List<User> lateRecommendSubscribers = subscribers.apply(recommenderId, messagesService.getMessage(posterMid)); - assertThat(lateRecommendSubscribers.size(), is(0)); - int readerMid = messagesService.createMessage(readerId, "PEOPLE", null, null); - messagesService.recommendMessage(readerMid, recommenderId); - assertThat(subscribers.apply(recommenderId, messagesService.getMessage(readerMid)).size(), is(0)); - } - @Test - public void mentionsInComments() { - int posterId = userService.createUser("p", "secret"); - int commenterId = userService.createUser("cc", "secret"); - User commenter = userService.getUserByUID(commenterId).get(); - int mentionerId = userService.createUser("mmm", "secret"); - User mentioner = userService.getUserByUID(mentionerId).get(); - int mid = messagesService.createMessage(posterId, "who is dick?", null, null); - Message msg = messagesService.getMessage(mid); - int rid = messagesService.createReply(mid, 0, commenter, - "@mmm is dick", null); - Message reply = messagesService.getReply(mid, rid); - assertThat(subscriptionService.getUsersSubscribedToComments(msg, reply).size(), is(1)); - subscriptionService.subscribeUser(mentioner, commenter); - assertThat(subscriptionService.getUsersSubscribedToComments(msg, reply).size(), is(1)); - privacyQueriesService.blacklistUser(mentioner, commenter); - assertThat(subscriptionService.getUsersSubscribedToComments(msg, reply).size(), is(0)); - } - @Test - public void xmppStatusApi() throws Exception { - Supplier<XMPPStatus> getStatus = () -> { - try { - MvcResult result = mockMvc.perform(get("/api/xmpp-status").with(httpBasic(ugnichName, ugnichPassword))) - .andExpect(status().isOk()).andReturn(); - return jsonMapper.readValue(result.getResponse().getContentAsString(), XMPPStatus.class); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - }; - assertThat(getStatus.get().getInbound(), is(nullValue())); - ConnectionIn test = new ConnectionIn(server, new Socket("localhost", server.getServerPort())); - test.from.add(Jid.of("test")); - server.getInConnections().clear(); - server.addConnectionIn(test); - assertThat(getStatus.get().getInbound().size(), is(1)); - } - @Test - public void credentialsShouldNeverBeSerialized() throws Exception { - int uid = userService.createUser("yyy", "xxxx"); - User yyy = userService.getUserByUID(uid).get(); - assertThat(yyy.getCredentials(), is("xxxx")); - ObjectMapper jsonMapper = new ObjectMapper(); - jsonMapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT); - String jsonUser = jsonMapper.writeValueAsString(yyy); - Map<String, Object> user = JsonPath.read(jsonUser, "$"); - // only uid, name and uri - assertThat(user.keySet().size(), is(3)); - - JAXBContext context = JAXBContext - .newInstance(User.class); - Marshaller m = context.createMarshaller(); - - StringWriter sw = new StringWriter(); - m.marshal(yyy, sw); - - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - DocumentBuilder db = dbf.newDocumentBuilder(); - Document doc = db.parse(new ByteArrayInputStream(sw.toString().getBytes(StandardCharsets.UTF_8))); - Element juickNode = doc.getDocumentElement(); - NamedNodeMap attrs = juickNode.getAttributes(); - // uid, name, xmlns, xmlns:user - assertThat(attrs.getLength(), is(4)); - } - @Test - public void bannedUserBlogandPostShouldReturn404() throws Exception { - String userName = "isilmine"; - String userPassword = "secret"; - String msgText = "автор этого поста был забанен"; - - User isilmine = userService.getUserByUID(userService.createUser(userName, userPassword)).orElseThrow(IllegalStateException::new); - int mid = messagesService.createMessage(isilmine.getUid(), msgText, null, null); - mockMvc.perform(get(String.format("/api/thread?mid=%d", mid)).with(httpBasic(ugnichName, ugnichPassword))) - .andExpect(status().isOk()); - jdbcTemplate.update("UPDATE users SET banned=1 WHERE id=?", isilmine.getUid()); - mockMvc.perform(get(String.format("/api/thread?mid=%d", mid)).with(httpBasic(ugnichName, ugnichPassword))) - .andExpect(status().isNotFound()); - mockMvc.perform(get("/api/messages?uname=isilmine").with(httpBasic(ugnichName, ugnichPassword))) - .andExpect(status().isNotFound()); - mockMvc.perform(get("/api/info/isilmine").with(httpBasic(ugnichName, ugnichPassword))) - .andExpect(status().isNotFound()); - mockMvc.perform(get("/api/info/ugnich").with(httpBasic(ugnichName, ugnichPassword))) - .andExpect(status().isOk()); - } - - @Test - public void emptyPasswordMeansUserIsDisabled() throws Exception { - String userName = "oldschooluser"; - String userPassword = ""; - - userService.createUser(userName, userPassword); - - mockMvc.perform(get("/api/auth").with(httpBasic(userName, userPassword))).andExpect(status().isUnauthorized()); - mockMvc.perform(post("/login") - .param("username", userName) - .param("password", userPassword)).andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/login?error=1")); - } - @Test - public void bannedUserShouldBeShadowedFromRecommendationsList() throws IOException { - int ermineId = userService.createUser("ermine", "secret"); - int monstreekId = userService.createUser("monstreek", "secret"); - int pogoId = userService.createUser("pogo", "secret"); - int fmapId = userService.createUser("fmap", "secret"); - int mid = messagesService.createMessage(monstreekId, "KURWA", null, null); - assertThat(messagesService.recommendMessage(mid, ermineId), is(MessagesService.RecommendStatus.Added)); - assertThat(messagesService.recommendMessage(mid, fmapId), is(MessagesService.RecommendStatus.Added)); - assertThat(messagesService.recommendMessage(mid, pogoId), is(MessagesService.RecommendStatus.Added)); - assertThat(messagesService.getMessage(mid).getLikes(), is(3)); - assertThat(CollectionUtils.isEqualCollection(messagesService.getMessageRecommendations(mid), Arrays.asList("fmap", "ermine", "pogo")), is(true)); - privacyQueriesService.blacklistUser(userService.getUserByName("monstreek"), userService.getUserByName("pogo")); - assertThat(messagesService.getMessage(mid).getLikes(), is(3)); - assertThat(CollectionUtils.isEqualCollection(messagesService.getMessageRecommendations(mid), Arrays.asList("fmap", "ermine")), is(true)); - } - @Test - public void bannedUserShouldNotBeVisibleToOthers() { - jdbcTemplate.execute("DELETE FROM messages"); - int casualUserId = userService.createUser("user", "secret"); - int bannedUserId = userService.createUser("banned", "banned"); - jdbcTemplate.update("UPDATE users SET banned=1 WHERE id=?", bannedUserId); - messagesService.createMessage(bannedUserId, "KURWA", null, Collections.emptyList()); - assertThat(messagesService.getAll(casualUserId, 0).size(), is(0)); - assertThat(messagesService.getDiscussions(casualUserId, 0L).size(), is(0)); - assertThat(messagesService.getDiscussions(0, 0L).size(), is(0)); - assertThat(messagesService.getAll(bannedUserId, 0).size(), is(1)); - int mid = messagesService.createMessage(casualUserId, "PEACE", null, Collections.emptyList()); - User banned = userService.getUserByName("banned"); - int bannedRid = messagesService.createReply(mid, 0, banned, "KURWA", null); - int casualRid = messagesService.createReply(mid, 0, userService.getUserByName("user"), "DOOR", null); - assertThat(messagesService.getReplies(AnonymousUser.INSTANCE, mid).size(), is(1)); - assertThat(messagesService.getMessages(AnonymousUser.INSTANCE, Collections.singletonList(mid)).get(0).getReplies(), is(1)); - assertThat(messagesService.getReplies(banned, mid).size(), is(2)); - assertThat(messagesService.getMessages(banned, Collections.singletonList(mid)).get(0).getReplies(), is(2)); - } - - @Test - public void accountUrlShouldBeExposedOverWebfinger() throws Exception { - mockMvc.perform(get("/.well-known/webfinger?resource=acct:ugnich@localhost")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.subject", is("acct:ugnich@localhost"))) - .andExpect(jsonPath("$.links", hasSize(1))) - .andExpect(jsonPath("$.links[0].href", is("http://localhost:8080/u/ugnich"))); - mockMvc.perform(get("/.well-known/webfinger?resource=acct:durov@localhost")) - .andExpect(status().isNotFound()); - Person ugnich = (Person) signatureManager.discoverPerson("ugnich@juick.com").get(); - assertThat(ugnich.getName(), is(ugnichName)); - } - @Test - public void userProfileAndBlogShouldBeExposedAsActivityStream() throws Exception { - mockMvc.perform(get("/u/ugnich").accept(Context.LD_JSON_MEDIA_TYPE)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.icon.url", is("http://localhost:8080/i/a/1.png"))) - .andExpect(jsonPath("$.publicKey.publicKeyPem", is(keystoreManager.getPublicKeyPem()))); - jdbcTemplate.execute("DELETE FROM messages"); - List<Integer> mids = IteratorUtils.toList(IntStream.rangeClosed(1, 30) - .mapToObj(i -> messagesService.createMessage(ugnich.getUid(), - String.format("message %d", i), null, null)) - .collect(Collectors.toCollection(ArrayDeque::new)).descendingIterator()); - List<Integer> midsPage = mids.stream().limit(20).collect(Collectors.toList()); - mockMvc.perform(get("/u/ugnich/blog").accept(Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.orderedItems", hasSize(20))) - .andExpect(jsonPath("$.next", is("http://localhost:8080/u/ugnich/blog?before=" + midsPage.get(midsPage.size() - 1)))); - } - @Test - public void postWithoutTagsShouldNotHaveAsteriskInTitle() throws Exception { - String msgText = "Привет, я - Угнич"; - int mid = messagesService.createMessage(ugnich.getUid(), msgText, null, null); - HtmlPage threadPage = webClient.getPage(String.format("http://localhost:8080/ugnich/%d", mid)); - assertThat(threadPage.getTitleText(), equalTo("ugnich:")); - } - @Test - public void repliesList() throws IOException { - int mid = messagesService.createMessage(ugnich.getUid(), "hello", null, null); - IntStream.range(1, 15).forEach(i -> - messagesService.createReply(mid, i-1, freefd, String.valueOf(i-1), null )); - - HtmlPage threadPage = webClient.getPage(String.format("http://localhost:8080/ugnich/%d", mid)); - assertThat(threadPage.getWebResponse().getStatusCode(), equalTo(200)); - Long visibleItems = StreamSupport.stream(threadPage.getHtmlElementById("replies") - .getChildElements().spliterator(), false).filter(e -> { - StyleElement display = e.getStyleElement("display"); - return display == null || !display.getValue().equals("none"); - }).count(); - assertThat(visibleItems, equalTo(14L)); - } - @Test - public void userShouldNotSeeReplyButtonToBannedUser() throws Exception { - int mid = messagesService.createMessage(ugnich.getUid(), "freefd bl me", null, null); - messagesService.createReply(mid, 0, ugnich, "yo", null); - MvcResult loginResult = mockMvc.perform(post("/login") - .param("username", freefdName) - .param("password", freefdPassword)) - .andExpect(status().isFound()).andReturn(); - Cookie loginCookie = loginResult.getResponse().getCookie("juick-remember-me"); - webClient.setCookieManager(new CookieManager()); - webClient.getCookieManager().addCookie( - new com.gargoylesoftware.htmlunit.util.Cookie(loginCookie.getDomain(), - loginCookie.getName(), - loginCookie.getValue())); - HtmlPage threadPage = webClient.getPage(String.format("http://localhost:8080/ugnich/%d", mid)); - assertThat(threadPage.getWebResponse().getStatusCode(), equalTo(200)); - assertThat(threadPage.querySelectorAll(".msg-comment-target").isEmpty(), equalTo(false)); - assertThat(threadPage.querySelectorAll(".a-thread-comment").isEmpty(), equalTo(false)); - privacyQueriesService.blacklistUser(freefd, ugnich); - assertThat(userService.isInBLAny(freefd.getUid(), ugnich.getUid()), equalTo(true)); - int renhaId = userService.createUser("renha", "secret"); - messagesService.createReply(mid, 0, userService.getUserByUID(renhaId).orElseThrow(IllegalStateException::new), - "people", null); - threadPage = webClient.getPage(String.format("http://localhost:8080/ugnich/%d", mid)); - assertThat(threadPage.getWebResponse().getStatusCode(), equalTo(200)); - assertThat(threadPage.querySelectorAll(".msg-comment-target").isEmpty(), equalTo(true)); - assertThat(threadPage.querySelectorAll(".a-thread-comment").isEmpty(), equalTo(true)); - } - @Test - public void correctTagsEscaping() throws PebbleException, IOException { - PebbleTemplate template = pebbleEngine.getTemplate("views/test"); - Writer writer = new StringWriter(); - template.evaluate(writer, - Collections.singletonMap("tagsList", - Collections.singletonList(StringEscapeUtils.escapeHtml4(new Tag(">_<").getName())))); - String output = writer.toString().trim(); - assertThat(output, equalTo("<a href=\"/ugnich/?tag=%26gt%3B_%26lt%3B\">>_<</a>")); - } - - public DomElement fetchMeta(String url, String name) throws IOException { - HtmlPage page = webClient.getPage(url); - DomElement emptyMeta = new DomElement("", "meta", null, null); - return page.getElementsByTagName("meta").stream() - .filter(t -> t.getAttribute("name").equals(name)).findFirst().orElse(emptyMeta); - } - @Test - public void testTwitterCards() throws Exception { - - int mid = messagesService.createMessage(ugnich.getUid(), "without image", null, null); - - assertThat(fetchMeta(String.format("http://localhost:8080/ugnich/%d", mid), "twitter:card") - .getAttribute("content"), equalTo("summary")); - int mid2 = messagesService.createMessage(ugnich.getUid(), "with image", "png", null); - Message message = messagesService.getMessage(mid2); - assertThat(fetchMeta(String.format("http://localhost:8080/ugnich/%d", mid2), "twitter:card") - .getAttribute("content"), equalTo("summary_large_image")); - assertThat(fetchMeta(String.format("http://localhost:8080/ugnich/%d", mid2), "og:description") - .getAttribute("content"), - startsWith(StringEscapeUtils.escapeHtml4(MessageUtils.getMessageHashTags(message)))); - } - @Test - public void hashLoginShouldNotUseSession() throws Exception { - String hash = userService.getHashByUID(ugnich.getUid()); - MvcResult hashLoginResult = mockMvc.perform(get("/?show=my&hash=" + hash)) - .andExpect(status().isOk()) - .andExpect(model().attribute("visitor", hasProperty("authHash", equalTo(hash)))) - .andExpect(content().string(containsString(hash))) - .andReturn(); - Cookie rememberMeFromHash = hashLoginResult.getResponse().getCookie("juick-remember-me"); - MvcResult formLoginResult = mockMvc.perform(post("/login") - .param("username", ugnichName) - .param("password", ugnichPassword)) - .andExpect(status().is3xxRedirection()).andReturn(); - Cookie rememberMeFromForm = formLoginResult.getResponse().getCookie("juick-remember-me"); - mockMvc.perform(get("/?show=my").cookie(rememberMeFromForm)).andExpect(status().isOk()) - .andExpect(model().attribute("visitor", hasProperty("authHash", equalTo(hash)))) - .andExpect(content().string(containsString(hash))); - mockMvc.perform(get("/?show=my").cookie(rememberMeFromHash)).andExpect(status().isOk()) - .andExpect(model().attribute("visitor", hasProperty("authHash", equalTo(hash)))) - .andExpect(content().string(containsString(hash))); - } - @Test - public void nonExistentBlogShouldReturn404() throws Exception { - mockMvc.perform(get("/ololoe/")).andExpect(status().isNotFound()); - } - @Test - public void discussionsShouldBePageableByTimestamp() throws Exception { - String msgText = "Привет, я снова Угнич"; - int mid = messagesService.createMessage(ugnich.getUid(), msgText, null, null); - int midNew = messagesService.createMessage(ugnich.getUid(), "Я более новый Угнич", null, null); - MvcResult loginResult = mockMvc.perform(post("/login") - .param("username", freefdName) - .param("password", freefdPassword)) - .andExpect(status().is3xxRedirection()).andReturn(); - Cookie loginCookie = loginResult.getResponse().getCookie("juick-remember-me"); - webClient.setCookieManager(new CookieManager()); - webClient.getCookieManager().addCookie( - new com.gargoylesoftware.htmlunit.util.Cookie(loginCookie.getDomain(), - loginCookie.getName(), - loginCookie.getValue())); - String discussionsUrl = "http://localhost:8080/"; - HtmlPage discussions = webClient.getPage(discussionsUrl); - assertThat(discussions.querySelectorAll("article").size(), is(0)); - subscriptionService.subscribeMessage(messagesService.getMessage(mid), freefd); - discussions = (HtmlPage) discussions.refresh(); - assertThat(discussions.querySelectorAll("article").size(), is(1)); - subscriptionService.subscribeMessage(messagesService.getMessage(midNew), freefd); - discussions = (HtmlPage) discussions.refresh(); - assertThat(discussions.querySelectorAll("article").size(), is(2)); - assertThat(discussions.querySelectorAll("article").get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(midNew))); - messagesService.createReply(mid, 0, freefd, "I'm replied", null); - discussions = (HtmlPage) discussions.refresh(); - assertThat(discussions.querySelectorAll("article").size(), is(2)); - assertThat(discussions.querySelectorAll("article").get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(mid))); - Message msg = messagesService.getMessage(mid); - HtmlPage discussionsOld = webClient.getPage(discussionsUrl + "?to=" + msg.getUpdated().toEpochMilli()); - assertThat(discussionsOld.querySelectorAll("article").size(), is(1)); - assertThat(discussionsOld.querySelectorAll("article").get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(midNew))); - List<Integer> newMids = IntStream.rangeClosed(1, 19).map(i -> messagesService.createMessage(ugnich.getUid(), String.valueOf(i), null, null)).boxed().collect(Collectors.toList()); - for (Integer m : newMids) { - subscriptionService.subscribeMessage(messagesService.getMessage(m), freefd); - } - discussions = (HtmlPage) discussions.refresh(); - assertThat(discussions.querySelectorAll("article").size(), is(20)); - assertThat(discussions.querySelectorAll("article") - .get(19).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(mid))); - messagesService.createReply(midNew, 0, freefd, "I'm replied", null); - discussions = (HtmlPage) discussions.refresh(); - assertThat(discussions.querySelectorAll("article") - .get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(midNew))); - Message old = messagesService.getMessage(newMids.get(0)); - discussionsOld = webClient.getPage(discussionsUrl + "?to=" + old.getUpdated().toEpochMilli()); - assertThat(discussionsOld.querySelectorAll("article").size(), is(1)); - assertThat(discussionsOld.querySelectorAll("article") - .get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), is(String.valueOf(mid))); - } - @Test - public void redirectParamShouldCorrectlyRedirectLoggedUser() throws Exception { - MvcResult formLoginResult = mockMvc.perform(post("/login") - .param("username", ugnichName) - .param("password", ugnichPassword)) - .andExpect(status().isFound()).andReturn(); - Cookie rememberMeFromForm = formLoginResult.getResponse().getCookie("juick-remember-me"); - mockMvc.perform(get("/login").cookie(rememberMeFromForm)) - .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/")); - mockMvc.perform(get("/login?redirect=false").cookie(rememberMeFromForm)) - .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/login/success")); - } - @Test - public void anythingRedirects() throws Exception { - int mid = messagesService.createMessage(ugnich.getUid(), "yo", null, null); - mockMvc.perform(get(String.format("/%d", mid))) - .andExpect(status().isMovedPermanently()) - .andExpect(redirectedUrl(String.format("/%s/%d", ugnich.getName(), mid))); - } - @Test - public void appAssociationsTest() throws Exception { - mockMvc.perform((get("/.well-known/apple-app-site-association"))) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)) - .andExpect(jsonPath("$.webcredentials.apps[0]", is(appId))); - } - @Test - public void notificationsTests() throws Exception { - jdbcTemplate.execute("DELETE FROM messages"); - jdbcTemplate.execute("DELETE FROM replies"); - jdbcTemplate.execute("DELETE FROM subscr_messages"); - MvcResult loginResult = mockMvc.perform(post("/login") - .param("username", freefdName) - .param("password", freefdPassword)) - .andExpect(status().is3xxRedirection()).andReturn(); - Cookie loginCookie = loginResult.getResponse().getCookie("juick-remember-me"); - webClient.setCookieManager(new CookieManager()); - webClient.getCookieManager().addCookie( - new com.gargoylesoftware.htmlunit.util.Cookie(loginCookie.getDomain(), - loginCookie.getName(), - loginCookie.getValue())); - int mid = messagesService.createMessage(ugnich.getUid(), "new test", null, null); - subscriptionService.subscribeMessage(messagesService.getMessage(mid), freefd); - int rid = messagesService.createReply(mid, 0, ugnich, "new reply", null); - HtmlPage discussionsPage = webClient.getPage("http://localhost:8080/?show=discuss"); - assertThat(discussionsPage.querySelectorAll("#global a .badge").size(), is(1)); - HtmlPage unreadThread = webClient.getPage(String.format("http://localhost:8080/ugnich/%d", mid)); - assertThat(unreadThread.querySelectorAll("#global a .badge").size(), is(0)); - } - @Test - public void escapeSqlTests() { - String sql = String.format("SELECT * FROM table WHERE data='%s'", Utils.encodeSphinx("';-- DROP TABLE table")); - assertThat(sql, is("SELECT * FROM table WHERE data='\\';-- DROP TABLE table\'")); - } - @Test - public void swaggerOutput() throws Exception { - MvcResult result = mockMvc.perform(get("/v2/api-docs") - .accept(MediaType.APPLICATION_JSON_UTF8)) - .andExpect(status().isOk()) - .andReturn(); - String outputDir = System.getProperty("io.springfox.staticdocs.outputDir"); - if (StringUtils.isNotEmpty(outputDir)) { - Files.createDirectories(Paths.get(outputDir)); - BufferedWriter writer = Files.newBufferedWriter(Paths.get(outputDir, "swagger.json"), StandardCharsets.UTF_8); - writer.write(result.getResponse().getContentAsString()); - writer.flush(); - } - } - @Test - public void newMessageShouldNotContainDuplicatedTags() throws Exception { - CommandResult result = commandsManager.processCommand(ugnich, "*test1 *test2 *test1 test3", emptyUri); - assertThat(result.getNewMessage().isPresent(), is(true)); - Message msg = result.getNewMessage().get(); - assertThat(msg.getTags().size(), is (2)); - assertThat(msg.getTags().get(0).getName(), is("test1")); - assertThat(msg.getTags().get(1).getName(), is("test2")); - assertThat(msg.getText(), is("test3")); - } - @Test - public void oneClickUnsubscribe() throws Exception { - mockMvc.perform(post("/settings/unsubscribe") - .param("hash", "123456") - .param("List-Unsubscribe", "One-Click")).andExpect(status().isBadRequest()); - mockMvc.perform(post("/settings/unsubscribe") - .param("hash", userService.getHashByUID(ugnich.getUid())) - .param("List-Unsubscribe", "One-Click")).andExpect(status().isOk()); - } - @Test - public void ActivityDeserialization() throws IOException { - String followJsonStr = IOUtils.toString(new ClassPathResource("follow.json").getURI(), StandardCharsets.UTF_8); - Follow follow = (Follow)jsonMapper.readValue(followJsonStr, Context.class); - String personJsonStr = IOUtils.toString(new ClassPathResource("person.json").getURI(), StandardCharsets.UTF_8); - Person person = (Person)jsonMapper.readValue(personJsonStr, Context.class); - String undoJsonStr = IOUtils.toString(new ClassPathResource("undo.json").getURI(), StandardCharsets.UTF_8); - Undo undo = jsonMapper.readValue(undoJsonStr, Undo.class); - String undoFollower = (String)((Map)undo.getObject()).get("object"); - String createJsonStr = IOUtils.toString(new ClassPathResource("create.json").getURI(), StandardCharsets.UTF_8); - Create create = jsonMapper.readValue(createJsonStr, Create.class); - Map<String, Object> note = (Map<String, Object>) create.getObject(); - Map<String, Object> attachmentObj = (Map<String, Object> )((List<Object>) note.get("attachment")).get(0); - String attachment = attachmentObj != null ? (String)attachmentObj.get("url") : StringUtils.EMPTY; - String deleteJsonStr = IOUtils.toString(new ClassPathResource("delete.json").getURI(), StandardCharsets.UTF_8); - Delete delete = jsonMapper.readValue(deleteJsonStr, Delete.class); - int mid = messagesService.createMessage(ugnich.getUid(), "YO", "", null); - User extUser = new User(); - extUser.setUri(URI.create("http://localhost:8080/users/xwatt")); - int rid = messagesService.createReply(mid, 0, extUser, "PEOPLE", null); - Message replyFromExt = messagesService.getReply(mid, rid); - String extMessageUri = "http://localhost:8080/statuses/12345"; - messagesService.updateReplyUri(replyFromExt, URI.create(extMessageUri)); - int rid2 = messagesService.createReply(mid, rid, ugnich, "HI", null); - - Message replyToExt = messagesService.getReply(mid, rid2); - Note replyNote = activityPubManager.makeNote(replyToExt); - assertThat(replyNote.getInReplyTo(), equalTo(extMessageUri)); - String noteStr = IOUtils.toString(new ClassPathResource("mention.json").getURI(), StandardCharsets.UTF_8); - Note create2 = jsonMapper.readValue(noteStr, Note.class); - jsonMapper.readValue(IOUtils.toString(new ClassPathResource("webfinger.json").getURI(), StandardCharsets.UTF_8), Account.class); - NodeInfo info = jsonMapper.readValue(IOUtils.toString(new ClassPathResource("xnodeinfo2.json").getURI(), StandardCharsets.UTF_8), NodeInfo.class); - assertThat(info.getUsage().getUsers().getActiveHalfyear(), is(42)); - } - @Test - public void activitySerialization() throws Exception { - Message msgNoTags = commandsManager.processCommand(ugnich, "people", emptyUri).getNewMessage().get(); - String json = jsonMapper.writeValueAsString(Context.build(activityPubManager.makeNote(msgNoTags))); - Message msg = commandsManager.processCommand(ugnich, "*shit happens", emptyUri).getNewMessage().get(); - Note note = activityPubManager.makeNote(msg); - json = jsonMapper.writeValueAsString(Context.build(note)); - Note replyNote = new Note(); - replyNote.setId("http://localhost:8080/n/2-1"); - replyNote.setInReplyTo(activityPubManager.messageUri(msg)); - replyNote.setAttributedTo("http://localhost:8080/u/freefd"); - replyNote.setTo(Collections.singletonList(activityPubManager.personUri(ugnich))); - replyNote.setContent("HI"); - Create create = new Create(); - create.setActor("http://localhost:8080/u/freefd"); - create.setObject(replyNote); - signatureManager.post((Person) signatureManager.getContext(URI.create("http://localhost:8080/u/freefd")).get(), - (Person) signatureManager.getContext(URI.create("http://localhost:8080/u/ugnich")).get(), create); - Message replyToExt = commandsManager.processCommand(ugnich, String.format("#%d/1 PSSH YOBA ETO TI", msg.getMid()), emptyUri).getNewMessage().get(); - json = jsonMapper.writeValueAsString(Context.build(activityPubManager.makeNote(messagesService.getReply(replyToExt.getMid(), replyToExt.getRid())))); - } - @Test - public void signingSpec() throws IOException { - Person from = (Person) signatureManager.getContext(URI.create("http://localhost:8080/u/freefd")).get(); - Person to = (Person) signatureManager.getContext(URI.create("http://localhost:8080/u/ugnich")).get(); - Follow follow = new Follow(); - follow.setActor("http://localhost:8080/u/freefd"); - follow.setObject("http://localhost:8080/u/ugnich"); - signatureManager.post(from, to, follow); - } - @Test - public void hostmeta() throws Exception { - MvcResult result = mockMvc.perform(get("/.well-known/host-meta")) - .andExpect(status().isOk()).andReturn(); - String xrd = result.getResponse().getContentAsString(); - result = mockMvc.perform(get("/.well-known/x-nodeinfo2")) - .andExpect(status().isOk()).andReturn(); - } - @Test - public void pms() throws Exception { - jdbcTemplate.execute("DELETE FROM pm"); - jdbcTemplate.execute("DELETE FROM bl_users"); - CommandResult res = commandsManager.processCommand(ugnich, "@freefd DICK", emptyUri); - assertThat(res.getNewMessage(), is(Optional.empty())); - assertThat(res.getText(), is("Private message sent")); - MvcResult result = mockMvc.perform(get("/api/groups_pms") - .with(httpBasic(freefdName, freefdPassword))) - .andExpect(status().isOk()) - .andReturn(); - PrivateChats chats = jsonMapper.readValue(result.getResponse().getContentAsString(), PrivateChats.class); - assertThat(chats.getUsers().size(), is(1)); - } - @Test - public void seenTests() { - Instant now = Instant.now(); - int newUserUid = userService.createUser("newuser", "assword"); - assertThat(userService.getUserByUID(newUserUid).get().getSeen(), is(nullValue())); - messagesService.createMessage(newUserUid, "YO", "", null); - assertThat(userService.getUserByUID(newUserUid).get().getSeen(), greaterThanOrEqualTo(now)); - } -} diff --git a/juick-server/src/test/resources/2915104.jpg b/juick-server/src/test/resources/2915104.jpg Binary files differdeleted file mode 100644 index 7f0fc3ba..00000000 --- a/juick-server/src/test/resources/2915104.jpg +++ /dev/null diff --git a/juick-server/src/test/resources/cmyk.jpg b/juick-server/src/test/resources/cmyk.jpg Binary files differdeleted file mode 100644 index 40af259c..00000000 --- a/juick-server/src/test/resources/cmyk.jpg +++ /dev/null diff --git a/juick-server/src/test/resources/create.json b/juick-server/src/test/resources/create.json deleted file mode 100644 index 42d20161..00000000 --- a/juick-server/src/test/resources/create.json +++ /dev/null @@ -1 +0,0 @@ -{"type":"Create","id":"https://mastodon.social/users/xwatt/statuses/100850249548292153/activity","published":"2018-10-06T19:04:44Z","to":["https://www.w3.org/ns/activitystreams#Public"],"actor":"https://mastodon.social/users/xwatt","object":{"id":"https://mastodon.social/users/xwatt/statuses/100850249548292153","type":"Note","inReplyTo":"https://juick.com/m/2922602","published":"2018-10-06T19:04:44Z","url":"https://mastodon.social/@xwatt/100850249548292153","attributedTo":"https://mastodon.social/users/xwatt","to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://mastodon.social/users/xwatt/followers","https://juick.com/u/TJ"],"sensitive":false,"atomUri":"https://mastodon.social/users/xwatt/statuses/100850249548292153","inReplyToAtomUri":"https://juick.com/m/2922602","conversation":"tag:mastodon.social,2018-10-04:objectId=57333900:objectType=Conversation","content":"<p><span class=\"h-card\"><a href=\"https://juick.com/u/TJ\" class=\"u-url mention\">@<span>TJ</span></a></span> YO</p>","contentMap":{"en":"<p><span class=\"h-card\"><a href=\"https://juick.com/u/TJ\" class=\"u-url mention\">@<span>TJ</span></a></span> YO</p>"},"attachment":[{"type":"Document","mediaType":"image/jpeg","url":"https://files.mastodon.social/media_attachments/files/006/922/030/original/04057122ea7c6570.jpeg"}],"tag":[{"type":"Mention","href":"https://juick.com/u/TJ","name":"@TJ@juick.com"}]},"type":"Create","@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","sensitive":"as:sensitive","movedTo":{"@id":"as:movedTo","@type":"@id"},"Hashtag":"as:Hashtag","ostatus":"http://ostatus.org#","atomUri":"ostatus:atomUri","inReplyToAtomUri":"ostatus:inReplyToAtomUri","conversation":"ostatus:conversation","toot":"http://joinmastodon.org/ns#","Emoji":"toot:Emoji","focalPoint":{"@container":"@list","@id":"toot:focalPoint"},"featured":{"@id":"toot:featured","@type":"@id"},"schema":"http://schema.org#","PropertyValue":"schema:PropertyValue","value":"schema:value"}]}
\ No newline at end of file diff --git a/juick-server/src/test/resources/data.sql b/juick-server/src/test/resources/data.sql deleted file mode 100644 index 102b11f4..00000000 --- a/juick-server/src/test/resources/data.sql +++ /dev/null @@ -1,8 +0,0 @@ -INSERT INTO tags(tag_id, name) VALUES(2, 'juick'); -INSERT INTO reactions (like_id, description) VALUES (1, 'like'); -INSERT INTO reactions (like_id, description) VALUES (2, 'love'); -INSERT INTO reactions (like_id, description) VALUES (3, 'lol'); -INSERT INTO reactions (like_id, description) VALUES (4, 'hmm'); -INSERT INTO reactions (like_id, description) VALUES (5, 'angry'); -INSERT INTO reactions (like_id, description) VALUES (6, 'uhblya'); -INSERT INTO reactions (like_id, description) VALUES (7, 'ugh'); diff --git a/juick-server/src/test/resources/delete.json b/juick-server/src/test/resources/delete.json deleted file mode 100644 index 9bd3fdea..00000000 --- a/juick-server/src/test/resources/delete.json +++ /dev/null @@ -1 +0,0 @@ -{"type":"Delete","id":"https://mastodon.social/users/xwatt/statuses/100850777554564322#delete","to":["https://www.w3.org/ns/activitystreams#Public"],"actor":"https://mastodon.social/users/xwatt","object":{"id":"https://mastodon.social/users/xwatt/statuses/100850777554564322","type":"Tombstone","atomUri":"https://mastodon.social/users/xwatt/statuses/100850777554564322"},"type":"Delete","@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","sensitive":"as:sensitive","movedTo":{"@id":"as:movedTo","@type":"@id"},"Hashtag":"as:Hashtag","ostatus":"http://ostatus.org#","atomUri":"ostatus:atomUri","inReplyToAtomUri":"ostatus:inReplyToAtomUri","conversation":"ostatus:conversation","toot":"http://joinmastodon.org/ns#","Emoji":"toot:Emoji","focalPoint":{"@container":"@list","@id":"toot:focalPoint"},"featured":{"@id":"toot:featured","@type":"@id"},"schema":"http://schema.org#","PropertyValue":"schema:PropertyValue","value":"schema:value"}]}
\ No newline at end of file diff --git a/juick-server/src/test/resources/follow.json b/juick-server/src/test/resources/follow.json deleted file mode 100644 index 93d46c24..00000000 --- a/juick-server/src/test/resources/follow.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1", - { - "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", - "sensitive": "as:sensitive", - "movedTo": { - "@id": "as:movedTo", - "@type": "@id" - }, - "Hashtag": "as:Hashtag", - "ostatus": "http://ostatus.org#", - "atomUri": "ostatus:atomUri", - "inReplyToAtomUri": "ostatus:inReplyToAtomUri", - "conversation": "ostatus:conversation", - "toot": "http://joinmastodon.org/ns#", - "Emoji": "toot:Emoji", - "focalPoint": { - "@container": "@list", - "@id": "toot:focalPoint" - }, - "featured": { - "@id": "toot:featured", - "@type": "@id" - }, - "schema": "http://schema.org#", - "PropertyValue": "schema:PropertyValue", - "value": "schema:value" - } - ], - "id": "https://mastodon.social/970f0d76-9ea7-46cd-a3e9-278e255f082d", - "type": "Follow", - "actor": "https://mastodon.social/users/xwatt", - "object": "https://x.juick.com/u/gegege", - "signature": { - "type": "RsaSignature2017", - "creator": "https://mastodon.social/users/xwatt#main-key", - "created": "2018-10-02T09:39:29Z", - "signatureValue": "lkxaKueSjT0nxCnT15hR8e1yQ7RsUCF0gBaiSAtXmN0tT3g7OcQPZUzUvCFF2aXB8xGFHMv+7Rp+jegR8rszuNRIghUxsOfYL5da2mD5UrpIlxiW4FxZjbni0klUF9GhRWfBYLIMumUsl9UElZPxtpYjlDQ7kCzYqnwbGgUiI0ehBJrHQJHET0pcyeSdGoRlXwD3I4c59nbr22CT026FBRNSJIxJj865ij5vg0j0q0/2ep+8ztya3x0+aYSrFn8WGO4Y2muCJtKurH2ROv8yyVgaIyFaUx6uvBf6pO3oGfWrm5if0P924LLlReXBItbleZrp0y2jPE7RriZsZmuFbg==" - } -}
\ No newline at end of file diff --git a/juick-server/src/test/resources/mention.json b/juick-server/src/test/resources/mention.json deleted file mode 100644 index c51265f1..00000000 --- a/juick-server/src/test/resources/mention.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1", - { - "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", - "sensitive": "as:sensitive", - "movedTo": { - "@id": "as:movedTo", - "@type": "@id" - }, - "Hashtag": "as:Hashtag", - "ostatus": "http://ostatus.org#", - "atomUri": "ostatus:atomUri", - "inReplyToAtomUri": "ostatus:inReplyToAtomUri", - "conversation": "ostatus:conversation", - "toot": "http://joinmastodon.org/ns#", - "Emoji": "toot:Emoji", - "focalPoint": { - "@container": "@list", - "@id": "toot:focalPoint" - }, - "featured": { - "@id": "toot:featured", - "@type": "@id" - }, - "schema": "http://schema.org#", - "PropertyValue": "schema:PropertyValue", - "value": "schema:value" - } - ], - "id": "https://mastodonsocial.ru/users/inhosin/statuses/100946127454503070", - "type": "Note", - "summary": null, - "inReplyTo": "https://juick.com/n/2923741-40", - "published": "2018-10-23T17:27:45Z", - "url": "https://mastodonsocial.ru/@inhosin/100946127454503070", - "attributedTo": "https://mastodonsocial.ru/users/inhosin", - "to": [ - "https://www.w3.org/ns/activitystreams#Public" - ], - "cc": [ - "https://mastodonsocial.ru/users/inhosin/followers", - "https://juick.com/u/vt" - ], - "sensitive": false, - "atomUri": "https://mastodonsocial.ru/users/inhosin/statuses/100946127454503070", - "inReplyToAtomUri": "https://juick.com/n/2923741-40", - "conversation": "tag:mastodonsocial.ru,2018-10-16:objectId=609790:objectType=Conversation", - "content": "<p><span class=\"h-card\"><a href=\"https://juick.com/vt/\" class=\"u-url mention\">@<span>vt</span></a></span> а лайки между серверами ходят? И ещё вопрос: нельзя всёже в ответ транслировать ник, чтобы нотификация поступала, а то приходится лопатить ленту что искать ответы. Я сейчас тоже с ActivityPub ковыряюсь.</p>", - "contentMap": { - "ru": "<p><span class=\"h-card\"><a href=\"https://juick.com/vt/\" class=\"u-url mention\">@<span>vt</span></a></span> а лайки между серверами ходят? И ещё вопрос: нельзя всёже в ответ транслировать ник, чтобы нотификация поступала, а то приходится лопатить ленту что искать ответы. Я сейчас тоже с ActivityPub ковыряюсь.</p>" - }, - "attachment": [], - "tag": [ - { - "type": "Mention", - "href": "https://juick.com/u/vt", - "name": "@vt@juick.com" - } - ] -}
\ No newline at end of file diff --git a/juick-server/src/test/resources/nojfif.jpg b/juick-server/src/test/resources/nojfif.jpg Binary files differdeleted file mode 100644 index 16ddec1b..00000000 --- a/juick-server/src/test/resources/nojfif.jpg +++ /dev/null diff --git a/juick-server/src/test/resources/person.json b/juick-server/src/test/resources/person.json deleted file mode 100644 index 67e88257..00000000 --- a/juick-server/src/test/resources/person.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1", - { - "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", - "sensitive": "as:sensitive", - "movedTo": { - "@id": "as:movedTo", - "@type": "@id" - }, - "Hashtag": "as:Hashtag", - "ostatus": "http://ostatus.org#", - "atomUri": "ostatus:atomUri", - "inReplyToAtomUri": "ostatus:inReplyToAtomUri", - "conversation": "ostatus:conversation", - "toot": "http://joinmastodon.org/ns#", - "Emoji": "toot:Emoji", - "focalPoint": { - "@container": "@list", - "@id": "toot:focalPoint" - }, - "featured": { - "@id": "toot:featured", - "@type": "@id" - }, - "schema": "http://schema.org#", - "PropertyValue": "schema:PropertyValue", - "value": "schema:value" - } - ], - "id": "https://mastodon.social/users/xwatt", - "type": "Person", - "following": "https://mastodon.social/users/xwatt/following", - "followers": "https://mastodon.social/users/xwatt/followers", - "inbox": "https://mastodon.social/users/xwatt/inbox", - "outbox": "https://mastodon.social/users/xwatt/outbox", - "featured": "https://mastodon.social/users/xwatt/collections/featured", - "preferredUsername": "xwatt", - "name": "", - "summary": "\u003cp\u003e\u003c/p\u003e", - "url": "https://mastodon.social/@xwatt", - "manuallyApprovesFollowers": false, - "publicKey": { - "id": "https://mastodon.social/users/xwatt#main-key", - "owner": "https://mastodon.social/users/xwatt", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuCYg1WrzerteFgLcbnRC\n1/pL5jFY05iuB4ycRlxxCpwpDihKdcmJ8nVEpFc/CIRtRRA3Oq1a+yF4L5X3Bwi8\nA8ajKHNtiPd4eeGdGvEJidf8cR8Bmfmrzt669Tmja+5Cr1CaFX9mYXhQoY6CqIxR\nDrPAAUb0CHJV+Ta6QkieCaGxYsvdg6Gg+8aw6k60vBswS6fmNsykH9xrovqtBb9M\ncKglyOA2W2FgswYtzRRKT5QQU4x/hfWMYuIEMhnke3U5k2gzb/qnJM2otaR0NzJ0\nkW+Fu7av5E6Ur1sUe1hTHpDxaAmNC+br19wTn6zh4Wt1UJp+Os56hBTSq50WKcfW\nhQIDAQAB\n-----END PUBLIC KEY-----\n" - }, - "tag": [], - "attachment": [], - "endpoints": { - "sharedInbox": "https://mastodon.social/inbox" - } -}
\ No newline at end of file diff --git a/juick-server/src/test/resources/templates/views/test.html b/juick-server/src/test/resources/templates/views/test.html deleted file mode 100644 index 7700be6f..00000000 --- a/juick-server/src/test/resources/templates/views/test.html +++ /dev/null @@ -1,2 +0,0 @@ -{% import "views/macros/tags" %} -{{ tags("ugnich", tagsList)}}
\ No newline at end of file diff --git a/juick-server/src/test/resources/undo.json b/juick-server/src/test/resources/undo.json deleted file mode 100644 index 371c6bd6..00000000 --- a/juick-server/src/test/resources/undo.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1", - { - "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", - "sensitive": "as:sensitive", - "movedTo": { - "@id": "as:movedTo", - "@type": "@id" - }, - "Hashtag": "as:Hashtag", - "ostatus": "http://ostatus.org#", - "atomUri": "ostatus:atomUri", - "inReplyToAtomUri": "ostatus:inReplyToAtomUri", - "conversation": "ostatus:conversation", - "toot": "http://joinmastodon.org/ns#", - "Emoji": "toot:Emoji", - "focalPoint": { - "@container": "@list", - "@id": "toot:focalPoint" - }, - "featured": { - "@id": "toot:featured", - "@type": "@id" - }, - "schema": "http://schema.org#", - "PropertyValue": "schema:PropertyValue", - "value": "schema:value" - } - ], - "id": "https://mastodon.social/users/xwatt#follows/1553133/undo", - "type": "Undo", - "actor": "https://mastodon.social/users/xwatt", - "object": { - "id": "https://mastodon.social/c5cfbc0b-3e4b-423a-aa51-1c38e1202371", - "type": "Follow", - "actor": "https://mastodon.social/users/xwatt", - "object": "https://x.juick.com/u/gegege" - }, - "signature": { - "type": "RsaSignature2017", - "creator": "https://mastodon.social/users/xwatt#main-key", - "created": "2018-10-02T10:27:33Z", - "signatureValue": "OOK3WDLgVMo6u1l/I1o4hrcOf12X2ryOa/xAeuYf7ncKQJbzFXohUYCrBtgUKjnOQJLHY/HbhhFE1SBXMCMUbPNF8KeztxWKqWnXtXNSHVv6Shxl+9yxZoGaAgpkYjn9qQGTAm4SXmV0RM49cz84PfLAJI1fUecoVclhVWXJuaSz4yZ/UteySjBMB5h5nPWGDmz4usviZ9EZTihr3xkFfkg+CPYwr5SYWDe5W1MxeKoosEck6Yhwt8OOf/eGB5guN81lp90O76oO6LGhblnYhMKxsOJf4ahF9qeCNajXk/qiwSfl6IwVADYlFB1GGTdIPXUUob5HhjX27Bpyah0lgA==" - } -}
\ No newline at end of file diff --git a/juick-server/src/test/resources/webfinger.json b/juick-server/src/test/resources/webfinger.json deleted file mode 100644 index 55f9e4f3..00000000 --- a/juick-server/src/test/resources/webfinger.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "subject": "acct:Gargron@mastodon.social", - "aliases": [ - "https://mastodon.social/@Gargron", - "https://mastodon.social/users/Gargron" - ], - "links": [ - { - "rel": "http://webfinger.net/rel/profile-page", - "type": "text/html", - "href": "https://mastodon.social/@Gargron" - }, - { - "rel": "http://schemas.google.com/g/2010#updates-from", - "type": "application/atom+xml", - "href": "https://mastodon.social/users/Gargron.atom" - }, - { - "rel": "self", - "type": "application/activity+json", - "href": "https://mastodon.social/users/Gargron" - }, - { - "rel": "salmon", - "href": "https://mastodon.social/api/salmon/1" - }, - { - "rel": "magic-public-key", - "href": "data:application/magic-public-key,RSA.vXc4vkECU2_CeuSo1wtnFoim94Ne1jBMYxTZ9wm2YTdJq1oiZKif06I2fOqDzY_4q_S9uccrE9Bkajv1dnkOVm31QjWlhVpSKynVxEWjVBO5Ienue8gND0xvHIuXf87o61poqjEoepvsQFElA5ymovljWGSA_jpj7ozygUZhCXtaS2W5AD5tnBQUpcO0lhItYPYTjnmzcc4y2NbJV8hz2s2G8qKv8fyimE23gY1XrPJg-cRF-g4PqFXujjlJ7MihD9oqtLGxbu7o1cifTn3xBfIdPythWu5b4cujNsB3m3awJjVmx-MHQ9SugkSIYXV0Ina77cTNS0M2PYiH1PFRTw==.AQAB" - }, - { - "rel": "http://ostatus.org/schema/1.0/subscribe", - "template": "https://mastodon.social/authorize_interaction?uri={uri}" - } - ] -} diff --git a/juick-server/src/test/resources/xnodeinfo2.json b/juick-server/src/test/resources/xnodeinfo2.json deleted file mode 100644 index 14be6394..00000000 --- a/juick-server/src/test/resources/xnodeinfo2.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "version": "1.0", - "server": { - "baseUrl": "https://example.com", - "name": "Example diaspora* server", - "software": "diaspora", - "version": "0.5.0" - }, - "protocols": ["diaspora", "zot"], - "services": { - "inbound": ["gnusocial"], - "outbound": ["facebook", "twitter"] - }, - "openRegistrations": true, - "usage": { - "users": { - "total": 123, - "activeHalfyear": 42, - "activeMonth": 23 - }, - "localPosts": 500, - "localComments": 1000 - } -}
\ No newline at end of file |