/* * Overchan Android (Meta Imageboard Client) * Copyright (C) 2014-2016 miku-nyan <https://github.com/miku-nyan> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package nya.miku.wishmaster.chans.sich; import java.io.ByteArrayOutputStream; import java.nio.charset.Charset; import java.util.List; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang3.tuple.Pair; import cz.msebera.android.httpclient.Header; import cz.msebera.android.httpclient.HttpHeaders; import cz.msebera.android.httpclient.entity.mime.content.ByteArrayBody; import cz.msebera.android.httpclient.message.BasicHeader; import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.support.v4.content.res.ResourcesCompat; import nya.miku.wishmaster.R; import nya.miku.wishmaster.api.AbstractVichanModule; import nya.miku.wishmaster.api.interfaces.CancellableTask; import nya.miku.wishmaster.api.interfaces.ProgressListener; import nya.miku.wishmaster.api.models.AttachmentModel; import nya.miku.wishmaster.api.models.BadgeIconModel; import nya.miku.wishmaster.api.models.BoardModel; import nya.miku.wishmaster.api.models.DeletePostModel; import nya.miku.wishmaster.api.models.PostModel; import nya.miku.wishmaster.api.models.SendPostModel; import nya.miku.wishmaster.api.models.SimpleBoardModel; import nya.miku.wishmaster.api.models.UrlPageModel; import nya.miku.wishmaster.api.util.ChanModels; import nya.miku.wishmaster.common.IOUtils; import nya.miku.wishmaster.http.ExtendedMultipartBuilder; import nya.miku.wishmaster.http.streamer.HttpRequestModel; import nya.miku.wishmaster.http.streamer.HttpResponseModel; import nya.miku.wishmaster.http.streamer.HttpStreamer; import nya.miku.wishmaster.lib.org_json.JSONObject; public class SichModule extends AbstractVichanModule { private static final String CHAN_NAME = "sich.co.ua"; private static final String DEFAULT_DOMAIN = "sich.co.ua"; private static final String[] ATTACHMENT_FORMATS = new String[] { "bmp", "gif", "jpeg", "jpg", "png", "mp4", "webm" }; private static final SimpleBoardModel[] BOARDS = new SimpleBoardModel[] { ChanModels.obtainSimpleBoardModel(CHAN_NAME, "b", "Балачки", "Основа", true), ChanModels.obtainSimpleBoardModel(CHAN_NAME, "int", "International", "Основа", false), ChanModels.obtainSimpleBoardModel(CHAN_NAME, "a", "Аніме", "Тематика", false), ChanModels.obtainSimpleBoardModel(CHAN_NAME, "f", "Фап", "Тематика", true), ChanModels.obtainSimpleBoardModel(CHAN_NAME, "po", "Політика", "Тематика", false), ChanModels.obtainSimpleBoardModel(CHAN_NAME, "soc", "Соціум", "Тематика", false), }; private static final Pattern ERROR_PATTERN = Pattern.compile("<h2 [^>]*>(.*?)</h2>"); public SichModule(SharedPreferences preferences, Resources resources) { super(preferences, resources); } @Override public String getChanName() { return CHAN_NAME; } @Override public String getDisplayingName() { return "Січ"; } @Override public Drawable getChanFavicon() { return ResourcesCompat.getDrawable(resources, R.drawable.favicon_sich, null); } @Override protected boolean canCloudflare() { return true; } @Override protected String getUsingDomain() { return DEFAULT_DOMAIN; } @Override protected boolean canHttps() { return true; } @Override protected boolean useHttpsDefaultValue() { return true; } @Override protected SimpleBoardModel[] getBoardsList() { return BOARDS; } @Override public BoardModel getBoard(String shortName, ProgressListener listener, CancellableTask task) throws Exception { BoardModel model = super.getBoard(shortName, listener, task); model.bumpLimit = 250; model.allowNames = false; model.allowSage = false; model.allowEmails = false; model.attachmentsMaxCount = 4; model.attachmentsFormatFilters = ATTACHMENT_FORMATS; return model; } @Override protected PostModel mapPostModel(JSONObject object, String boardName) { PostModel model = super.mapPostModel(object, boardName); model.comment = model.comment.replaceAll( "style=\"text-decoration:line-through;\">", "class=\"s\">" ); model.comment = model.comment.replaceAll( "class=\"quote dice\">", "class=\"unkfunc\">" ); if (boardName.equals("int")) { String country = object.optString("country"); if (country != null && country.length() > 0) { model.icons = new BadgeIconModel[] { new BadgeIconModel() }; model.icons[0].source = "/static/flags/" + country.toLowerCase(Locale.US) + ".png"; model.icons[0].description = object.optString("country_name", country); } } return model; } @Override protected AttachmentModel mapAttachment(JSONObject object, String boardName, boolean isSpoiler) { String ext = object.optString("ext", ""); if (!ext.equals("")) { AttachmentModel attachment = new AttachmentModel(); switch (ext) { case ".jpeg": case ".jpg": case ".png": attachment.type = AttachmentModel.TYPE_IMAGE_STATIC; break; case ".gif": attachment.type = AttachmentModel.TYPE_IMAGE_GIF; break; case ".webm": case ".mp4": attachment.type = AttachmentModel.TYPE_VIDEO; break; default: attachment.type = AttachmentModel.TYPE_OTHER_FILE; } attachment.size = object.optInt("fsize", -1); if (attachment.size > 0) attachment.size = Math.round(attachment.size / 1024f); attachment.width = object.optInt("w", -1); attachment.height = object.optInt("h", -1); attachment.originalName = object.optString("filename", "") + ext; attachment.isSpoiler = isSpoiler; String tim = object.optString("tim", ""); String _t_ext = object.optString("_t_ext", ".png"); if (tim.length() > 0 && _t_ext != ".webm") { attachment.thumbnail = isSpoiler ? null : ("/" + boardName + "/thumb/" + tim + _t_ext); attachment.path = "/" + boardName + "/src/" + tim + ext; return attachment; } } return null; } @Override public String sendPost(SendPostModel model, ProgressListener listener, CancellableTask task) throws Exception { UrlPageModel urlModel = new UrlPageModel(); urlModel.chanName = CHAN_NAME; urlModel.boardName = model.boardName; if (model.threadNumber == null) { urlModel.type = UrlPageModel.TYPE_BOARDPAGE; urlModel.boardPage = UrlPageModel.DEFAULT_FIRST_PAGE; } else { urlModel.type = UrlPageModel.TYPE_THREADPAGE; urlModel.threadNumber = model.threadNumber; } String referer = buildUrl(urlModel); List<Pair<String, String>> fields = VichanAntiBot.getFormValues(referer, task, httpClient); if (task != null && task.isCancelled()) throw new Exception("interrupted"); ExtendedMultipartBuilder postEntityBuilder = ExtendedMultipartBuilder.create(). setCharset(Charset.forName("UTF-8")).setDelegates(listener, task); for (Pair<String, String> pair : fields) { if (pair.getKey().equals("spoiler")) continue; String val; switch (pair.getKey()) { case "subject": val = model.subject; break; case "body": val = model.comment; break; case "password": val = model.password; break; default: val = pair.getValue(); } int i = 1; String fileNo; switch (pair.getKey()) { case "file": case "file2": case "file3": case "file4": fileNo = pair.getKey().replaceAll("[\\D]", ""); if (fileNo != "") { i = Integer.parseInt(fileNo); } if (model.attachments == null || model.attachments.length < i) { postEntityBuilder.addPart(pair.getKey(), new ByteArrayBody(new byte[0], "")); } else { postEntityBuilder.addFile(pair.getKey(), model.attachments[i - 1], model.randomHash); } break; default: postEntityBuilder.addString(pair.getKey(), val); } } String url = getUsingUrl() + "post.php"; Header[] customHeaders = new Header[] { new BasicHeader(HttpHeaders.REFERER, referer) }; HttpRequestModel request = HttpRequestModel.builder().setPOST(postEntityBuilder.build()).setCustomHeaders(customHeaders).setNoRedirect(true).build(); HttpResponseModel response = null; try { response = HttpStreamer.getInstance().getFromUrl(url, request, httpClient, listener, task); if (response.statusCode == 200 || response.statusCode == 400) { ByteArrayOutputStream output = new ByteArrayOutputStream(1024); IOUtils.copyStream(response.stream, output); String htmlResponse = output.toString("UTF-8"); Matcher errorMatcher = ERROR_PATTERN.matcher(htmlResponse); if (errorMatcher.find()) throw new Exception(errorMatcher.group(1)); } else if (response.statusCode == 303) { for (Header header : response.headers) { if (header != null && HttpHeaders.LOCATION.equalsIgnoreCase(header.getName())) { return fixRelativeUrl(header.getValue()); } } } throw new Exception(response.statusCode + " - " + response.statusReason); } finally { if (response != null) response.release(); } } @Override protected String getDeleteFormValue(DeletePostModel model) { return model.boardName.equals("int") ? "Delete" : "Видалити"; } @Override protected String getReportFormValue(DeletePostModel model) { return model.boardName.equals("int") ? "Report" : "Поскаржитися"; } }