Esempio n. 1
0
        public static void ParseSmbConfShareList(SharesList dstList, string smbConfContent)
        {
            // note: some smb.conffeatures are not handled. Like special variables and includes.
            // TODO special case for [homes] share

            var parser = new IniDataParser();

            parser.Configuration.CommentRegex = new System.Text.RegularExpressions.Regex(@"^[#;](.*)");
            var iniData = parser.Parse(smbConfContent);

            foreach (var shareIniSection in iniData.Sections)
            {
                string shareName = shareIniSection.SectionName;
                if (shareName == "global")
                {
                    continue;
                }

                if (!shareIniSection.Keys.ContainsKey("path"))
                {
                    throw new Exception(String.Format("share {0} doesn't have local path specified", shareName));
                }

                string shareLocalPath = shareIniSection.Keys["path"];

                dstList.AddOrReplace(new Share(shareName, new TokenizedLocalPath(shareLocalPath, '/')));
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Loads the usershare shares.
        /// </summary>
        public static void LoadUserShares(SharesList dstList)
        {
            // Use "net usershare info" command for easy access to samba shares without root.
            // These are limited to user shares, so we still need to parse smb.conf for
            // system-wide shares.
            // Not that I could read them directly from /var/lib/samba/usershares, but the hope
            // is that the net command filters some permissions, so I don't have to. Actually
            // I haven't checked if it does, so it may make sense in order to remove one
            // dipendency.

            string output;

            using (System.Diagnostics.Process p = new System.Diagnostics.Process()) {
                p.StartInfo.FileName               = "net";
                p.StartInfo.Arguments              = "usershare info";
                p.StartInfo.RedirectStandardError  = false;
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.UseShellExecute        = false;
                if (!p.Start())
                {
                    throw new Exception("could not start command \"net usershare info\" to get list of shares");
                }

                output = p.StandardOutput.ReadToEnd();

                p.WaitForExit();

                if (p.ExitCode != 0)
                {
                    throw new Exception(string.Format("command \"net usershare info\" returned error code {0}", p.ExitCode));
                }
            }

            ParseNetUserShareList(dstList, output);
        }
Esempio n. 3
0
        public static void LoadGlobalShares(SharesList dstList)
        {
            // There are multiple ways actually:
            // 1. Lounch the "net share" command
            // 2. Read shares from registry
            // 3. Call native methods from mpr.dll
            // 4. System.Management

            // Side note: actually there's something like Win32_ShareToDirectory in Windows,
            // but I already have all the nice abstraction around having a share list...

            using (ManagementClass exportedShares = new ManagementClass("Win32_Share")) {
                ManagementObjectCollection shares = exportedShares.GetInstances();

                List <Share> addWithHigherPriority = new List <Share>(shares.Count);
                List <Share> addWithLowerPriority  = new List <Share>(shares.Count);

                foreach (ManagementObject share in shares)
                {
                    // Reference: https://msdn.microsoft.com/en-us/library/aa394435.aspx
                    ShareType type = (ShareType)Convert.ToUInt32(share["Type"]);

                    string shareName = share["Name"].ToString();
                    string localPath = share["Path"].ToString();

                    if (type == ShareType.DiskDrive || type == ShareType.Device)
                    {
                        addWithHigherPriority.Add(new Share(shareName, new TokenizedLocalPath(localPath, '\\')));
                    }
                    else if (type == ShareType.DiskDriveAdmin || type == ShareType.DeviceAdmin)
                    {
                        // Take it, but add it with a lower priority.
                        // This way the explicit shares take priority, as these are usually
                        // the ones the user wants to be used and often have lesser
                        // access restrictions.
                        addWithLowerPriority.Add(new Share(shareName, new TokenizedLocalPath(localPath, '\\')));
                    }
                    else
                    {
                        // skip everything else
                    }
                }

                // First add the shares that should take lower priority.
                foreach (Share share in addWithLowerPriority)
                {
                    dstList.AddOrReplace(share);
                }

                // Then add the ones with higher priority, overwriting eventual shares.
                foreach (Share share in addWithHigherPriority)
                {
                    dstList.AddOrReplace(share);
                }
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Loads the global samba shares from smb.conf.
        /// </summary>
        public static void LoadGlobalSambaShares(SharesList dstList)
        {
            string smbConfPath = "/etc/samba/smb.conf";

            if (!File.Exists(smbConfPath))
            {
                throw new FileNotFoundException(smbConfPath);
            }

            string content = File.ReadAllText(smbConfPath);

            ParseSmbConfShareList(dstList, content);
        }
Esempio n. 5
0
        public static void ParseNetUserShareList(SharesList dstList, string shareListIniContent)
        {
            var parser  = new IniDataParser();
            var iniData = parser.Parse(shareListIniContent);

            foreach (var shareIniSection in iniData.Sections)
            {
                string shareName = shareIniSection.SectionName;
                if (!shareIniSection.Keys.ContainsKey("path"))
                {
                    throw new Exception(String.Format("share {0} doesn't have local path specified", shareName));
                }

                string shareLocalPath = shareIniSection.Keys["path"];
                dstList.AddOrReplace(new Share(shareName, new TokenizedLocalPath(shareLocalPath, '/')));
            }
        }
Esempio n. 6
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);
         }
     }
 }
Esempio n. 7
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);
        }