content: /*
   * © 2017 AgNO3 Gmbh & Co. KG
   * 
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   * 
   * This library 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
   * Lesser General Public License for more details.
   * 
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  package jcifs.tests;
  
  
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertNotNull;
  import static org.junit.Assert.assertTrue;
  
  import java.io.IOException;
  import java.net.MalformedURLException;
  import java.net.UnknownHostException;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Locale;
  import java.util.Map;
  import java.util.Set;
  
  import org.junit.Assert;
  import org.junit.Assume;
  import org.junit.Ignore;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  import org.junit.runners.Parameterized;
  import org.junit.runners.Parameterized.Parameters;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import jcifs.CIFSContext;
  import jcifs.CIFSException;
  import jcifs.CloseableIterator;
  import jcifs.ResolverType;
  import jcifs.SmbConstants;
  import jcifs.SmbResource;
  import jcifs.SmbTreeHandle;
  import jcifs.config.DelegatingConfiguration;
  import jcifs.context.CIFSContextWrapper;
  import jcifs.netbios.NameServiceClientImpl;
  import jcifs.smb.DosFileFilter;
  import jcifs.smb.SmbException;
  import jcifs.smb.SmbFile;
  import jcifs.smb.SmbFilenameFilter;
  import jcifs.smb.SmbUnsupportedOperationException;
  
  
  /**
   * @author mbechler
   *
   */
  @RunWith ( Parameterized.class )
  @SuppressWarnings ( "javadoc" )
  public class EnumTest extends BaseCIFSTest {
  
      private static final Logger log = LoggerFactory.getLogger(EnumTest.class);
  
  
      /**
       * @param name
       * @param properties
       */
      public EnumTest ( String name, Map<String, String> properties ) {
          super(name, properties);
      }
  
  
      @Parameters ( name = "{0}" )
      public static Collection<Object> configs () {
          return getConfigs("smb1", "noUnicode", "forceUnicode", "noNTStatus", "noNTSmbs", "smb2", "smb30", "smb31");
      }
  
  
      @Ignore ( "This causes a connection to whatever local master browser is available, config may be incompatible with it" )
      @Test
      public void testBrowse () throws MalformedURLException, CIFSException {
          CIFSContext ctx = withAnonymousCredentials();
          try ( SmbFile smbFile = new SmbFile("smb://", ctx) ) {
              try ( CloseableIterator<SmbResource> it = smbFile.children() ) {
                  while ( it.hasNext() ) {
                      try ( SmbResource serv = it.next() ) {
                          System.err.println(serv.getName());
                      }
                  }
              }
          }
          catch ( SmbUnsupportedOperationException e ) {
              Assume.assumeTrue("Browsing unsupported", false);
          }
      }
  
  
      @Test
      public void testBrowseDomain () throws MalformedURLException, CIFSException {
          CIFSContext ctx = withAnonymousCredentials();
          try ( SmbFile smbFile = new SmbFile("smb://" + getRequiredProperty(TestProperties.TEST_DOMAIN_SHORT), ctx) ) {
              // if domain is resolved through DNS this will be treated as a server and will enumerate shares instead
              Assume.assumeTrue("Not workgroup", SmbConstants.TYPE_WORKGROUP == smbFile.getType());
              try ( CloseableIterator<SmbResource> it = smbFile.children() ) {
                  while ( it.hasNext() ) {
                      try ( SmbResource serv = it.next() ) {
                          System.err.println(serv.getName());
                          assertEquals(SmbConstants.TYPE_SERVER, serv.getType());
                          assertTrue(serv.isDirectory());
                      }
                  }
              }
          }
          catch ( SmbUnsupportedOperationException e ) {
              Assume.assumeTrue("Browsing unsupported", false);
          }
      }
  
  
      @Test
      public void testBrowseDomainNetbios () throws MalformedURLException, CIFSException {
  
          // only do this if a WINS server is enabled
          getRequiredProperty("jcifs.netbios.wins");
  
          CIFSContext bctx = withAnonymousCredentials();
  
          // ensure that the domain name gets resolved through WINS so that
          // it gets the workgroup type.
          CIFSContext ctx = withConfig(bctx, new DelegatingConfiguration(bctx.getConfig()) {
  
              @Override
              public List<ResolverType> getResolveOrder () {
                  return Arrays.asList(ResolverType.RESOLVER_WINS);
              }
          });
  
          // need to override NameServiceClient as it otherwise gets initialized with the original config
          final NameServiceClientImpl nsc = new NameServiceClientImpl(ctx);
          ctx = new CIFSContextWrapper(ctx) {
  
              @Override
              public jcifs.NameServiceClient getNameServiceClient () {
                  return nsc;
              }
          };
  
          try ( SmbFile smbFile = new SmbFile("smb://" + getRequiredProperty(TestProperties.TEST_DOMAIN_SHORT), ctx) ) {
              // if domain is resolved through DNS this will be treated as a server and will enumerate shares instead
              Assume.assumeTrue("Not workgroup", SmbConstants.TYPE_WORKGROUP == smbFile.getType());
              try ( CloseableIterator<SmbResource> it = smbFile.children() ) {
                  while ( it.hasNext() ) {
                      try ( SmbResource serv = it.next() ) {
                          System.err.println(serv.getName());
                          assertEquals(SmbConstants.TYPE_SERVER, serv.getType());
                          assertTrue(serv.isDirectory());
                      }
                  }
              }
          }
          catch ( SmbUnsupportedOperationException e ) {
              Assume.assumeTrue("Browsing unsupported", false);
          }
      }
  
  
      @Test
      public void testShareEnum () throws MalformedURLException, CIFSException {
          try ( SmbFile smbFile = new SmbFile("smb://" + getTestServer(), withTestNTLMCredentials(getContext())) ) {
              String[] list = smbFile.list();
              assertNotNull(list);
              assertTrue("No share found", list.length > 0);
              log.debug(Arrays.toString(list));
          }
      }
  
  
      @Test
      public void testDomainSeverEnum () throws MalformedURLException, CIFSException {
          try ( SmbFile smbFile = new SmbFile("smb://" + getTestDomain(), withTestNTLMCredentials(getContext())) ) {
              String[] list = smbFile.list();
              assertNotNull(list);
              log.debug(Arrays.toString(list));
          }
          catch ( SmbUnsupportedOperationException e ) {
              Assume.assumeTrue(false);
          }
      }
  
  
      @Test
      public void testDFSShareEnum () throws CIFSException, MalformedURLException {
          String dfsRoot = getDFSRootURL();
          try ( SmbFile smbFile = new SmbFile(dfsRoot, withTestNTLMCredentials(getContext())) ) {
              String[] list = smbFile.list();
              assertNotNull(list);
              assertTrue("No share found", list.length > 0);
              log.debug(Arrays.toString(list));
  
              String shareUrl = getTestShareURL();
              String link = shareUrl.substring(dfsRoot.length());
  
              Set<String> listLinks = new HashSet<>(Arrays.asList(list));
              int firstSep = link.indexOf('/');
              if ( firstSep == link.length() - 1 ) {
                  // single level
                  assertTrue("Link not found " + link, listLinks.contains(link));
              }
              else {
                  link = link.substring(0, firstSep + 1);
                  // single level
                  assertTrue("First component of link not found" + link, listLinks.contains(link));
              }
          }
      }
  
  
      @Test
      public void testDirEnum () throws CIFSException, MalformedURLException, UnknownHostException {
          try ( SmbFile f = createTestDirectory() ) {
              try ( SmbFile a = new SmbFile(f, "a");
                    SmbFile b = new SmbFile(f, "b");
                    SmbFile c = new SmbFile(f, "c") ) {
  
                  a.createNewFile();
                  b.createNewFile();
                  c.createNewFile();
  
                  String[] names = f.list();
                  assertNotNull(names);
                  assertEquals(3, names.length);
                  Arrays.sort(names);
                  Assert.assertArrayEquals(new String[] {
                      "a", "b", "c"
                  }, names);
  
                  SmbFile[] files = f.listFiles();
                  assertNotNull(files);
                  assertEquals(3, files.length);
  
                  for ( SmbFile cf : files ) {
                      assertTrue(cf.exists());
                      cf.close();
                  }
              }
              finally {
                  f.delete();
              }
          }
      }
  
  
      @Test
      public void testDirEnumLarge () throws CIFSException, MalformedURLException, UnknownHostException {
          try ( SmbFile f = createTestDirectory() ) {
              try {
                  String prefix = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
                  int n = getContext().getConfig().getMaximumBufferSize() / 100;
                  for ( int i = 0; i < n; i++ ) {
                      try ( SmbResource c = f.resolve(String.format("%s%d.tmp", prefix, i)) ) {
                          c.createNewFile();
                      }
                  }
  
                  String[] found = f.list();
                  Arrays.sort(found);
  
                  for ( int i = 0; i < n; i++ ) {
                      Assert.assertTrue(Arrays.binarySearch(found, String.format("%s%d.tmp", prefix, i)) >= 0);
                  }
              }
              finally {
                  f.delete();
              }
          }
  
      }
  
  
      @Test
      public void testEnumDeepUnresolved () throws IOException {
          try ( SmbFile r = getDefaultShareRoot();
                SmbFile f = new SmbFile(r, "enum-test/a/b/") ) {
  
              try ( SmbResource c = f.resolve("c/") ) {
                  if ( !c.exists() ) {
                      c.mkdirs();
                  }
              }
  
              Set<String> names = new HashSet<>();
              try ( CloseableIterator<SmbResource> chld = f.children() ) {
                  while ( chld.hasNext() ) {
  
                      try ( SmbResource next = chld.next() ) {
                          try ( CloseableIterator<SmbResource> children = next.children() ) {}
                          names.add(next.getName());
                      }
                  }
              }
  
              assertTrue("Test directory  enum-test/a/b/c/ not found", names.contains("c/"));
          }
  
          try ( SmbFile r = getDefaultShareRoot();
                SmbFile f = new SmbFile(r, "enum-test/a/b/c/") ) {
              f.exists();
          }
  
      }
  
  
      @Test
      public void testEnumDeepVirgin () throws IOException {
  
          try ( SmbFile r = getDefaultShareRoot();
                SmbFile f = new SmbFile(r, "x-test/") ) {
              if ( !f.exists() ) {
                  f.mkdirs();
              }
          }
  
          // fresh connection
          CIFSContext ctx = getNewContext();
          try ( SmbFile r = new SmbFile(getTestShareURL(), withTestNTLMCredentials(ctx));
                SmbFile f = new SmbFile(r, "x-test/") ) {
  
              Set<String> names = new HashSet<>();
              try ( CloseableIterator<SmbResource> chld = f.children() ) {
                  while ( chld.hasNext() ) {
  
                      try ( SmbResource next = chld.next() ) {
                          if ( next.isDirectory() ) {
                              try (CloseableIterator<SmbResource> children = next.children()) {
                              }
                          }
                          names.add(next.getName());
                      }
                  }
              }
          }
  
      }
  
  
      @Test
      public void testEnumDeepUnresolvedCasing () throws IOException {
  
          String testShareURL = getTestShareURL().toUpperCase(Locale.ROOT);
  
          try ( SmbFile r = new SmbFile(testShareURL, withTestNTLMCredentials(getContext()));
                SmbFile f = new SmbFile(r, "enum-test/a/b/") ) {
  
              try ( SmbResource c = f.resolve("c/") ) {
                  if ( !c.exists() ) {
                      c.mkdirs();
                  }
              }
  
              Set<String> names = new HashSet<>();
              try ( CloseableIterator<SmbResource> chld = f.children() ) {
                  while ( chld.hasNext() ) {
  
                      try ( SmbResource next = chld.next() ) {
                          try ( CloseableIterator<SmbResource> children = next.children() ) {}
                          names.add(next.getName());
                      }
                  }
              }
  
              assertTrue("Test directory  enum-test/a/b/c/ not found", names.contains("c/"));
          }
  
          try ( SmbFile r = getDefaultShareRoot();
                SmbFile f = new SmbFile(r, "enum-test/a/b/c/") ) {
              f.exists();
          }
  
      }
  
  
      @Test
      public void testDirFilenameFilterEnum () throws CIFSException, MalformedURLException, UnknownHostException {
          try ( SmbFile f = createTestDirectory() ) {
              try ( SmbFile a = new SmbFile(f, "a.txt");
                    SmbFile b = new SmbFile(f, "b.txt");
                    SmbFile c = new SmbFile(f, "c.bar") ) {
  
                  a.createNewFile();
                  b.createNewFile();
                  c.createNewFile();
  
                  String[] names = f.list(new SmbFilenameFilter() {
  
                      @Override
                      public boolean accept ( SmbFile dir, String name ) throws SmbException {
                          return name.endsWith(".txt");
                      }
                  });
                  assertNotNull(names);
                  assertEquals(2, names.length);
                  Arrays.sort(names);
                  Assert.assertArrayEquals(new String[] {
                      "a.txt", "b.txt"
                  }, names);
  
                  SmbFile[] files = f.listFiles(new SmbFilenameFilter() {
  
                      @Override
                      public boolean accept ( SmbFile dir, String name ) throws SmbException {
                          return name.equals("c.bar");
                      }
                  });
                  assertNotNull(files);
                  assertEquals(1, files.length);
  
                  for ( SmbFile cf : files ) {
                      assertTrue(cf.exists());
                      cf.close();
                  }
              }
              finally {
                  f.delete();
              }
          }
      }
  
  
      @Test
      public void testDirDosFilterEnum () throws CIFSException, MalformedURLException, UnknownHostException {
          try ( SmbFile f = createTestDirectory() ) {
              try ( SmbFile a = new SmbFile(f, "a.txt");
                    SmbFile b = new SmbFile(f, "b.txt");
                    SmbFile c = new SmbFile(f, "c.bar") ) {
  
                  a.createNewFile();
                  b.createNewFile();
                  c.createNewFile();
  
                  SmbFile[] files = f.listFiles(new DosFileFilter("*.txt", -1));
                  assertNotNull(files);
                  assertEquals(2, files.length);
                  for ( SmbFile cf : files ) {
                      assertTrue(cf.exists());
                      cf.close();
                  }
              }
              finally {
                  f.delete();
              }
          }
      }
  
  
      @Test
      public void testPatternEnum () throws CIFSException, MalformedURLException, UnknownHostException {
          try ( SmbFile f = createTestDirectory() ) {
              try ( SmbFile a = new SmbFile(f, "a.txt");
                    SmbFile b = new SmbFile(f, "b.txt");
                    SmbFile c = new SmbFile(f, "c.bar") ) {
  
                  a.createNewFile();
                  b.createNewFile();
                  c.createNewFile();
  
                  SmbFile[] files = f.listFiles("*.txt");
                  assertNotNull(files);
                  assertEquals(2, files.length);
                  for ( SmbFile cf : files ) {
                      assertTrue(cf.exists());
                      cf.close();
                  }
  
                  int n = 0;
                  try ( CloseableIterator<SmbResource> children = f.children("*.txt") ) {
                      while ( children.hasNext() ) {
                          try ( SmbResource r = children.next() ) {
                              assertTrue(r.exists());
                              n++;
                          }
                      }
                  }
                  assertEquals(2, n);
              }
              finally {
                  f.delete();
              }
          }
      }
  
  
      @Test
      public void testAttributeEnum () throws CIFSException, MalformedURLException, UnknownHostException {
  
          try ( SmbFile f = createTestDirectory() ) {
              try ( SmbFile a = new SmbFile(f, "a/");
                    SmbFile b = new SmbFile(f, "b.txt");
                    SmbFile c = new SmbFile(f, "c.bar") ) {
  
                  a.mkdir();
  
                  b.createNewFile();
                  boolean haveHidden = false;
                  try {
                      b.setAttributes(SmbConstants.ATTR_HIDDEN);
                      haveHidden = true;
                  }
                  catch ( SmbUnsupportedOperationException e ) {}
  
                  c.createNewFile();
                  boolean haveArchive = false;
                  try {
                      c.setAttributes(SmbConstants.ATTR_ARCHIVE);
                      haveArchive = true;
                  }
                  catch ( SmbUnsupportedOperationException e ) {}
  
                  SmbFile[] dirs = f.listFiles(new DosFileFilter("*", SmbConstants.ATTR_DIRECTORY));
                  assertNotNull(dirs);
                  assertEquals(1, dirs.length);
  
                  if ( haveHidden && !Boolean.parseBoolean(getProperties().getOrDefault("test.skip.hidden", "false")) ) {
                      SmbFile[] hidden = f.listFiles(new DosFileFilter("*", SmbConstants.ATTR_HIDDEN));
                      assertNotNull(hidden);
                      assertEquals(1, hidden.length);
                  }
  
                  if ( haveArchive ) {
                      SmbFile[] archive = f.listFiles(new DosFileFilter("*", SmbConstants.ATTR_ARCHIVE));
                      assertNotNull(archive);
                      assertEquals(1, archive.length);
                  }
              }
              finally {
                  f.delete();
              }
          }
      }
  
  
      @Test
      public void testEmptyEnum () throws CIFSException, MalformedURLException, UnknownHostException {
          try ( SmbFile f = createTestDirectory() ) {
              try {
                  SmbFile[] files = f.listFiles(new DosFileFilter("*.txt", 0));
                  assertNotNull(files);
                  assertEquals(0, files.length);
  
                  files = f.listFiles();
                  assertNotNull(files);
                  assertEquals(0, files.length);
              }
              finally {
                  f.delete();
              }
          }
      }
  
  
      @Test
      // BUG #15
      // this cannot be reproduced against samba, samba always subtracts
      // 8 bytes from the output buffer length, probably to mitigate
      // against this issue
      public void testEnumBufferSize () throws IOException {
          CIFSContext ctx = getContext();
          int origBufferSize = ctx.getConfig().getMaximumBufferSize();
          // odd buffer size that does match the alignment
          int tryBufferSize = 1023;
          final int bufSize[] = new int[] {
              origBufferSize
          };
          ctx = withConfig(ctx, new DelegatingConfiguration(ctx.getConfig()) {
  
              @Override
              public int getMaximumBufferSize () {
                  return bufSize[ 0 ];
              }
          });
          ctx = withTestNTLMCredentials(ctx);
          try ( SmbResource root = ctx.get(getTestShareURL());
                SmbResource f = root.resolve(makeRandomDirectoryName()) ) {
  
              try ( SmbTreeHandle treeHandle = ( (SmbFile) root ).getTreeHandle() ) {
                  Assume.assumeTrue("Not SMB2", treeHandle.isSMB2());
              }
  
              f.mkdir();
              try {
  
                  for ( int i = 0; i < 5; i++ ) {
                      // each entry 94 byte + 2 * name length
                      // = 128 byte per entry
                      try ( SmbResource r = f.resolve(String.format("%04x%s", i, repeat('X', 13))) ) {
                          r.createNewFile();
                      }
                  }
                  // == 5*128 = 640
  
                  // . and .. entries = 200 byte (includes alignment)
  
                  // + 64 byte header
                  // + 8 byte query response overhead
                  // + 110 bytes entry
                  // -> 1022 predicted message size <= 1023 maximum buffer size
                  // 112 bytes to alignment
                  // -> aligned to 1024 > 1023 maximum buffer size
  
                  // 110 byte entry = 16 byte name = 8 char length
                  try ( SmbResource r = f.resolve(repeat('Y', 8)) ) {
                      r.createNewFile();
                  }
  
                  bufSize[ 0 ] = tryBufferSize;
  
                  try ( CloseableIterator<SmbResource> chld = f.children() ) {
                      while ( chld.hasNext() ) {
                          chld.next();
                      }
                  }
                  finally {
                      bufSize[ 0 ] = origBufferSize;
                  }
              }
              finally {
                  f.delete();
              }
          }
      }
  
  
      @Test
      // BUG #16
      public void testListCountRollover () throws IOException {
          testListCount(5, 4); // 4 + 2 (.,..) files
      }
  
  
      @Test
      // BUG #16
      public void testListCountExact () throws IOException {
          testListCount(5, 3); // 3 + 2 (.,..) files
      }
  
  
      @Test
      // BUG #16
      public void testListCountMoreThanTwo () throws IOException {
          testListCount(5, 10); // 10 + 2 (.,..) files
      }
  
  
  
  
      private void testListCount ( final int pageSize, int numFiles ) throws CIFSException {
          CIFSContext ctx = getContext();
          ctx = withConfig(ctx, new DelegatingConfiguration(ctx.getConfig()) {
  
              @Override
              public int getListCount () {
                  return pageSize;
              }
          });
          ctx = withTestNTLMCredentials(ctx);
          try ( SmbResource root = ctx.get(getTestShareURL());
                SmbResource f = root.resolve(makeRandomDirectoryName()) ) {
              f.mkdir();
              try {
                  for ( int i = 0; i < numFiles; i++ ) {
                      try ( SmbResource r = f.resolve(String.format("%04x", i)) ) {
                          r.createNewFile();
                      }
                  }
  
                  int cnt = 0;
                  try ( CloseableIterator<SmbResource> chld = f.children() ) {
                      while ( chld.hasNext() ) {
                          try ( SmbResource next = chld.next() ) {
                              cnt++;
                          }
                      }
                  }
  
                  assertEquals(numFiles, cnt);
              }
              finally {
                  f.delete();
              }
          }
      }
  
  
      @Test
      // BUG #61
      public void testListTrailingSlash () throws MalformedURLException, UnknownHostException, CIFSException {
          CIFSContext ctx = getContext();
          try ( SmbFile f = createTestDirectory() ) {
              try ( SmbFile a = new SmbFile(f, "a/");
                    SmbFile b = new SmbFile(a, "b.txt");
                    SmbFile c = new SmbFile(a, "c.txt") ) {
  
                  a.mkdir();
                  b.createNewFile();
                  c.createNewFile();
  
                  CIFSContext tc = withTestNTLMCredentials(ctx);
  
                  String url = getTestShareURL() + f.getName() + "a/";
  
                  try ( SmbFile f2 = new SmbFile(url, tc) ) {
                      f2.list();
                  }
              }
              finally {
                  f.delete();
              }
          }
  
      }
  
      @Test
      public void testListSmallBufferSize() throws CIFSException {
          int numFiles = 100;
          CIFSContext ctx = getContext();
          ctx = withConfig(ctx, new DelegatingConfiguration(ctx.getConfig()) {
  
              @Override
              public int getListSize () {
                  return 1024;
              }
          });
          ctx = withTestNTLMCredentials(ctx);
          try ( SmbResource root = ctx.get(getTestShareURL());
                SmbResource f = root.resolve(makeRandomDirectoryName()) ) {
              f.mkdir();
              try {
                  for ( int i = 0; i < numFiles; i++ ) {
                      try ( SmbResource r = f.resolve(String.format("%04x", i)) ) {
                          r.createNewFile();
                      }
                  }
  
                  int cnt = 0;
                  try ( CloseableIterator<SmbResource> chld = f.children() ) {
                      while ( chld.hasNext() ) {
                          try ( SmbResource next = chld.next() ) {
                              cnt++;
                          }
                      }
                  }
  
                  assertEquals(numFiles, cnt);
              }
              finally {
  
                  String[] s = ((SmbFile)f).list();
  
                  f.delete();
              }
          }
      }
  
      private static String repeat ( char c, int n ) {
          char chs[] = new char[n];
          for ( int i = 0; i < n; i++ ) {
              chs[ i ] = c;
          }
          return new String(chs);
      }
  
  }
  