using UnityEditor; using UnityEngine; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Xml.Serialization; using Perforce.P4; using log4net; namespace P4Connect { /// <summary> /// A collection of static methods that allow us to check the perforce connection settings /// </summary> public class VerifySettings { private static readonly ILog log = LogManager.GetLogger(typeof (VerifySettings)); public static string LastWorkspaceMapping; private static readonly string fakeuser = "p4connect"; /// <summary> /// If rv is true. Server is valid /// If rv is false. Message is valid /// </summary> /// <param name="rv">pass / fail</param> /// <param name="cfg">ConnectionConfig reference</param> /// <param name="message">error message</param> public delegate void ServerInfoResult(bool rv, ConnectionConfig cfg, string message); public delegate void TestResult(bool rv, string message); /// <summary> /// Checks whether the source control setting is correct /// </summary> /// <returns></returns> public static bool CheckMetaFiles() { return EditorSettings.externalVersionControl.Contains("Meta Files"); } // Delegate Echos Perforce commands to the p4connect log static void CommandEcho(String data) { if (Config.EchoP4Commands) { log.Debug(data); } } /// <summary> /// Checks that the server is reachable /// Does not need a user, password or client /// </summary> /// <param name="cfg">Connection configuration</param> /// <param name="handler">callback handler</param> /// <returns>true if successful</returns> public static bool CheckServerUri(ConnectionConfig cfg, ServerInfoResult handler = null) { bool serverValid = false; if (cfg.Server.Length == 0) { cfg.ServerValid = false; if (handler != null) { handler(serverValid, cfg, "Server address cannot be empty"); } return serverValid; } string[] pieces = cfg.Server.Split(':'); if (pieces.Count() < 2) { cfg.ServerValid = false; if (handler != null) { handler(serverValid, cfg, "Server address needs both hostname and port, with a colon between them"); } return serverValid; } Repository depot = null; // IDisposable try { Server testServer = new Server(new ServerAddress(cfg.Server)); depot = new Repository(testServer); Connection con = depot.Connection; con.UserName = fakeuser; con.Client = null; if (!String.IsNullOrEmpty(cfg.Hostname)) { System.Environment.SetEnvironmentVariable("P4HOST", cfg.Hostname); } bool rv = con.Connect(Version.ConnectOptions); con.CommandEcho += CommandEcho; // If that didn't throw an exception, make sure the server is reported as online serverValid = (testServer.State == ServerState.Online); cfg.ServerValid = serverValid; if (serverValid) { //Debug.Log("SERVERVALID in CheckServerURI"); cfg.ApiLevel = con.ApiLevel; cfg.IsCaseSensitive = testServer.Metadata.CaseSensitive; cfg.IsUnicode = testServer.Metadata.UnicodeEnabled; } if (handler != null) { handler(serverValid, cfg, serverValid ? "Connected" : "Connection Failed"); } con.CommandEcho -= CommandEcho; con.Disconnect(); } catch (P4Exception ex) { if (ex.ErrorCode == P4ClientError.MsgRpc_HostKeyUnknown) { //Debug.Log("Ignoring Trust Error in CheckServerURI"); serverValid = true; // ignore trust errors during URI test } CheckP4Exception(ex, cfg); if (handler != null) { handler(serverValid, cfg, ex.Message); } } finally { if (depot != null) depot.Dispose(); } System.Environment.SetEnvironmentVariable("P4HOST", ""); return serverValid; } /// <summary> /// Get a list of Users from the server /// Does not need a user, password or client /// </summary> /// <param name="cfg">Connection configuration</param> /// <param name="handler">callback handler</param> /// <returns>true if successful</returns> public static IList<User> GetUsers(ConnectionConfig cfg, TestResult handler = null) { IList<User> users = null; Repository depot = null; // IDisposable try { Server testServer = new Server(new ServerAddress(cfg.Server)); depot = new Repository(testServer); Connection con = depot.Connection; con.UserName = String.Empty; con.Client = null; if (con.Connect(Version.ConnectOptions)) { con.CommandEcho -= CommandEcho; UsersCmdOptions opts = new UsersCmdOptions(Perforce.P4.UsersCmdFlags.None, 20); users = depot.GetUsers(opts, null); con.CommandEcho -= CommandEcho; } } catch (P4Exception ex) { CheckP4Exception(ex, cfg); if (handler != null) { handler(false, ex.Message); } } finally { if (depot != null) depot.Dispose(); } return users; } /// <summary> /// Get User from the server given username /// </summary> /// <param name="cfg">connection configuration</param> /// <param name="username">user name</param> /// <param name="handler">callback handler</param> /// <returns>User record for user specified</returns> public static User GetUser(ConnectionConfig cfg, string username, TestResult handler = null) { User user = null; Repository depot = null; // IDisposable try { Server testServer = new Server(new ServerAddress(cfg.Server)); depot = new Repository(testServer); Connection con = depot.Connection; con.UserName = cfg.User; if (con.Connect(Version.ConnectOptions)) { con.CommandEcho += CommandEcho; con.Credential = con.Login(cfg.Password, true); cfg.PasswordValid = (con.Credential != null); var opts = new UserCmdOptions(Perforce.P4.UserCmdFlags.Output); user = depot.GetUser(username,opts); con.CommandEcho -= CommandEcho; } } catch (P4Exception ex) { CheckP4Exception(ex, cfg); if (handler != null) { handler(false, ex.Message); } } finally { if (depot != null) depot.Dispose(); } return user; } /// <summary> /// Save a User to the server given servername, user /// </summary> /// <param name="cfg">Connection configuration</param> /// <param name="user">User class reference</param> /// <param name="handler">callback handler</param> /// <returns>returns User record of user just saved</returns> public static User SaveUser(ConnectionConfig cfg, User user, TestResult handler = null) { Repository depot = null; // IDisposable try { Server testServer = new Server(new ServerAddress(cfg.Server)); depot = new Repository(testServer); Connection con = depot.Connection; con.UserName = cfg.User; if (con.Connect(Version.ConnectOptions)) { con.CommandEcho += CommandEcho; con.Credential = con.Login(cfg.Password, true); cfg.PasswordValid = (con.Credential != null); user = depot.CreateUser(user); con.CommandEcho -= CommandEcho; } } catch (P4Exception ex) { CheckP4Exception(ex, cfg); if (handler != null) { handler(false, ex.Message); } } finally { if (depot != null) depot.Dispose(); } return user; } /// <summary> /// See if a specific user already exists on this server /// </summary> /// <param name="cfg">Connection configuration</param> /// <param name="username">user name</param> /// <returns>true if the user already exists on the server</returns> public static bool UserExists(ConnectionConfig cfg, string username) { IList<User> users = new List<User>(); try { users = VerifySettings.GetUsers(cfg, UserBrowseResponse); } catch (Exception ex) { Debug.Log("GetUsers Exception: " + ex.Message); } if (!users.Any()) { Debug.Log("no users found"); return false; } return users.Any(u => string.Equals(u.Id, username, cfg.IsCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)); } /// <summary> /// Query the server for a list of workspaces available to "user" /// Must be wrapped in a "try" block /// </summary> /// <param name="cfg">Connection configuration</param> /// <param name="user">username to look up</param> /// <param name="handler">callback handler</param> /// <returns>an IList of Client</returns> public static IList<Client> GetWorkspacesByUser(ConnectionConfig cfg, string user, TestResult handler = null) { // Debug.Log("Getting workspaces from " + cfg.Server + " for user: "+ user); IList<Client> clients = null; Repository depot = null; // IDisposable try { Server testServer = new Server(new ServerAddress(cfg.Server)); depot = new Repository(testServer); Connection con = depot.Connection; con.UserName = cfg.User; if (con.Connect(Version.ConnectOptions)) { con.CommandEcho += CommandEcho; con.Credential = con.Login(cfg.Password, true); cfg.PasswordValid = (con.Credential != null); if (cfg.PasswordValid) { ClientsCmdOptions opts = new ClientsCmdOptions(Perforce.P4.ClientsCmdFlags.None, user, "", 20, ""); clients = depot.GetClients(opts); } con.CommandEcho -= CommandEcho; } } catch (P4Exception ex) { CheckP4Exception(ex, cfg); if (handler != null) { handler(false, ex.Message); } } finally { if (depot != null) depot.Dispose(); } return clients; } public static void UserBrowseResponse(bool result, string info) { string summary = result ? "Users Retrieved" : "Failed to retrieve users"; EditorUtility.DisplayDialog(summary, info, "ok"); } public static void WorkspaceBrowseResponse(bool result, string info) { string summary = result ? "Workspaces Retrieved" : "Failed to retrieve workspaces"; EditorUtility.DisplayDialog(summary, info, "ok"); } /// <summary> /// See if a specific workspace already exists on this server /// </summary> /// <param name="cfg">connection configuration</param> /// <param name="workspace">workspace name</param> /// <returns>true if specified workspace exists on the server</returns> public static bool WorkspaceExists(ConnectionConfig cfg, string workspace) { // Get the list of all Workspaces var clients = new List<Client>(); try { clients = GetWorkspacesByUser(cfg, cfg.User, WorkspaceBrowseResponse).ToList(); } catch (Exception ex) { Debug.Log("GetClients Exception: " + ex.Message); } if (!clients.Any()) { return false; } return clients.Any(c => string.Equals(c.Name, workspace, cfg.IsCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)); } /// <summary> /// Retrieve a workspace from the server as username /// given a workspace name /// </summary> /// <param name="cfg">Connection Configuration</param> /// <param name="workspace">name of workspace</param> /// <param name="handler">callback handler</param> /// <returns>Client Workspace record requested</returns> public static Client GetWorkspace(ConnectionConfig cfg, string workspace, TestResult handler = null) { Client client = null; Repository depot = null; // IDisposable try { Server testServer = new Server(new ServerAddress(cfg.Server)); depot = new Repository(testServer); Connection con = depot.Connection; con.UserName = cfg.User; if (con.Connect(Version.ConnectOptions)) { con.CommandEcho += CommandEcho; var opts = new ClientCmdOptions(Perforce.P4.ClientCmdFlags.Output); client = depot.GetClient(workspace, opts); con.CommandEcho -= CommandEcho; } } catch (P4Exception ex) { CheckP4Exception(ex, cfg); if (handler != null) { handler(false, ex.Message); } } finally { if (depot != null) depot.Dispose(); } return client; } /// <summary> /// Save a Workspace to given server as user /// This is used to update server with modified or new user /// </summary> /// <param name="cfg">connection configuration</param> /// <param name="workspace">workspace to write</param> /// <param name="handler">callback</param> public static void SaveWorkspace(ConnectionConfig cfg, Client workspace, TestResult handler = null) { Repository depot = null; // IDisposable try { Server testServer = new Server(new ServerAddress(cfg.Server)); depot = new Repository(testServer); Connection con = depot.Connection; con.UserName = cfg.User; if (con.Connect(Version.ConnectOptions)) { con.CommandEcho += CommandEcho; depot.CreateClient(workspace); con.CommandEcho -= CommandEcho; } } catch (P4Exception ex) { CheckP4Exception(ex, cfg); if (handler != null) { handler(false, ex.Message); } } finally { if (depot != null) depot.Dispose(); } } public static void PromptForPassword() { PasswordPrompt.Init(); } public static bool PromptForTrust(string trustKey) { return EditorUtility.DisplayDialog("Untrusted Server!", trustKey, "Trust This Server", "Cancel"); } /// <summary> /// Check if username and password are valid /// Uses ConnectionConfig settings for connection /// </summary> /// <param name="cfg" >Connection Configuration</param> /// <returns>true if successful</returns> public static bool CheckUsernamePassword(ConnectionConfig cfg) { if (PasswordPrompt.Active) { return false; } // Debug.Log("CheckUsernamePassword :" + cfg.ToString()); bool trustedServer = false; retry_connection: Repository depot = null; // IDisposable try { bool rv = false; Server testServer = new Server(new ServerAddress(cfg.Server)); depot = new Repository(testServer); Connection con = depot.Connection; if (!String.IsNullOrEmpty(cfg.Hostname)) { System.Environment.SetEnvironmentVariable("P4HOST", cfg.Hostname); } if (!String.IsNullOrEmpty(cfg.Charset)) { System.Environment.SetEnvironmentVariable("P4CHARSET", cfg.Charset); } con.UserName = cfg.User; if (trustedServer) { rv = con.TrustAndConnect(Version.ConnectOptions, "-y", ""); } else { rv = con.Connect(Version.ConnectOptions); } if (rv) { con.CommandEcho += CommandEcho; cfg.UserValid = true; Credential credentials = con.Login(cfg.Password, true); cfg.PasswordValid = (credentials != null); //// If that didn't throw an exception, make sure the server is reported as online //serverValid = (testServer.State == ServerState.Online); //cfg.ServerValid = serverValid; //if (serverValid) //{ // cfg.ApiLevel = con.ApiLevel; // cfg.IsCaseSensitive = testServer.Metadata.CaseSensitive; // cfg.IsUnicode = testServer.Metadata.UnicodeEnabled; //} con.CommandEcho -= CommandEcho; } con.Disconnect(); } catch (P4Exception ex) { if (ex.ErrorCode == P4ClientError.MsgRpc_HostKeyUnknown) { // Unknown trust certificate... if (PromptForTrust(ex.Message)) { trustedServer = true; goto retry_connection; } } CheckP4Exception(ex, cfg); cfg.PasswordValid = false; } finally { if (depot != null) depot.Dispose(); } System.Environment.SetEnvironmentVariable("P4HOST", ""); System.Environment.SetEnvironmentVariable("P4CHARSET", ""); return cfg.PasswordValid; } /// <summary> /// Check that the workspace is valid /// </summary> /// <parm name="cfg">Connection Configuration</parm> /// <returns>true if workspace is valid</returns> public static bool CheckWorkspace(ConnectionConfig cfg) { if (PasswordPrompt.Active) { return false; } // System.Console.WriteLine("CheckWorkspace()"); if (String.IsNullOrEmpty(cfg.Workspace)) return false; cfg.WorkspaceValid = false; Repository depot = null; // IDisposable try { // Open a connection, assume a secure one, won't hurt if it's not needed Server testServer = new Server(new ServerAddress(cfg.Server)); depot = new Repository(testServer); Connection con = depot.Connection; if (!String.IsNullOrEmpty(cfg.Hostname)) { System.Environment.SetEnvironmentVariable("P4HOST", cfg.Hostname); } if (!String.IsNullOrEmpty(cfg.Charset)) { System.Environment.SetEnvironmentVariable("P4CHARSET", cfg.Charset); } con.UserName = cfg.User; // Open the connection and try to get the list of opened files on the workspace con.Client = new Client(); con.Client.Name = cfg.Workspace; con.Connect(Version.ConnectOptions); con.CommandEcho += CommandEcho; Credential credentials = con.Login(cfg.Password, true); con.Credential = credentials; // Try to get the list of opened files, it'll throw an exception if workspace is invalid depot.GetOpenedFiles(null, null); con.CommandEcho -= CommandEcho; con.Disconnect(); cfg.WorkspaceValid = true; } catch (P4Exception p4Ex) { CheckP4Exception(p4Ex, cfg); cfg.WorkspaceValid = false; } finally { depot.Dispose(); } System.Environment.SetEnvironmentVariable("P4HOST", ""); System.Environment.SetEnvironmentVariable("P4CHARSET", ""); return cfg.WorkspaceValid; } /// <summary> /// Verifies that the project root is valid /// </summary> public static bool CheckProjectRoot(ConnectionConfig cfg) { Repository depot = null; // IDisposable bool rootValid = false; try { // Open a connection, assume a secure one, won't hurt if it's not needed Server testServer = new Server(new ServerAddress(cfg.Server)); depot = new Repository(testServer); Connection con = depot.Connection; if (!string.IsNullOrEmpty(cfg.Hostname)) { System.Environment.SetEnvironmentVariable("P4HOST", cfg.Hostname); } if (!string.IsNullOrEmpty(cfg.Charset)) { System.Environment.SetEnvironmentVariable("P4CHARSET", cfg.Charset); } con.UserName = cfg.User; Client myclient = new Client(); myclient.Name = cfg.Workspace; con.Client = myclient; if (con.Connect(Version.ConnectOptions)) { con.CommandEcho += CommandEcho; cfg.ServerValid = true; //con.Trust(new TrustCmdOptions(TrustCmdFlags.AutoAccept), ""); Credential credentials = con.Login(cfg.Password, true); con.Credential = credentials; // Run "p4 client" to get workspace information ClientMetadata metaData = depot.GetClientMetadata(); if (metaData != null && metaData.Root != null) { LastWorkspaceMapping = metaData.Root.Replace('/', System.IO.Path.DirectorySeparatorChar); rootValid = Utils.IsDirOrValidSubDirectoryOf(Main.RootPath, LastWorkspaceMapping); } // Run "p4 where" to get ClientProjectRoot var spec = FileSpec.LocalSpec(System.IO.Path.Combine(Main.RootPath, "...")); var mappings = con.Client.GetClientFileMappings(spec); if (mappings != null && mappings.Count > 0) { Config.ClientProjectRoot = mappings[0].ClientPath.Path; Config.DepotProjectRoot = mappings[0].DepotPath.Path; //log.Debug("ClientProjectRoot: " + Config.ClientProjectRoot); //log.Debug("DepotProjectRoot: " + Config.DepotProjectRoot); } else { Debug.LogError("Unable to determine Project Root! "); } con.CommandEcho -= CommandEcho; con.Disconnect(); } } catch (P4Exception p4Ex) { CheckP4Exception(p4Ex, cfg); rootValid = false; } catch (Exception ex) { Debug.LogException(ex); rootValid = false; } finally { depot.Dispose(); } System.Environment.SetEnvironmentVariable("P4HOST", ""); System.Environment.SetEnvironmentVariable("P4CHARSET", ""); cfg.ProjectRootValid = rootValid; return rootValid; } /// <summary> /// Check P4Exceptions as they are recieved. look for Password Errors so we can invoke the Password Dialog /// <param name="p4Ex">The Exception thrown</param> /// <param name="cfg">Connection Configuration</param> static void CheckP4Exception(P4Exception p4Ex, ConnectionConfig cfg) { if(p4Ex.ErrorCode == P4ClientError.MsgServer_BadPassword || p4Ex.ErrorCode == P4ClientError.MsgServer_BadPassword0 || p4Ex.ErrorCode == P4ClientError.MsgServer_BadPassword1) { if (!PasswordPrompt.Active) { PromptForPassword(); } } else if (p4Ex.ErrorCode == P4ClientError.MsgRpc_HostKeyUnknown) { // Ignore this. } else { //Debug.Log("Unhandled P4Exception: " + p4Ex.ErrorCode + " msg: " + p4Ex.Message); //Debug.Log(P4ErrorToString(p4Ex.ErrorCode)); Debug.LogException(p4Ex); } } /// <summary> /// Return a string describing the components of a P4Error /// </summary> /// <param name="errorCode"></param> /// <returns>string description of error</returns> static string P4ErrorToString(int errorCode) { int subcode = unchecked(errorCode & 0x3ff); int unique = unchecked(errorCode & 0xffff); int argcount = unchecked((errorCode >> 24) & 0x0f); ErrorGeneric gen = (ErrorGeneric)unchecked((errorCode >> 16) & 0xff); ErrorSubsystem subsys = (ErrorSubsystem)unchecked((errorCode >> 10) & 0x3f); ErrorSeverity sev = (ErrorSeverity)unchecked((errorCode >> 28) & 0x0f); return ("P4Error: subsystem: " + subsys.ToString() + " code: " + subcode.ToString() + " unique: " + unique.ToString() + " generic: " + gen.ToString() + " severity: " + sev.ToString()); } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 19284 | ava | "Forking branch Main of perforce-software-p4connect to ava-p4connect." | ||
//guest/perforce_software/p4connect/main/src/P4Connect/P4Connect/P4Connect.VerifySettings.cs | |||||
#5 | 19279 | Norman Morse |
Update workshop from internal changes. These Changes are the basis of the 2016.2 release - Dropped support for Unity 4 - Built in Unity 5 - Fixed assembly misidentifications. - Fixed problem where configuration dialog would pop up between runs |
||
#4 | 18942 | Norman Morse | 2016.1 Patch 2 changes | ||
#3 | 18665 | Norman Morse | 16.2 preparation checkin | ||
#2 | 18418 | Norman Morse |
Many changes from the dev branch. Icons are packaged differently. Style Sheet is created / used New ConnectionWizard Dialog New PasswordDialog Generate -a tickets Update version to 2016.1.0.0 |
||
#1 | 16209 | Norman Morse | Move entire source tree into "main" branch so workshop code will act correctly. | ||
//guest/perforce_software/p4connect/src/P4Connect/P4Connect/P4Connect.VerifySettings.cs | |||||
#8 | 15244 | Norman Morse |
Better Directory support in "add" "get latest" "refresh" and other commands. Improved Project Root detection Various Bug Fixes and Clean up |
||
#7 | 12568 | Norman Morse | Fixed some error handling during Perforce Configuration | ||
#6 | 12565 | Norman Morse |
Integrated from Dev Branch Made ChangeManager into Static Class. Improved close window behavior for when connection is invalid Fixed localOpenFiles not updating on submit |
||
#5 | 12553 | Norman Morse |
integrate from internal main Build fixes for EC. Major changes to Configuration and re-initialization code. Bug fixes |
||
#4 | 12512 | Norman Morse | Integrate from Dev branch, preparing for Beta3 release | ||
#3 | 12251 | Norman Morse |
Fixes for Beta 2 release Mostly Configuration dialog bug fixes |
||
#2 | 12135 | Norman Morse |
Integrate dev branch changes into main. This code is the basiis of the 2.7 BETA release which provides Unity 5 compatibility |
||
#1 | 10940 | Norman Morse |
Inital Workshop release of P4Connect. Released under BSD-2 license |