View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.net.ftp.parser;
19  
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.io.FilenameFilter;
23  import java.io.InputStream;
24  import java.util.Arrays;
25  import java.util.Calendar;
26  import java.util.Comparator;
27  import java.util.TimeZone;
28  
29  import org.apache.commons.net.ftp.FTP;
30  import org.apache.commons.net.ftp.FTPClientConfig;
31  import org.apache.commons.net.ftp.FTPFile;
32  import org.apache.commons.net.ftp.FTPFileFilters;
33  import org.apache.commons.net.ftp.FTPListParseEngine;
34  import org.junit.Test;
35  
36  /**
37   * Attempt comparison of LIST and MLSD listings
38   *
39   * TODO - needs some work.
40   */
41  public class MLSDComparison {
42  
43      private final Comparator<FTPFile> cmp = (o1, o2) -> {
44          final String n1 = o1.getName();
45          final String n2 = o2.getName();
46          return n1.compareTo(n2);
47      };
48  
49      /**
50       * Compare two instances to see if they are the same, ignoring any uninitialized fields.
51       *
52       * @param a first instance
53       * @param b second instance
54       * @return true if the initialized fields are the same
55       * @since 3.0
56       */
57      public boolean areEquivalent(final FTPFile a, final FTPFile b) {
58          return a.getName().equals(b.getName()) && areSame(a.getSize(), b.getSize(), -1L) &&
59  //            areSame(a.getUser(), b.getUser()) &&
60  //            areSame(a.getGroup(), b.getGroup()) &&
61                  areSame(a.getTimestamp(), b.getTimestamp()) &&
62  //            areSame(a.getType(), b.getType(), UNKNOWN_TYPE) &&
63  //            areSame(a.getHardLinkCount(), b.getHardLinkCount(), 0) &&
64  //            areSame(a._permissions, b._permissions)
65                  true;
66      }
67  
68      private boolean areSame(final Calendar a, final Calendar b) {
69          return a == null || b == null || areSameDateTime(a, b);
70      }
71  
72      private boolean areSame(final long a, final long b, final long d) {
73          return a == d || b == d || a == b;
74      }
75  
76      // compare permissions: default is all false, but that is also a possible
77      // state, so this may miss some differences
78  //    private boolean areSame(boolean[][] a, boolean[][] b) {
79  //        return isDefault(a) || isDefault(b) || Arrays.deepEquals(a, b);
80  //    }
81  
82      // Is the array in its default state?
83  //    private boolean isDefault(boolean[][] a) {
84  //        for(boolean[] r : a){
85  //            for(boolean rc : r){
86  //                if (rc) { // not default
87  //                    return false;
88  //                }
89  //            }
90  //        }
91  //        return true;
92  //    }
93  
94      private boolean areSameDateTime(final Calendar a, final Calendar b) {
95          final TimeZone UTC = TimeZone.getTimeZone("UTC");
96          final Calendar ac = Calendar.getInstance(UTC);
97          ac.setTime(a.getTime());
98          final Calendar bc = Calendar.getInstance(UTC);
99          bc.setTime(b.getTime());
100         return isSameDay(ac, bc) && isSameTime(ac, bc);
101     }
102 
103     private void compareSortedLists(final FTPFile[] lst, final FTPFile[] mlst) {
104         Arrays.sort(lst, cmp);
105         Arrays.sort(mlst, cmp);
106         FTPFile first, second;
107         final int firstl = lst.length;
108         final int secondl = mlst.length;
109         int one = 0, two = 0;
110         first = lst[one++];
111         second = mlst[two++];
112         int cmp;
113         while (one < firstl || two < secondl) {
114 //            String fs1 = first.toFormattedString();
115 //            String fs2 = second.toFormattedString();
116             final String rl1 = first.getRawListing();
117             final String rl2 = second.getRawListing();
118             cmp = first.getName().compareTo(second.getName());
119             if (cmp == 0) {
120                 if (first.getName().endsWith("HEADER.html")) {
121                     cmp = 0;
122                 }
123                 if (!areEquivalent(first, second)) {
124 //                    System.out.println(rl1);
125 //                    System.out.println(fs1);
126                     final long tdiff = first.getTimestamp().getTimeInMillis() - second.getTimestamp().getTimeInMillis();
127                     System.out.println("Minutes diff " + tdiff / (1000 * 60));
128 //                    System.out.println(fs2);
129 //                    System.out.println(rl2);
130 //                    System.out.println();
131 //                    fail();
132                 }
133                 if (one < firstl) {
134                     first = lst[one++];
135                 }
136                 if (two < secondl) {
137                     second = mlst[two++];
138                 }
139             } else if (cmp < 0) {
140                 if (!first.getName().startsWith(".")) { // skip hidden files
141                     System.out.println("1: " + rl1);
142                 }
143                 if (one < firstl) {
144                     first = lst[one++];
145                 }
146             } else {
147                 System.out.println("2: " + rl2);
148                 if (two < secondl) {
149                     second = mlst[two++];
150                 }
151             }
152         }
153     }
154 
155     private boolean isSameDay(final Calendar a, final Calendar b) {
156         final int ad = a.get(Calendar.DAY_OF_MONTH);
157         final int bd = b.get(Calendar.DAY_OF_MONTH);
158         return a.get(Calendar.YEAR) == b.get(Calendar.YEAR) && a.get(Calendar.MONTH) == b.get(Calendar.MONTH) && ad == bd;
159     }
160 
161     private boolean isSameTime(final Calendar a, final Calendar b) {
162         final int ah = a.get(Calendar.HOUR_OF_DAY);
163         final int bh = b.get(Calendar.HOUR_OF_DAY);
164         final int am = a.get(Calendar.MINUTE);
165         final int bm = b.get(Calendar.MINUTE);
166         final int as = a.get(Calendar.SECOND);
167         final int bs = b.get(Calendar.SECOND);
168         // @formatter:off
169         return ah == 0 && am == 0 && as == 0
170                 || bh == 0 && bm == 0 && bs == 0
171                 || ah == bh && am == bm; // ignore seconds
172         // @formatter:om
173     }
174 
175     @Test
176     public void testFile() throws Exception {
177         final File path = new File(DownloadListings.DOWNLOAD_DIR);
178         final FilenameFilter filter = (dir, name) -> name.endsWith("_mlsd.txt");
179         final File[] files = path.listFiles(filter);
180         if (files != null) {
181             for (final File mlsd : files) {
182                 // System.out.println(mlsd);
183                 FTPListParseEngine engine = new FTPListParseEngine(MLSxEntryParser.getInstance());
184                 try (final InputStream is = new FileInputStream(mlsd)) {
185                     engine.readServerList(is, FTP.DEFAULT_CONTROL_ENCODING);
186                 }
187                 final FTPFile[] mlsds = engine.getFiles(FTPFileFilters.ALL);
188                 final File listFile = new File(mlsd.getParentFile(), mlsd.getName().replace("_mlsd", "_list"));
189                 try (final InputStream inputStream = new FileInputStream(listFile)) {
190                     final FTPClientConfig cfg = new FTPClientConfig();
191                     cfg.setServerTimeZoneId("GMT");
192                     final UnixFTPEntryParser parser = new UnixFTPEntryParser(cfg);
193                     engine = new FTPListParseEngine(parser);
194                     engine.readServerList(inputStream, FTP.DEFAULT_CONTROL_ENCODING);
195                     compareSortedLists(mlsds, engine.getFiles(FTPFileFilters.ALL));
196                 }
197             }
198         }
199     }
200 
201 //    private boolean areSame(int a, int b, int d) {
202 //        return a == d || b == d || a == b;
203 //    }
204 //
205 //    private boolean areSame(String a, String b) {
206 //        return a.length() == 0 || b.length() == 0 || a.equals(b);
207 //    }
208 }