package org.loklak.api.iot; import org.eclipse.jetty.util.log.Log; import org.json.JSONArray; import org.json.JSONObject; import org.loklak.data.DAO; import org.loklak.harvester.HarvestingFrequency; import org.loklak.objects.ImportProfileEntry; import org.loklak.objects.MessageEntry; import org.loklak.objects.SourceType; import org.loklak.objects.Timeline; import org.loklak.objects.UserEntry; import org.loklak.server.Query; import java.io.IOException; import java.util.List; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; public class PushServletHelper { /* Fields that can be updated */ public static final String[] FIELDS_TO_COMPARE = { "link", "text" // can embed rich content }; public static PushReport saveMessagesAndImportProfile( JSONArray messages, int fileHash, Query post, SourceType sourceType, String screenName) throws IOException { PushReport report = new PushReport(); List<String> importedMsgIds = new ArrayList<>(); for (Object message_obj : messages) { JSONObject message = (JSONObject) message_obj; message.put("screen_name", screenName); JSONObject user = (JSONObject) message.remove("user"); if (user != null) user.put("screen_name", screenName); MessageEntry messageEntry = new MessageEntry(message); UserEntry userEntry = new UserEntry(user != null ? user : new JSONObject()); boolean successful; report.incrementRecordCount(); try { DAO.MessageWrapper mw = new DAO.MessageWrapper(messageEntry, userEntry, true); successful = DAO.writeMessage(mw); } catch (Exception e) { Log.getLog().warn(e); report.incrementErrorCount(); continue; } if (successful) { report.incrementNewCount(); importedMsgIds.add((String) message.get("id_str")); } else { report.incrementKnownCount((String) message.get("id_str")); } } if (report.getNewCount() > 0 ) { ImportProfileEntry importProfileEntry = saveImportProfile(fileHash, post, sourceType, screenName, importedMsgIds); report.setImportProfile(importProfileEntry); } return report; } protected static ImportProfileEntry saveImportProfile(int fileHash, Query post, SourceType sourceType, String screenName, List<String> importedMsgIds) throws IOException { ImportProfileEntry importProfileEntry ; JSONObject profile = new JSONObject(); profile.put("client_host", post.getClientHost()); profile.put("imported", importedMsgIds); profile.put("importer", screenName); String harvesting_freq = post.get("harvesting_freq", ""); // optional parameter 'public' to String public_profile = post.get("public", ""); ImportProfileEntry.PrivacyStatus privacyStatus; if ("".equals(public_profile) || !"true".equals(public_profile)){ privacyStatus = ImportProfileEntry.PrivacyStatus.PRIVATE; } else { privacyStatus = ImportProfileEntry.PrivacyStatus.PUBLIC; } if (!"".equals(harvesting_freq)) { try { profile.put("harvesting_freq", HarvestingFrequency.valueOf(Integer.parseInt(harvesting_freq)).getFrequency()); } catch (IllegalArgumentException e) { Log.getLog().warn(e); throw new IOException("Unsupported 'harvesting_freq' parameter value : " + harvesting_freq); } } else { profile.put("harvesting_freq", HarvestingFrequency.NEVER.getFrequency()); } String lifetime_str = post.get("lifetime", ""); if (!"".equals(lifetime_str)) { long lifetime; try { lifetime = Long.parseLong(lifetime_str); } catch (NumberFormatException e) { Log.getLog().warn(e); throw new IOException("Invalid lifetime parameter (must be an integer) : " + lifetime_str); } profile.put("lifetime", lifetime); } else { profile.put("lifetime", Integer.MAX_VALUE); } profile.put("source_url", post.get("url", "")); profile.put("source_type", sourceType.toString()); profile.put("source_hash", fileHash); profile.put("id_str", computeImportProfileId(profile, fileHash)); Date currentDate = new Date(); profile.put("created_at" , currentDate); profile.put("last_modified", currentDate); profile.put("last_harvested", currentDate); profile.put("privacy_status", privacyStatus.name()); List<String> sharers = new ArrayList<>(); sharers.add(screenName); profile.put("sharers", sharers); try { importProfileEntry = new ImportProfileEntry(profile); } catch (Exception e) { Log.getLog().warn(e); throw new IOException("Unable to create import profile : " + e.getMessage()); } boolean success = DAO.writeImportProfile(importProfileEntry, true); if (!success) { Log.getLog().warn("Error saving import profile from " + post.getClientHost()); throw new IOException("Unable to save import profile : " + importProfileEntry); } return importProfileEntry; } public static String buildJSONResponse(String callback, PushReport pushReport) throws IOException { // generate json JSONObject json = new JSONObject(true); json.put("status", "ok"); json.put("records", pushReport.getRecordCount()); json.put("new", pushReport.getNewCount()); json.put("known", pushReport.getKnownCount()); json.put("knownIds", pushReport.getKnownMessageIds()); json.put("error", pushReport.getErrorCount()); ImportProfileEntry importProfile = pushReport.getImportProfile(); if (importProfile != null) json.put("importProfile", importProfile.toJSON()); json.put("message", "pushed"); // build result String result = ""; boolean jsonp = callback != null && callback.length() > 0; if (jsonp) result += callback + "("; result += json.toString(2); if (jsonp) result += ");"; return result; } private static String computeImportProfileId(JSONObject importProfile, int fileHash) { String importer = (String) importProfile.get("importer"); String source_url = (String) importProfile.get("source_url"); return source_url + "_" + importer + "_" + fileHash; } public static String checkMessageExistence(JSONObject message) { String source_type = (String) message.get("source_type"); JSONArray location_point = message.getJSONArray("location_point"); Double latitude = (Double) location_point.get(0); Double longitude = (Double) location_point.get(1); String query = "/source_type=" + source_type + " /location=" + latitude + "," + longitude; // search only latest message DAO.SearchLocalMessages search = new DAO.SearchLocalMessages(query, Timeline.Order.CREATED_AT, 0, 1, 0); Iterator<MessageEntry> it = search.timeline.iterator(); while (it.hasNext()) { MessageEntry messageEntry = it.next(); if (compareMessage(messageEntry.toJSON(), message)) { return messageEntry.getIdStr(); } } return null; } private static boolean compareMessage(JSONObject m1, JSONObject m2) { for (String field : FIELDS_TO_COMPARE) { if (!m1.has(field) && m2.has(field)) return false; if (m1.has(field) && !m2.has(field)) return false; if (!m1.has(field) && !m2.has(field)) continue; if (!m1.get(field).equals(m2.get(field))) return false; } return true; } public static String computeMessageId(JSONObject message, SourceType sourceType) throws Exception { JSONArray location = message.getJSONArray("location_point"); if (location == null) { throw new Exception("location_point not found"); } String longitude, latitude; try { Object rawLon = location.get(1); longitude = rawLon instanceof Integer ? Integer.toString((Integer) rawLon) : (rawLon instanceof Double ? Double.toString((Double) rawLon) : (String) rawLon); Object rawLat = location.get(0); latitude = rawLat instanceof Integer ? Integer.toString((Integer) rawLat) : (rawLat instanceof Double ? Double.toString((Double) rawLat) : (String) rawLat); } catch (ClassCastException e) { throw new ClassCastException("Unable to extract lat, lon from location_point " + e.getMessage()); } // Modification time = 'mtime' value. If not found, take current time Object mtime = message.get("mtime"); if (mtime == null) { mtime = Long.toString(System.currentTimeMillis()); message.put("mtime", mtime); } // Id format : <source_type>_<lat>_<lon>_<mtime> return sourceType.toString() + "_" + latitude + "_" + longitude + "_" + mtime; } }