2434 lines
57 KiB
C++
2434 lines
57 KiB
C++
/*******************************************************************************
|
|
|
|
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 "ConnectionManager.h"
|
|
#include "P4Connection.h"
|
|
|
|
#include <spec.h>
|
|
#include <debug.h>
|
|
#include <ignore.h>
|
|
#include <hostenv.h>
|
|
#include "ticket.h"
|
|
#include "error.h"
|
|
#include "errornum.h"
|
|
#include "strarray.h"
|
|
#include "P4BridgeEnviro.h"
|
|
#include <stdexcept>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
|
|
#define DELETE_OBJECT(obj) { if( obj != NULL ) { delete obj; obj = NULL; } }
|
|
#define DELETE_ARRAY(obj) { if( obj != NULL ) { delete[] obj; obj = NULL; } }
|
|
|
|
#define HANDLE_EXCEPTION(ppErrStr) HandleException(__FILE__, __LINE__, __FUNCTION__, GetExceptionCode(), GetExceptionInformation(), ppErrStr)
|
|
#define HANDLE_EXCEPTION_NOSTR() HandleException(__FILE__, __LINE__, __FUNCTION__, GetExceptionCode(), GetExceptionInformation(), NULL)
|
|
#pragma comment(lib,"Version.lib")
|
|
|
|
// Epic: Only define for local debugging as it is very, very spammy
|
|
// #define EPIC_DEBUG_SPAM_LOG 1
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
P4BridgeEnviro P4BridgeServer::_enviro;
|
|
ILockable P4BridgeServer::envLock;
|
|
|
|
// This is were the pointer to the log callback is stored if set by the user.
|
|
LogCallbackFn * P4BridgeServer::pLogFn = NULL;
|
|
|
|
int HandleException_Static(unsigned int c, struct _EXCEPTION_POINTERS *e)
|
|
{
|
|
#ifdef _WIN32
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/******************************************************************************
|
|
// LogMessage: no formatting version, avoids getting a %blah in message with no args
|
|
//
|
|
******************************************************************************/
|
|
int P4BridgeServer::LogMessageNoArgs(int log_level, const char * file, int line, const char * message)
|
|
{
|
|
|
|
#ifndef EPIC_DEBUG_SPAM_LOG
|
|
if (log_level == 4)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
if (!pLogFn)
|
|
return 0;
|
|
|
|
__try
|
|
{
|
|
return (*pLogFn)(log_level, file, line, message);
|
|
}
|
|
#ifdef _WIN32
|
|
__except (HandleException_Static(GetExceptionCode(), GetExceptionInformation()))
|
|
{
|
|
// bad ptr?
|
|
pLogFn = NULL;
|
|
}
|
|
#else
|
|
catch (int e) {
|
|
// bad ptr?
|
|
pLogFn = NULL;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
// LogMessage: Use the client logging callback function (if set) to log a
|
|
// message in the callers log.
|
|
******************************************************************************/
|
|
int P4BridgeServer::LogMessage(int log_level, const char * file, int line, const char * message, ...)
|
|
{
|
|
#ifndef EPIC_DEBUG_SPAM_LOG
|
|
if (log_level == 4)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
if (pLogFn)
|
|
{
|
|
va_list args;
|
|
va_start(args, message);
|
|
|
|
int buffSize = 1024;
|
|
char* buff1 = NULL;
|
|
|
|
int len = -1;
|
|
while (len < 0)
|
|
{
|
|
DELETE_ARRAY(buff1)
|
|
buff1 = new char[buffSize];
|
|
#ifdef _WIN32
|
|
len = vsnprintf_s( buff1, buffSize, buffSize - 1, message, args);
|
|
#else
|
|
len = vsnprintf( buff1, buffSize, message, args);
|
|
#endif
|
|
buffSize *= 2;
|
|
}
|
|
|
|
int ret = 0;
|
|
|
|
__try
|
|
{
|
|
ret = (*pLogFn)(log_level, file, line, buff1);
|
|
}
|
|
#ifdef _WIN32
|
|
__except (HandleException_Static(GetExceptionCode(), GetExceptionInformation()))
|
|
{
|
|
// bad ptr?
|
|
pLogFn = NULL;
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
// bad ptr?
|
|
pLogFn = NULL;
|
|
}
|
|
#endif
|
|
DELETE_ARRAY(buff1)
|
|
|
|
return ret;
|
|
}
|
|
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),
|
|
initialized(false),
|
|
pConnection(NULL),
|
|
charset(CharSetApi::NOCONV),
|
|
fileCharset(CharSetApi::NOCONV),
|
|
runThreadId(0),
|
|
pTransfer(NULL),
|
|
pParallelTransferCallbackFn(NULL)
|
|
{
|
|
}
|
|
|
|
P4BridgeClient* P4BridgeServer::get_ui()
|
|
{
|
|
LOG_ENTRY();
|
|
return getConnection()->getUi();
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* Constructer
|
|
*
|
|
* Create a P4BridgeServer and connect to the specified P4 Server.
|
|
*
|
|
******************************************************************************/
|
|
|
|
P4BridgeServer::P4BridgeServer( const char *p4port,
|
|
const char *user,
|
|
const char *pass,
|
|
const char *ws_client) :
|
|
p4base(Type()),
|
|
isUnicode(-1),
|
|
useLogin(0),
|
|
supportsExtSubmit(0),
|
|
initialized(false),
|
|
pConnection(NULL),
|
|
charset(CharSetApi::NOCONV),
|
|
fileCharset(CharSetApi::NOCONV),
|
|
runThreadId(0),
|
|
pTransfer(NULL),
|
|
pParallelTransferCallbackFn(NULL)
|
|
{
|
|
LOG_DEBUG3(4,"Creating a new P4BridgeServer on %s for user, %s, and client, %s", p4port, user, ws_client);
|
|
Locker.InitCritSection();
|
|
|
|
LOCK(&Locker);
|
|
|
|
disposed = 0;
|
|
|
|
isUnicode = -1;
|
|
useLogin = 0;
|
|
supportsExtSubmit = 0;
|
|
connecting = 0;
|
|
|
|
// 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 untagged protocol
|
|
if (p4port) this->p4port = p4port;
|
|
if (user) this->user = user;
|
|
if (ws_client) this->client = ws_client;
|
|
if (pass) this->password = pass;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* Destructor
|
|
*
|
|
* Close the connection and free up resources.
|
|
*
|
|
******************************************************************************/
|
|
|
|
P4BridgeServer::~P4BridgeServer(void)
|
|
{
|
|
if (disposed != 0)
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
LOCK(&Locker);
|
|
|
|
disposed = 1;
|
|
|
|
// Clear the the callbacks
|
|
pTaggedOutputCallbackFn = NULL;
|
|
pErrorCallbackFn = NULL;
|
|
pInfoResultsCallbackFn = NULL;
|
|
pTextResultsCallbackFn = NULL;
|
|
pBinaryResultsCallbackFn = NULL;
|
|
pPromptCallbackFn = NULL;
|
|
pResolveCallbackFn = NULL;
|
|
pResolveACallbackFn = NULL;
|
|
pParallelTransferCallbackFn = NULL;
|
|
|
|
close_connection();
|
|
|
|
delete pConnection;
|
|
}
|
|
|
|
Locker.FreeCriticalSection();
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* connected
|
|
*
|
|
* Connect to the specified P4 Server, create a UI.
|
|
*
|
|
******************************************************************************/
|
|
|
|
int P4BridgeServer::connected( P4ClientError **err )
|
|
{
|
|
__try
|
|
{
|
|
return connected_int( err );
|
|
}
|
|
#ifdef _WIN32
|
|
__except (HANDLE_EXCEPTION_NOSTR())
|
|
{
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
|
|
connecting = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void P4BridgeServer::setInitialized(bool _initialized)
|
|
{
|
|
initialized = _initialized;
|
|
}
|
|
|
|
bool P4BridgeServer::isInitialized() const
|
|
{
|
|
return initialized;
|
|
}
|
|
|
|
int P4BridgeServer::connected_int( P4ClientError **err )
|
|
{
|
|
LOCK(&Locker);
|
|
LOG_ENTRY();
|
|
|
|
*err = NULL;
|
|
|
|
if((connecting) || (isInitialized()))
|
|
{
|
|
LOG_DEBUG2(4, "Connecting: %d, isInitialized(): %d", connecting, isInitialized());
|
|
return 1; // already connected
|
|
}
|
|
connecting = 1;
|
|
// Set the Unicode flag to unknown, to force a retest
|
|
isUnicode = -1;
|
|
|
|
apiLevel = -1;
|
|
useLogin = -1;
|
|
supportsExtSubmit = -1;
|
|
|
|
LOG_LOC();
|
|
if (GetServerProtocols(err))
|
|
{
|
|
p4debug.SetLevel("-vnet.maxwait=5");
|
|
|
|
setInitialized(true);
|
|
|
|
connecting = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
LOG_LOC();
|
|
close_connection();
|
|
|
|
setInitialized(false);
|
|
connecting = 0;
|
|
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 )
|
|
{
|
|
__try
|
|
{
|
|
return connect_and_trust_int( err, trust_flag, fingerprint );
|
|
}
|
|
#ifdef _WIN32
|
|
__except (HANDLE_EXCEPTION_NOSTR())
|
|
{
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
|
|
connecting = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int P4BridgeServer::connect_and_trust_int( P4ClientError **err, char* trust_flag, char* fingerprint )
|
|
{
|
|
LOCK(&Locker);
|
|
|
|
if (connecting || isInitialized())
|
|
{
|
|
return 1; // already connected
|
|
}
|
|
connecting = 1;
|
|
LOG_LOC();
|
|
P4Connection* pCon = getConnection();
|
|
|
|
char** args = new char*[2];
|
|
args[0] = (char *) "-d";
|
|
|
|
run_command( "trust", 0, 1, args, 1 );
|
|
|
|
args[0] = trust_flag;
|
|
args[1] = fingerprint;
|
|
|
|
if (!run_command( "trust", 0, 1, args, (fingerprint != NULL)?2:1 ))
|
|
{
|
|
P4ClientError *e = pCon->getUi()->GetErrorResults();
|
|
if ((e!= NULL) && (e->Severity >= E_FAILED))
|
|
{
|
|
*err = e;
|
|
}
|
|
|
|
disconnect();
|
|
return 0;
|
|
}
|
|
|
|
// Set the Unicode flag to unknown, to force a retest
|
|
isUnicode = -1;
|
|
|
|
apiLevel = -1;
|
|
useLogin = -1;
|
|
supportsExtSubmit = -1;
|
|
|
|
if (GetServerProtocols(err))
|
|
{
|
|
p4debug.SetLevel("-vnet.maxwait=5");
|
|
|
|
setInitialized(true);
|
|
|
|
connecting = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
LOG_LOC();
|
|
close_connection();
|
|
|
|
setInitialized(false);
|
|
connecting = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* close_connection
|
|
*
|
|
* Final disconnect from the P4 Server.
|
|
*
|
|
******************************************************************************/
|
|
|
|
int P4BridgeServer::close_connection()
|
|
{
|
|
LOCK(&Locker);
|
|
LOG_ENTRY();
|
|
|
|
// Close connections
|
|
Error e;
|
|
if (pConnection)
|
|
{
|
|
LOG_LOC();
|
|
pConnection->Final(&e);
|
|
}
|
|
|
|
// the ClientUser (P4BridgeClient) deletes the transfer object
|
|
// this implies that we're probably not managing the transfer object
|
|
// in the expected way due to the P4API.Net layer's configuration
|
|
// abilities. Anyway, don't delete it.
|
|
pTransfer = NULL;
|
|
|
|
// should we check e? if the connection was invalid Final()
|
|
// will return a bad result, so ignore it and complete the cleanup
|
|
#if 0
|
|
if (e.Test())
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
DELETE_OBJECT(pConnection);
|
|
|
|
// Set the Unicode flag to unknown, to force a retest
|
|
isUnicode = -1;
|
|
|
|
apiLevel = -1;
|
|
useLogin = -1;
|
|
supportsExtSubmit = -1;
|
|
setInitialized(false);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* disconnect
|
|
*
|
|
* Disconnect from the P4 Server after a command, but save protocols and other
|
|
* settings.
|
|
*
|
|
******************************************************************************/
|
|
|
|
int P4BridgeServer::disconnect( void )
|
|
{
|
|
LOCK(&Locker);
|
|
LOG_ENTRY();
|
|
|
|
if (pConnection)
|
|
{
|
|
pConnection->Disconnect();
|
|
// don't delete it. it's possible that someone would
|
|
// disconnect (p4api.net auto-disconnects after N seconds)
|
|
// and fetch the results later. This used to break a GetConfig()
|
|
// test in p4api.net, but refactoring how set_cwd works seems
|
|
// to have made the behavior identical without the explicit delete here
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* get_charset
|
|
*
|
|
* Get the character set from the environment.
|
|
*
|
|
******************************************************************************/
|
|
|
|
string P4BridgeServer::get_charset( )
|
|
{
|
|
LOG_ENTRY();
|
|
// TODO: store the string charset name instead of potentially regenerating a connection
|
|
return getConnection()->GetCharset().Text();
|
|
}
|
|
|
|
CharSetApi::CharSet GetDefaultCharSet()
|
|
{
|
|
#ifdef _WIN32
|
|
switch (GetACP())
|
|
{
|
|
case 437: return CharSetApi::WIN_US_OEM;
|
|
case 737: return CharSetApi::CP737;
|
|
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 1253: return CharSetApi::CP1253;
|
|
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 28597: return CharSetApi::ISO8859_7;
|
|
case 28605: return CharSetApi::ISO8859_15;
|
|
case 65001: return CharSetApi::UTF_8;
|
|
|
|
default:
|
|
case 1252: return CharSetApi::WIN_US_ANSI;
|
|
}
|
|
#else
|
|
// EPIC: add locale info and map to char set here
|
|
return CharSetApi::UTF_8;
|
|
#endif
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* 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.
|
|
*
|
|
******************************************************************************/
|
|
|
|
string 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
|
|
filecs = CharSetApi::WIN_US_ANSI;
|
|
|
|
LOG_LOC();
|
|
string filec = getConnection()->GetCharset().Text();
|
|
|
|
if (!filec.empty())
|
|
{
|
|
filecs = CharSetApi::Lookup(filec.c_str());
|
|
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(cs) );
|
|
|
|
// record for reconnects
|
|
charset = cs;
|
|
fileCharset = filecs;
|
|
|
|
LOG_LOC();
|
|
getConnection()->SetCharset( cs, filecs );
|
|
|
|
return "";
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* set_cwd
|
|
*
|
|
* Set the working directory.
|
|
*
|
|
******************************************************************************/
|
|
|
|
void P4BridgeServer::set_cwd( const char* newCwd )
|
|
{
|
|
// cache for later
|
|
pCwd = (newCwd) ? newCwd : "";
|
|
LOG_DEBUG2(4, "Setting 0x%llu cwd to %s", pConnection, pCwd.c_str());
|
|
LOG_DEBUG1(4, "P4CONFIG: %s", this->Get("P4CONFIG"));
|
|
// don't create a connection just for the CWD, but if the connection existed
|
|
// already and is unconnected, go ahead and delete it. It will be re-created
|
|
// on-demand
|
|
if (pConnection)
|
|
{
|
|
if (!pConnection->IsConnected())
|
|
{
|
|
DELETE_OBJECT(pConnection); // pCwd will get set when the connection is created
|
|
}
|
|
else
|
|
{
|
|
pConnection->SetCwd(pCwd.c_str());
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* get_cwd
|
|
*
|
|
* Get the working directory.
|
|
*
|
|
******************************************************************************/
|
|
|
|
static StrBuf EmptStr("");
|
|
|
|
string P4BridgeServer::get_cwd( void )
|
|
{
|
|
LOG_LOC();
|
|
return getConnection()->GetCwd().Text();
|
|
|
|
}
|
|
|
|
// After the Run call is complete, with no exceptions thrown
|
|
// it is time to look for the "hidden" parallel sync errors - see job076982
|
|
void checkForParallelError(int existingErrors, P4Connection* client, const char *cmd, P4BridgeClient* ui)
|
|
{
|
|
// client does not clear its error count, so we need to detect that the error count changed
|
|
// but we never got any HandleError() calls in our ClientUser object (P4BridgeClient)
|
|
// we could check that it's a cmd that is parallel available (sync and submit), but
|
|
// this is a little more future-friendly
|
|
if (existingErrors != client->GetErrors() && ui->GetErrorResults() == NULL)
|
|
{
|
|
ui->HandleError(E_FAILED, 0, "Error detected during parallel operation");
|
|
}
|
|
}
|
|
|
|
void P4BridgeServer::Run_int(P4Connection* client, const char *cmd, P4BridgeClient* ui)
|
|
{
|
|
string* pErrorString = NULL;
|
|
|
|
__try
|
|
{
|
|
int existingErrors = client->GetErrors();
|
|
LOG_DEBUG1(4, "Run_int is running '%s'", cmd);
|
|
client->Run(cmd, ui);
|
|
LOG_DEBUG1(4, "Run_int returned from '%s'", cmd);
|
|
checkForParallelError(existingErrors, client, cmd, ui);
|
|
}
|
|
#ifdef _WIN32
|
|
__except (HANDLE_EXCEPTION(&pErrorString))
|
|
{
|
|
if (ui)
|
|
{
|
|
ui->HandleError( E_FATAL, 0, pErrorString->c_str() );
|
|
DELETE_OBJECT(pErrorString);
|
|
}
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
char *GetInfo(char* lpstrVffInfo, char *InfoItem)
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
char* szResult = new char[256];
|
|
char szGetName[256];
|
|
LPSTR lpVersion; // String pointer to Item text
|
|
DWORD dwVerHnd=0; // An 'ignored' parameter, always '0'
|
|
UINT uVersionLen;
|
|
BOOL bRetCode;
|
|
|
|
// Get a codepage from base_file_info_sctructure
|
|
lstrcpy(szGetName, "\\VarFileInfo\\Translation");
|
|
uVersionLen = 0;
|
|
lpVersion = NULL;
|
|
bRetCode = VerQueryValue((LPVOID)lpstrVffInfo,
|
|
(LPSTR)szGetName,
|
|
(void **)&lpVersion,
|
|
(UINT *)&uVersionLen);
|
|
if ( bRetCode && uVersionLen && lpVersion)
|
|
{
|
|
sprintf_s(szResult, 256, "%04x%04x", (WORD)(*((DWORD *)lpVersion)),
|
|
(WORD)(*((DWORD *)lpVersion)>>16));
|
|
}
|
|
else {
|
|
// 041904b0 is a very common one, because it means:
|
|
// US English/Russia, Windows MultiLingual characterset
|
|
// Or to pull it all apart:
|
|
// 04------ = SUBLANG_ENGLISH_USA
|
|
// --09---- = LANG_ENGLISH
|
|
// --19---- = LANG_RUSSIA
|
|
// ----04b0 = 1200 = Codepage for Windows:Multilingual
|
|
lstrcpy(szResult, "041904b0");
|
|
}
|
|
|
|
// Add a codepage to base_file_info_sctructure
|
|
sprintf_s (szGetName, 256, "\\StringFileInfo\\%s\\", szResult);
|
|
// Get a specific item
|
|
lstrcat (szGetName, InfoItem);
|
|
|
|
uVersionLen = 0;
|
|
lpVersion = NULL;
|
|
bRetCode = VerQueryValue((LPVOID)lpstrVffInfo,
|
|
(LPSTR)szGetName,
|
|
(void **)&lpVersion,
|
|
(UINT *)&uVersionLen);
|
|
if ( bRetCode && uVersionLen && lpVersion)
|
|
{
|
|
lstrcpy(szResult, lpVersion);
|
|
}
|
|
else
|
|
{
|
|
delete[] szResult;
|
|
szResult = NULL;
|
|
}
|
|
|
|
// if szResult is NULL return an empty string
|
|
return szResult == nullptr ? "" : szResult;
|
|
#else
|
|
|
|
// EPIC: todo
|
|
return (char *) "";
|
|
|
|
#endif
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* 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 cmdId, int tagged, char **args, int argc )
|
|
{
|
|
P4ClientError *err = NULL;
|
|
LOG_ENTRY();
|
|
|
|
if( connected( &err ) )
|
|
{
|
|
LOG_LOC();
|
|
DELETE_OBJECT(err)
|
|
}
|
|
Error e;
|
|
|
|
StrBuf msg;
|
|
|
|
P4Connection* connection = getConnection(cmdId);
|
|
if(!connection)
|
|
{
|
|
LOG_ERROR1( "Error getting connection for command: %d", cmdId );
|
|
return 0;
|
|
}
|
|
|
|
P4BridgeClient* ui = connection->getUi();
|
|
if (ui)
|
|
{
|
|
if (err != NULL)
|
|
{
|
|
// couldn't connect
|
|
ui->HandleError( err );
|
|
return 0;
|
|
}
|
|
ui->clear_results();
|
|
}
|
|
else
|
|
{
|
|
LOG_ERROR("connection did not have a P4BridgeClient ui object");
|
|
return 0;
|
|
}
|
|
|
|
connection->IsAlive(1);
|
|
|
|
// Connect to server
|
|
if(connection->Dropped())
|
|
{
|
|
connection->Final(&e);
|
|
if( e.Test() )
|
|
{
|
|
ui->HandleError(&e);
|
|
return 0;
|
|
}
|
|
connection->clientNeedsInit = 1;
|
|
}
|
|
if (connection->clientNeedsInit)
|
|
{
|
|
connection->Init( &e );
|
|
if( e.Test() )
|
|
{
|
|
ui->HandleError(&e);
|
|
return 0;
|
|
}
|
|
connection->clientNeedsInit = 0;
|
|
}
|
|
bool setProdName = pProgramName.empty();
|
|
bool setProdVer = pProgramVer.empty();
|
|
|
|
#ifdef _WIN32
|
|
char* pModPath = NULL;
|
|
if (setProdName || setProdVer)
|
|
{
|
|
// need to get the module path to set either the name and/or version
|
|
pModPath = new char[MAX_PATH];
|
|
if ( GetModuleFileName( NULL, pModPath, MAX_PATH ) == 0 )
|
|
{
|
|
delete[] pModPath ;
|
|
pModPath = NULL;
|
|
}
|
|
}
|
|
// Label Connections for p4 monitor
|
|
|
|
if (setProdVer && pModPath)
|
|
{
|
|
DWORD sz = GetFileVersionInfoSize(pModPath, NULL);
|
|
UINT BufLen;
|
|
|
|
if (sz > 0)
|
|
{
|
|
VS_FIXEDFILEINFO *pFileInfo;
|
|
char* lpData = new char[sz];
|
|
if (GetFileVersionInfo( pModPath, 0, sz, lpData ))
|
|
{
|
|
pProgramVer = GetInfo(lpData, "ProductVersion");
|
|
|
|
if (pProgramVer.empty())
|
|
{
|
|
if (VerQueryValue( lpData, "\\", (LPVOID *) &pFileInfo, (PUINT)&BufLen ) )
|
|
{
|
|
WORD MajorVersion = HIWORD(pFileInfo->dwProductVersionMS);
|
|
WORD MinorVersion = LOWORD(pFileInfo->dwProductVersionMS);
|
|
WORD BuildNumber = HIWORD(pFileInfo->dwProductVersionLS);
|
|
WORD RevisionNumber = LOWORD(pFileInfo->dwProductVersionLS);
|
|
|
|
std::stringstream ss;
|
|
ss << MajorVersion << "." << MinorVersion << "." << BuildNumber << "." << RevisionNumber;
|
|
pProgramVer = ss.str();
|
|
}
|
|
}
|
|
}
|
|
if (setProdName)
|
|
{
|
|
char* prodName = GetInfo(lpData, "ProductName");
|
|
if (prodName)
|
|
{
|
|
pProgramName = prodName;
|
|
setProdName = false;
|
|
}
|
|
}
|
|
free (lpData);
|
|
}
|
|
#ifdef _DEBUG
|
|
// in debug versions use the error message saying why we couldn't get a version string
|
|
// as the version string.
|
|
else
|
|
{
|
|
LPTSTR errorText = NULL;
|
|
|
|
DWORD err = GetLastError();
|
|
DWORD retSize=FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
|
|
FORMAT_MESSAGE_FROM_SYSTEM|
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
NULL,
|
|
err,
|
|
LANG_NEUTRAL,
|
|
(LPTSTR)&errorText,
|
|
0,
|
|
NULL );
|
|
pProgramVer = errorText;
|
|
LocalFree(errorText);
|
|
}
|
|
#endif
|
|
}
|
|
if (setProdName && pModPath)
|
|
{
|
|
// need to set the product name, and we're not going to do
|
|
// later when we set the product version, and we have the
|
|
// path of the module that loaded the dll
|
|
int idx1 = 0;
|
|
int idx2 = 0;
|
|
while ((idx1 < MAX_PATH) && (pModPath[idx1] != '\0'))
|
|
{
|
|
if (pModPath[idx1++] == '\\')
|
|
{
|
|
// interested in the character after the last '\\'
|
|
idx2 = idx1;
|
|
}
|
|
}
|
|
if (idx2 < idx1)
|
|
{
|
|
pProgramName = new char[(idx1 - idx2) + 1];
|
|
idx1 = idx2;
|
|
while ((idx1 < MAX_PATH) && (pModPath[idx1] != '\0'))
|
|
{
|
|
pProgramName[idx1-idx2] = pModPath[idx1++];
|
|
}
|
|
pProgramName[idx1-idx2] = '\0';
|
|
}
|
|
}
|
|
DELETE_ARRAY(pModPath);
|
|
|
|
#endif
|
|
|
|
if (!pProgramName.empty())
|
|
connection->SetProg( pProgramName.c_str() );
|
|
else
|
|
connection->SetProg( "dot-net-api-p4" );
|
|
|
|
if (!pProgramVer.empty())
|
|
connection->SetVersion( pProgramVer.c_str() );
|
|
else
|
|
connection->SetVersion( "NoVersionSpecified"); //Nobody liked "1.0" );
|
|
|
|
connection->SetVar(P4Tag::v_tag, tagged ? "yes" : 0);
|
|
|
|
connection->SetArgv( argc, args );
|
|
connection->SetBreak(connection);
|
|
if (pParallelTransferCallbackFn) {
|
|
// make a transfer object?
|
|
if (!pTransfer)
|
|
pTransfer = new ParallelTransfer(this);
|
|
ui->SetTransfer(pTransfer);
|
|
}
|
|
else
|
|
{
|
|
// make sure we clear it
|
|
ui->SetTransfer(NULL);
|
|
}
|
|
|
|
Run_int(connection, cmd, ui);
|
|
|
|
P4ClientError* errors = ui->GetErrorResults();
|
|
if (errors != NULL)
|
|
{
|
|
int maxSeverity = errors->MaxSeverity();
|
|
if ( maxSeverity >= 3 )
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
if(connection->Dropped())
|
|
{
|
|
connection->Final(&e);
|
|
if( e.Test() )
|
|
{
|
|
ui->HandleError(&e);
|
|
}
|
|
connection->clientNeedsInit = 1;
|
|
}
|
|
|
|
if (connection->IsAlive() == 0)
|
|
{
|
|
// the command was cancelled
|
|
return 0;
|
|
}
|
|
|
|
// Fix for Job085941
|
|
// In parallel activities, error reports may "get eaten" but the api now provides the GetErrors() call
|
|
// which returns a non-zero error count if one of the "transmit" threads has had a problem.
|
|
//if (connection->GetErrors())
|
|
//{
|
|
// return 0;
|
|
//}
|
|
|
|
return 1;
|
|
}
|
|
|
|
P4Connection* P4BridgeServer::getConnection(int id /*= 99999999*/)
|
|
{
|
|
if (!pConnection)
|
|
{
|
|
LOG_LOC();
|
|
pConnection = new P4Connection(this, id);
|
|
if (!client.empty()) pConnection->SetClient(client.c_str());
|
|
if (!user.empty()) pConnection->SetUser(user.c_str());
|
|
if (!p4port.empty()) pConnection->SetPort(p4port.c_str());
|
|
LOG_DEBUG2(4, "getting connection with P4PORT=%s/%s", p4port.c_str(), pConnection->GetPort().Text());
|
|
if (!password.empty()) pConnection->SetPassword(password.c_str());
|
|
if (!pProgramName.empty()) pConnection->SetProg(pProgramName.c_str());
|
|
if (!pProgramVer.empty()) pConnection->SetVersion(pProgramVer.c_str());
|
|
if (!pCwd.empty()) pConnection->SetCwd(pCwd.c_str());
|
|
if (!ticketFile.empty()) pConnection->SetTicketFile(ticketFile.c_str());
|
|
|
|
pConnection->SetProtocol("specstring", "");
|
|
pConnection->SetProtocol("enableStreams", "");
|
|
pConnection->SetProtocol("enableGraph", "");
|
|
pConnection->SetProtocol("unicode", "");
|
|
|
|
// also set the extraProtocols
|
|
for (std::map<string, string>::iterator it = extraProtocols.begin(); it != extraProtocols.end(); ++it)
|
|
pConnection->SetProtocol(it->first.c_str(), it->second.c_str());
|
|
|
|
// Set the character set for the untagged client
|
|
pConnection->SetCharset(charset, fileCharset);
|
|
}
|
|
else
|
|
{
|
|
// TODO: eliminate cmdId?
|
|
// just set the cmdId
|
|
pConnection->cmdId = id;
|
|
}
|
|
return pConnection;
|
|
}
|
|
|
|
void P4BridgeServer::cancel_command(int cmdId)
|
|
{
|
|
LOG_ENTRY();
|
|
getConnection()->cancel_command();
|
|
}
|
|
|
|
bool P4BridgeServer::IsConnected()
|
|
{
|
|
return pConnection && pConnection->IsConnected();
|
|
}
|
|
|
|
int P4BridgeServer::GetServerProtocols(P4ClientError **err)
|
|
{
|
|
LOG_ENTRY();
|
|
|
|
if (isUnicode >= 0)
|
|
{
|
|
// already read the protocols
|
|
return 1;
|
|
}
|
|
|
|
LOG_LOC();
|
|
// 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.
|
|
|
|
P4Connection* pCon = getConnection();
|
|
|
|
// abort if we can't proceed (need at least to get a value for server2)
|
|
if (!run_command("help", 0, 1, NULL, 0))
|
|
{
|
|
LOG_DEBUG(4, "run help command failed");
|
|
// store the error
|
|
*err = pCon->getUi()->GetErrorResults();
|
|
|
|
// clear the error pointer or it will get deleted when/if the connection is
|
|
// closed due to the error
|
|
pCon->getUi()->ClearErrorResults();
|
|
|
|
// even if it failed, it may have gotten enough protocol information to proceed
|
|
// this can happen in situations where the server is so locked down that even "help" requires a login
|
|
// note that the GetProtocol() return pointer is only valid until the next GetProtocol call
|
|
StrPtr* server2 = pCon->GetProtocol("server2");
|
|
if (!server2 || server2->Length() == 0 || server2->Atoi() == 0)
|
|
{
|
|
disconnect();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Check server level
|
|
{
|
|
StrPtr *server2 = pCon->GetProtocol("server2");
|
|
apiLevel = (server2) ? server2->Atoi() : 0;
|
|
|
|
// 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;
|
|
}
|
|
|
|
// check the unicode setting
|
|
{
|
|
StrPtr *unicode = pCon->GetProtocol(P4Tag::v_unicode);
|
|
if (unicode && unicode->Length() && unicode->Atoi())
|
|
{
|
|
isUnicode = 1;
|
|
}
|
|
else
|
|
{
|
|
isUnicode = 0;
|
|
}
|
|
}
|
|
|
|
// LOG_DEBUG4(0, "api, useLogin, isUnicode, supportsExtSubmit: %d, %d, %d", apiLevel, useLogin, isUnicode, supportsExtSubmit);
|
|
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( )
|
|
{
|
|
P4ClientError* err = NULL;
|
|
GetServerProtocols(&err);
|
|
DELETE_OBJECT(err);
|
|
|
|
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( )
|
|
{
|
|
P4ClientError* err = NULL;
|
|
GetServerProtocols(&err);
|
|
DELETE_OBJECT(err);
|
|
|
|
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()
|
|
{
|
|
LOG_LOC();
|
|
P4ClientError* err = NULL;
|
|
GetServerProtocols(&err);
|
|
DELETE_OBJECT(err);
|
|
|
|
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()
|
|
{
|
|
P4ClientError* err = NULL;
|
|
GetServerProtocols(&err);
|
|
DELETE_OBJECT(err);
|
|
|
|
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)
|
|
LOG_ENTRY();
|
|
close_connection();
|
|
|
|
if (newPort)
|
|
{
|
|
this->p4port = newPort;
|
|
}
|
|
|
|
if (newUser)
|
|
{
|
|
this->user = newUser;
|
|
}
|
|
|
|
if (newPassword)
|
|
{
|
|
this->password = newPassword;
|
|
}
|
|
|
|
if (newClient)
|
|
{
|
|
this->client = newClient;
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* set_client
|
|
*
|
|
* Set the workspace used for the connection.
|
|
*
|
|
******************************************************************************/
|
|
|
|
void P4BridgeServer::set_client( const char* newVal )
|
|
{
|
|
// close the connection to force reconnection with new value(s)
|
|
LOG_ENTRY();
|
|
this->client = (newVal ? newVal : "");
|
|
if (pConnection)
|
|
pConnection->SetClient(this->client.c_str());
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* set_user
|
|
*
|
|
* Set the user name used for the connection.
|
|
*
|
|
******************************************************************************/
|
|
|
|
void P4BridgeServer::set_user( const char* newVal )
|
|
{
|
|
// close the connection to force reconnection with new value(s)
|
|
LOG_ENTRY();
|
|
this->user = (newVal ? newVal : "");
|
|
// close_connection();
|
|
if (pConnection)
|
|
pConnection->SetUser(this->user.c_str());
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* set_port
|
|
*
|
|
* Set the port (hostname:portnumber) used for the connection.
|
|
*
|
|
******************************************************************************/
|
|
|
|
void P4BridgeServer::set_port( const char* newVal )
|
|
{
|
|
LOG_ENTRY();
|
|
// close the connection to force reconnection with new value(s)
|
|
close_connection();
|
|
this->p4port = (newVal ? newVal : "");
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* set_password
|
|
*
|
|
* Set the password used for the connection.
|
|
*
|
|
******************************************************************************/
|
|
|
|
void P4BridgeServer::set_password( const char* newVal )
|
|
{
|
|
// close the connection to force reconnection with new value(s)
|
|
LOG_ENTRY();
|
|
this->password = (newVal ? newVal : "");
|
|
if (pConnection)
|
|
pConnection->SetPassword(this->password.c_str());
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* set_ticketFile
|
|
*
|
|
* Set the ticket file used for the connection.
|
|
*
|
|
******************************************************************************/
|
|
|
|
void P4BridgeServer::set_ticketFile(const char* newVal)
|
|
{
|
|
// close the connection to force reconnection with new value(s)
|
|
LOG_ENTRY();
|
|
this->ticketFile = (newVal ? newVal : "");
|
|
if (pConnection)
|
|
pConnection->SetTicketFile(this->ticketFile.c_str());
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* set_programName
|
|
*
|
|
* Set the program name used for the connection.
|
|
*
|
|
****************************************************if (pConnection)**************************/
|
|
|
|
void P4BridgeServer::set_programName( const char* newVal )
|
|
{
|
|
pProgramName = (newVal ? newVal : "");
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* set_programVer
|
|
*
|
|
* Set the program version used for the connection.
|
|
*
|
|
******************************************************************************/
|
|
|
|
void P4BridgeServer::set_programVer( const char* newVal )
|
|
{
|
|
pProgramVer = (newVal ? newVal : "");
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* get_client
|
|
*
|
|
* Get the workspace used for the connection.
|
|
*
|
|
******************************************************************************/
|
|
|
|
string P4BridgeServer::get_client()
|
|
{
|
|
LOG_ENTRY();
|
|
return getConnection()->GetClient().Text();
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* get_user
|
|
*
|
|
* Get the user name used for the connection.
|
|
*
|
|
******************************************************************************/
|
|
|
|
string P4BridgeServer::get_user()
|
|
{
|
|
LOG_ENTRY();
|
|
return getConnection()->GetUser().Text();
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* get_port
|
|
*
|
|
* Get the user port used for the connection.
|
|
*
|
|
******************************************************************************/
|
|
|
|
string P4BridgeServer::get_port()
|
|
{
|
|
LOG_ENTRY();
|
|
return getConnection()->GetPort().Text();
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* get_password
|
|
*
|
|
* Get the password used for the connection.
|
|
*
|
|
******************************************************************************/
|
|
|
|
string P4BridgeServer::get_password()
|
|
{
|
|
LOG_ENTRY();
|
|
return getConnection()->GetPassword().Text();
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* get_ticketFile
|
|
*
|
|
* Get the ticket file used for the connection.
|
|
*
|
|
******************************************************************************/
|
|
|
|
string P4BridgeServer::get_ticketFile()
|
|
{
|
|
LOG_ENTRY();
|
|
return ticketFile;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* get_programName
|
|
*
|
|
* Get the program name used for the connection.
|
|
*
|
|
******************************************************************************/
|
|
|
|
string P4BridgeServer::get_programName()
|
|
{
|
|
return pProgramName;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* get_programVer
|
|
*
|
|
* Get the program version used for the connection.
|
|
*
|
|
******************************************************************************/
|
|
|
|
string P4BridgeServer::get_programVer()
|
|
{
|
|
return pProgramVer;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* get_config
|
|
*
|
|
* Get the config file used for the connection, if any.
|
|
*
|
|
******************************************************************************/
|
|
|
|
string P4BridgeServer::get_config_Int(const char * cwd)
|
|
{
|
|
// NOTE: do not use _enviro, this is a hypothetical question about a directory
|
|
Enviro enviroLocal;
|
|
LOG_LOC();
|
|
// update (not set) P4CONFIG to _enviro's. This allows API users to use "Update"
|
|
// to alter the P4CONFIG locally without setting in the system registry
|
|
LOCK(&envLock);
|
|
// if the P4CONFIG for the env is null, don't bother updating enviroLocal
|
|
LOG_DEBUG1(4, "_enviro.Get(P4CONFIG) = %s", _enviro.Get("P4CONFIG"));
|
|
const char* curConfig = _enviro.Get("P4CONFIG");
|
|
if (curConfig != NULL)
|
|
enviroLocal.Update("P4CONFIG", curConfig);
|
|
// reload the configuration
|
|
LOG_DEBUG1(4, "Set enviroLocal to %s", cwd);
|
|
enviroLocal.Config(StrRef(cwd));
|
|
|
|
StrBuf sb = enviroLocal.GetConfig();
|
|
LOG_DEBUG1(4, "enviroLocal config is %s", sb.Text());
|
|
|
|
if (sb == "noconfig")
|
|
return sb.Text();
|
|
|
|
const StrArray* ret = enviroLocal.GetConfigs();
|
|
const StrBuf* sbp = ret->Get(0);
|
|
LOG_DEBUG1(4, "enviroLocal config is %s", sbp->Text());
|
|
return sbp->Text();
|
|
}
|
|
|
|
string P4BridgeServer::get_config(const char * cwd)
|
|
{
|
|
LOG_ENTRY();
|
|
return P4BridgeServer::get_config_Int(cwd);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* get_config
|
|
*
|
|
* Get the config file used for the connection, if any.
|
|
*
|
|
******************************************************************************/
|
|
|
|
string P4BridgeServer::get_config_Int()
|
|
{
|
|
LOG_ENTRY();
|
|
P4ClientError *err = NULL;
|
|
if ( !connected( &err ) )
|
|
{
|
|
return "";
|
|
}
|
|
|
|
P4Connection* pCon = getConnection();
|
|
// if this seems weird, it's probably because it is
|
|
// pCon may actually be disconnected, as this class
|
|
// considers "connected" as "i got the protocol data
|
|
// but may not actually be talking to the server".
|
|
// The config info is read when the Client gets created,
|
|
// and apparently we want something more responsive
|
|
LOG_DEBUG1(4, "pCon CWD: %s", pCon->GetCwd().Text());
|
|
const StrPtr& ret = pCon->GetConfig();
|
|
StrBuf sb = pCon->GetConfig(); ;// = ret;
|
|
|
|
if (sb == "noconfig")
|
|
return ret.Text();
|
|
|
|
const StrArray* retA = pCon->GetConfigs();
|
|
const StrBuf* sbp = retA->Get(0);
|
|
LOG_DEBUG1(4, "pCon CWD: %s", sbp->Text());
|
|
return sbp->Text();
|
|
}
|
|
|
|
string P4BridgeServer::get_config()
|
|
{
|
|
return P4BridgeServer::get_config_Int();
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* HandleException
|
|
*
|
|
* Handle any platform exceptions. The Microsoft Structured Exception Handler
|
|
* allows software to catch platform exceptions such as array overrun. The
|
|
* exception is logged, but the application will continue to run.
|
|
*
|
|
******************************************************************************/
|
|
|
|
#ifdef _WIN32
|
|
int P4BridgeServer::HandleException(const char* fname, unsigned int line, const char* func, unsigned int c, struct _EXCEPTION_POINTERS *e, string** ppErrorString)
|
|
{
|
|
if (!this->disposed) // hopefully didn't get called on an already deleted object
|
|
{
|
|
unsigned int code = c;
|
|
struct _EXCEPTION_POINTERS *ep = e;
|
|
|
|
// Log the exception
|
|
const char * exType = "Unknown";
|
|
|
|
switch (code)
|
|
{
|
|
case EXCEPTION_ACCESS_VIOLATION:
|
|
exType = "EXCEPTION_ACCESS_VIOLATION\r\n";
|
|
break;
|
|
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
|
exType = "EXCEPTION_DATATYPE_MISALIGNMENT\r\n";
|
|
break;
|
|
case EXCEPTION_BREAKPOINT:
|
|
exType = "EXCEPTION_BREAKPOINT\r\n";
|
|
break;
|
|
case EXCEPTION_SINGLE_STEP:
|
|
exType = "EXCEPTION_SINGLE_STEP\r\n";
|
|
break;
|
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
|
exType = "EXCEPTION_ARRAY_BOUNDS_EXCEEDED\r\n";
|
|
break;
|
|
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
|
exType = "EXCEPTION_FLT_DENORMAL_OPERAND\r\n";
|
|
break;
|
|
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
|
exType = "EXCEPTION_FLT_DIVIDE_BY_ZERO\r\n";
|
|
break;
|
|
case EXCEPTION_FLT_INEXACT_RESULT:
|
|
exType = "EXCEPTION_FLT_INEXACT_RESULT\r\n";
|
|
break;
|
|
case EXCEPTION_FLT_INVALID_OPERATION:
|
|
exType = "EXCEPTION_FLT_INVALID_OPERATION\r\n";
|
|
break;
|
|
case EXCEPTION_FLT_OVERFLOW:
|
|
exType = "EXCEPTION_FLT_OVERFLOW\r\n";
|
|
break;
|
|
case EXCEPTION_FLT_STACK_CHECK:
|
|
exType = "EXCEPTION_FLT_STACK_CHECK\r\n";
|
|
break;
|
|
case EXCEPTION_FLT_UNDERFLOW:
|
|
exType = "EXCEPTION_FLT_UNDERFLOW\r\n";
|
|
break;
|
|
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
|
exType = "EXCEPTION_INT_DIVIDE_BY_ZERO\r\n";
|
|
break;
|
|
case EXCEPTION_INT_OVERFLOW:
|
|
exType = "EXCEPTION_INT_OVERFLOW\r\n";
|
|
break;
|
|
case EXCEPTION_PRIV_INSTRUCTION:
|
|
exType = "EXCEPTION_PRIV_INSTRUCTION\r\n";
|
|
break;
|
|
case EXCEPTION_IN_PAGE_ERROR:
|
|
exType = "EXCEPTION_IN_PAGE_ERROR\r\n";
|
|
break;
|
|
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
|
exType = "EXCEPTION_ILLEGAL_INSTRUCTION\r\n";
|
|
break;
|
|
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
|
exType = "EXCEPTION_NONCONTINUABLE_EXCEPTION\r\n";
|
|
break;
|
|
case EXCEPTION_STACK_OVERFLOW:
|
|
exType = "EXCEPTION_STACK_OVERFLOW\r\n";
|
|
break;
|
|
case EXCEPTION_INVALID_DISPOSITION:
|
|
exType = "EXCEPTION_INVALID_DISPOSITION\r\n";
|
|
break;
|
|
case EXCEPTION_GUARD_PAGE:
|
|
exType = "EXCEPTION_GUARD_PAGE\r\n";
|
|
break;
|
|
case EXCEPTION_INVALID_HANDLE:
|
|
exType = "EXCEPTION_INVALID_HANDLE\r\n";
|
|
break;
|
|
default:
|
|
printf("UNKNOWN EXCEPTION\r\n");
|
|
break;
|
|
}
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << fname << "(" << line << "): " << func << " : Exception Detected: ("
|
|
"0x" << std::uppercase << std::setfill('0') << std::setw(4) << std::hex << code << ")" << exType;
|
|
LOG_ERROR(ss.str().c_str());
|
|
if (ppErrorString)
|
|
{
|
|
*ppErrorString = new string();
|
|
**ppErrorString = ss.str().c_str();
|
|
}
|
|
}
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
int P4BridgeServer::sHandleException(unsigned int c, struct _EXCEPTION_POINTERS *e)
|
|
{
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
#endif
|
|
|
|
const char* P4BridgeServer::Get_Int( const char *var )
|
|
{
|
|
LOCK(&envLock);
|
|
return _enviro.Get( var );
|
|
}
|
|
|
|
const char* P4BridgeServer::Get( const char *var )
|
|
{
|
|
return P4BridgeServer::Get_Int( var );
|
|
}
|
|
|
|
void P4BridgeServer::Set_Int( const char *var, const char *value )
|
|
{
|
|
LOCK(&envLock);
|
|
|
|
// Enviro is a little weird, if you set a NULL value it deletes from the
|
|
// registry but does not clear the symbol table value (it sort of does,
|
|
// but the value is still there).
|
|
Error e;
|
|
_enviro.Set( var, value, &e );
|
|
// workaround for P4-16150, also call Update to modify the cache
|
|
_enviro.Update(var, value);
|
|
|
|
if( e.Test() )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
void P4BridgeServer::Set( const char *var, const char *value )
|
|
{
|
|
__try
|
|
{
|
|
return P4BridgeServer::Set_Int( var, value );
|
|
}
|
|
#ifdef _WIN32
|
|
__except (P4BridgeServer::sHandleException(GetExceptionCode(), GetExceptionInformation()))
|
|
{
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void P4BridgeServer::Update_Int( const char *var, const char *value )
|
|
{
|
|
LOCK(&envLock);
|
|
_enviro.Update( var, value);;
|
|
}
|
|
|
|
void P4BridgeServer::Update( const char *var, const char *value )
|
|
{
|
|
__try
|
|
{
|
|
return P4BridgeServer::Update_Int( var, value );
|
|
}
|
|
#ifdef _WIN32
|
|
__except (P4BridgeServer::sHandleException(GetExceptionCode(), GetExceptionInformation()))
|
|
{
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void P4BridgeServer::Reload_Int()
|
|
{
|
|
LOCK(&envLock);
|
|
_enviro.Reload();
|
|
}
|
|
|
|
void P4BridgeServer::Reload()
|
|
{
|
|
__try
|
|
{
|
|
return P4BridgeServer::Reload_Int();
|
|
}
|
|
#ifdef _WIN32
|
|
__except (P4BridgeServer::sHandleException(GetExceptionCode(), GetExceptionInformation()))
|
|
{
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void P4BridgeServer::SetProtocol_Int(const char *var, const char *value)
|
|
{
|
|
// Note: this must be called before connecting or the server will ignore
|
|
// and we only do that when getConnection() is called
|
|
LOG_ENTRY();
|
|
// save for new connections
|
|
extraProtocols[var] = value;
|
|
// if one has already been created, set it there too (might not be connected, just the object)
|
|
if (pConnection)
|
|
{
|
|
if (pConnection->IsConnected())
|
|
{
|
|
LOG_DEBUG2(4, "Trying to set %s=%s on and active connection, need to disconnect first", var, value);
|
|
}
|
|
pConnection->SetProtocol(var, value);
|
|
}
|
|
}
|
|
|
|
void P4BridgeServer::SetProtocol(const char *var, const char *value)
|
|
{
|
|
__try
|
|
{
|
|
return P4BridgeServer::SetProtocol_Int(var, value);
|
|
}
|
|
#ifdef _WIN32
|
|
__except (P4BridgeServer::sHandleException(GetExceptionCode(), GetExceptionInformation()))
|
|
{
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* CallTextResultsCallbackFn
|
|
*
|
|
* Simple wrapper to call the callback function (if it has been set) within a
|
|
* SEH __try block to catch any platform exception. SEH __try blocks must
|
|
* be contained in simple functions or you will get Compiler Error C2712,
|
|
* "cannot use __try in functions that require object unwinding"
|
|
*
|
|
******************************************************************************/
|
|
|
|
void P4BridgeServer::CallTextResultsCallbackFn(int cmdId, const char *data)
|
|
{
|
|
string* pErrorString = NULL;
|
|
__try
|
|
{
|
|
if ((cmdId > 0) && (pTextResultsCallbackFn != NULL))
|
|
{
|
|
(*pTextResultsCallbackFn)( cmdId, data );
|
|
}
|
|
}
|
|
#ifdef _WIN32
|
|
__except (HANDLE_EXCEPTION(&pErrorString))
|
|
{
|
|
LOG_LOC();
|
|
getConnection()->getUi()->HandleError( E_FATAL, 0, pErrorString->c_str() );
|
|
DELETE_OBJECT(pErrorString);
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* CallInfoResultsCallbackFn
|
|
*
|
|
* Simple wrapper to call the callback function (if it has been set) within a
|
|
* SEH __try block to catch any platform exception. SEH __try blocks must
|
|
* be contained in simple functions or you will get Compiler Error C2712,
|
|
* "cannot use __try in functions that require object unwinding"
|
|
*
|
|
******************************************************************************/
|
|
|
|
void P4BridgeServer::CallInfoResultsCallbackFn( int cmdId, int msgId, char level, const char *data )
|
|
{
|
|
string* pErrorString = NULL;
|
|
__try
|
|
{
|
|
if ((cmdId > 0) && (pInfoResultsCallbackFn != NULL))
|
|
{
|
|
int nlevel = (int)(level - '0');
|
|
(*pInfoResultsCallbackFn)( cmdId, msgId, nlevel, data );
|
|
}
|
|
}
|
|
#ifdef _WIN32
|
|
__except (HANDLE_EXCEPTION(&pErrorString))
|
|
{
|
|
LOG_LOC();
|
|
getConnection()->getUi()->HandleError( E_FATAL, 0, pErrorString->c_str() );
|
|
DELETE_OBJECT(pErrorString);
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* CallTaggedOutputCallbackFn
|
|
*
|
|
* Simple wrapper to call the callback function (if it has been set) within a
|
|
* SEH __try block to catch any platform exception. SEH __try blocks must
|
|
* be contained in simple functions or you will get Compiler Error C2712,
|
|
* "cannot use __try in functions that require object unwinding"
|
|
*
|
|
******************************************************************************/
|
|
|
|
void P4BridgeServer::CallTaggedOutputCallbackFn( int cmdId, int objId, const char *pKey, const char * pVal )
|
|
{
|
|
string* pErrorString = NULL;
|
|
__try
|
|
{
|
|
if ((cmdId > 0) && (pTaggedOutputCallbackFn != NULL))
|
|
{
|
|
(*pTaggedOutputCallbackFn)( cmdId, objId, pKey, pVal );
|
|
}
|
|
}
|
|
#ifdef _WIN32
|
|
__except (HANDLE_EXCEPTION(&pErrorString))
|
|
{
|
|
LOG_LOC();
|
|
getConnection(cmdId)->getUi()->HandleError( E_FATAL, 0, pErrorString->c_str() );
|
|
DELETE_OBJECT(pErrorString);
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* CallErrorCallbackFn
|
|
*
|
|
* Simple wrapper to call the callback function (if it has been set) within a
|
|
* SEH __try block to catch any platform exception. SEH __try blocks must
|
|
* be contained in simple functions or you will get Compiler Error C2712,
|
|
* "cannot use __try in functions that require object unwinding"
|
|
*
|
|
******************************************************************************/
|
|
|
|
void P4BridgeServer::CallErrorCallbackFn( int cmdId, int severity, int errorId, const char * errMsg )
|
|
{
|
|
string* pErrorString = NULL;
|
|
__try
|
|
{
|
|
if ((cmdId > 0) && (pErrorCallbackFn != NULL))
|
|
{
|
|
(*pErrorCallbackFn)( cmdId, severity, errorId, errMsg );
|
|
}
|
|
}
|
|
#ifdef _WIN32
|
|
__except (HANDLE_EXCEPTION(&pErrorString))
|
|
{
|
|
// could cause infinite recursion if we keep producing errors
|
|
// when reporting errors
|
|
pErrorCallbackFn = NULL;
|
|
LOG_LOC();
|
|
getConnection()->getUi()->HandleError( E_FATAL, 0, pErrorString->c_str() );
|
|
DELETE_OBJECT(pErrorString);
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
}
|
|
/*******************************************************************************
|
|
*
|
|
* CallErrorCallbackFn
|
|
*
|
|
* Simple wrapper to call the callback function (if it has been set) within a
|
|
* SEH __try block to catch any platform exception. SEH __try blocks must
|
|
* be contained in simple functions or you will get Compiler Error C2712,
|
|
* "cannot use __try in functions that require object unwinding"
|
|
*
|
|
******************************************************************************/
|
|
|
|
void P4BridgeServer::CallBinaryResultsCallbackFn( int cmdId, void * data, int length )
|
|
{
|
|
string* pErrorString = NULL;
|
|
__try
|
|
{
|
|
if ((cmdId > 0) && (pBinaryResultsCallbackFn))
|
|
{
|
|
(*pBinaryResultsCallbackFn)( cmdId, (void *) data, length );
|
|
}
|
|
}
|
|
#ifdef _WIN32
|
|
__except (HANDLE_EXCEPTION(&pErrorString))
|
|
{
|
|
LOG_LOC();
|
|
getConnection()->getUi()->HandleError( E_FATAL, 0, pErrorString->c_str() );
|
|
DELETE_OBJECT(pErrorString);
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Set the call back function to receive the tagged output
|
|
void P4BridgeServer::SetTaggedOutputCallbackFn(IntTextTextCallbackFn* pNew)
|
|
{
|
|
pTaggedOutputCallbackFn = pNew;
|
|
}
|
|
|
|
// Set the call back function to receive the error output
|
|
void P4BridgeServer::SetErrorCallbackFn(IntIntIntTextCallbackFn* pNew)
|
|
{
|
|
pErrorCallbackFn = pNew;
|
|
}
|
|
|
|
void P4BridgeServer::Prompt( int cmdId, const StrPtr &msg, StrBuf &rsp,
|
|
int noEcho, Error *e )
|
|
{
|
|
string* pErrorString = NULL;
|
|
|
|
__try
|
|
{
|
|
if ((cmdId > 0) && (pPromptCallbackFn))
|
|
{
|
|
char response[1024];
|
|
|
|
(*pPromptCallbackFn)( cmdId, msg.Text(), response, sizeof(response), noEcho);
|
|
|
|
rsp.Set(response);
|
|
}
|
|
}
|
|
#ifdef _WIN32
|
|
__except (HANDLE_EXCEPTION(&pErrorString))
|
|
{
|
|
LOG_LOC();
|
|
getConnection()->getUi()->HandleError( E_FATAL, 0, pErrorString->c_str() );
|
|
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void P4BridgeServer::SetPromptCallbackFn( PromptCallbackFn * pNew)
|
|
{
|
|
pPromptCallbackFn = pNew;
|
|
}
|
|
|
|
void P4BridgeServer::SetParallelTransferCallbackFn(ParallelTransferCallbackFn * pNew)
|
|
{
|
|
pParallelTransferCallbackFn = pNew;
|
|
}
|
|
|
|
// Set the call back function to receive the information output
|
|
void P4BridgeServer::SetInfoResultsCallbackFn(IntIntIntTextCallbackFn* pNew)
|
|
{
|
|
pInfoResultsCallbackFn = pNew;
|
|
}
|
|
|
|
// Set the call back function to receive the text output
|
|
void P4BridgeServer::SetTextResultsCallbackFn(TextCallbackFn* pNew)
|
|
{
|
|
pTextResultsCallbackFn = pNew;
|
|
}
|
|
|
|
// Set the call back function to receive the binary output
|
|
void P4BridgeServer::SetBinaryResultsCallbackFn(BinaryCallbackFn* pNew)
|
|
{
|
|
pBinaryResultsCallbackFn = pNew;
|
|
}
|
|
|
|
// Callbacks for handling interactive resolve
|
|
int P4BridgeServer::Resolve( int cmdId, ClientMerge *m, Error *e )
|
|
{
|
|
if (pResolveCallbackFn == NULL)
|
|
{
|
|
return CMS_SKIP;
|
|
}
|
|
P4ClientMerge *merger = new P4ClientMerge(m);
|
|
int result = -1;
|
|
|
|
result = Resolve_int( cmdId, merger );
|
|
|
|
delete merger;
|
|
|
|
if (result == -1)
|
|
{
|
|
LOG_LOC();
|
|
return getConnection()->getUi()->ClientUser::Resolve( m, e );
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int P4BridgeServer::Resolve( int cmdId, ClientResolveA *r, int preview, Error *e )
|
|
{
|
|
if (pResolveACallbackFn == NULL)
|
|
{
|
|
return CMS_SKIP;
|
|
}
|
|
|
|
P4ClientResolve *resolver = new P4ClientResolve(r, isUnicode);
|
|
int result = -1;
|
|
|
|
result = Resolve_int( cmdId, resolver, preview, e);
|
|
|
|
delete resolver;
|
|
|
|
if (result == -1)
|
|
{
|
|
return CMS_SKIP;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void P4BridgeServer::SetResolveCallbackFn(ResolveCallbackFn * pNew)
|
|
{
|
|
pResolveCallbackFn = pNew;
|
|
}
|
|
|
|
void P4BridgeServer::SetResolveACallbackFn(ResolveACallbackFn * pNew)
|
|
{
|
|
pResolveACallbackFn = pNew;
|
|
}
|
|
|
|
int P4BridgeServer::Resolve_int( int cmdId, P4ClientMerge *merger)
|
|
{
|
|
int result = -1;
|
|
string* pErrorString = NULL;
|
|
__try
|
|
{
|
|
if ((cmdId > 0) && (pResolveCallbackFn != NULL))
|
|
{
|
|
result = (*pResolveCallbackFn)(cmdId, merger);
|
|
}
|
|
}
|
|
#ifdef _WIN32
|
|
__except (HANDLE_EXCEPTION(&pErrorString))
|
|
{
|
|
LOG_LOC();
|
|
getConnection()->getUi()->HandleError( E_FATAL, 0, pErrorString->c_str() );
|
|
DELETE_OBJECT(pErrorString);
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
int P4BridgeServer::Resolve_int( int cmdId, P4ClientResolve *resolver, int preview, Error *e)
|
|
{
|
|
int result = -1;
|
|
string* pErrorString = NULL;
|
|
|
|
__try
|
|
{
|
|
if ((cmdId > 0) && (pResolveACallbackFn != NULL))
|
|
{
|
|
result = (*pResolveACallbackFn)(cmdId, resolver, preview);
|
|
}
|
|
}
|
|
#ifdef _WIN32
|
|
__except (HANDLE_EXCEPTION(&pErrorString))
|
|
{
|
|
LOG_LOC();
|
|
getConnection()->getUi()->HandleError( E_FATAL, 0, pErrorString->c_str() );
|
|
DELETE_OBJECT(pErrorString);
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
return result;
|
|
}
|
|
|
|
int P4BridgeServer::IsIgnored_Int( const StrPtr &path )
|
|
{
|
|
ClientApi client;
|
|
Error e;
|
|
client.SetCharset("utf8");
|
|
Ignore* ignore = client.GetIgnore();
|
|
const StrPtr ignoreFile = client.GetIgnoreFile();
|
|
if(!ignore)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return ignore->Reject( path, ignoreFile );
|
|
}
|
|
|
|
int P4BridgeServer::IsIgnored( const StrPtr &path )
|
|
{
|
|
__try
|
|
{
|
|
return IsIgnored_Int(path);
|
|
}
|
|
#ifdef _WIN32
|
|
__except (P4BridgeServer::sHandleException(GetExceptionCode(), GetExceptionInformation()))
|
|
{
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
string P4BridgeServer::GetTicketFile()
|
|
{
|
|
LOCK(&envLock);
|
|
StrBuf ticketfile;
|
|
char* c;
|
|
HostEnv h;
|
|
|
|
// ticketfile - where users login tickets are stashed
|
|
if( c = _enviro.Get( "P4TICKETS" ) )
|
|
{
|
|
ticketfile.Set( c );
|
|
}
|
|
else
|
|
{
|
|
h.GetTicketFile( ticketfile, &_enviro );
|
|
}
|
|
return ticketfile.Text();
|
|
}
|
|
|
|
string P4BridgeServer::GetTicket(char* uri, char* user)
|
|
{
|
|
LOCK(&envLock);
|
|
StrBuf ticketfile;
|
|
char* c;
|
|
HostEnv h;
|
|
|
|
// ticketfile - where users login tickets are stashed
|
|
c = _enviro.Get( "P4TICKETS" );
|
|
if (c)
|
|
{
|
|
ticketfile.Set( c );
|
|
}
|
|
else
|
|
{
|
|
h.GetTicketFile( ticketfile, &_enviro );
|
|
}
|
|
Ticket t(&ticketfile);
|
|
StrBuf port(uri);
|
|
StrBuf userStr(user);
|
|
|
|
return t.GetTicket(port, userStr);
|
|
}
|
|
|
|
LogCallbackFn* P4BridgeServer::SetLogCallFn(LogCallbackFn *log_fn)
|
|
{
|
|
LogCallbackFn* old = pLogFn;
|
|
pLogFn = log_fn;
|
|
return old;
|
|
}
|
|
|
|
int P4BridgeServer::DoTransferInternal(
|
|
const char *cmd,
|
|
std::vector<const char*> &argList,
|
|
StrDictListIterator *varDictIterator,
|
|
int threads,
|
|
Error *e)
|
|
{
|
|
LOG_ENTRY();
|
|
__try
|
|
{
|
|
// TODO: Error* management, not clear if it's needed
|
|
return pParallelTransferCallbackFn((int*)this, cmd, argList.data(), (int) argList.size(), (int*) varDictIterator, threads);
|
|
}
|
|
#ifdef _WIN32
|
|
__except (HANDLE_EXCEPTION_NOSTR())
|
|
{
|
|
LOG_ERROR("An exception occured handling a parallel operation");
|
|
return 1;
|
|
}
|
|
#else
|
|
catch (int e)
|
|
{
|
|
LOG_ERROR("An exception occured handling a parallel operation");
|
|
return 1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int P4BridgeServer::DoTransfer(
|
|
ClientApi *client,
|
|
ClientUser *ui,
|
|
const char *cmd,
|
|
StrArray &args,
|
|
StrDict &pVars,
|
|
int threads,
|
|
Error *e)
|
|
{
|
|
LOG_ENTRY();
|
|
if (!pParallelTransferCallbackFn)
|
|
return 1; // we're not prepared to handle this
|
|
|
|
// this function should not return until all of the threads have completed,
|
|
// so local (stack) transforms of P4 objects to .Net objects is OK
|
|
std::vector<const char*> argList;
|
|
for (int i = 0; i < args.Count(); i++)
|
|
{
|
|
argList.push_back(args.Get(i)->Value());
|
|
}
|
|
|
|
// copy the pVars to a local StrDictList for the iterator
|
|
StrDictList dList;
|
|
dList.Data()->CopyVars(pVars);
|
|
StrDictListIterator varDictIterator(&dList);
|
|
|
|
return DoTransferInternal(cmd, argList, &varDictIterator, threads, e);
|
|
}
|
|
|
|
// Epic
|
|
void P4BridgeServer::SetConnectionHost(const char* hostname)
|
|
{
|
|
if (pConnection)
|
|
{
|
|
pConnection->SetHost(hostname);
|
|
}
|
|
}
|
|
|
|
// the transfer shim
|
|
ParallelTransfer::ParallelTransfer(P4BridgeServer* pServer) :
|
|
p4base(Type()),
|
|
pBridgeServer(pServer)
|
|
{
|
|
}
|
|
|
|
ParallelTransfer::~ParallelTransfer()
|
|
{
|
|
LOG_LOC();
|
|
}
|
|
|
|
void ParallelTransfer::SetBridgeServer(P4BridgeServer* pServer)
|
|
{
|
|
pBridgeServer = pServer;
|
|
}
|
|
|
|
|
|
int ParallelTransfer::Transfer(ClientApi* client,
|
|
ClientUser *ui,
|
|
const char *cmd,
|
|
StrArray &args,
|
|
StrDict &pVars,
|
|
int threads,
|
|
Error *e)
|
|
{
|
|
LOG_ENTRY();
|
|
// not bridge server to tell? fail!
|
|
return (!pBridgeServer) ? 1 : pBridgeServer->DoTransfer(client, ui, cmd, args, pVars, threads, e);
|
|
}
|