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    *   https://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                ;
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;
107         FTPFile second;
108         final int firstl = lst.length;
109         final int secondl = mlst.length;
110         int one = 0;
111         int two = 0;
112         first = lst[one++];
113         second = mlst[two++];
114         int cmp;
115         while (one < firstl || two < secondl) {
116 //            String fs1 = first.toFormattedString();
117 //            String fs2 = second.toFormattedString();
118             final String rl1 = first.getRawListing();
119             final String rl2 = second.getRawListing();
120             cmp = first.getName().compareTo(second.getName());
121             if (cmp == 0) {
122                 if (first.getName().endsWith("HEADER.html")) {
123                     cmp = 0;
124                 }
125                 if (!areEquivalent(first, second)) {
126 //                    System.out.println(rl1);
127 //                    System.out.println(fs1);
128                     final long tdiff = first.getTimestamp().getTimeInMillis() - second.getTimestamp().getTimeInMillis();
129                     System.out.println("Minutes diff " + tdiff / (1000 * 60));
130 //                    System.out.println(fs2);
131 //                    System.out.println(rl2);
132 //                    System.out.println();
133 //                    fail();
134                 }
135                 if (one < firstl) {
136                     first = lst[one++];
137                 }
138                 if (two < secondl) {
139                     second = mlst[two++];
140                 }
141             } else if (cmp < 0) {
142                 if (!first.getName().startsWith(".")) { // skip hidden files
143                     System.out.println("1: " + rl1);
144                 }
145                 if (one < firstl) {
146                     first = lst[one++];
147                 }
148             } else {
149                 System.out.println("2: " + rl2);
150                 if (two < secondl) {
151                     second = mlst[two++];
152                 }
153             }
154         }
155     }
156 
157     private boolean isSameDay(final Calendar a, final Calendar b) {
158         final int ad = a.get(Calendar.DAY_OF_MONTH);
159         final int bd = b.get(Calendar.DAY_OF_MONTH);
160         return a.get(Calendar.YEAR) == b.get(Calendar.YEAR) && a.get(Calendar.MONTH) == b.get(Calendar.MONTH) && ad == bd;
161     }
162 
163     private boolean isSameTime(final Calendar a, final Calendar b) {
164         final int ah = a.get(Calendar.HOUR_OF_DAY);
165         final int bh = b.get(Calendar.HOUR_OF_DAY);
166         final int am = a.get(Calendar.MINUTE);
167         final int bm = b.get(Calendar.MINUTE);
168         final int as = a.get(Calendar.SECOND);
169         final int bs = b.get(Calendar.SECOND);
170         // @formatter:off
171         return ah == 0 && am == 0 && as == 0
172                 || bh == 0 && bm == 0 && bs == 0
173                 || ah == bh && am == bm; // ignore seconds
174         // @formatter:om
175     }
176 
177     @Test
178     public void testFile() throws Exception {
179         final File path = new File(DownloadListings.DOWNLOAD_DIR);
180         final FilenameFilter filter = (dir, name) -> name.endsWith("_mlsd.txt");
181         final File[] files = path.listFiles(filter);
182         if (files != null) {
183             for (final File mlsd : files) {
184                 // System.out.println(mlsd);
185                 FTPListParseEngine engine = new FTPListParseEngine(MLSxEntryParser.getInstance());
186                 try (InputStream is = new FileInputStream(mlsd)) {
187                     engine.readServerList(is, FTP.DEFAULT_CONTROL_ENCODING);
188                 }
189                 final FTPFile[] mlsds = engine.getFiles(FTPFileFilters.ALL);
190                 final File listFile = new File(mlsd.getParentFile(), mlsd.getName().replace("_mlsd", "_list"));
191                 try (InputStream inputStream = new FileInputStream(listFile)) {
192                     final FTPClientConfig cfg = new FTPClientConfig();
193                     cfg.setServerTimeZoneId("GMT");
194                     final UnixFTPEntryParser parser = new UnixFTPEntryParser(cfg);
195                     engine = new FTPListParseEngine(parser);
196                     engine.readServerList(inputStream, FTP.DEFAULT_CONTROL_ENCODING);
197                     compareSortedLists(mlsds, engine.getFiles(FTPFileFilters.ALL));
198                 }
199             }
200         }
201     }
202 
203 //    private boolean areSame(int a, int b, int d) {
204 //        return a == d || b == d || a == b;
205 //    }
206 //
207 //    private boolean areSame(String a, String b) {
208 //        return a.length() == 0 || b.length() == 0 || a.equals(b);
209 //    }
210 }