| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.openmeetings.backup; |
| |
| import static java.util.Map.entry; |
| import static java.util.UUID.randomUUID; |
| import static org.apache.openmeetings.db.bind.Constants.APPOINTMENT_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.APPOINTMENT_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.CALENDAR_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.CALENDAR_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.CFG_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.CFG_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.CHAT_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.CHAT_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.CONTACT_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.CONTACT_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.EXTRA_MENU_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.EXTRA_MENU_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.FILE_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.FILE_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.GROUP_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.GROUP_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.MMEMBER_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.MMEMBER_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.MSG_FOLDER_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.MSG_FOLDER_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.MSG_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.MSG_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.OAUTH_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.OAUTH_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.POLL_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.POLL_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.RECORDING_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.RECORDING_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.ROOM_FILE_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.ROOM_FILE_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.ROOM_GRP_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.ROOM_GRP_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.ROOM_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.ROOM_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.USER_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.USER_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.VERSION_LIST_NODE; |
| import static org.apache.openmeetings.db.bind.Constants.VERSION_NODE; |
| import static org.apache.openmeetings.db.entity.user.PrivateMessage.INBOX_FOLDER_ID; |
| import static org.apache.openmeetings.db.entity.user.PrivateMessage.SENT_FOLDER_ID; |
| import static org.apache.openmeetings.db.entity.user.PrivateMessage.TRASH_FOLDER_ID; |
| import static org.apache.openmeetings.util.OmFileHelper.BCKP_RECORD_FILES; |
| import static org.apache.openmeetings.util.OmFileHelper.BCKP_ROOM_FILES; |
| import static org.apache.openmeetings.util.OmFileHelper.CSS_DIR; |
| import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_CSS; |
| import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_JPG; |
| import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_MP4; |
| import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_PNG; |
| import static org.apache.openmeetings.util.OmFileHelper.FILES_DIR; |
| import static org.apache.openmeetings.util.OmFileHelper.FILE_NAME_FMT; |
| import static org.apache.openmeetings.util.OmFileHelper.GROUP_CSS_PREFIX; |
| import static org.apache.openmeetings.util.OmFileHelper.GROUP_LOGO_DIR; |
| import static org.apache.openmeetings.util.OmFileHelper.GROUP_LOGO_PREFIX; |
| import static org.apache.openmeetings.util.OmFileHelper.PROFILES_DIR; |
| import static org.apache.openmeetings.util.OmFileHelper.PROFILES_PREFIX; |
| import static org.apache.openmeetings.util.OmFileHelper.RECORDING_FILE_NAME; |
| import static org.apache.openmeetings.util.OmFileHelper.WML_DIR; |
| import static org.apache.openmeetings.util.OmFileHelper.getCssDir; |
| import static org.apache.openmeetings.util.OmFileHelper.getFileExt; |
| import static org.apache.openmeetings.util.OmFileHelper.getFileName; |
| import static org.apache.openmeetings.util.OmFileHelper.getGroupCss; |
| import static org.apache.openmeetings.util.OmFileHelper.getGroupLogo; |
| import static org.apache.openmeetings.util.OmFileHelper.getName; |
| import static org.apache.openmeetings.util.OmFileHelper.getStreamsHibernateDir; |
| import static org.apache.openmeetings.util.OmFileHelper.getUploadFilesDir; |
| import static org.apache.openmeetings.util.OmFileHelper.getUploadProfilesUserDir; |
| import static org.apache.openmeetings.util.OmFileHelper.getUploadWmlDir; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_APPOINTMENT_REMINDER_MINUTES; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_CALENDAR_ROOM_CAPACITY; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_CAM_FPS; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_CRYPT; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_CSP_FRAME; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DASHBOARD_RSS_FEED1; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DASHBOARD_RSS_FEED2; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DASHBOARD_SHOW_CHAT; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DASHBOARD_SHOW_MYROOMS; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DASHBOARD_SHOW_RSS; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DEFAULT_GROUP_ID; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DEFAULT_LANG; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DEFAULT_LDAP_ID; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DOCUMENT_DPI; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DOCUMENT_QUALITY; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_EMAIL_AT_REGISTER; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_EMAIL_VERIFICATION; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_EXT_PROCESS_TTL; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_IGNORE_BAD_SSL; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_KEYCODE_ARRANGE; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_KEYCODE_MUTE; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_KEYCODE_MUTE_OTHERS; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_LOGIN_MIN_LENGTH; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_MAX_UPLOAD_SIZE; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_MIC_ECHO; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_MIC_NOISE; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_MIC_RATE; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_MYROOMS_ENABLED; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_PASS_MIN_LENGTH; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_PATH_FFMPEG; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_PATH_IMAGEMAGIC; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_PATH_OFFICE; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_PATH_SOX; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_RECORDING_ENABLED; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_REGISTER_FRONTEND; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_REGISTER_OAUTH; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_REGISTER_SOAP; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_REMINDER_MESSAGE; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_REPLY_TO_ORGANIZER; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SCREENSHARING_ALLOW_REMOTE; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SCREENSHARING_FPS; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SCREENSHARING_FPS_SHOW; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SCREENSHARING_QUALITY; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SIP_ENABLED; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SIP_EXTEN_CONTEXT; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SIP_ROOM_PREFIX; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SMTP_PASS; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SMTP_PORT; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SMTP_SERVER; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SMTP_SYSTEM_EMAIL; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SMTP_TIMEOUT; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SMTP_TIMEOUT_CON; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SMTP_TLS; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SMTP_USER; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.getDefaultTimezone; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.getMinLoginLength; |
| |
| import java.awt.event.KeyEvent; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.TreeMap; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import java.util.function.BiConsumer; |
| import java.util.function.Consumer; |
| import java.util.regex.Pattern; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipInputStream; |
| |
| import javax.xml.bind.JAXBContext; |
| import javax.xml.bind.Unmarshaller; |
| import javax.xml.stream.XMLInputFactory; |
| import javax.xml.stream.XMLStreamConstants; |
| import javax.xml.stream.XMLStreamReader; |
| import javax.xml.transform.stream.StreamSource; |
| |
| import org.apache.commons.io.FileUtils; |
| import org.apache.commons.io.IOUtils; |
| import org.apache.commons.text.WordUtils; |
| import org.apache.openmeetings.backup.converter.WbConverter; |
| import org.apache.openmeetings.core.converter.DocumentConverter; |
| import org.apache.openmeetings.db.bind.adapter.AppointmentAdapter; |
| import org.apache.openmeetings.db.bind.adapter.FileAdapter; |
| import org.apache.openmeetings.db.bind.adapter.GroupAdapter; |
| import org.apache.openmeetings.db.bind.adapter.OmCalendarAdapter; |
| import org.apache.openmeetings.db.bind.adapter.RoomAdapter; |
| import org.apache.openmeetings.db.bind.adapter.UserAdapter; |
| import org.apache.openmeetings.db.dao.basic.ChatDao; |
| import org.apache.openmeetings.db.dao.basic.ConfigurationDao; |
| import org.apache.openmeetings.db.dao.calendar.AppointmentDao; |
| import org.apache.openmeetings.db.dao.calendar.MeetingMemberDao; |
| import org.apache.openmeetings.db.dao.calendar.OmCalendarDao; |
| import org.apache.openmeetings.db.dao.file.BaseFileItemDao; |
| import org.apache.openmeetings.db.dao.file.FileItemDao; |
| import org.apache.openmeetings.db.dao.record.RecordingDao; |
| import org.apache.openmeetings.db.dao.room.ExtraMenuDao; |
| import org.apache.openmeetings.db.dao.room.PollDao; |
| import org.apache.openmeetings.db.dao.room.RoomDao; |
| import org.apache.openmeetings.db.dao.server.LdapConfigDao; |
| import org.apache.openmeetings.db.dao.server.OAuth2Dao; |
| import org.apache.openmeetings.db.dao.user.GroupDao; |
| import org.apache.openmeetings.db.dao.user.PrivateMessageDao; |
| import org.apache.openmeetings.db.dao.user.PrivateMessageFolderDao; |
| import org.apache.openmeetings.db.dao.user.UserContactDao; |
| import org.apache.openmeetings.db.dao.user.UserDao; |
| import org.apache.openmeetings.db.dto.room.Whiteboard; |
| import org.apache.openmeetings.db.entity.basic.ChatMessage; |
| import org.apache.openmeetings.db.entity.basic.Configuration; |
| import org.apache.openmeetings.db.entity.calendar.Appointment; |
| import org.apache.openmeetings.db.entity.calendar.MeetingMember; |
| import org.apache.openmeetings.db.entity.calendar.OmCalendar; |
| import org.apache.openmeetings.db.entity.file.BaseFileItem; |
| import org.apache.openmeetings.db.entity.file.FileItem; |
| import org.apache.openmeetings.db.entity.record.Recording; |
| import org.apache.openmeetings.db.entity.record.RecordingChunk; |
| import org.apache.openmeetings.db.entity.room.ExtraMenu; |
| import org.apache.openmeetings.db.entity.room.Room; |
| import org.apache.openmeetings.db.entity.room.RoomFile; |
| import org.apache.openmeetings.db.entity.room.RoomGroup; |
| import org.apache.openmeetings.db.entity.room.RoomModerator; |
| import org.apache.openmeetings.db.entity.room.RoomPoll; |
| import org.apache.openmeetings.db.entity.room.RoomPollAnswer; |
| import org.apache.openmeetings.db.entity.server.LdapConfig; |
| import org.apache.openmeetings.db.entity.server.OAuthServer; |
| import org.apache.openmeetings.db.entity.user.Group; |
| import org.apache.openmeetings.db.entity.user.GroupUser; |
| import org.apache.openmeetings.db.entity.user.PrivateMessage; |
| import org.apache.openmeetings.db.entity.user.PrivateMessageFolder; |
| import org.apache.openmeetings.db.entity.user.User; |
| import org.apache.openmeetings.db.entity.user.UserContact; |
| import org.apache.openmeetings.db.util.AuthLevelUtil; |
| import org.apache.openmeetings.db.util.XmlHelper; |
| import org.apache.openmeetings.util.OmFileHelper; |
| import org.apache.openmeetings.util.StoredFile; |
| import org.apache.openmeetings.util.crypt.SCryptImplementation; |
| import org.apache.wicket.util.string.Strings; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.springframework.beans.factory.annotation.Autowired; |
| import org.springframework.stereotype.Component; |
| |
| @Component |
| public class BackupImport { |
| private static final Logger log = LoggerFactory.getLogger(BackupImport.class); |
| private static final Map<String, String> outdatedConfigKeys = Map.ofEntries( |
| entry("crypt_ClassName", CONFIG_CRYPT) |
| , entry("system_email_addr", CONFIG_SMTP_SYSTEM_EMAIL) |
| , entry("smtp_server", CONFIG_SMTP_SERVER) |
| , entry("smtp_port", CONFIG_SMTP_PORT) |
| , entry("email_username", CONFIG_SMTP_USER) |
| , entry("email_userpass", CONFIG_SMTP_PASS) |
| , entry("default_lang_id", CONFIG_DEFAULT_LANG) |
| , entry("allow_frontend_register", CONFIG_REGISTER_FRONTEND) |
| , entry("max_upload_size", CONFIG_MAX_UPLOAD_SIZE) |
| , entry("rss_feed1", CONFIG_DASHBOARD_RSS_FEED1) |
| , entry("rss_feed2", CONFIG_DASHBOARD_RSS_FEED2) |
| , entry("oauth2.ignore_bad_ssl", CONFIG_IGNORE_BAD_SSL) |
| , entry("default.quality.screensharing", CONFIG_SCREENSHARING_QUALITY) |
| , entry("default.fps.screensharing", CONFIG_SCREENSHARING_FPS) |
| , entry("ldap_default_id", CONFIG_DEFAULT_LDAP_ID) |
| , entry("default_group_id", CONFIG_DEFAULT_GROUP_ID) |
| , entry("imagemagick_path", CONFIG_PATH_IMAGEMAGIC) |
| , entry("sox_path", CONFIG_PATH_SOX) |
| , entry("ffmpeg_path", CONFIG_PATH_FFMPEG) |
| , entry("office.path", CONFIG_PATH_OFFICE) |
| , entry("red5sip.enable", CONFIG_SIP_ENABLED) |
| , entry("red5sip.room_prefix", CONFIG_SIP_ROOM_PREFIX) |
| , entry("red5sip.exten_context", CONFIG_SIP_EXTEN_CONTEXT) |
| , entry("sendEmailAtRegister", CONFIG_EMAIL_AT_REGISTER) |
| , entry("sendEmailWithVerficationCode", CONFIG_EMAIL_VERIFICATION) |
| , entry("swftools_zoom", CONFIG_DOCUMENT_DPI) |
| , entry("swftools_jpegquality", CONFIG_DOCUMENT_QUALITY) |
| , entry("sms.subject", CONFIG_REMINDER_MESSAGE) |
| , entry("exclusive.audio.keycode", CONFIG_KEYCODE_MUTE_OTHERS) |
| , entry("header.csp.frame.options", CONFIG_CSP_FRAME) |
| ); |
| private static final Map<String, Configuration.Type> configTypes = Map.ofEntries( |
| entry(CONFIG_REGISTER_FRONTEND, Configuration.Type.BOOL) |
| , entry(CONFIG_REGISTER_SOAP, Configuration.Type.BOOL) |
| , entry(CONFIG_REGISTER_OAUTH, Configuration.Type.BOOL) |
| , entry(CONFIG_SMTP_TLS, Configuration.Type.BOOL) |
| , entry(CONFIG_EMAIL_AT_REGISTER, Configuration.Type.BOOL) |
| , entry(CONFIG_EMAIL_VERIFICATION, Configuration.Type.BOOL) |
| , entry(CONFIG_SIP_ENABLED, Configuration.Type.BOOL) |
| , entry(CONFIG_SCREENSHARING_FPS_SHOW, Configuration.Type.BOOL) |
| , entry(CONFIG_SCREENSHARING_ALLOW_REMOTE, Configuration.Type.BOOL) |
| , entry(CONFIG_DASHBOARD_SHOW_MYROOMS, Configuration.Type.BOOL) |
| , entry(CONFIG_DASHBOARD_SHOW_CHAT, Configuration.Type.BOOL) |
| , entry(CONFIG_DASHBOARD_SHOW_RSS, Configuration.Type.BOOL) |
| , entry(CONFIG_REPLY_TO_ORGANIZER, Configuration.Type.BOOL) |
| , entry(CONFIG_IGNORE_BAD_SSL, Configuration.Type.BOOL) |
| , entry(CONFIG_MYROOMS_ENABLED, Configuration.Type.BOOL) |
| , entry(CONFIG_DEFAULT_GROUP_ID, Configuration.Type.NUMBER) |
| , entry(CONFIG_SMTP_PORT, Configuration.Type.NUMBER) |
| , entry(CONFIG_SMTP_TIMEOUT_CON, Configuration.Type.NUMBER) |
| , entry(CONFIG_SMTP_TIMEOUT, Configuration.Type.NUMBER) |
| , entry(CONFIG_DEFAULT_LANG, Configuration.Type.NUMBER) |
| , entry(CONFIG_DOCUMENT_DPI, Configuration.Type.NUMBER) |
| , entry(CONFIG_DOCUMENT_QUALITY, Configuration.Type.NUMBER) |
| , entry(CONFIG_SCREENSHARING_QUALITY, Configuration.Type.NUMBER) |
| , entry(CONFIG_SCREENSHARING_FPS, Configuration.Type.NUMBER) |
| , entry(CONFIG_MAX_UPLOAD_SIZE, Configuration.Type.NUMBER) |
| , entry(CONFIG_APPOINTMENT_REMINDER_MINUTES, Configuration.Type.NUMBER) |
| , entry(CONFIG_LOGIN_MIN_LENGTH, Configuration.Type.NUMBER) |
| , entry(CONFIG_PASS_MIN_LENGTH, Configuration.Type.NUMBER) |
| , entry(CONFIG_CALENDAR_ROOM_CAPACITY, Configuration.Type.NUMBER) |
| , entry(CONFIG_KEYCODE_ARRANGE, Configuration.Type.HOTKEY) |
| , entry(CONFIG_KEYCODE_MUTE_OTHERS, Configuration.Type.HOTKEY) |
| , entry(CONFIG_KEYCODE_MUTE, Configuration.Type.HOTKEY) |
| , entry(CONFIG_DEFAULT_LDAP_ID, Configuration.Type.NUMBER) |
| , entry(CONFIG_CAM_FPS, Configuration.Type.NUMBER) |
| , entry(CONFIG_MIC_RATE, Configuration.Type.NUMBER) |
| , entry(CONFIG_MIC_ECHO, Configuration.Type.BOOL) |
| , entry(CONFIG_MIC_NOISE, Configuration.Type.BOOL) |
| , entry(CONFIG_EXT_PROCESS_TTL, Configuration.Type.NUMBER) |
| , entry(CONFIG_RECORDING_ENABLED, Configuration.Type.BOOL) |
| ); |
| private static final Pattern UUID_PATTERN = Pattern.compile("^[\\da-f]{8}(?:-[\\da-f]{4}){3}-[\\da-f]{12}$"); |
| |
| @Autowired |
| private AppointmentDao appointmentDao; |
| @Autowired |
| private OmCalendarDao calendarDao; |
| @Autowired |
| private RoomDao roomDao; |
| @Autowired |
| private UserDao userDao; |
| @Autowired |
| private RecordingDao recordingDao; |
| @Autowired |
| private PrivateMessageFolderDao privateMessageFolderDao; |
| @Autowired |
| private PrivateMessageDao privateMessageDao; |
| @Autowired |
| private MeetingMemberDao meetingMemberDao; |
| @Autowired |
| private LdapConfigDao ldapConfigDao; |
| @Autowired |
| private FileItemDao fileItemDao; |
| @Autowired |
| private UserContactDao userContactDao; |
| @Autowired |
| private PollDao pollDao; |
| @Autowired |
| private ConfigurationDao cfgDao; |
| @Autowired |
| private ChatDao chatDao; |
| @Autowired |
| private OAuth2Dao auth2Dao; |
| @Autowired |
| private GroupDao groupDao; |
| @Autowired |
| private ExtraMenuDao menuDao; |
| @Autowired |
| private DocumentConverter docConverter; |
| |
| private final Map<Long, Long> ldapMap = new HashMap<>(); |
| private final Map<Long, Long> oauthMap = new HashMap<>(); |
| private final Map<Long, Long> userMap = new HashMap<>(); |
| private final Map<Long, Long> groupMap = new HashMap<>(); |
| private final Map<Long, Long> calendarMap = new HashMap<>(); |
| private final Map<Long, Long> appointmentMap = new HashMap<>(); |
| private final Map<Long, Long> roomMap = new HashMap<>(); |
| private final Map<Long, Long> fileItemMap = new HashMap<>(); |
| private final Map<Long, Long> messageFolderMap = new HashMap<>(); |
| private final Map<Long, Long> userContactMap = new HashMap<>(); |
| private final Map<String, String> fileMap = new HashMap<>(); |
| private final Map<String, String> hashMap = new HashMap<>(); |
| |
| private static File validate(String ename, File intended) throws IOException { |
| final String intendedPath = intended.getCanonicalPath(); |
| // for each entry to be extracted |
| File fentry = new File(intended, ename); |
| final String canonicalPath = fentry.getCanonicalPath(); |
| |
| if (canonicalPath.startsWith(intendedPath)) { |
| return fentry; |
| } else { |
| throw new IllegalStateException("File is outside extraction target directory."); |
| } |
| } |
| |
| private static File unzip(InputStream is) throws IOException { |
| File f = OmFileHelper.getNewDir(OmFileHelper.getUploadImportDir(), randomUUID().toString()); |
| log.debug("##### EXTRACTING BACKUP TO: {}", f); |
| |
| try (ZipInputStream zis = new ZipInputStream(is)) { |
| ZipEntry zipentry = null; |
| while ((zipentry = zis.getNextEntry()) != null) { |
| // for each entry to be extracted |
| File fentry = validate(zipentry.getName(), f); |
| File dir = zipentry.isDirectory() ? fentry : fentry.getParentFile(); |
| if (!dir.exists() && !dir.mkdirs()) { |
| log.warn("Failed to create folders: {}", dir); |
| } |
| if (!fentry.isDirectory()) { |
| try (FileOutputStream fos = FileUtils.openOutputStream(fentry)) { |
| IOUtils.copy(zis, fos); |
| } |
| zis.closeEntry(); |
| } |
| } |
| } |
| return f; |
| } |
| |
| public void performImport(InputStream is, AtomicInteger progress) throws Exception { |
| File f = null; |
| boolean success = false; |
| try { |
| progress.set(0); |
| cleanup(); |
| messageFolderMap.put(INBOX_FOLDER_ID, INBOX_FOLDER_ID); |
| messageFolderMap.put(SENT_FOLDER_ID, SENT_FOLDER_ID); |
| messageFolderMap.put(TRASH_FOLDER_ID, TRASH_FOLDER_ID); |
| |
| f = unzip(is); |
| |
| BackupVersion ver = getVersion(f); |
| progress.set(2); |
| importConfigs(f); |
| progress.set(7); |
| importGroups(f); |
| progress.set(12); |
| importLdap(f); |
| progress.set(17); |
| importOauth(f); |
| progress.set(22); |
| importUsers(f); |
| progress.set(27); |
| importRooms(f); |
| progress.set(32); |
| importRoomGroups(f); |
| progress.set(37); |
| importChat(f); |
| progress.set(42); |
| importCalendars(f); |
| progress.set(47); |
| importAppointments(f); |
| progress.set(52); |
| importMeetingMembers(f); |
| progress.set(57); |
| importRecordings(f); |
| progress.set(62); |
| importPrivateMsgFolders(f); |
| progress.set(67); |
| importContacts(f); |
| progress.set(72); |
| importPrivateMsgs(f); |
| progress.set(77); |
| List<FileItem> files = importFiles(f); |
| progress.set(82); |
| importPolls(f); |
| progress.set(87); |
| importRoomFiles(f); |
| progress.set(92); |
| importExtraMenus(f); |
| progress.set(95); |
| |
| log.info("Extra menus import complete, starting copy of files and folders"); |
| /* |
| * ##################### Import real files and folders |
| */ |
| importFolders(f); |
| progress.set(97); |
| |
| if (ver.compareTo(BackupVersion.get("4.0.0")) < 0) { |
| for (FileItem bfi : files) { |
| if (bfi.isDeleted()) { |
| continue; |
| } |
| if (BaseFileItem.Type.PRESENTATION == bfi.getType()) { |
| convertOldPresentation(bfi); |
| fileItemDao.updateBase(bfi); |
| } |
| if (BaseFileItem.Type.WML_FILE == bfi.getType()) { |
| convertWb(bfi); |
| } |
| } |
| } |
| log.info("File explorer item import complete"); |
| success = true; |
| } finally { |
| if (f != null) { |
| log.info("Clearing temp files ..."); |
| FileUtils.deleteDirectory(f); |
| } |
| cleanup(); |
| if (success) { |
| progress.set(100); |
| } |
| } |
| } |
| |
| private void convertWb(FileItem bfi) { |
| try { |
| Whiteboard wb = WbConverter.convert(bfi); |
| wb.save(bfi.getFile().toPath()); |
| } catch (Exception e) { |
| log.error("Unexpected error while converting WB", e); |
| } |
| } |
| |
| void cleanup() { |
| ldapMap.clear(); |
| oauthMap.clear(); |
| userMap.clear(); |
| groupMap.clear(); |
| calendarMap.clear(); |
| appointmentMap.clear(); |
| roomMap.clear(); |
| messageFolderMap.clear(); |
| userContactMap.clear(); |
| fileMap.clear(); |
| hashMap.clear(); |
| messageFolderMap.put(INBOX_FOLDER_ID, INBOX_FOLDER_ID); |
| messageFolderMap.put(SENT_FOLDER_ID, SENT_FOLDER_ID); |
| messageFolderMap.put(TRASH_FOLDER_ID, TRASH_FOLDER_ID); |
| } |
| |
| static BackupVersion getVersion(File base) { |
| List<BackupVersion> list = new ArrayList<>(1); |
| readList(base, "version.xml", VERSION_LIST_NODE, VERSION_NODE, BackupVersion.class, list::add, true); |
| return list.isEmpty() ? new BackupVersion() : list.get(0); |
| } |
| |
| /* |
| * ##################### Import Configs |
| */ |
| void importConfigs(File base) throws Exception { |
| final Map<Integer, String> keyMap = new HashMap<>(); |
| Arrays.stream(KeyEvent.class.getDeclaredFields()) |
| .filter(fld -> fld.getName().startsWith("VK_")) |
| .forEach(fld -> { |
| try { |
| keyMap.put(fld.getInt(null), "Shift+" + WordUtils.capitalizeFully(fld.getName().substring(3))); |
| } catch (IllegalArgumentException|IllegalAccessException e) { |
| log.error("Unexpected exception while building KEY map {}", fld); |
| } |
| }); |
| |
| Class<Configuration> eClazz = Configuration.class; |
| JAXBContext jc = JAXBContext.newInstance(eClazz); |
| Unmarshaller unmarshaller = jc.createUnmarshaller(); |
| unmarshaller.setAdapter(new UserAdapter(userDao, userMap)); |
| |
| readList(unmarshaller, base, "configs.xml", CFG_LIST_NODE, CFG_NODE, eClazz, c -> { |
| if (c.getKey() == null || c.isDeleted()) { |
| return; |
| } |
| String newKey = outdatedConfigKeys.get(c.getKey()); |
| if (newKey != null) { |
| c.setKey(newKey); |
| } |
| Configuration.Type type = configTypes.get(c.getKey()); |
| if (type != null) { |
| c.setType(type); |
| if (Configuration.Type.BOOL == type) { |
| c.setValue(String.valueOf("1".equals(c.getValue()) || "yes".equals(c.getValue()) || "true".equals(c.getValue()))); |
| } else if (Configuration.Type.HOTKEY == type) { |
| try { |
| int val = c.getValueN().intValue(); |
| c.setValue(keyMap.get(val)); |
| } catch(Exception e) { |
| //no-op, value is already HOTKEY |
| } |
| } |
| } |
| Configuration cfg = cfgDao.forceGet(c.getKey()); |
| if (cfg != null && !cfg.isDeleted()) { |
| log.warn("Non deleted configuration with same key is found! old value: {}, new value: {}", cfg.getValue(), c.getValue()); |
| } |
| c.setId(cfg == null ? null : cfg.getId()); |
| if (c.getUser() != null && c.getUser().getId() == null) { |
| c.setUser(null); |
| } |
| if (CONFIG_CRYPT.equals(c.getKey())) { |
| try { |
| Class<?> clazz = Class.forName(c.getValue()); |
| clazz.getDeclaredConstructor().newInstance(); |
| } catch (Exception e) { |
| log.warn("Not existing Crypt class found {}, replacing with SCryptImplementation", c.getValue()); |
| c.setValue(SCryptImplementation.class.getCanonicalName()); |
| } |
| } |
| cfgDao.update(c, null); |
| }); |
| } |
| |
| /* |
| * ##################### Import Groups |
| */ |
| void importGroups(File base) throws Exception { |
| log.info("Configs import complete, starting group import"); |
| readList(base, "organizations.xml", GROUP_LIST_NODE, GROUP_NODE, Group.class, g -> { |
| Long oldId = g.getId(); |
| g.setId(null); |
| g = groupDao.update(g, null); |
| groupMap.put(oldId, g.getId()); |
| }); |
| } |
| |
| /* |
| * ##################### Import LDAP Configs |
| */ |
| Long importLdap(File base) { |
| log.info("Groups import complete, starting LDAP config import"); |
| Long[] defaultLdapId = {cfgDao.getLong(CONFIG_DEFAULT_LDAP_ID, null)}; |
| readList(base, "ldapconfigs.xml", "ldapconfigs", "ldapconfig", LdapConfig.class, c -> { |
| if (Strings.isEmpty(c.getName()) || "local DB [internal]".equals(c.getName())) { |
| return; |
| } |
| Long oldId = c.getId(); |
| c.setId(null); |
| c = ldapConfigDao.update(c, null); |
| if (defaultLdapId[0] == null) { |
| defaultLdapId[0] = c.getId(); |
| } |
| if (oldId != null) { |
| ldapMap.put(oldId, c.getId()); |
| } |
| }); |
| return defaultLdapId[0]; |
| } |
| |
| /* |
| * ##################### OAuth2 servers |
| */ |
| void importOauth(File base) { |
| log.info("Ldap config import complete, starting OAuth2 server import"); |
| readList(base, "oauth2servers.xml", OAUTH_LIST_NODE, OAUTH_NODE, OAuthServer.class |
| , s -> { |
| Long oldId = s.getId(); |
| s.setId(null); |
| s = auth2Dao.update(s, null); |
| if (oldId != null) { |
| oauthMap.put(oldId, s.getId()); |
| } |
| }, false); |
| } |
| |
| /* |
| * ##################### Import Users |
| */ |
| void importUsers(File base) throws Exception { |
| log.info("OAuth2 servers import complete, starting user import"); |
| String jNameTimeZone = getDefaultTimezone(); |
| //add existent emails from database |
| List<User> users = userDao.getAllUsers(); |
| final Set<String> userEmails = new HashSet<>(); |
| final Set<UserKey> userLogins = new HashSet<>(); |
| for (User u : users){ |
| if (u.getAddress() != null && !Strings.isEmpty(u.getAddress().getEmail())) { |
| userEmails.add(u.getAddress().getEmail()); |
| } |
| userLogins.add(new UserKey(u)); |
| } |
| Class<User> eClazz = User.class; |
| JAXBContext jc = JAXBContext.newInstance(eClazz); |
| Unmarshaller unmarshaller = jc.createUnmarshaller(); |
| unmarshaller.setAdapter(new GroupAdapter(groupDao, groupMap)); |
| int minLoginLength = getMinLoginLength(); |
| |
| readList(unmarshaller, base, "users.xml", USER_LIST_NODE, USER_NODE, eClazz, u -> { |
| if (u.getLogin() == null || u.isDeleted()) { |
| return; |
| } |
| // check that email is unique |
| if (u.getAddress() != null && u.getAddress().getEmail() != null && User.Type.USER == u.getType()) { |
| if (userEmails.contains(u.getAddress().getEmail())) { |
| log.warn("Email is duplicated for user {}", u); |
| String updateEmail = String.format("modified_by_import_<%s>%s", randomUUID(), u.getAddress().getEmail()); |
| u.getAddress().setEmail(updateEmail); |
| } |
| userEmails.add(u.getAddress().getEmail()); |
| } |
| if (u.getType() == User.Type.LDAP) { |
| if (u.getDomainId() != null && ldapMap.containsKey(u.getDomainId())) { |
| u.setDomainId(ldapMap.get(u.getDomainId())); |
| } else { |
| log.error("Unable to find Domain for ID: {}", u.getDomainId()); |
| } |
| } |
| if (u.getType() == User.Type.OAUTH) { |
| if (u.getDomainId() != null && oauthMap.containsKey(u.getDomainId())) { |
| u.setDomainId(oauthMap.get(u.getDomainId())); |
| } else { |
| log.error("Unable to find Domain for ID: {}", u.getDomainId()); |
| } |
| } |
| if (userLogins.contains(new UserKey(u))) { |
| log.warn("LOGIN is duplicated for USER {}", u); |
| String updateLogin = String.format("modified_by_import_<%s>%s", randomUUID(), u.getLogin()); |
| u.setLogin(updateLogin); |
| } |
| userLogins.add(new UserKey(u)); |
| if (u.getGroupUsers() != null) { |
| for (Iterator<GroupUser> iter = u.getGroupUsers().iterator(); iter.hasNext();) { |
| GroupUser gu = iter.next(); |
| if (gu.getGroup().getId() == null) { |
| iter.remove(); |
| continue; |
| } |
| gu.setUser(u); |
| } |
| } |
| if (u.getType() == User.Type.CONTACT && u.getLogin().length() < minLoginLength) { |
| u.setLogin(randomUUID().toString()); |
| } |
| |
| String tz = u.getTimeZoneId(); |
| if (tz == null) { |
| u.setTimeZoneId(jNameTimeZone); |
| } |
| |
| Long userId = u.getId(); |
| u.setId(null); |
| if (u.getSipUser() != null && u.getSipUser().getId() != 0) { |
| u.getSipUser().setId(0); |
| } |
| if (AuthLevelUtil.hasLoginLevel(u.getRights()) && !Strings.isEmpty(u.getActivatehash())) { |
| u.setActivatehash(null); |
| } |
| if (u.getExternalType() != null) { |
| Group g = groupDao.getExternal(u.getExternalType()); |
| u.addGroup(g); |
| } |
| userDao.update(u, Long.valueOf(-1)); |
| userMap.put(userId, u.getId()); |
| }); |
| } |
| |
| /* |
| * ##################### Import Rooms |
| */ |
| void importRooms(File base) throws Exception { |
| log.info("Users import complete, starting room import"); |
| Class<Room> eClazz = Room.class; |
| JAXBContext jc = JAXBContext.newInstance(eClazz); |
| Unmarshaller unmarshaller = jc.createUnmarshaller(); |
| unmarshaller.setAdapter(new UserAdapter(userDao, userMap)); |
| |
| readList(unmarshaller, base, "rooms.xml", ROOM_LIST_NODE, ROOM_NODE, eClazz, r -> { |
| Long roomId = r.getId(); |
| |
| if (r.getOwnerId() != null) { |
| Long newOwnerId = userMap.get(r.getOwnerId()); |
| if (newOwnerId == null) { |
| return; // owner was deleted |
| } |
| r.setOwnerId(newOwnerId); |
| } |
| // We need to reset ids as openJPA reject to store them otherwise |
| r.setId(null); |
| if (r.getModerators() != null) { |
| for (Iterator<RoomModerator> i = r.getModerators().iterator(); i.hasNext();) { |
| RoomModerator rm = i.next(); |
| if (rm.getUser().getId() == null) { |
| i.remove(); |
| } |
| } |
| } |
| r = roomDao.update(r, null); |
| roomMap.put(roomId, r.getId()); |
| }); |
| } |
| |
| /* |
| * ##################### Import Room Groups |
| */ |
| void importRoomGroups(File base) throws Exception { |
| log.info("Room import complete, starting room groups import"); |
| Class<RoomGroup> eClazz = RoomGroup.class; |
| JAXBContext jc = JAXBContext.newInstance(eClazz); |
| Unmarshaller unmarshaller = jc.createUnmarshaller(); |
| unmarshaller.setAdapter(new RoomAdapter(roomDao, roomMap)); |
| unmarshaller.setAdapter(new GroupAdapter(groupDao, groupMap)); |
| |
| readList(unmarshaller, base, "rooms_organisation.xml", ROOM_GRP_LIST_NODE, ROOM_GRP_NODE, eClazz, rg -> { |
| if (rg.getRoom() == null || rg.getGroup() == null) { |
| return; |
| } |
| Room r = roomDao.get(rg.getRoom().getId()); |
| if (r == null || rg.getGroup().getId() == null) { |
| return; |
| } |
| if (r.getGroups() == null) { |
| r.setGroups(new ArrayList<>()); |
| } |
| rg.setId(null); |
| rg.setRoom(r); |
| r.getGroups().add(rg); |
| roomDao.update(r, null); |
| }); |
| } |
| |
| /* |
| * ##################### Import Chat messages |
| */ |
| void importChat(File base) throws Exception { |
| log.info("Room groups import complete, starting chat messages import"); |
| Class<ChatMessage> eClazz = ChatMessage.class; |
| JAXBContext jc = JAXBContext.newInstance(eClazz); |
| Unmarshaller unmarshaller = jc.createUnmarshaller(); |
| unmarshaller.setAdapter(new UserAdapter(userDao, userMap)); |
| unmarshaller.setAdapter(new RoomAdapter(roomDao, roomMap)); |
| |
| readList(unmarshaller, base, "chat_messages.xml", CHAT_LIST_NODE, CHAT_NODE, eClazz, m -> { |
| m.setId(null); |
| if (m.getFromUser() == null || m.getFromUser().getId() == null |
| || (m.getToRoom() != null && m.getToRoom().getId() == null) |
| || (m.getToUser() != null && m.getToUser().getId() == null)) |
| { |
| return; |
| } |
| chatDao.update(m, m.getSent()); |
| }); |
| } |
| |
| /* |
| * ##################### Import Calendars |
| */ |
| void importCalendars(File base) throws Exception { |
| log.info("Chat messages import complete, starting calendar import"); |
| Class<OmCalendar> eClazz = OmCalendar.class; |
| JAXBContext jc = JAXBContext.newInstance(eClazz); |
| Unmarshaller unmarshaller = jc.createUnmarshaller(); |
| unmarshaller.setAdapter(new UserAdapter(userDao, userMap)); |
| |
| readList(unmarshaller, base, "calendars.xml", CALENDAR_LIST_NODE, CALENDAR_NODE, eClazz, c -> { |
| Long id = c.getId(); |
| c.setId(null); |
| c = calendarDao.update(c); |
| calendarMap.put(id, c.getId()); |
| }, true); |
| } |
| |
| /* |
| * ##################### Import Appointments |
| */ |
| void importAppointments(File base) throws Exception { |
| log.info("Calendar import complete, starting appointement import"); |
| Class<Appointment> eClazz = Appointment.class; |
| JAXBContext jc = JAXBContext.newInstance(eClazz); |
| Unmarshaller unmarshaller = jc.createUnmarshaller(); |
| unmarshaller.setAdapter(new UserAdapter(userDao, userMap)); |
| unmarshaller.setAdapter(new RoomAdapter(roomDao, roomMap)); |
| unmarshaller.setAdapter(new OmCalendarAdapter(calendarDao, calendarMap)); |
| |
| readList(unmarshaller, base, "appointements.xml", APPOINTMENT_LIST_NODE, APPOINTMENT_NODE, eClazz, a -> { |
| Long appId = a.getId(); |
| |
| // We need to reset this as openJPA reject to store them otherwise |
| a.setId(null); |
| if (a.getOwner() != null && a.getOwner().getId() == null) { |
| a.setOwner(null); |
| } |
| if (a.getRoom() == null || a.getRoom().getId() == null) { |
| log.warn("Appointment without room was found, skipping: {}", a); |
| return; |
| } |
| if (a.getStart() == null || a.getEnd() == null) { |
| log.warn("Appointment without start/end time was found, skipping: {}", a); |
| return; |
| } |
| a = appointmentDao.update(a, null, false); |
| appointmentMap.put(appId, a.getId()); |
| }); |
| } |
| |
| /* |
| * ##################### Import MeetingMembers |
| * |
| * Reminder Invitations will be NOT send! |
| */ |
| void importMeetingMembers(File base) throws Exception { |
| log.info("Appointement import complete, starting meeting members import"); |
| Class<MeetingMember> eClazz = MeetingMember.class; |
| JAXBContext jc = JAXBContext.newInstance(eClazz); |
| Unmarshaller unmarshaller = jc.createUnmarshaller(); |
| unmarshaller.setAdapter(new UserAdapter(userDao, userMap)); |
| unmarshaller.setAdapter(new AppointmentAdapter(appointmentDao, appointmentMap)); |
| |
| readList(unmarshaller, base, "meetingmembers.xml", MMEMBER_LIST_NODE, MMEMBER_NODE, eClazz, ma -> { |
| ma.setId(null); |
| meetingMemberDao.update(ma); |
| }); |
| } |
| |
| private boolean isInvalidFile(BaseFileItem file, final Map<Long, Long> folders) { |
| if (file.isDeleted()) { |
| return true; |
| } |
| if (file.getParentId() != null && file.getParentId() > 0) { |
| Long newFolder = folders.get(file.getParentId()); |
| if (newFolder == null) { |
| //folder was deleted |
| return true; |
| } else { |
| file.setParentId(newFolder); |
| } |
| } else { |
| file.setParentId(null); |
| } |
| if (file.getRoomId() != null) { |
| Long newRoomId = roomMap.get(file.getRoomId()); |
| if (newRoomId == null) { |
| return true; // room was deleted |
| } |
| file.setRoomId(newRoomId); |
| } |
| if (file.getOwnerId() != null) { |
| Long newOwnerId = userMap.get(file.getOwnerId()); |
| if (newOwnerId == null) { |
| return true; // owner was deleted |
| } |
| file.setOwnerId(newOwnerId); |
| } |
| if (file.getInsertedBy() != null) { |
| Long newInsertedBy = userMap.get(file.getInsertedBy()); |
| file.setInsertedBy(newInsertedBy); |
| } |
| if (file.getGroupId() != null) { |
| Long newGroupId = groupMap.get(file.getGroupId()); |
| if (newGroupId == null) { |
| return true; // owner was deleted |
| } |
| file.setGroupId(newGroupId); |
| } |
| return false; |
| } |
| |
| private <T extends BaseFileItem> void saveTree( |
| File baseDir |
| , String fileName |
| , String listNodeName |
| , String nodeName |
| , Class<T> clazz |
| , Map<Long, Long> folders |
| , Consumer<T> save |
| ) |
| { |
| TreeMap<Long, T> items = new TreeMap<>(); |
| readList(baseDir, fileName, listNodeName, nodeName, clazz, f -> items.put(f.getId(), f), false); |
| FileTree<T> tree = new FileTree<>(); |
| TreeMap<Long, T> remain = new TreeMap<>(); |
| int counter = items.size(); //max iterations |
| while (counter > 0 && !items.isEmpty()) { |
| Entry<Long, T> e = items.pollFirstEntry(); |
| if (e == null) { |
| break; |
| } else { |
| if (!tree.add(e.getValue())) { |
| remain.put(e.getKey(), e.getValue()); |
| } |
| } |
| if (items.isEmpty()) { |
| counter = Math.min(counter - 1, remain.size()); |
| items.putAll(remain); |
| remain.clear(); |
| } |
| } |
| remain.entrySet().forEach(e -> log.warn("Doungling file/recording: {}", e.getValue())); |
| tree.process(f -> isInvalidFile(f, folders), save); |
| } |
| /* |
| * ##################### Import Recordings |
| */ |
| void importRecordings(File base) throws Exception { |
| log.info("Meeting members import complete, starting recordings server import"); |
| final Map<Long, Long> folders = new HashMap<>(); |
| saveTree(base, "flvRecordings.xml", RECORDING_LIST_NODE, RECORDING_NODE, Recording.class, folders, r -> { |
| Long recId = r.getId(); |
| r.setId(null); |
| if (r.getChunks() != null) { |
| for (RecordingChunk chunk : r.getChunks()) { |
| chunk.setId(null); |
| chunk.setRecording(r); |
| } |
| } |
| checkHash(r, recordingDao, (oldHash, newHash) -> { |
| if (!Strings.isEmpty(oldHash) && oldHash.startsWith(RECORDING_FILE_NAME)) { |
| String name = getFileName(oldHash); |
| fileMap.put(String.format(FILE_NAME_FMT, name, EXTENSION_JPG), String.format(FILE_NAME_FMT, newHash, EXTENSION_PNG)); |
| fileMap.put(String.format("%s.%s.%s", name, "flv", EXTENSION_MP4), String.format(FILE_NAME_FMT, newHash, EXTENSION_MP4)); |
| } |
| }); |
| r = recordingDao.update(r); |
| if (BaseFileItem.Type.FOLDER == r.getType()) { |
| folders.put(recId, r.getId()); |
| } |
| fileItemMap.put(recId, r.getId()); |
| }); |
| } |
| |
| private void checkHash(BaseFileItem file, BaseFileItemDao dao, BiConsumer<String, String> consumer) { |
| String oldHash = file.getHash(); |
| if (Strings.isEmpty(oldHash) || !UUID_PATTERN.matcher(oldHash).matches() || dao.get(oldHash, BaseFileItem.class) != null) { |
| file.setHash(randomUUID().toString()); |
| hashMap.put(oldHash, file.getHash()); |
| if (consumer != null) { |
| consumer.accept(oldHash, file.getHash()); |
| } |
| } else { |
| hashMap.put(file.getHash(), file.getHash()); |
| } |
| } |
| |
| /* |
| * ##################### Import Private Message Folders |
| */ |
| void importPrivateMsgFolders(File base) { |
| log.info("Recording import complete, starting private message folder import"); |
| readList(base, "privateMessageFolder.xml", MSG_FOLDER_LIST_NODE, MSG_FOLDER_NODE, PrivateMessageFolder.class, p -> { |
| Long folderId = p.getId(); |
| PrivateMessageFolder storedFolder = privateMessageFolderDao.get(folderId); |
| if (storedFolder == null) { |
| p.setId(null); |
| Long newFolderId = privateMessageFolderDao.addPrivateMessageFolderObj(p); |
| messageFolderMap.put(folderId, newFolderId); |
| } |
| }); |
| } |
| |
| /* |
| * ##################### Import User Contacts |
| */ |
| private void importContacts(File base) throws Exception { |
| log.info("Private message folder import complete, starting user contacts import"); |
| Class<UserContact> eClazz = UserContact.class; |
| JAXBContext jc = JAXBContext.newInstance(eClazz); |
| Unmarshaller unmarshaller = jc.createUnmarshaller(); |
| unmarshaller.setAdapter(new UserAdapter(userDao, userMap)); |
| |
| readList(unmarshaller, base, "userContacts.xml", CONTACT_LIST_NODE, CONTACT_NODE, eClazz, uc -> { |
| Long ucId = uc.getId(); |
| UserContact storedUC = userContactDao.get(ucId); |
| |
| if (storedUC == null && uc.getContact() != null && uc.getContact().getId() != null) { |
| uc.setId(null); |
| if (uc.getOwner() != null && uc.getOwner().getId() == null) { |
| uc.setOwner(null); |
| } |
| uc = userContactDao.update(uc); |
| userContactMap.put(ucId, uc.getId()); |
| } |
| }); |
| } |
| |
| /* |
| * ##################### Import Private Messages |
| */ |
| private void importPrivateMsgs(File base) throws Exception { |
| log.info("Usercontact import complete, starting private messages item import"); |
| Class<PrivateMessage> eClazz = PrivateMessage.class; |
| JAXBContext jc = JAXBContext.newInstance(eClazz); |
| Unmarshaller unmarshaller = jc.createUnmarshaller(); |
| unmarshaller.setAdapter(new UserAdapter(userDao, userMap)); |
| unmarshaller.setAdapter(new RoomAdapter(roomDao, roomMap)); |
| |
| readList(unmarshaller, base, "privateMessages.xml", MSG_LIST_NODE, MSG_NODE, eClazz, p -> { |
| p.setId(null); |
| p.setFolderId(messageFolderMap.get(p.getFolderId())); |
| p.setUserContactId(userContactMap.get(p.getUserContactId())); |
| if (p.getRoom() != null && p.getRoom().getId() == null) { |
| p.setRoom(null); |
| } |
| if (p.getTo() != null && p.getTo().getId() == null) { |
| p.setTo(null); |
| } |
| if (p.getFrom() != null && p.getFrom().getId() == null) { |
| p.setFrom(null); |
| } |
| if (p.getOwner() != null && p.getOwner().getId() == null) { |
| p.setOwner(null); |
| } |
| privateMessageDao.update(p, null); |
| }); |
| } |
| |
| /* |
| * ##################### Import File-Explorer Items |
| */ |
| private List<FileItem> importFiles(File base) throws Exception { |
| log.info("Private message import complete, starting file explorer item import"); |
| List<FileItem> result = new ArrayList<>(); |
| final Map<Long, Long> folders = new HashMap<>(); |
| saveTree(base, "fileExplorerItems.xml", FILE_LIST_NODE, FILE_NODE, FileItem.class, folders, file -> { |
| Long fId = file.getId(); |
| // We need to reset this as openJPA reject to store them otherwise |
| file.setId(null); |
| checkHash(file, fileItemDao, null); |
| file = fileItemDao.update(file); |
| if (BaseFileItem.Type.FOLDER == file.getType()) { |
| folders.put(fId, file.getId()); |
| } |
| result.add(file); |
| fileItemMap.put(fId, file.getId()); |
| }); |
| return result; |
| } |
| |
| /* |
| * ##################### Import Room Polls |
| */ |
| private void importPolls(File base) throws Exception { |
| log.info("File explorer item import complete, starting room poll import"); |
| Class<RoomPoll> eClazz = RoomPoll.class; |
| JAXBContext jc = JAXBContext.newInstance(eClazz); |
| Unmarshaller unmarshaller = jc.createUnmarshaller(); |
| unmarshaller.setAdapter(new UserAdapter(userDao, userMap)); |
| unmarshaller.setAdapter(new RoomAdapter(roomDao, roomMap)); |
| |
| readList(unmarshaller, base, "roompolls.xml", POLL_LIST_NODE, POLL_NODE, eClazz, rp -> { |
| rp.setId(null); |
| if (rp.getRoom() == null || rp.getRoom().getId() == null) { |
| //room was deleted |
| return; |
| } |
| if (rp.getCreator() == null || rp.getCreator().getId() == null) { |
| rp.setCreator(null); |
| } |
| for (RoomPollAnswer rpa : rp.getAnswers()) { |
| if (rpa.getVotedUser() == null || rpa.getVotedUser().getId() == null) { |
| rpa.setVotedUser(null); |
| } |
| } |
| pollDao.update(rp); |
| }); |
| } |
| |
| /* |
| * ##################### Import Room Files |
| */ |
| private void importRoomFiles(File base) throws Exception { |
| log.info("Poll import complete, starting room files import"); |
| Class<RoomFile> eClazz = RoomFile.class; |
| JAXBContext jc = JAXBContext.newInstance(eClazz); |
| Unmarshaller unmarshaller = jc.createUnmarshaller(); |
| unmarshaller.setAdapter(new FileAdapter(fileItemDao, fileItemMap)); |
| |
| readList(unmarshaller, base, "roomFiles.xml", ROOM_FILE_LIST_NODE, ROOM_FILE_NODE, eClazz, rf -> { |
| Room r = roomDao.get(roomMap.get(rf.getRoomId())); |
| if (r == null || rf.getFile() == null || rf.getFile().getId() == null) { |
| return; |
| } |
| if (r.getFiles() == null) { |
| r.setFiles(new ArrayList<>()); |
| } |
| rf.setId(null); |
| rf.setRoomId(r.getId()); |
| r.getFiles().add(rf); |
| roomDao.update(r, null); |
| }, true); |
| } |
| |
| void importExtraMenus(File base) throws Exception { |
| log.info("Room files complete, starting extra menus import"); |
| Class<ExtraMenu> eClazz = ExtraMenu.class; |
| JAXBContext jc = JAXBContext.newInstance(eClazz); |
| Unmarshaller unmarshaller = jc.createUnmarshaller(); |
| unmarshaller.setAdapter(new GroupAdapter(groupDao, groupMap)); |
| |
| readList(unmarshaller, base, "extraMenus.xml", EXTRA_MENU_LIST_NODE, EXTRA_MENU_NODE, eClazz, m -> { |
| if (Strings.isEmpty(m.getName()) || Strings.isEmpty(m.getLink())) { |
| return; |
| } |
| m.setId(null); |
| menuDao.update(m, null); |
| }, true); |
| } |
| |
| private static <T> void readList(File baseDir, String fileName, String listNodeName, String nodeName, Class<T> clazz, Consumer<T> consumer) { |
| readList(baseDir, fileName, listNodeName, nodeName, clazz, consumer, false); |
| } |
| |
| private static <T> void readList(Unmarshaller unmarshaller, File baseDir, String fileName, String listNodeName, String nodeName, Class<T> clazz, Consumer<T> consumer) { |
| readList(unmarshaller, baseDir, fileName, listNodeName, nodeName, clazz, consumer, false); |
| } |
| |
| private static <T> void readList(File baseDir, String fileName, String listNodeName, String nodeName, Class<T> clazz, Consumer<T> consumer, boolean notThow) { |
| readList(null, baseDir, fileName, listNodeName, nodeName, clazz, consumer, notThow); |
| } |
| |
| private static <T> void readList(Unmarshaller inUnmarshaller, File baseDir, String fileName, String listNodeName, String nodeName, Class<T> clazz, Consumer<T> consumer, boolean notThow) { |
| File xml = new File(baseDir, fileName); |
| if (!xml.exists()) { |
| final String msg = fileName + " missing"; |
| if (notThow) { |
| log.debug(msg); |
| return; |
| } else { |
| throw new BackupException(msg); |
| } |
| } |
| try { |
| Unmarshaller unmarshaller = inUnmarshaller; |
| if (inUnmarshaller == null) { |
| JAXBContext jc = JAXBContext.newInstance(clazz); |
| unmarshaller = jc.createUnmarshaller(); |
| } |
| XMLInputFactory xif = XmlHelper.createInputFactory(); |
| StreamSource xmlSource = new StreamSource(xml); |
| XMLStreamReader xsr = xif.createXMLStreamReader(xmlSource); |
| boolean listNodeFound = false; |
| while (xsr.getEventType() != XMLStreamConstants.END_DOCUMENT) { |
| if (xsr.isStartElement()) { |
| if (!listNodeFound && listNodeName.equals(xsr.getLocalName())) { |
| listNodeFound = true; |
| } else if (nodeName.equals(xsr.getLocalName())) { |
| T o = unmarshaller.unmarshal(xsr, clazz).getValue(); |
| consumer.accept(o); |
| } |
| } |
| xsr.next(); |
| } |
| } catch (Exception e) { |
| throw new BackupException(e); |
| } |
| } |
| |
| private static Long getPrefixedId(String prefix, File f, Map<Long, Long> map) { |
| String n = getFileName(f.getName()); |
| Long id = null; |
| if (n.indexOf(prefix) > -1) { |
| id = importLongType(n.substring(prefix.length(), n.length())); |
| } |
| return id == null ? null : map.get(id); |
| } |
| |
| private void processGroupFiles(File baseDir) throws IOException { |
| log.debug("Entered group logo folder"); |
| for (File f : baseDir.listFiles()) { |
| String ext = getFileExt(f.getName()); |
| if (EXTENSION_PNG.equals(ext)) { |
| Long id = getPrefixedId(GROUP_LOGO_PREFIX, f, groupMap); |
| if (id != null) { |
| FileUtils.copyFile(f, getGroupLogo(id, false)); |
| } |
| } else if (EXTENSION_CSS.equals(ext)) { |
| Long id = getPrefixedId(GROUP_CSS_PREFIX, f, groupMap); |
| if (id != null) { |
| FileUtils.copyFile(f, getGroupCss(id, false)); |
| } |
| } |
| } |
| } |
| |
| private static void changeHash(File f, File dir, String hash, String inExt) throws IOException { |
| String ext = inExt == null ? getFileExt(f.getName()) : inExt; |
| FileUtils.copyFile(f, new File(dir, getName(hash, ext))); |
| } |
| |
| private void processFiles(File baseDir) throws IOException { |
| log.debug("Entered FILES folder"); |
| for (File rf : baseDir.listFiles()) { |
| String oldHash = OmFileHelper.getFileName(rf.getName()); |
| String hash = hashMap.get(oldHash); |
| if (hash == null) { |
| continue; |
| } |
| File dir = new File(getUploadFilesDir(), hash); |
| // going to fix images |
| if (rf.isFile() && rf.getName().endsWith(EXTENSION_JPG)) { |
| changeHash(rf, dir, hash, EXTENSION_JPG); |
| } else { |
| for (File f : rf.listFiles()) { |
| FileUtils.copyFile(f, new File(dir |
| , f.getName().startsWith(oldHash) ? getName(hash, getFileExt(f.getName())) : f.getName())); |
| } |
| } |
| } |
| } |
| |
| private void processProfiles(File baseDir) throws IOException { |
| log.debug("Entered profiles folder"); |
| for (File profile : baseDir.listFiles()) { |
| Long id = getPrefixedId(PROFILES_PREFIX, profile, userMap); |
| if (id != null) { |
| FileUtils.copyDirectory(profile, getUploadProfilesUserDir(id)); |
| } |
| } |
| } |
| |
| private void processWmls(File baseDir) throws IOException { |
| log.debug("Entered WML folder"); |
| File dir = getUploadWmlDir(); |
| for (File wml : baseDir.listFiles()) { |
| String oldHash = OmFileHelper.getFileName(wml.getName()); |
| String hash = hashMap.get(oldHash); |
| if (hash == null) { |
| continue; |
| } |
| changeHash(wml, dir, hash, null); |
| } |
| } |
| |
| private void processFilesRoot(File baseDir) throws IOException { |
| // Now check the room files and import them |
| final File roomFilesFolder = new File(baseDir, BCKP_ROOM_FILES); |
| log.debug("roomFilesFolder PATH {} ", roomFilesFolder.getCanonicalPath()); |
| if (!roomFilesFolder.exists()) { |
| return; |
| } |
| |
| for (File file : roomFilesFolder.listFiles()) { |
| if (file.isDirectory()) { |
| String fName = file.getName(); |
| if (PROFILES_DIR.equals(fName)) { |
| processProfiles(file); |
| } else if (FILES_DIR.equals(fName)) { |
| processFiles(file); |
| } else if (GROUP_LOGO_DIR.equals(fName)) { |
| processGroupFiles(file); |
| } else if (WML_DIR.equals(fName)) { |
| processWmls(file); |
| } |
| } |
| } |
| } |
| |
| private void importFolders(File baseDir) throws IOException { |
| processFilesRoot(baseDir); |
| |
| // Now check the recordings and import them |
| final File recDir = new File(baseDir, BCKP_RECORD_FILES); |
| log.debug("sourceDirRec PATH {}", recDir.getCanonicalPath()); |
| if (recDir.exists()) { |
| final File hiberDir = getStreamsHibernateDir(); |
| for (File r : recDir.listFiles()) { |
| String n = fileMap.get(r.getName()); |
| if (n != null) { |
| FileUtils.copyFile(r, new File(hiberDir, n)); |
| } else { |
| String oldHash = OmFileHelper.getFileName(r.getName()); |
| String hash = hashMap.get(oldHash); |
| if (hash == null) { |
| FileUtils.copyFileToDirectory(r, hiberDir); |
| } else { |
| changeHash(r, hiberDir, hash, null); |
| } |
| } |
| } |
| } |
| final File cssDir = new File(baseDir, CSS_DIR); |
| if (cssDir.exists()) { |
| final File wCssDir = getCssDir(); |
| for (File css : cssDir.listFiles()) { |
| FileUtils.copyFileToDirectory(css, wCssDir); |
| } |
| } |
| } |
| |
| private static Long importLongType(String value) { |
| Long val = null; |
| try { |
| val = Long.valueOf(value); |
| } catch (Exception e) { |
| // no-op |
| } |
| return val; |
| } |
| |
| private void convertOldPresentation(FileItem fi) { |
| File f = fi.getOriginal(); |
| if (f != null && f.exists()) { |
| try { |
| StoredFile sf = new StoredFile(fi.getHash(), getFileExt(f.getName()), f); |
| docConverter.convertPDF(fi, sf); |
| } catch (Exception e) { |
| log.error("Unexpected exception while converting OLD format presentations", e); |
| } |
| } |
| } |
| |
| private static class UserKey { |
| private final String login; |
| private final User.Type type; |
| private final Long domainId; |
| |
| UserKey(User u) { |
| this.login = u.getLogin(); |
| this.type = u.getType(); |
| this.domainId = u.getDomainId(); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(domainId, login, type); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj == null) { |
| return false; |
| } |
| if (getClass() != obj.getClass()) { |
| return false; |
| } |
| UserKey other = (UserKey) obj; |
| return Objects.equals(domainId, other.domainId) && Objects.equals(login, other.login) && type == other.type; |
| } |
| } |
| } |