forked from SteamWar/SteamWar
Introduce GDPRQuery command for GDPR data generation, refactor SQL statements, and improve null safety across modules.
Signed-off-by: Chaoscaot <max@maxsp.de>
This commit is contained in:
@@ -53,7 +53,7 @@ object EventFightTable : IntIdTable("EventFight", "FightID") {
|
||||
val spectatePort = integer("SpectatePort").nullable()
|
||||
val bestOf = integer("BestOf")
|
||||
val ergebnis = integer("Ergebnis")
|
||||
val fight = integer("Fight").entityId().nullable()
|
||||
val fight = optReference("Fight", FightTable)
|
||||
}
|
||||
|
||||
class EventFight(id: EntityID<Int>) : IntEntity(id), Comparable<EventFight> {
|
||||
|
||||
@@ -45,7 +45,7 @@ public final class GameModeConfig<M, W> {
|
||||
}
|
||||
|
||||
private static String internalName(File f) {
|
||||
return f.getName().replace(".yml", "");
|
||||
return f != null ? f.getName().replace(".yml", "") : constWarGear(null);
|
||||
}
|
||||
|
||||
private static final Map<String, GameModeConfig<?, String>> byFileName;
|
||||
|
||||
@@ -36,7 +36,7 @@ import java.time.Instant
|
||||
import java.util.Date
|
||||
import java.util.function.Consumer
|
||||
|
||||
object PunishmentTable : IntIdTable("Punishment", "PunishmentId") {
|
||||
object PunishmentTable : IntIdTable("Punishments", "PunishmentId") {
|
||||
val userId = reference("UserId", SteamwarUserTable)
|
||||
val punisher = reference("Punisher", SteamwarUserTable)
|
||||
val type = enumerationByName("Type", 32, Punishment.PunishmentType::class)
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.jetbrains.exposed.v1.core.dao.id.EntityID
|
||||
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.v1.core.eq
|
||||
import org.jetbrains.exposed.v1.core.lowerCase
|
||||
import org.jetbrains.exposed.v1.core.or
|
||||
import org.jetbrains.exposed.v1.dao.IntEntity
|
||||
import org.jetbrains.exposed.v1.dao.IntEntityClass
|
||||
import org.jetbrains.exposed.v1.jdbc.select
|
||||
@@ -48,7 +49,7 @@ class Team(id: EntityID<Int>) : IntEntity(id) {
|
||||
fun byId(id: Int) = teamCache.computeIfAbsent(id) { useDb { Team[id] } }
|
||||
|
||||
@JvmStatic
|
||||
fun get(name: String) = useDb { find { TeamTable.name.lowerCase() eq name.lowercase() }.firstOrNull() }
|
||||
fun get(name: String) = useDb { find { TeamTable.name.lowerCase() eq name.lowercase() or (TeamTable.kuerzel.lowerCase() eq name.lowercase()) }.firstOrNull() }
|
||||
|
||||
@JvmStatic
|
||||
fun getAll() = useDb { all().toList() }
|
||||
@@ -70,7 +71,7 @@ class Team(id: EntityID<Int>) : IntEntity(id) {
|
||||
private set
|
||||
private var teamAddress by TeamTable.address
|
||||
private var teamPort by TeamTable.port
|
||||
val members by lazy { SteamwarUserTable.select(SteamwarUserTable.id).where { SteamwarUserTable.team eq teamId }.map { it[SteamwarUserTable.id].value } }
|
||||
val members by lazy { useDb { SteamwarUserTable.select(SteamwarUserTable.id).where { SteamwarUserTable.team eq teamId }.map { it[SteamwarUserTable.id].value } } }
|
||||
|
||||
fun size() = useDb { SteamwarUser.find { SteamwarUserTable.team eq teamId }.count().toInt() }
|
||||
fun disband(user: SteamwarUser) = useDb {
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.jetbrains.exposed.v1.core.Expression
|
||||
import org.jetbrains.exposed.v1.core.ResultRow
|
||||
import org.jetbrains.exposed.v1.core.StdOutSqlLogger
|
||||
import org.jetbrains.exposed.v1.core.statements.StatementType
|
||||
import org.jetbrains.exposed.v1.core.statements.api.RowApi
|
||||
import org.jetbrains.exposed.v1.dao.IntEntity
|
||||
import org.jetbrains.exposed.v1.dao.IntEntityClass
|
||||
import org.jetbrains.exposed.v1.jdbc.Database
|
||||
@@ -34,14 +33,13 @@ import org.jetbrains.exposed.v1.jdbc.statements.jdbc.JdbcResult
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import java.io.File
|
||||
import java.sql.ResultSet
|
||||
import java.util.Properties
|
||||
|
||||
object KotlinDatabase {
|
||||
lateinit var db: Database
|
||||
var db: Database? = null
|
||||
|
||||
fun ensureConnected() {
|
||||
if(KotlinDatabase::db.isInitialized) return
|
||||
if(db != null) return
|
||||
|
||||
val file = File(System.getProperty("user.home"), "mysql.properties")
|
||||
|
||||
@@ -61,6 +59,12 @@ object KotlinDatabase {
|
||||
password = password
|
||||
)
|
||||
}
|
||||
|
||||
fun close() {
|
||||
db?.connector()?.close()
|
||||
TransactionManager.defaultDatabase?.let { TransactionManager.closeAndUnregister(it) }
|
||||
db = null
|
||||
}
|
||||
}
|
||||
|
||||
fun <T: Any?> useDb(statement: JdbcTransaction.() -> T): T {
|
||||
|
||||
@@ -19,15 +19,39 @@
|
||||
|
||||
package de.steamwar.sql.internal
|
||||
|
||||
import de.steamwar.sql.SteamwarUser
|
||||
import org.jetbrains.exposed.v1.core.*
|
||||
import org.jetbrains.exposed.v1.jdbc.name
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager
|
||||
import java.sql.ResultSet
|
||||
import java.sql.SQLException
|
||||
|
||||
class Statement {
|
||||
data class Statement(val statement: String) {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun closeAll() = TransactionManager.defaultDatabase?.let { TransactionManager.closeAndUnregister(it) }
|
||||
fun closeAll() = KotlinDatabase.close()
|
||||
|
||||
@JvmStatic
|
||||
fun productionDatabase() = TransactionManager.defaultDatabase?.name == "production"
|
||||
}
|
||||
|
||||
fun <T> select(user: ResultSetUser<T>, vararg args: Any): T? = useDb {
|
||||
exec(statement, args = args.map { getArgType(it) }) {
|
||||
user.use(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getArgType(obj: Any) = when(obj) {
|
||||
is String -> VarCharColumnType() to obj
|
||||
is Int -> IntegerColumnType() to obj
|
||||
is Long -> LongColumnType() to obj
|
||||
is Boolean -> BooleanColumnType() to obj
|
||||
is SteamwarUser -> IntegerColumnType() to obj.id.value
|
||||
else -> error("Unknown type: ${obj::class.simpleName}")
|
||||
}
|
||||
|
||||
interface ResultSetUser<T> {
|
||||
@Throws(SQLException::class)
|
||||
fun use(rs: ResultSet): T?
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,14 @@ import java.util.stream.Collectors;
|
||||
public class PersonalKit {
|
||||
private final InternalKit kit;
|
||||
|
||||
public String getName() {
|
||||
return kit.getName();
|
||||
}
|
||||
|
||||
public boolean isInUse() {
|
||||
return kit.getInUse();
|
||||
}
|
||||
|
||||
public String getRawInventory() {
|
||||
return kit.getRawInventory();
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public class ArenaMode {
|
||||
if(!folder.exists())
|
||||
return;
|
||||
|
||||
for(File file : Arrays.stream(folder.listFiles((file, name) -> name.endsWith(".yml") && !name.endsWith(".kits.yml"))).sorted().toList()) {
|
||||
for(File file : Arrays.stream(folder.listFiles((file, name) -> name.endsWith(".yml") && !name.endsWith(".kits.yml") && !name.equals("config.yml"))).sorted().toList()) {
|
||||
GameModeConfig<String, String> gameModeConfig = new GameModeConfig<>(file, GameModeConfig.ToString, GameModeConfig.ToString, GameModeConfig.ToInternalName, false);
|
||||
if (!gameModeConfig.Server.loaded) continue;
|
||||
|
||||
@@ -61,6 +61,7 @@ public class ArenaMode {
|
||||
}
|
||||
if (gameModeConfig.Schematic.loaded && gameModeConfig.Schematic.Type != SchematicType.Normal) {
|
||||
bySchemType.put(gameModeConfig.Schematic.Type, gameModeConfig);
|
||||
System.out.println(gameModeConfig.configFile.getAbsolutePath());
|
||||
SchematicType checkType = gameModeConfig.Schematic.Type.checkType();
|
||||
if (checkType != null) bySchemType.put(checkType, gameModeConfig);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.velocitycore.commands;
|
||||
|
||||
import de.steamwar.linkage.Linked;
|
||||
import de.steamwar.velocitycore.VelocityCore;
|
||||
import de.steamwar.command.SWCommand;
|
||||
import de.steamwar.messages.Chatter;
|
||||
import de.steamwar.messages.PlayerChatter;
|
||||
import de.steamwar.sql.SteamwarUser;
|
||||
import de.steamwar.sql.UserPerm;
|
||||
import de.steamwar.sql.internal.Statement;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
@Linked
|
||||
public class GDPRQuery extends SWCommand {
|
||||
|
||||
public GDPRQuery() {
|
||||
super("gdprquery", UserPerm.ADMINISTRATION);
|
||||
}
|
||||
|
||||
@Register
|
||||
public void generate(PlayerChatter sender) {
|
||||
generate(sender, sender.user());
|
||||
}
|
||||
|
||||
@Register
|
||||
public void generate(Chatter sender, SteamwarUser user) {
|
||||
VelocityCore.schedule(() -> {
|
||||
try {
|
||||
createZip(sender, user);
|
||||
} catch (IOException e) {
|
||||
throw new SecurityException("Could not create zip", e);
|
||||
}
|
||||
}).schedule();
|
||||
}
|
||||
|
||||
private void createZip(Chatter sender, SteamwarUser user) throws IOException {
|
||||
sender.system("GDPR_STATUS_WEBSITE");
|
||||
|
||||
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(user.getUserName() + ".zip"));
|
||||
|
||||
copy(getClass().getClassLoader().getResourceAsStream("GDPRQueryREADME.md"), out, "README.md");
|
||||
copy(getClass().getClassLoader().getResourceAsStream("GDPRQueryREADME.md"), out, "README.txt");
|
||||
|
||||
sender.system("GDPR_STATUS_WORLD");
|
||||
copyBauwelt(user, out, "/home/minecraft/userworlds/" + user.getUUID().toString(), "BuildWorld12");
|
||||
copyBauwelt(user, out, "/home/minecraft/userworlds15/" + user.getId(), "BuildWorld15");
|
||||
|
||||
sender.system("GDPR_STATUS_INVENTORIES");
|
||||
copyPlayerdata(user, out, "/home/minecraft/userworlds", "BuildInventories12");
|
||||
copyPlayerdata(user, out, "/home/minecraft/userworlds15", "BuildInventories15");
|
||||
|
||||
sender.system("GDPR_STATUS_DATABASE");
|
||||
sqlCSV(user, out, bannedIPs, "BannedIPs.csv");
|
||||
sqlCSV(user, out, bauweltMember, "BuildMember.csv");
|
||||
sqlCSV(user, out, bauweltMembers, "BuildMembers.csv");
|
||||
sqlCSV(user, out, checkedSchems, "SchematicChecksessions.csv");
|
||||
sqlCSV(user, out, userElo, "UserElo.csv");
|
||||
sqlCSV(user, out, fights, "Fights.csv");
|
||||
sqlCSV(user, out, ignoredPlayers, "IgnoredPlayers.csv");
|
||||
sqlCSV(user, out, ignoringPlayers, "IgnoringPlayers.csv");
|
||||
sqlCSV(user, out, schematicMember, "SchematicMember.csv");
|
||||
sqlCSV(user, out, schematicMembers, "SchematicMembers.csv");
|
||||
sqlCSV(user, out, pollAnswers, "PollAnswers.csv");
|
||||
sqlCSV(user, out, punishments, "Punishments.csv");
|
||||
sqlCSV(user, out, sessions, "Sessions.csv");
|
||||
sqlCSV(user, out, userData, "UserData.csv");
|
||||
sqlCSV(user, out, personalKits, "PersonalKits.csv");
|
||||
sqlCSV(user, out, schematics, "Schematics.csv");
|
||||
|
||||
personalKits(user, out);
|
||||
schematics(user, out);
|
||||
userConfig(user, out);
|
||||
|
||||
sender.system("GDPR_STATUS_LOGS");
|
||||
copyLogs(user, out, new File("/logs"), "logs");
|
||||
|
||||
out.close();
|
||||
sender.system("GDPR_STATUS_FINISHED");
|
||||
}
|
||||
|
||||
private static final Statement bannedIPs = new Statement("SELECT Timestamp, IP FROM BannedUserIPs WHERE UserID = ?");
|
||||
private static final Statement bauweltMember = new Statement("SELECT BauweltID AS Bauwelt, WorldEdit, World FROM BauweltMember WHERE MemberID = ?");
|
||||
private static final Statement bauweltMembers = new Statement("SELECT u.UserName AS 'User', m.WorldEdit AS WorldEdit, m.World AS World FROM BauweltMember m INNER JOIN UserData u ON m.MemberID = u.id WHERE m.BauweltID = ?");
|
||||
private static final Statement checkedSchems = new Statement("SELECT NodeName AS Schematic, StartTime, EndTime, DeclineReason AS Result FROM CheckedSchematic WHERE NodeOwner = ? ORDER BY StartTime ASC");
|
||||
private static final Statement userElo = new Statement("SELECT GameMode, Elo, Season FROM Elo WHERE UserID = ?");
|
||||
private static final Statement fights = new Statement("SELECT p.Team AS Team, p.Kit AS Kit, p.Kills AS Kills, p.IsOut AS Died, f.GameMode AS GameMode, f.Server AS Server, f.StartTime AS StartTime, f.Duration AS Duration, (f.BlueLeader = p.UserID) AS IsBlueLeader, (f.RedLeader = p.UserID) AS IsRedLeader, f.Win AS Winner, f.WinCondition AS WinCondition FROM Fight f INNER JOIN FightPlayer p ON f.FightID = p.FightID WHERE p.UserID = ? ORDER BY StartTime ASC");
|
||||
private static final Statement ignoredPlayers = new Statement("SELECT u.UserName AS IgnoredPlayer FROM IgnoredPlayers i INNER JOIN UserData u ON i.Ignored = u.id WHERE Ignorer = ?");
|
||||
private static final Statement ignoringPlayers = new Statement("SELECT Ignorer AS IgnoringPlayers FROM IgnoredPlayers WHERE Ignored = ?");
|
||||
private static final Statement schematicMember = new Statement("SELECT s.NodeName AS SchematicName, u.UserName AS SchematicOwner FROM NodeMember m INNER JOIN SchematicNode s ON m.NodeId = s.NodeId INNER JOIN UserData u ON s.NodeOwner = u.id WHERE m.UserId = ?");
|
||||
private static final Statement schematicMembers = new Statement("SELECT s.NodeName AS SchematicName, u.UserName AS Member FROM NodeMember m INNER JOIN SchematicNode s ON m.NodeId = s.NodeId INNER JOIN UserData u ON m.UserId = u.id WHERE s.NodeOwner = ?");
|
||||
private static final Statement pollAnswers = new Statement("SELECT Question, Answer FROM PollAnswer WHERE UserID = ?");
|
||||
private static final Statement punishments = new Statement("SELECT Type, StartTime, EndTime, Perma, Reason FROM Punishments WHERE UserId = ?");
|
||||
private static final Statement sessions = new Statement("SELECT StartTime, EndTime FROM Session WHERE UserID = ?");
|
||||
private static final Statement userData = new Statement("SELECT * FROM UserData WHERE id = ?");
|
||||
private static final Statement personalKits = new Statement("SELECT GameMode, Name, InUse FROM PersonalKit WHERE UserID = ?");
|
||||
private static final Statement personalKitData = new Statement("SELECT GameMode, Name, Inventory, Armor FROM PersonalKit WHERE UserID = ?");
|
||||
private static final Statement schematics = new Statement("SELECT NodeName AS SchematicName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank FROM SchematicNode WHERE NodeOwner = ?");
|
||||
private static final Statement schematicData = new Statement("SELECT NodeName, ParentNode, NodeFormat, NodeData FROM SchematicNode WHERE NodeOwner = ?");
|
||||
private static final Statement userConfig = new Statement("SELECT * FROM UserConfig WHERE User = ?");
|
||||
|
||||
private void sqlCSV(SteamwarUser user, ZipOutputStream out, Statement statement, String path) throws IOException {
|
||||
write(stream -> statement.select(rs -> {
|
||||
try {
|
||||
OutputStreamWriter writer = new OutputStreamWriter(stream);
|
||||
int columns = rs.getMetaData().getColumnCount();
|
||||
|
||||
for(int i = 1; i <= columns; i++) {
|
||||
writer.write(rs.getMetaData().getColumnLabel(i));
|
||||
writer.write(";");
|
||||
}
|
||||
writer.write("\n");
|
||||
|
||||
while(rs.next()) {
|
||||
for(int i = 1; i <= columns; i++) {
|
||||
try {
|
||||
writer.write(rs.getString(i));
|
||||
} catch (NullPointerException e) {
|
||||
// ignored
|
||||
}
|
||||
writer.write(";");
|
||||
}
|
||||
writer.write("\n");
|
||||
}
|
||||
writer.flush();
|
||||
} catch (IOException e) {
|
||||
throw new SecurityException("Could not write file", e);
|
||||
}
|
||||
return null;
|
||||
}, user.getId()), out, path);
|
||||
}
|
||||
|
||||
private void personalKits(SteamwarUser user, ZipOutputStream out) {
|
||||
personalKitData.select(rs -> {
|
||||
while(rs.next()) {
|
||||
try {
|
||||
String path = "PersonalKit/" + rs.getString("GameMode") + "/" + rs.getString("Name");
|
||||
try(InputStream data = rs.getBinaryStream("Inventory")) {
|
||||
copy(data, out, path + ".Inventory.yml");
|
||||
}
|
||||
try(InputStream data = rs.getBinaryStream("Armor")) {
|
||||
copy(data, out, path + ".Armor.yml");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SecurityException("Could not export PersonalKits", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, user.getId());
|
||||
}
|
||||
|
||||
private void schematics(SteamwarUser user, ZipOutputStream out) {
|
||||
schematicData.select(rs -> {
|
||||
while(rs.next()) {
|
||||
String name = (rs.getString("ParentNode") != null ? rs.getString("ParentNode") : "") + ":" + rs.getString("NodeName");
|
||||
boolean format = rs.getBoolean("NodeFormat");
|
||||
try(InputStream data = rs.getBinaryStream("NodeData")) {
|
||||
copy(data, out, "Schematics/" + name + (format ? ".schem" : ".schematic"));
|
||||
} catch (IOException e) {
|
||||
throw new SecurityException("Could not export Schematic", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, user.getId());
|
||||
}
|
||||
|
||||
private void userConfig(SteamwarUser user, ZipOutputStream out) {
|
||||
userConfig.select(rs -> {
|
||||
while(rs.next()) {
|
||||
String name = rs.getString("Config");
|
||||
try(InputStream data = rs.getBinaryStream("Value")) {
|
||||
copy(data, out, name + ".yapion");
|
||||
} catch (IOException e) {
|
||||
throw new SecurityException("Could not export UserConfig", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, user.getId());
|
||||
}
|
||||
|
||||
private void copyLogs(SteamwarUser user, ZipOutputStream out, File log, String outFile) throws IOException {
|
||||
if (log.isDirectory()) {
|
||||
for(File logfile : log.listFiles()) {
|
||||
copyLogs(user, out, logfile, outFile + "/" + logfile.getName().replace(".gz", ""));
|
||||
}
|
||||
} else {
|
||||
Process reader = new ProcessBuilder("zgrep", "^.*" + user.getUserName() + "\\( issued server command:\\| moved too quickly!\\| executed command:\\| lost connection:\\||\\|»\\|\\[\\|\\]\\).*$", log.getPath()).start();
|
||||
copy(reader.getInputStream(), out, outFile);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyBauwelt(SteamwarUser user, ZipOutputStream out, String inDir, String outDir) throws IOException {
|
||||
File world = new File(inDir);
|
||||
if(!world.exists())
|
||||
return;
|
||||
|
||||
copy(new File(world, "level.dat"), out, outDir + "/level.dat");
|
||||
|
||||
File region = new File(world, "region");
|
||||
for(File regionfile : region.listFiles()) {
|
||||
copy(regionfile, out, outDir + "/region/" + regionfile.getName());
|
||||
}
|
||||
|
||||
File poi = new File(world, "poi");
|
||||
if(poi.exists()) {
|
||||
for(File regionfile : poi.listFiles()) {
|
||||
copy(regionfile, out, outDir + "/poi/" + regionfile.getName());
|
||||
}
|
||||
}
|
||||
|
||||
File playerdata = new File(world, "playerdata/" + user.getUUID().toString() + ".dat");
|
||||
if(playerdata.exists())
|
||||
copy(playerdata, out, outDir + "/playerdata/" + user.getUUID().toString() + ".dat");
|
||||
}
|
||||
|
||||
private void copyPlayerdata(SteamwarUser user, ZipOutputStream out, String inDir, String outDir) throws IOException {
|
||||
File worlds = new File(inDir);
|
||||
String path = "playerdata/" + user.getUUID().toString() + ".dat";
|
||||
|
||||
int i = 0;
|
||||
for(File world : worlds.listFiles()) {
|
||||
File playerdata = new File(world, path);
|
||||
if(!playerdata.exists())
|
||||
continue;
|
||||
|
||||
copy(playerdata, out, outDir + "/" + (i++) + "/" + user.getUUID().toString() + ".dat");
|
||||
}
|
||||
}
|
||||
|
||||
private void copy(File file, ZipOutputStream out, String path) throws IOException {
|
||||
try(FileInputStream in = new FileInputStream(file)) {
|
||||
copy(in, out, path);
|
||||
}
|
||||
}
|
||||
|
||||
private void copy(InputStream in, ZipOutputStream out, String path) throws IOException {
|
||||
boolean initialized = false;
|
||||
|
||||
int bytes;
|
||||
for(byte[] buf = new byte[8192]; (bytes = in.read(buf)) > 0; ) {
|
||||
if(!initialized) {
|
||||
ZipEntry entry = new ZipEntry(path);
|
||||
out.putNextEntry(entry);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
out.write(buf, 0, bytes);
|
||||
}
|
||||
|
||||
if(initialized) {
|
||||
out.closeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
private void write(Writer writer, ZipOutputStream out, String path) throws IOException {
|
||||
ZipEntry entry = new ZipEntry(path);
|
||||
out.putNextEntry(entry);
|
||||
writer.accept(out);
|
||||
out.closeEntry();
|
||||
}
|
||||
|
||||
private interface Writer {
|
||||
void accept(OutputStream stream) throws IOException;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user