Index: /main/trunk/gli/src/org/greenstone/gatherer/metadata/DocXMLFile.java
===================================================================
--- /main/trunk/gli/src/org/greenstone/gatherer/metadata/DocXMLFile.java	(revision 33756)
+++ /main/trunk/gli/src/org/greenstone/gatherer/metadata/DocXMLFile.java	(revision 33757)
@@ -34,7 +34,6 @@
 import org.greenstone.gatherer.util.Utility;
 
-import org.apache.commons.codec.binary.Base64;
-
-//import org.greenstone.gatherer.feedback.Base64;
+//import org.greenstone.gatherer.feedback.Base64; // decode() from Base64 didn't work
+import org.apache.commons.codec.binary.Base64; // decoding from Base64 works
 
 /** This class represents one doc.xml file */
@@ -58,4 +57,88 @@
     }
 
+	/**
+	 * Checks if various versions of the file object's filename, denoted relatively by file_relative_path,
+	 * occur in the source_file_name_to_description_elements_mapping map
+	*/
+	private ArrayList findSourceFileMapKeyMatch(File file, String file_relative_path) {
+		ArrayList description_elements_list = null;
+				
+		System.err.println("Looking for key " + file_relative_path);
+		description_elements_list = (ArrayList) source_file_name_to_description_elements_mapping.get(file_relative_path);
+		if(description_elements_list != null) {
+			System.err.println("   Found key matching REGULAR filepath: " + file_relative_path);
+			return description_elements_list;			
+		}
+		else if(!Utility.isWindows()) { // couldn't find a matching key, we're done
+			System.err.println("Unable to find meta for regular file path form " + file_relative_path);
+			return null;
+		}
+		
+		// Now we can try windows short filename as map key
+		
+		String win_short_file_relative_path = "";
+		try{
+			win_short_file_relative_path = Utility.getWindowsShortFileName(file.getAbsolutePath());				
+			//System.err.println("@@@ Searching for short file name: " + win_short_file_relative_path);
+		} catch(Exception e) { // we're done trying to find a matching key
+			System.err.println("Failed to convert to windows short file name: " + win_short_file_relative_path);			
+			return null;
+		}
+		
+		// Got a windows short file name, lop off import folder again
+		int import_index = win_short_file_relative_path.indexOf("import");
+		if (import_index != -1) {
+			win_short_file_relative_path = win_short_file_relative_path.substring(import_index + "import".length() + 1);
+		}
+			
+		System.err.println("### Looking for Windows short file name |" + win_short_file_relative_path +  "| in map of sourcefilenames to doc.xml's ex meta.");
+		description_elements_list = (ArrayList) source_file_name_to_description_elements_mapping.get(win_short_file_relative_path);
+		if (description_elements_list != null) {
+			System.err.println("   Found key matching FULL win shortfile path: " + win_short_file_relative_path);
+			return description_elements_list; // found
+		}
+		
+		// else, check whether a map key is matched by any REMAINING combination of windows shortfile path and regular path:
+		// - windows shortfilename's rel-dir-path with regular tailname
+		// - and regular rel-dir-path with windows shortfilename's tailname
+				
+		String shortFileTailName = win_short_file_relative_path;
+		String shortFileRelDirPath = "";
+		int lastSep = win_short_file_relative_path.lastIndexOf(File.separator);
+		if(lastSep != -1) {			
+			shortFileTailName = win_short_file_relative_path.substring(lastSep+1);
+			shortFileRelDirPath = win_short_file_relative_path.substring(0, lastSep+1); // include the slash
+		}
+		
+		String fileTailName = file_relative_path;
+		String fileRelDirPath = "";
+		lastSep = file_relative_path.lastIndexOf(File.separator);
+		if(lastSep != -1) {			
+			fileTailName = file_relative_path.substring(lastSep+1);
+			fileRelDirPath = file_relative_path.substring(0, lastSep+1); // include the slash
+		}
+		
+		String path = shortFileRelDirPath + fileTailName;
+		System.err.println("### Looking for Windows short file name |" + path +  "| in map of sourcefilenames to doc.xml's ex meta.");
+		description_elements_list = (ArrayList) source_file_name_to_description_elements_mapping.get(path);
+		
+		if(description_elements_list != null) {
+			System.err.println("   Found key matching MIX of win shortfile path and regular path: " + path);
+			return description_elements_list; // found
+		}
+
+		// try the other combination
+		path = fileRelDirPath + shortFileTailName;
+		System.err.println("### Looking for Windows short file name |" + path +  "| in map of sourcefilenames to doc.xml's ex meta.");
+		description_elements_list = (ArrayList) source_file_name_to_description_elements_mapping.get(path);
+		
+		if(description_elements_list != null) {
+			System.err.println("   Found key matching MIX of regular path and win shortfile path: " + path);
+			return description_elements_list; // found
+		}		
+		
+		return description_elements_list;
+	}
+	
 
     public ArrayList getMetadataExtractedFromFile(File file)
@@ -70,13 +153,18 @@
 	}
 
-	///for (Object relFilename : source_file_name_to_description_elements_mapping.keySet()) {
-	///    System.err.println("@@@ relFilename: " + relFilename);
-	///}
+	for (Object relFilename : source_file_name_to_description_elements_mapping.keySet()) {
+	    System.err.println("\n@@@ relFilename: " + relFilename);
+	}
 	
 	// Check whether this file (i.e. doc.xml or docmets.xml on inheritance) file contains extracted metadata for the specified file
-	ArrayList description_elements_list = (ArrayList) source_file_name_to_description_elements_mapping.get(file_relative_path);
+	//ArrayList description_elements_list = (ArrayList) source_file_name_to_description_elements_mapping.get(file_relative_path);
+	ArrayList description_elements_list = findSourceFileMapKeyMatch(file, file_relative_path);
 	if (description_elements_list == null) {
-	    // ...it doesn't
-	    return metadata_values;
+			// ...it doesn't
+			System.err.println("Unable to find meta for (regular file path form) " + file_relative_path);
+			if(Utility.isWindows()) {
+				System.err.println("    Or for windows shortFile path form, or for combinations with regular file path form");
+			}
+			return metadata_values; // we're done
 	}
 
@@ -275,5 +363,5 @@
 			}
 			
-			///System.err.println("@@@@ Found gsdlsourcefilename: " + gsdlsourcefilename_value);
+			System.err.println("@@@@ Found gsdlsourcefilename: " + gsdlsourcefilename_value);
 			// Remember this for quick access later
 			if (source_file_name_to_description_elements_mapping.get(gsdlsourcefilename_value) == null) {
@@ -326,5 +414,5 @@
 		gsdlsourcefilename_value = decodeSourceFilename(gsdlsourcefilename_value, fileRenameMethod, is_unix_path);
 		source_file_name_to_description_elements_mapping.put(gsdlsourcefilename_value, value_list);
-	    }
+	    }		
 	}
 	catch (FileNotFoundException exception) {
@@ -379,6 +467,6 @@
 		// General info: https://stackoverflow.com/questions/43089541/difference-between-basic-and-url-base64-encoding-in-java-8
 		byte[] bytes = Base64.decodeBase64(importFilePathParts[i].getBytes());
-		///System.err.println("Got base64 string: " + importFilePathParts[i]);
-		///System.err.println("Decoded from base64 to bytes: " + bytes);
+		System.err.println("Got base64 string: " + importFilePathParts[i]);
+		System.err.println("Decoded from base64 to bytes: " + new String(bytes, System.getProperty("file.encoding")));
 		// Using system file.encoding to interpret the resulting bytestring as a String,
 		// just as we always did with URL decoding method
@@ -397,5 +485,5 @@
 	decoded_gsdlsourcefilename += file_ext;
 	
-	///System.err.println("@@@@ decoded_gsdlsourcefilename: " + Utility.debugUnicodeString(decoded_gsdlsourcefilename));
+	System.err.println("@@@@ decoded_gsdlsourcefilename: " + Utility.debugUnicodeString(decoded_gsdlsourcefilename));
 
 	return decoded_gsdlsourcefilename;
Index: /main/trunk/gli/src/org/greenstone/gatherer/util/Utility.java
===================================================================
--- /main/trunk/gli/src/org/greenstone/gatherer/util/Utility.java	(revision 33756)
+++ /main/trunk/gli/src/org/greenstone/gatherer/util/Utility.java	(revision 33757)
@@ -114,4 +114,84 @@
 	  
 	  return result;
+	}
+	
+	/**
+	 * The following calls a method in WindowsNativeFunctions.java to retrieve Windows short file names
+	 * taken from http://dolf.trieschnigg.nl/eightpointthree/eightpointthree.html
+	 * which uses the the non-JNI NativeCall jar file for which WindowsNativeFunctions imports com.eaio.nativecall.*
+     *
+     * returns the short filename (8.3) for a file in Windows
+     * 
+     * @param longFileName - must be the full path to an actual existing file
+     * @return a string with the short filename, or null if an error occurred or the
+     *         file does not exist.
+     */
+    public static String getWindowsShortFileName(String longFileName) throws Exception {
+		if(!Utility.isWindows()) {
+			return longFileName;
+		} else {
+			//return WindowsNativeFunctions.getEightPointThree(longFileName);
+			return getMSDOSName(longFileName);
+		}
+    }
+	
+	/**	 
+	 * getMSDOSName() and its helper function getAbsolutePath(fileName)
+	 * are from https://stackoverflow.com/questions/18893284/how-to-get-short-filenames-in-windows-using-java
+	 * getMSDOSName() modified to use our SafeProcess class.
+	 *
+	 * @param fileName - the regular fileName to be converted. Must be the full path to an actual existing file
+	 * @return Windows shortfile name for the fileName parameter given.
+	 */
+	public static String getMSDOSName(String fileName)
+		throws IOException, InterruptedException {
+
+		/*
+		String path = getAbsolutePath(fileName);
+		
+		changed "+ fileName.toUpperCase() +" to "path"
+		Process process =
+			Runtime.getRuntime().exec(
+				"cmd /c for %I in (\"" + path + "\") do @echo %~fsI");
+		
+		process.waitFor();
+		
+		byte[] data = new byte[65536];
+		int size = process.getInputStream().read(data);
+
+		if (size <= 0) {
+			return null;
+		}
+
+		return new String(data, 0, size).replaceAll("\\r\\n", "");
+		*/
+		String path = getAbsolutePath(fileName);
+		
+		SafeProcess process = new SafeProcess("cmd /c for %I in (\"" + path + "\") do @echo %~fsI");
+		int returnVal = process.runProcess();
+		if(returnVal != 0) {
+			return null;
+		}
+
+		String data = process.getStdOutput();
+		if(data == null) {
+			return null;			
+		}
+		else return data.replaceAll("\\r\\n", "");
+	}
+	public static String getAbsolutePath(String fileName)
+		throws IOException {
+		File file = new File(fileName);
+		String path = file.getAbsolutePath();
+
+		if (file.exists() == false)
+			file = new File(path);
+
+		path = file.getCanonicalPath();
+
+		if (file.isDirectory() && (path.endsWith(File.separator) == false))
+			path += File.separator;
+
+		return path;
 	}
 	
Index: /main/trunk/greenstone2/perllib/doc.pm
===================================================================
--- /main/trunk/greenstone2/perllib/doc.pm	(revision 33756)
+++ /main/trunk/greenstone2/perllib/doc.pm	(revision 33757)
@@ -298,19 +298,17 @@
     my @dirs = ($sep eq "\\") ? split(/[\\\/]+/, $dirname) : split(/\//, $dirname);
     
-    my $dirpath = "";    
+    my $dirpath = "";
+	
+	# Don't encode the first folder ("import" or "tmp"): GLI's DocXMLFile.java looks for literal "import"
+	# or "tmp" before it knows what the file rename method to be used decode the rest of gsdlsourcefilename is.
+	if(scalar (@dirs) > 1) {
+		$dirpath = shift(@dirs); 
+	}
     foreach my $subdir (@dirs) {
-	##print STDERR "@@@@ Found subdir: $subdir\n";
-	
-	# The import folder can be called anything, including in non-ASCII encodings.
-	# Don't need to avoid encoding default import folder called "import", as it gets
-	# URL/base64 encoded to ITSELF.
-	# But can't encode (URL/base64 encode) any $ENV{'GSDLIMPORTDIR'} though if it's set,
-	# as BasePlugout::get_doc_dir() removes any $ENV{'GSDLIMPORTDIR'} prefix. Because if
-	# the $ENV{'GSDLIMPORTDIR'} part of gsdlsourcefilename is encoded here, the prefix
-	# won't match with $ENV{'GSDLIMPORTDIR'}
-
-	unless ($ENV{'GSDLIMPORTDIR'} && $subdir eq $ENV{'GSDLIMPORTDIR'}) { 
-	    $subdir = &util::rename_file($subdir, $rename_method);
-	}
+	print STDERR "@@@@ Found subdir: $subdir\n";
+	
+	$subdir = &util::rename_file($subdir, $rename_method);
+	#print STDERR "@@@@ encoded subdir: $subdir\n";
+	
 	$dirpath = &FileUtils::filenameConcatenate($dirpath, $subdir);
     }
Index: /main/trunk/greenstone2/perllib/unicode.pm
===================================================================
--- /main/trunk/greenstone2/perllib/unicode.pm	(revision 33756)
+++ /main/trunk/greenstone2/perllib/unicode.pm	(revision 33757)
@@ -697,7 +697,21 @@
 }
 
+# Base64 encoding does not encode a pure ASCII to itself. This is important to know.
+# If the $force_encode parameter is true, then this method WILL base64 encode whatever
+# string is passed in, including any plain ASCII string.
+# That means this method could double encode an already encoded string.
+# However, this method is necessary because on the GLI end, we can't detect whether a plain
+# ASCII string has been encoded or not. And if gsdlsourcefilerenamemethod is set to base64,
+# then gli will always attempt to decode all parts of the relative path gsdlsourcefilename
+# (except the "import" prefix, which is special) or none of the parts.
+sub force_base64_encode {
+    my ($text) = @_;
+    my $force_encode = 1;
+	return &base64_encode($text, $force_encode);
+}
+
 sub base64_encode {
-    my ($text) = @_;
-    if(!&conforms_to_mod_base64($text)) {
+    my ($text, $force_encode) = @_;
+    if($force_encode || !&conforms_to_mod_base64($text)) {
 	# return entity for underscore to underscore before encoding
 	$text =~ s/&\#095;/_/g;
Index: /main/trunk/greenstone2/perllib/util.pm
===================================================================
--- /main/trunk/greenstone2/perllib/util.pm	(revision 33756)
+++ /main/trunk/greenstone2/perllib/util.pm	(revision 33757)
@@ -1216,5 +1216,5 @@
     }
     elsif ($rename_method eq "base64") {
-	$tailname = &unicode::base64_encode($tailname);
+	$tailname = &unicode::force_base64_encode($tailname);
 	$tailname =~ s/\s*//sg;      # for some reason it adds spaces not just at end but also in middle
     }
