blob: 4953b6bf6924e242dce9ce2e4099eef35a7c3363 [file] [log] [blame]
/*
* 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;
}
}
}