2
2
3
3
import fi .helsinki .cs .tmc .langs .io .StudentFilePolicy ;
4
4
5
+ import com .google .common .collect .Sets ;
5
6
import org .apache .commons .compress .archivers .zip .ZipArchiveEntry ;
6
7
import org .apache .commons .compress .archivers .zip .ZipFile ;
7
8
import org .apache .commons .io .FileUtils ;
8
9
10
+ import org .apache .commons .io .IOUtils ;
9
11
import org .slf4j .Logger ;
10
12
import org .slf4j .LoggerFactory ;
11
13
14
+ import java .io .BufferedInputStream ;
15
+ import java .io .ByteArrayInputStream ;
12
16
import java .io .File ;
17
+ import java .io .FileInputStream ;
13
18
import java .io .FileNotFoundException ;
14
19
import java .io .IOException ;
15
20
import java .io .InputStream ;
16
21
import java .nio .file .Files ;
17
22
import java .nio .file .Path ;
18
23
import java .util .Enumeration ;
24
+ import java .util .Set ;
19
25
import java .util .zip .ZipEntry ;
20
26
21
27
public final class StudentFileAwareUnzipper implements Unzipper {
@@ -24,7 +30,8 @@ public final class StudentFileAwareUnzipper implements Unzipper {
24
30
25
31
private StudentFilePolicy filePolicy ;
26
32
27
- public StudentFileAwareUnzipper () {}
33
+ public StudentFileAwareUnzipper () {
34
+ }
28
35
29
36
public StudentFileAwareUnzipper (StudentFilePolicy filePolicy ) {
30
37
this .filePolicy = filePolicy ;
@@ -36,7 +43,9 @@ public void setStudentFilePolicy(StudentFilePolicy studentFilePolicy) {
36
43
}
37
44
38
45
@ Override
39
- public void unzip (Path zip , Path target ) throws IOException {
46
+ public UnzipResult unzip (Path zip , Path target ) throws IOException {
47
+ UnzipResult result = new UnzipResult (target );
48
+
40
49
log .info ("Unzipping {} to {}" , zip , target );
41
50
if (!Files .exists (zip )) {
42
51
log .error ("Attempted to unzip nonexistent archive {}" , zip );
@@ -48,54 +57,107 @@ public void unzip(Path zip, Path target) throws IOException {
48
57
Files .createDirectories (target );
49
58
}
50
59
60
+ Set <Path > pathsInZip = Sets .newHashSet ();
51
61
try (ZipFile zipFile = new ZipFile (zip .toFile ())) {
52
62
53
63
Enumeration <ZipArchiveEntry > entries = zipFile .getEntries ();
54
64
55
65
String projectDirInZip = findProjectDirInZip (zipFile .getEntries ());
56
- if (projectDirInZip == null ) {
57
- throw new RuntimeException ("No project in zip" );
58
- }
66
+
59
67
log .debug ("Project dir in zip: {}" , projectDirInZip );
60
68
61
69
while (entries .hasMoreElements ()) {
62
70
ZipArchiveEntry entry = entries .nextElement ();
63
71
64
72
if (entry .getName ().startsWith (projectDirInZip )) {
65
- String restOfPath = entry .getName ().substring (projectDirInZip .length ());
66
- restOfPath = trimSlashes (restOfPath );
67
-
68
- String destFileRelativePath =
69
- trimSlashes (restOfPath .replace ("/" , File .separator ));
70
- Path entryTargetPath = target .resolve (destFileRelativePath );
71
-
72
- log .debug (
73
- "Processing zipEntry with name {} to {}" ,
74
- entry .getName (),
75
- entryTargetPath );
76
- if (entry .isDirectory ()) {
73
+ String restOfPath = trimSlashes (entry .getName ().substring (projectDirInZip .length ()));
74
+
75
+ Path entryTargetPath = target .resolve (trimSlashes (restOfPath .replace ("/" , File .separator )));
76
+ pathsInZip .add (entryTargetPath );
77
+
78
+ log .debug ("Processing zipEntry with name {} to {}" , entry .getName (), entryTargetPath );
79
+ if (entry .isDirectory () || entryTargetPath .toFile ().isDirectory ()) {
77
80
Files .createDirectories (entryTargetPath );
78
- } else {
79
- if (allowedToUnzip (entryTargetPath , target )) {
80
- log .trace ("Allowed to unzip, unzipping" );
81
- InputStream entryContent = zipFile .getInputStream (entry );
82
- FileUtils .copyInputStreamToFile (entryContent , entryTargetPath .toFile ());
81
+ log .debug ("{} is a directory - creating and off to the next file " , entry .getName ());
82
+ continue ;
83
+ }
84
+ boolean shouldWrite ;
85
+ InputStream entryContent = zipFile .getInputStream (entry );
86
+ if (Files .exists (entryTargetPath )) {
87
+ log .trace ("Allowed to unzip, unzipping" );
88
+ byte [] entryData = IOUtils .toByteArray (entryContent );
89
+ if (fileContentEquals (target .toFile (), entryData )) {
90
+ shouldWrite = false ;
91
+ result .unchangedFiles .add (entryTargetPath );
92
+ } else if (allowedToUnzip (entryTargetPath , target )) {
93
+ shouldWrite = true ;
94
+ result .overwrittenFiles .add (entryTargetPath );
83
95
} else {
84
- log .trace ("Not allowed to unzip, skipping file" );
96
+ shouldWrite = false ;
97
+ result .skippedFiles .add (entryTargetPath );
85
98
}
99
+ } else {
100
+ shouldWrite = true ;
101
+ result .newFiles .add (entryTargetPath );
102
+ }
103
+ if (shouldWrite ) {
104
+ FileUtils .copyInputStreamToFile (entryContent , entryTargetPath .toFile ());
105
+ } else {
106
+ log .trace ("Not allowed to unzip, skipping file" );
107
+ result .skippedFiles .add (entryTargetPath );
86
108
}
87
-
88
109
log .debug ("Done with file {}" , entryTargetPath );
110
+
89
111
} else {
90
112
log .debug ("Skipping non project file from zip - {}" , entry .getName ());
91
113
}
92
114
}
93
115
}
116
+
94
117
log .debug ("Done unzipping" );
118
+ deleteFilesNotInZip (target , target , result , pathsInZip );
119
+ return null ;
120
+ }
121
+
122
+ // TODO: validate
123
+ private void deleteFilesNotInZip (Path projectDir , Path curDir , UnzipResult result , Set <Path > pathsInZip ) throws IOException {
124
+
125
+ for (File file : curDir .toFile ().listFiles ()) {
126
+ // Path relPath = Paths.get(trimSlashes(file.getPath().substring(projectDir.getPath().length())));
127
+ Path filePath = file .toPath ();
128
+ if (file .isDirectory ()) {
129
+ deleteFilesNotInZip (projectDir , file .toPath (), result , pathsInZip );
130
+ }
131
+
132
+ if (!pathsInZip .contains (filePath )) {
133
+ if (mayDelete (filePath , null )) {
134
+ if (file .isDirectory () && file .listFiles ().length > 0 ) {
135
+ // Won't delete directories if they still have contents
136
+ result .skippedDeletingFiles .add (filePath );
137
+ } else {
138
+ file .delete ();
139
+ result .deletedFiles .add (filePath );
140
+ }
141
+ } else {
142
+ result .skippedDeletingFiles .add (filePath );
143
+ }
144
+ }
145
+ }
146
+ }
147
+
148
+ private boolean fileContentEquals (File file , byte [] data ) throws IOException {
149
+ if (file .isDirectory ()) {
150
+ return false ;
151
+ }
152
+ InputStream fileIs = new BufferedInputStream (new FileInputStream (file ));
153
+ InputStream dataIs = new ByteArrayInputStream (data );
154
+ boolean eq = IOUtils .contentEquals (fileIs , dataIs );
155
+ fileIs .close ();
156
+ dataIs .close ();
157
+ return eq ;
95
158
}
96
159
97
160
private String findProjectDirInZip (Enumeration <ZipArchiveEntry > zipEntries ) throws IOException {
98
- ZipEntry zent ;
99
161
while (zipEntries .hasMoreElements ()) {
100
162
ZipArchiveEntry element = zipEntries .nextElement ();
101
163
String name = element .getName ();
@@ -108,7 +170,7 @@ private String findProjectDirInZip(Enumeration<ZipArchiveEntry> zipEntries) thro
108
170
return dirname (name );
109
171
}
110
172
}
111
- return null ;
173
+ throw new RuntimeException ( "No project in zip" ) ;
112
174
}
113
175
114
176
private String dirname (String zipPath ) {
@@ -145,4 +207,19 @@ private boolean allowedToUnzip(Path file, Path projectRoot) {
145
207
146
208
return true ;
147
209
}
210
+
211
+ private boolean mayDelete (Path file , Path projectRoot ) {
212
+ if (!Files .exists (file )) {
213
+ log .trace ("File does not exist, don't delete it" );
214
+ return false ;
215
+ }
216
+
217
+ log .trace ("File exists, checking whether it's studentfile is allowed" );
218
+
219
+ if (filePolicy .mayDelete (file , projectRoot )) {
220
+ log .trace ("File {} can be deleted" , file );
221
+ return true ;
222
+ }
223
+ return false ;
224
+ }
148
225
}
0 commit comments