/******************************************************************************* Copyright (c) 2010, Perforce Software, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ /******************************************************************************* * Name : P4BridgeServer.cpp * * Author : dbb * * Description : P4BridgeServer * ******************************************************************************/ #include "StdAfx.h" #include "P4BridgeServer.h" #include <strtable.h> #include <strarray.h> #include <i18napi.h> #include <spec.h> #include <debug.h> #if __APPLE__ #include <langinfo.h> #endif bool CheckErrorId(const ErrorId &eid, const ErrorId &tgt) { return eid.Subsystem() == tgt.Subsystem() && eid.SubCode() == tgt.SubCode(); } bool CheckErrorId(Error &e, const ErrorId &tgt) { if (e.Test()) { // iterate through the ErrorIds in this Error for (int i = 0; ; ++i) { ErrorId *eid = e.GetId(i); if (eid == NULL) break; if (CheckErrorId(*eid, tgt) ) { return true; } } } return false; } // This is were the pointer to the log callback is stored if set by the user. LogCallbackFn * P4BridgeServer::pLogFn = NULL; /****************************************************************************** // LogMessage: Use the client logging callback function (if set) to log a // message in the callers log. ******************************************************************************/ int P4BridgeServer::LogMessage(int log_level, char * file, int line, char * message, ...) { if (pLogFn) { va_list args; va_start(args, message); char buff1[1024]; char buff2[1024]; vsnprintf( buff1, sizeof(buff1), message, args); _snprintf(buff2, sizeof(buff2),"%s [%s@%d]", buff1, file, line); return (*pLogFn)(log_level, file, line, buff2); } return 0; } /******************************************************************************* * * Default Constructer * * Protected, should not be used by a client to create a P4BridgeServer. * ******************************************************************************/ P4BridgeServer::P4BridgeServer(void) : p4base(Type()), isUnicode(-1), useLogin(0), supportsExtSubmit(0) { } /******************************************************************************* * * Constructer * * Create a P4BridgeServer and connect to the specified P4 Server. * ******************************************************************************/ P4BridgeServer::P4BridgeServer( const char *server, const char *user, const char *pass, const char *ws_client) : p4base(Type()), isUnicode(-1), useLogin(0), supportsExtSubmit(0) { clientNeedsInit = 1; // Clear the the callbacks pTaggedOutputCallbackFn = NULL; pErrorCallbackFn = NULL; pInfoResultsCallbackFn = NULL; pTextResultsCallbackFn = NULL; pBinaryResultsCallbackFn = NULL; pPromptCallbackFn = NULL; pResolveCallbackFn = NULL; pResolveACallbackFn = NULL; // connect to the server using a tagged protocol //client_tagged = new ClientApi; //if( server ) // client_tagged->SetPort( server ); //if( user ) // client_tagged->SetUser( user ); //if( pass ) // client_tagged->SetPassword( pass ); //if( ws_client ) // client_tagged->SetClient( ws_client ); //client_tagged->SetProtocol( "tag", "" ); //// return spec string when getting spec based objects //client_tagged->SetProtocol( "specstring", "" ); //client_tagged->SetProtocol( "api", "70" ); // 2006.1 api //client_tagged->SetProtocol( "enableStreams", "" ); // // Load the current P4CHARSET if set. // //if( client_tagged->GetCharset().Length() ) // char * cs = client_tagged->GetCharset().Text(); // connect to the server using a untagged protocol client = new ClientApi; if( server ) client->SetPort( server ); if( user ) client->SetUser( user ); if( pass ) client->SetPassword( pass ); if( ws_client ) client->SetClient( ws_client ); client->SetProtocol( "specstring", "" ); client->SetProtocol( "api", "72" ); // 2006.1 api client->SetProtocol( "enableStreams", "" ); p4debug.SetLevel("-vnet.maxwait=5"); if( client->GetCharset().Length() ) char * cs = client->GetCharset().Text(); ui = NULL; cwd = NULL; pProgramName = NULL; pProgramVer = NULL; } /******************************************************************************* * * Destructor * * Close the connection and free up resources. * ******************************************************************************/ P4BridgeServer::~P4BridgeServer(void) { // Clear the the callbacks pTaggedOutputCallbackFn = NULL; pErrorCallbackFn = NULL; pInfoResultsCallbackFn = NULL; pTextResultsCallbackFn = NULL; pBinaryResultsCallbackFn = NULL; pPromptCallbackFn = NULL; pResolveCallbackFn = NULL; pResolveACallbackFn = NULL; close_connection(); delete client; client = NULL; //delete client_tagged; //client_tagged = NULL; if( cwd ) delete cwd; cwd = NULL; if (pProgramName) delete[] pProgramName; pProgramName = NULL; if (pProgramVer) delete pProgramName; pProgramVer = NULL; } /******************************************************************************* * * connected * * Connect to the specified P4 Server, create a UI. * ******************************************************************************/ int P4BridgeServer::connected( P4ClientError **err ) { if( ui ) return 1; // already connected // Create the p4client instance ui = new P4BridgeClient; //char* arg = "-y"; //char** args = &arg; //run_command( "trust", 1, args, 1 ); //if (!run_command( "trust", 1, args, 1 )) //{ // P4ClientError *e = ui->GetErrorResults(); // if (e!= NULL) // { // *err = CopyStr(e->Message); // } // return 0; //} // Set the Unicode flag to unknown, to force a retest isUnicode = -1; apiLevel = -1; useLogin = -1; supportsExtSubmit = -1; if (GetServerProtocols()) { return 1; } P4ClientError *e = ui->GetErrorResults(); if (e!= NULL) { *err = new P4ClientError(e); } close_connection(); return 0; } /******************************************************************************* * * connect_and_trust * * Connect to the specified P4 Server, create a UI, and establish a trust * relationship. * ******************************************************************************/ int P4BridgeServer::connect_and_trust( P4ClientError **err, char* trust_flag, char* fingerprint ) { if( ui ) return 1; // already connected // Create the p4client instance ui = new P4BridgeClient; char** args = new char*[2]; args[0] = "-d"; run_command( "trust", 1, args, 1 ); args[0] = trust_flag; args[1] = fingerprint; if (!run_command( "trust", 1, args, (fingerprint != NULL)?2:1 )) { P4ClientError *e = ui->GetErrorResults(); if ((e!= NULL) && (e->Severity >= E_FAILED)) { *err = e; } return 0; } // Set the Unicode flag to unknown, to force a retest isUnicode = -1; apiLevel = -1; useLogin = -1; supportsExtSubmit = -1; if (GetServerProtocols()) { return 1; } P4ClientError *e = ui->GetErrorResults(); if (e!= NULL) { *err = e; } close_connection(); return 0; } /******************************************************************************* * * close_connection * * Final disconnect from the P4 Server. * ******************************************************************************/ void P4BridgeServer::close_connection() { // Close connections if ((client) && (clientNeedsInit == 0)) { Error e; client->Final( &e ); clientNeedsInit = 1; } if( ui ) { delete ui; } // Set the Unicode flag to unknown, to force a retest isUnicode = -1; apiLevel = -1; useLogin = -1; supportsExtSubmit = -1; ui = NULL; } /******************************************************************************* * * disconnect * * Disconnect from the P4 Server after a command, but save protocols and other * settings. * ******************************************************************************/ void P4BridgeServer::disconnect() { // Close connections if ((client) && (clientNeedsInit == 0)) { Error e; client->Final( &e ); clientNeedsInit = 1; } } /******************************************************************************* * * get_charset * * Get the character set from the environment. * ******************************************************************************/ const StrPtr* P4BridgeServer::get_charset( ) { return &client->GetCharset(); } CharSetApi::CharSet GetDefaultCharSet() { #if _WIN32 switch (GetACP()) #elif __APPLE__ char* codeset = nl_langinfo(CODESET); int codesetInt = 0; sscanf(codeset, "CP%d", &codesetInt); switch (codesetInt) #endif { case 437: return CharSetApi::WIN_US_OEM; case 932: return CharSetApi::SHIFTJIS; case 936: return CharSetApi::CP936; case 949: return CharSetApi::CP949; case 950: return CharSetApi::CP950; case 1200: return CharSetApi::UTF_16_LE_BOM; case 1201: return CharSetApi::UTF_16_BE_BOM; case 1251: return CharSetApi::WIN_CP_1251; case 10000: return CharSetApi::MACOS_ROMAN; case 12000: return CharSetApi::UTF_32_LE_BOM; case 12001: return CharSetApi::UTF_32_BE_BOM; case 20866: return CharSetApi::KOI8_R; case 20932: return CharSetApi::EUCJP; case 28591: return CharSetApi::ISO8859_1; case 28595: return CharSetApi::ISO8859_5; case 28605: return CharSetApi::ISO8859_15; case 65001: return CharSetApi::UTF_8; default: case 1252: return CharSetApi::WIN_US_ANSI; } } /******************************************************************************* * * set_charset * * Set the character set for encoding Unicode strings for command parameters * and output. Optionally, a separate encoding can be specified for the * contents of files that are directly saved in the client's file system. * ******************************************************************************/ char * P4BridgeServer::set_charset( const char* c, const char * filec ) { CharSetApi::CharSet cs; if (c) { // Lookup the correct enum for the specified character set for the API cs = CharSetApi::Lookup( c ); if( cs < 0 ) { StrBuf m; m = "Unknown or unsupported charset: "; m.Append( c ); LOG_ERROR( m.Text() ); return m.Text(); } } else { cs = CharSetApi::UTF_8; c = CharSetApi::Name(cs); } CharSetApi::CharSet filecs; // Lookup the correct enum for the specified character set for file // contents if (filec) { filecs = CharSetApi::Lookup( filec ); if( filecs < 0 ) { StrBuf m; m = "Unknown or unsupported charset: "; m.Append( filec ); LOG_ERROR( m.Text() ); return m.Text(); } } else { // default value CharSetApi::WIN_CP_1251; const StrPtr& filec = client->GetCharset(); filecs = CharSetApi::Lookup(filec.Text()); if ((int)filecs < 0) { // not set, get a value based on the system code page filecs = GetDefaultCharSet(); } } LOG_INFO1( "[P4] Setting charset: %s\n", CharSetApi::Name(filecs) ); // Set the character set for the untagged client client->SetCharset(CharSetApi::Name(filecs)); client->SetTrans( CharSetApi::NOCONV, filecs, cs, CharSetApi::NOCONV ); // Set the character set for the untagged client //client_tagged->SetCharset(CharSetApi::Name(filecs)); //client_tagged->SetTrans( cs, filecs, cs, cs ); // Tell the UI to use Unicode ui->UseUnicode(1); return NULL; } /******************************************************************************* * * set_cwd * * Set the working directory. * ******************************************************************************/ void P4BridgeServer::set_cwd( const char* newCwd ) { if( !newCwd ) return; if( cwd ) { delete cwd; cwd = NULL; } cwd = new StrBuf(newCwd); client->SetCwd( cwd ); //client_tagged->SetCwd( cwd ); } /******************************************************************************* * * get_cwd * * Get the working directory. * ******************************************************************************/ const StrPtr& P4BridgeServer::get_cwd( void ) { return client->GetCwd(); } /******************************************************************************* * * run_command * * Run a command using the supplied parameters. The command can either be run * in tagged or untagged protocol. If the target server supports Unicode, the * strings in the parameter list need to be encoded in the character set * specified by a previous call to set_charset(). * ******************************************************************************/ int P4BridgeServer::run_command( const char *cmd, int tagged, char **args, int argc ) { P4ClientError *err = NULL; if( !connected( &err ) ) { return 0; } Error e; StrBuf msg; // Connect to server //client->Init( &e ); if ((client) && (clientNeedsInit)) { client->Init( &e ); clientNeedsInit = 0; } if( e.Test() ) { //e.Fmt( &msg, EF_NEWLINE ); //err = CopyStr( msg.Text() ); //LOG_ERROR1( "Error connecting to server: %s", err ); ui->HandleError(&e); return 0; } // Label Connections for p4 monitor if (pProgramName) client->SetProg( pProgramName ); else client->SetProg( "dot-net-api-p4" ); if (pProgramVer) client->SetVersion( pProgramVer ); else client->SetVersion( "1.0" ); if( cwd ) client->SetCwd( cwd ); client->SetVar(P4Tag::v_tag, tagged ? "yes" : 0); client->SetArgv( argc, args ); ui->clear_results(); isAlive = 1; client->SetBreak(this); client->Run( cmd, ui ); P4ClientError* errors = ui->GetErrorResults(); // close the server connection //client->Final( &e ); if( e.Test() ) { //e.Fmt( &msg, EF_NEWLINE ); //err = CopyStr( msg.Text() ); //LOG_ERROR1( "Error connecting to server: %s", err ); ui->HandleError(&e); } if (errors != NULL) { int maxSeverity = errors->MaxSeverity(); int eFail = E_FAIL; if ( maxSeverity >= 3 ) { return 0; } } return 1; } void P4BridgeServer::cancel_command() { isAlive = 0; } int P4BridgeServer::GetServerProtocols() { if (isUnicode >= 0) { // already read the protocols return 1; } // set to 0 for now so we don't call this again when running the help // command to get the protocols isUnicode = 0; // running the 'help' command on the server is the only command that // does not lock any tables on the server, so it has the least impact. if (!run_command( "help", 1, NULL, 0 )) { bool ok = false; P4ClientError* error = ui->GetErrorResults(); while (error) { //int tc = ErrorOf(13, 334, 0, 0, 0); if (error->ErrorCode == ErrorOf(13, 334, 0, 0, 0)) { ok = true; break; } error= error->Next; } if (!ok) { isUnicode = -1; return 0; } } StrPtr *st = 0; if ( st = client->GetProtocol( P4Tag::v_unicode ) ) { if( st->Length() && st->Atoi() ) { isUnicode = 1; } else { isUnicode = 0; } } // Check server level st = client->GetProtocol( "server2" ); if ( st != NULL ) { apiLevel = st->Atoi(); // Login/logout capable [2004.2 higher] if ( apiLevel >= SERVER_SECURITY_PROTOCOL ) { useLogin = 1; } else { useLogin = 0; } // Supports new submit options [2006.2 higher] if ( apiLevel >= SERVER_EXTENDED_SUBMIT ) { supportsExtSubmit = 1; } else { supportsExtSubmit = 0; } } return 1; } /******************************************************************************* * * unicodeServer * * Does the connected server support unicode? If already determined, return the * cached results, otherwise issue a help command and query the server to see * if Unicode support is enabled. * ******************************************************************************/ int P4BridgeServer::unicodeServer( ) { GetServerProtocols(); return isUnicode; } /******************************************************************************* * * APILevel * * The API level the connected server supports If already determined, return the * cached results, otherwise issue a help command and query the server to see * what protocols the server supports. * ******************************************************************************/ int P4BridgeServer::APILevel( ) { GetServerProtocols(); return apiLevel; } /******************************************************************************* * * UseLogin * * Does the connected server require the login command be used? If already * determined, return the cached results, otherwise issue a help command and * query the server to see if Unicode support is enabled. * ******************************************************************************/ int P4BridgeServer::UseLogin() { GetServerProtocols(); return useLogin; } //Does the connected sever support extended submit options (2006.2 higher)? /******************************************************************************* * * SupportsExtSubmit * * Does the connected server support extended submit options (2006.2 higher)? * If already determined, return the cached results, otherwise issue a help * command and query the server to see if Unicode support is enabled. * ******************************************************************************/ int P4BridgeServer::SupportsExtSubmit() { GetServerProtocols(); return supportsExtSubmit; } /******************************************************************************* * * SetConnection * * Set some or all of the parameters used for the connection. * ******************************************************************************/ void P4BridgeServer::set_connection(const char* newPort, const char* newUser, const char* newPassword, const char* newClient) { // close the connection to force reconnection with new value(s) close_connection(); if (newPort) { client->SetPort( newPort ); } if (newUser) { client->SetUser( newUser ); } if (newUser) { client->SetPassword( newUser ); } if (newClient) { client->SetClient( newClient ); } } /******************************************************************************* * * set_client * * Set the workspace used for the connection. * ******************************************************************************/ void P4BridgeServer::set_client( const char* newVal ) { if (client) { // close the connection to force reconnection with new value(s) // close_connection(); client->SetClient( newVal ); //client_tagged->SetClient( newVal ); } } /******************************************************************************* * * set_user * * Set the user name used for the connection. * ******************************************************************************/ void P4BridgeServer::set_user( const char* newVal ) { if (client) { // close the connection to force reconnection with new value(s) // close_connection(); client->SetUser( newVal ); //client_tagged->SetUser( newVal ); } } /******************************************************************************* * * set_port * * Set the port (hostname:portnumber) used for the connection. * ******************************************************************************/ void P4BridgeServer::set_port( const char* newVal ) { if (client) { // close the connection to force reconnection with new value(s) close_connection(); client->SetPort( newVal); //client_tagged->SetPort( newVal ); } } /******************************************************************************* * * set_password * * Set the password used for the connection. * ******************************************************************************/ void P4BridgeServer::set_password( const char* newVal ) { if (client) { // close the connection to force reconnection with new value(s) // close_connection(); client->SetPassword( newVal ); //client_tagged->SetPassword( newVal ); } } /******************************************************************************* * * set_programName * * Set the program name used for the connection. * ******************************************************************************/ void P4BridgeServer::set_programName( const char* newVal ) { if (pProgramName) delete[] pProgramName; pProgramName = CopyStr(newVal); } /******************************************************************************* * * set_programVer * * Set the program version used for the connection. * ******************************************************************************/ void P4BridgeServer::set_programVer( const char* newVal ) { if (pProgramVer) delete pProgramVer; pProgramVer = CopyStr(newVal); } /******************************************************************************* * * get_client * * Get the workspace used for the connection. * ******************************************************************************/ const StrPtr* P4BridgeServer::get_client() { if (client) { return &client->GetClient(); } return NULL; } /******************************************************************************* * * get_user * * Get the user name used for the connection. * ******************************************************************************/ const StrPtr* P4BridgeServer::get_user() { if (client) { return &client->GetUser(); } return NULL; } /******************************************************************************* * * get_port * * Get the user port used for the connection. * ******************************************************************************/ const StrPtr* P4BridgeServer::get_port() { if (client) { return &client->GetPort(); } return NULL; } /******************************************************************************* * * get_password * * Get the password used for the connection. * ******************************************************************************/ const StrPtr* P4BridgeServer::get_password() { if (client) { return &client->GetPassword(); } return NULL; } /******************************************************************************* * * get_programName * * Get the program name used for the connection. * ******************************************************************************/ const char* P4BridgeServer::get_programName() { return pProgramName; } /******************************************************************************* * * get_programVer * * Get the program version used for the connection. * ******************************************************************************/ const char* P4BridgeServer::get_programVer() { return pProgramVer; } /******************************************************************************* * * get_config * * Get the config file used for the connection, if any. * ******************************************************************************/ const StrPtr* P4BridgeServer::get_config() { if (client) { return &client->GetConfig(); } else { return NULL; } } // KeepAlive functionality int P4BridgeServer::IsAlive() { return isAlive; }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#6 | 16210 | Norman Morse | Remove files from old locations | ||
#5 | 15400 | Norman Morse | Fixed p4connect version passed by command in OSX | ||
#4 | 14005 | Norman Morse |
Bridge cleanup for OSX warnings and fixes for Unicode: P4CHARSET and P4HOST issues |
||
#3 | 13942 | Norman Morse |
Fix Charset support for Assembla and unicode servers. Fix bug in Bridge where error messages were being deallocated before displayed Fixed P4Server to check the environment for P4CHARSET before trying to call "p4 set P4CHARSET". |
||
#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 |