示例#1
0
        /// <summary>
        /// Find a share that allows access to this local path.
        /// Note that it's possible for more than one share to match a specific path.
        /// In such case the most specific share will be chosen.
        /// For example if share A has local path /home/user and share B has local
        /// path /home/user/downloads, for localPath='/home/user/downloads/file.mp4'
        /// the method returns B.
        /// </summary>
        /// <returns>The parent share or null.</returns>
        /// <param name="localPath">Local absolute path of a file or directory.
        /// It doesn't necessarily need to exist, but needs to be a valid path.
        /// It should be a plain path, not an URI or UNC path and shouldn't be
        /// escaped in any way.
        /// </param>
        public Share FindParentShare(TokenizedLocalPath localPath)
        {
            Share result = null;

            ShareTreeEntry treeElem = treeRoot;

            foreach (string elem in localPath)
            {
                string elemCased = caseSensitiveFileSystem ? elem : elem.ToLowerInvariant();

                ShareTreeEntry childElem = null;
                treeElem.children.TryGetValue(elemCased, out childElem);

                if (childElem != null)
                {
                    if (childElem.share != null)
                    {
                        // Found a matching share! But continue searching in case
                        // there's another, more specific share.
                        result = childElem.share;
                    }

                    // Continue diving into the tree.
                    treeElem = childElem;
                }
                else
                {
                    // No more children.
                    break;
                }
            }

            return(result);
        }
示例#2
0
        /// <summary>
        /// Add the share to this list. If for any reason there's already another
        /// share for this local path, the new share replaces it.
        /// </summary>
        /// <param name="share">The share to add.</param>
        public void AddOrReplace(Share share)
        {
            if (!sharesByName.ContainsKey(share.Name))
            {
                sharesByName.Remove(share.Name);
            }
            sharesByName.Add(share.Name, share);

            TokenizedLocalPath path     = share.LocalPath;
            ShareTreeEntry     treeElem = treeRoot;

            foreach (string elem in share.LocalPath)
            {
                string elemCased = caseSensitiveFileSystem ? elem : elem.ToLowerInvariant();

                ShareTreeEntry childElem = null;
                treeElem.children.TryGetValue(elemCased, out childElem);

                if (childElem == null)
                {
                    childElem = new ShareTreeEntry(elem);
                    treeElem.children.Add(elemCased, childElem);
                }

                treeElem = childElem;
            }
            treeElem.share = share;
        }
        public override bool Equals(Object obj)
        {
            if (obj == null || GetType() != obj.GetType())
            {
                return(false);
            }

            TokenizedLocalPath other = (TokenizedLocalPath)obj;

            return(Array.Equals(this.pathElements, other.pathElements));
        }
        public static TokenizedLocalPath Combine(TokenizedLocalPath parentPath, TokenizedLocalPath relativePath)
        {
            if (relativePath == null || relativePath.IsEmpty())
            {
                return(parentPath);
            }

            string[] parentElems   = parentPath.pathElements;
            string[] relativeElems = relativePath.pathElements;
            string[] newElems      = new string[parentElems.Length + relativeElems.Length];
            Array.Copy(parentElems, newElems, parentElems.Length);
            Array.Copy(relativeElems, 0, newElems, parentElems.Length, relativeElems.Length);
            return(new TokenizedLocalPath(newElems));
        }
示例#5
0
 public static string MakeLink(LinkFormat linkFormat, string host, SharesList shares, TokenizedLocalPath localPath, char systemSeparator)
 {
     if (linkFormat == LinkFormat.LocalFile || linkFormat == LinkFormat.LocalPath)
     {
         return(ComposeLink(linkFormat, host, null, localPath, systemSeparator));
     }
     else
     {
         Share share = shares.FindParentShare(localPath);
         if (share != null)
         {
             TokenizedLocalPath relPath = TokenizedLocalPath.MakeRelative(share.LocalPath, localPath, shares.CaseSensitiveFileSystem);
             return(ComposeLink(linkFormat, host, share, relPath, systemSeparator));
         }
         else
         {
             return(null);
         }
     }
 }
        public static TokenizedLocalPath MakeRelative(TokenizedLocalPath parentPath, TokenizedLocalPath childPath, bool caseSensitiveFileSystem)
        {
            string[] parentElems = parentPath.pathElements;
            string[] childElems  = childPath.pathElements;

            // Check if the child path is really relative to parent.
            StringComparison comparision = caseSensitiveFileSystem ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase;

            for (int i = 0; i < parentElems.Length; i++)
            {
                string e1 = parentElems[i];
                string e2 = childElems[i];
                if (!string.Equals(e1, e2, comparision))
                {
                    throw new ArgumentException(string.Format("child path {0} is not relative to parent {1}", childPath.ToString(), parentPath.ToString()));
                }
            }

            string[] relativeElems = new string[childElems.Length - parentElems.Length];
            Array.Copy(childElems, parentElems.Length, relativeElems, 0, relativeElems.Length);
            return(new TokenizedLocalPath(relativeElems));
        }
示例#7
0
        public static string ComposeLink(LinkFormat linkFormat, string host, Share share, TokenizedLocalPath shareRelativePath, char systemSeparator)
        {
            StringBuilder sb = new StringBuilder();

            if (linkFormat == LinkFormat.Smb)
            {
                sb.Append("smb://");
                sb.Append(Uri.EscapeUriString(host));
                sb.Append('/');
                sb.Append(share.NameEscaped);
                if (shareRelativePath != null && !shareRelativePath.IsEmpty())
                {
                    sb.Append('/');
                    shareRelativePath.Format(sb, true, '/');
                }
            }
            else if (linkFormat == LinkFormat.Unc)
            {
                sb.Append("\\\\");
                sb.Append(host);
                sb.Append('\\');
                sb.Append(share.Name);
                if (shareRelativePath != null && !shareRelativePath.IsEmpty())
                {
                    sb.Append('\\');
                    shareRelativePath.Format(sb, false, '\\');
                }
            }
            else if (linkFormat == LinkFormat.UncEscaped)
            {
                sb.Append("\\\\");
                sb.Append(Uri.EscapeUriString(host));
                sb.Append('\\');
                sb.Append(share.NameEscaped);
                if (shareRelativePath != null && !shareRelativePath.IsEmpty())
                {
                    sb.Append('\\');
                    shareRelativePath.Format(sb, true, '\\');
                }
            }
            else if (linkFormat == LinkFormat.File)
            {
                sb.Append("file://");
                sb.Append(Uri.EscapeUriString(host));
                sb.Append('/');
                sb.Append(share.NameEscaped);
                if (shareRelativePath != null && !shareRelativePath.IsEmpty())
                {
                    sb.Append('/');
                    shareRelativePath.Format(sb, true, '/');
                }
            }
            else if (linkFormat == LinkFormat.LocalFile)
            {
                sb.Append("file://");
                if (share != null)
                {
                    share.LocalPath.Format(sb, true, '/');
                }
                if (shareRelativePath != null && !shareRelativePath.IsEmpty())
                {
                    if (share != null)
                    {
                        sb.Append('/');
                    }
                    shareRelativePath.Format(sb, true, '/');
                }
            }
            else if (linkFormat == LinkFormat.LocalPath)
            {
                if (share != null)
                {
                    share.LocalPath.Format(sb, false, systemSeparator);
                }
                if (shareRelativePath != null && !shareRelativePath.IsEmpty())
                {
                    if (share != null)
                    {
                        sb.Append(systemSeparator);
                    }
                    shareRelativePath.Format(sb, false, systemSeparator);
                }
            }
            else
            {
                throw new Exception("unexpected link format: " + linkFormat);
            }

            string link = sb.ToString();

            return(link);
        }
示例#8
0
        [STAThread]         // for Windows.Forms, which is used by clipboard
        public static int Main(string[] args)
        {
            // Failed unless otherwise reached the right point.
            int exitCode = 1;

            string     host        = null;
            string     sharesfile  = null;
            bool       showHelp    = false;
            bool       showVersion = false;
            LinkFormat linkFormat  = LinkFormat.Unc;
            HostType   hostType    = HostType.Netbios;

            // Not implemented for now to keep things simple. The problem with the clipboard is that it's normally tied to a widget toolkit.
            // Other than that, easy peasy.
            bool copyToClipboard = false;

            bool readStdIn = false;
            bool noStdErr  = false;

            // I don't really like changing local variables from lambdas, but it makes the code short.
            // If I get to know other command line parsing libs I may replace Mono.Options in the future.. maybe.
            var options = new OptionSet()
            {
                { "host=", "Custom hostname.",
                  (string v) => host = v },
                { "sharesfile=", "Provide a custom .ini file with share list. The format should correspond to outout of \"net usershare info\" command, which also matches smb.conf share list.",
                  (string v) => sharesfile = v },
                { "format=", "Link format. Either smb or unc. Default is " + linkFormat + ".",
                  (LinkFormat v) => linkFormat = v },
                { "hosttype=", "One of ip, ip6, netbios or hostname. Default is " + hostType + ". Ignored if custom host is specified.",
                  (HostType v) => hostType = v },
                { "help", "Show this message and exit.",
                  v => showHelp = v != null },
                { "stdin", "Read path list from strandard input.",
                  v => readStdIn = v != null },
                { "nostderr", "Write errors into stdout instead of stderr.",
                  v => noStdErr = v != null },
                { "copy", "Copy the result to clipboard instead of standard output.",
                  v => copyToClipboard = v != null },
                { "version", "Print version and exit.",
                  v => showVersion = v != null },
            };

            try {
                List <string> localPaths = options.Parse(args);

                TextWriter   output;
                StringWriter outputSW;
                if (copyToClipboard)
                {
                    outputSW = new StringWriter();
                    output   = outputSW;
                }
                else
                {
                    outputSW = null;
                    output   = Console.Out;
                }

                if (noStdErr)
                {
                    Console.SetError(output);
                }

                if (readStdIn)
                {
                    string s;
                    while ((s = Console.In.ReadLine()) != null)
                    {
                        localPaths.Add(s);
                    }
                }

                if (showVersion)
                {
                    Console.WriteLine(System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString());
                    exitCode = 0;
                }
                else if (showHelp)
                {
                    exitCode = 0;
                }
                else
                {
                    if (localPaths.Count < 1)
                    {
                        throw new InputError("local path not specified");
                    }

                    // Make the path searches case insensitive on Windows and
                    // case sensitive on anything else.
                    // This is inexact as on the same machine it's possible to have multiple
                    // file systems with different case sensitivity, but let's keep things
                    // simple. In worst case the link won't be created.
                    bool caseSensitiveFileSystem;
                    switch (Environment.OSVersion.Platform)
                    {
                    case PlatformID.Win32NT:
                        caseSensitiveFileSystem = false;
                        break;

                    case PlatformID.Win32Windows:
                        caseSensitiveFileSystem = false;
                        break;

                    case PlatformID.WinCE:
                        caseSensitiveFileSystem = false;
                        break;

                    default:
                        caseSensitiveFileSystem = true;
                        break;
                    }

                    SharesList shares = new SharesList(caseSensitiveFileSystem);

                    if (sharesfile == null)
                    {
                        switch (Environment.OSVersion.Platform)
                        {
                        case PlatformID.Unix:
                            // Load the samba shares.
                            SambaShareLoader.LoadUserShares(shares);
                            SambaShareLoader.LoadGlobalSambaShares(shares);
                            break;

                        //						case PlatformID.MacOSX:
                        case PlatformID.Win32NT:
                            WindowsShareLoader.LoadGlobalShares(shares);
                            break;

                        case PlatformID.Win32Windows:                                 // will it work?
                            WindowsShareLoader.LoadGlobalShares(shares);
                            break;

                        case PlatformID.WinCE:                                 // will it work?
                            WindowsShareLoader.LoadGlobalShares(shares);
                            break;

                        default:
                            throw new Exception(string.Format("Unknown platform {0}. Could not get LAN shares from the system.", Environment.OSVersion.Platform));
                        }
                    }
                    else
                    {
                        // For the custom share list we use the same format as the "net usershare info" command.
                        string shareListIniContent = File.ReadAllText(sharesfile, Encoding.UTF8);
                        SambaShareLoader.ParseNetUserShareList(shares, shareListIniContent);
                    }

                    if (host == null)
                    {
                        if (hostType == HostType.Ip || hostType == HostType.Ip6)
                        {
                            host = FindIPAddress(hostType == HostType.Ip6);
                        }
                        else if (hostType == HostType.Netbios)
                        {
                            host = System.Environment.MachineName;
                        }
                        else if (hostType == HostType.Hostname)
                        {
                            host = Dns.GetHostName();
                        }
                        else
                        {
                            throw new InputError(String.Format("unexpected host-type option: {0}", hostType));
                        }
                    }

                    /*
                     * Small note on complexity. For now the complexity is exponential.
                     * As long as there aren't many shares AND many paths to convert it
                     * shouldn't matter much. Otherwise a better solution is needed with
                     * some data structure. For example searching in a sorted list of
                     * shares using binary search.
                     *
                     * Could implement a binary search on SortedList<string,string> or
                     * use a ready made class prefixdictionary/trie.
                     */

                    bool first    = true;
                    bool foundAll = true;
                    foreach (string localPath in localPaths)
                    {
                        if (first)
                        {
                            first = false;
                        }
                        else
                        {
                            output.WriteLine();
                        }

                        string link = null;

                        TokenizedLocalPath tokenizedLocalPath = null;
                        try {
                            // Do some sanity checks and make the path absolute.
                            string localAbsolutePath;
                            {
                                //							Uri uri = null;
                                //							if (Uri.TryCreate(localPath, UriKind.RelativeOrAbsolute, out uri)) {
                                //								if (!uri.IsFile)
                                //									throw new ArgumentException(string.Format("Could not resolve path {0} to URI. Only file/directory paths are supported.", localPath));
                                //
                                //								// Specific case for URIs like file://host/..., but I haven't seen them in the wild.
                                //								if (uri.Host != null && uri.Host != "")
                                //									throw new ArgumentException(string.Format("Could not resolve path {0} to URI. Only file/directory paths are supported.", localPath));
                                //
                                //								// replace the path with the absolute one taken from the URI
                                //								localAbsolutePath = uri.LocalPath;
                                //							} else {
                                // use the passed path as-is
                                localAbsolutePath = localPath;
                                //							}
                            }

                            tokenizedLocalPath = new TokenizedLocalPath(localAbsolutePath, Path.DirectorySeparatorChar);
                        } catch (Exception ex) {
                            Console.Error.WriteLine(ex.ToString());
                        }

                        if (tokenizedLocalPath != null)
                        {
                            link = LinkMaker.MakeLink(linkFormat, host, shares, tokenizedLocalPath, Path.DirectorySeparatorChar);
                        }
                        else
                        {
                            link = localPath;
                        }

                        if (link != null)
                        {
                            output.Write(link);
                        }
                        else
                        {
                            Console.Error.WriteLine(string.Format("No share found containing local path \"{0}\"", localPath));
                            output.Write(localPath);
                            foundAll = false;
                        }
                    }

                    if (copyToClipboard)
                    {
                        string text = outputSW.ToString();
                        ClipboardHelper.CopyText(text);
                    }

                    if (foundAll)
                    {
                        // OK. If we reached this point then everything went O~K.
                        exitCode = 0;
                    }
                }
            } catch (InputError e) {
                Console.Error.WriteLine(e.Message);
                showHelp = true;
            } catch (OptionException e) {
                Console.Error.WriteLine(e.ToString());
                showHelp = true;
            } catch (Exception ex) {
                Console.Error.WriteLine("Unexpected error: " + ex.ToString());
            }

            if (showHelp)
            {
                Console.WriteLine("Usage:");
                Console.WriteLine("\t" + Path.GetFileNameWithoutExtension(System.Environment.CommandLine) + " <local-path> [<local-path2>, ...]");
                Console.WriteLine();
                Console.WriteLine("Options:");
                options.WriteOptionDescriptions(Console.Out);
            }

            return(exitCode);
        }
示例#9
0
 public Share(string name, TokenizedLocalPath localPath)
 {
     this.name        = name;
     this.nameEscaped = Uri.EscapeUriString(name);
     this.localPath   = localPath;
 }