client.cc #1

  • //
  • guest/
  • perforce_software/
  • p4/
  • 2016-1/
  • client/
  • client.cc
  • View
  • Commits
  • Open Download .zip Download (10 KB)
/*
 * Copyright 1995, 1996 Perforce Software.  All rights reserved.
 *
 * This file is part of Perforce - the FAST SCM System.
 */

# include <stdhdrs.h>

# include <debug.h>
# include <strbuf.h>
# include <strdict.h>
# include <strtable.h>
# include <error.h>
# include <i18napi.h>
# include <charcvt.h>
# include <transdict.h>
# include <options.h>
# include <handler.h>
# include <rpc.h>
# include <ident.h>
# include <enviro.h>
# include <ignore.h>
# include <filesys.h>

# include <msgclient.h>
# include <msgserver.h>
# include <msgrpc.h>
# include <msgsupp.h>
# include <p4tags.h>
# include <netportparser.h>

# include "clientuser.h"
# include "clientusernull.h"
# include "clientservice.h"
# include "clientmerge.h"
# include "client.h"

void clientTrust( Client *, Error * );

Ident p4api_ident = {
	        IdentMagic "P4API" "/" ID_OS "/" ID_REL "/" ID_PATCH,
		         ID_Y "/" ID_M "/" ID_D
};

Client::Client( Enviro *e ) : Rpc( &service )
{
	// Use the builtin ClientMerger until SetMerger is called.

	fromTransDialog = toTransDialog = NULL;
	translated = this;
	transfname = this;
	errors = 0;
	fatals = 0;
	is_unicode = 0;
	unknownUnicode = 1;
	content_charset = 0;
        output_charset = 0;
	ignore = new Ignore;
	lowerTag = upperTag = 0;
	authenticated = 0;
	ignoreList = 0;
	pubKeyChecked = 0;
	hostprotoset = 0;
	syncTime = 0;
	ownCwd = 1;

	protocolXfiles = -1;
	protocolNocase = 0;
	protocolSecurity = 0;
	protocolUnicode = 0;

	if( e )
	{
	    enviro = e;
	    ownEnviro = 0;
	}
	else
	{
	    enviro = new Enviro;
	    ownEnviro = 1;
	}

	/*
	 * If P4CONFIG is set, we'll need to load the environment
	 * from the .p4 file in the directory path.
	 */

	enviro->Config( GetCwd() );

	// Our standard implementation.
	// See msgs/p4tagl.cc for l_client setting.

	service.Dispatcher( clientDispatch );

	service.SetProtocol( P4Tag::v_cmpfile ); // has clientCompareFile #1737
	service.SetProtocol( P4Tag::v_client, P4Tag::l_client );

	buildInfo = p4api_ident.GetIdent();
}

Client::~Client()
{
	CleanupTrans();
	if( ownEnviro )
	    delete enviro;
	delete ignore;
}

void
Client::Init( Error *e )
{
	// Set up address and connect.
	errors = 0;

	// re-add host and port in protocol message
	hostprotoset = 0;

	// unicode setup if possible
	if( unknownUnicode )
	    SetupUnicode( e );

	if( !e->Test() )
	    service.SetEndpoint( GetPort().Text(), e );

	if( !e->Test() )
	    Connect( e );

	if( !e->Test() )
	{
	    DoHandshake( e );	// no-op if not ssl

	    if( !e->Test() && unknownUnicode )
	    {
		ClientUserNULL cnull( e );
		
		// discover unicode server status

		// if program name has not been set yet, use p4 api
		// ident as program name
		if( !programName.Length() )
		    SetVar( P4Tag::v_prog, p4api_ident.GetIdent() );

		Run( "discover", &cnull );

		// bad command is expected when connecting to
		// servers before 2014.2
		if( e->CheckId( MsgServer::BadCommand ) )
		{
		    e->Clear();
		    errors = 0;
		}

		// on trust errors, ignore them, and ignore the
		// unicode detection result
		if( e->CheckId( MsgRpc::HostKeyMismatch ) ||
		    e->CheckId( MsgRpc::HostKeyUnknown ) )
		{
		    e->Clear();
		    errors = 0;
		}
		else
		{
		if( !e->Test() )
		    LearnUnicode( e );
		}
		if( e->Test() )
		    (void) Final( e );
	    }

	    // don't bother testing for error since caller will handle

	    return;
	}

	e->Set( MsgClient::Connect );
}

void
Client::Run( const char *func, ClientUser *u )
{
	// Run the command async, and then wait.

	RunTag( func, u );

	WaitTag();
}

void
Client::RunTag( const char *func, ClientUser *u )
{
	Error e;

	// for assembla... job067346 - late protocol set
	// we set this late so that an application can SetHost
	// after creating the Client object and init.
	if( !hostprotoset )
	{
	    hostprotoset = 1;
	    if( GetInitRoot().Length() == 0 )
		SetProtocolDynamic( P4Tag::v_host, GetHost() );
	    SetProtocolDynamic( P4Tag::v_port, GetPort() );
	}

	// Warning: WaitTag() calls Dispatch() which may call Invoke().

	// Fill this tag slot
	// Below we garantee it's empty.

	tags[ upperTag ] = u;

	// Cheesy access to RPC vars, a la OutputStat
	// Equally cheesy access to environment

	if( u )
	{
	    u->varList = this;
	    u->enviro = enviro;
	    if( output_charset )
		u->SetOutputCharset( output_charset );
	}

	if( func && *func == 't' && !strcmp( func, "trust" ) )
	{
	    Loopback( &e );

	    clientTrust( this, &e );

	    if( e.Test() && u )
	        u->Message( &e );

	    return;
	}

	if( !pubKeyChecked )
	{
	    CheckKnownHost( &e, GetTrustFile() );
	    if( e.Test() )
	    {
		if( u )
		{
		    e << "p4 trust";
		    u->Message( &e );
		}
		SetError();
		Clear();
		return;
	    }
	    pubKeyChecked = 1;
	}

	if( programName.Length() )
	    SetVar( P4Tag::v_prog, programName );

	// Formulate up function name.
	// Set user/client/os/cwd
	// Invoke client's request.

	StrBuf s;
	s << "user-" << ( func ? func : "help" );

	GetEnv();
	Invoke( s.Text() );

	// Advance upper tag, and ensure the new slot is empty.

	int nextTag = ( upperTag + 1 ) % ClientTags;

	if( nextTag == lowerTag )
	    WaitTag( tags[ lowerTag ] );

	upperTag = nextTag;

	// Until successful dispatch (after init or setting user/passwd) we 
	// process one command at a time, so that they crypto goes through.

	if( !authenticated )
	    WaitTag();
}

void
Client::WaitTag( ClientUser *u )
{
	// While any RunTag() requests are outstanding, Dispatch().
	// Dispatch() returns exactly once for each Invoke().

	while( lowerTag != upperTag )
	{
	    Dispatch();

	    authenticated = 1;

	    // lowerTag is done; signal that
	    // and bump it.  If that was the
	    // tag we were waiting for, bail.

	    ClientUser *ui = tags[ lowerTag ];

	    if( Dropped() && !IoError()->CheckId( MsgRpc::Break ) )
		ui->Message( IoError() );

	    ui->Finished();

	    lowerTag = ( lowerTag + 1 ) % ClientTags;

	    if( u == ui )
		break;
	}
}

int
Client::Final( Error *e )
{
	// Dispatch returns when the other end is waiting for another
	// request.  We tell it to go away with ReleaseFinal.
	// Then we disconnect.

	ReleaseFinal();
	Disconnect();
	
	// Propagate any IO errors

	if( !e->Test() )
	    *e = *IoError();

	// Return non-zero if any errors have been detected.
	// Note that the server still sends some errors back on
	// stdout, which we don't detect.

	return e->Test() || GetErrors();
}

void
Client::GetEnv()
{
	/*
	 * Load up into RPC variables:
	 *
	 *	client - this client's name
	 *	cwd - the current working directory
	 *	host - this client's host name (for host locking)
	 *	language - preferred language for error messages
	 *	os - the name of the OS (for calculating paths)
	 *	user - the user's name
	 */

	const StrPtr &lang = GetLanguage();
	const StrPtr &initroot = GetInitRoot();

	translated->SetVar( P4Tag::v_client, GetClient() );
	translated->SetVar( P4Tag::v_cwd, GetCwd() );
	if( initroot.Length() )
	    translated->SetVar( P4Tag::v_initroot, initroot );
	else
	    SetVar( P4Tag::v_host, GetHost() );
	if( lang.Length() ) translated->SetVar( P4Tag::v_language, lang );
	SetVar( P4Tag::v_os, GetOs() );
	translated->SetVar( P4Tag::v_user, GetUser() );

	/*
	 *	unicode - set if all messages are in unicode
	 */

	if( is_unicode )
        {
            SetVar( P4Tag::v_unicode );
            SetVar( P4Tag::v_charset, content_charset );
        }
        else // no charset was defined
        {
            int cs = GuessCharset();
            if (cs != 0)
                SetVar( P4Tag::v_charset, cs );
        }

	SetVar( P4Tag::v_clientCase, StrBuf::CaseUsage() );

	int i = GetUi()->ProgressIndicator();
	if( i )
	    SetVar( P4Tag::v_progress, i );
}

void
Client::Confirm( const StrPtr *confirm )
{
	// pre 99.1 servers need GetEnv(), too, because they
	// reestablished DmCaller context each time. 

	if( protocolServer < 6 )
	    GetEnv();

	CopyVars();

	Invoke( confirm->Text() );
}

void
Client::NewHandler()
{
	if (translated != this)
	{
		((TransDict *)translated)->Clear();
	}
	if (transfname != this && transfname != translated)
	{
		((TransDict *)transfname)->Clear();
	}
}

void
Client::SetArgv( int ac, char * const * av)
{
	if (translated != this)
		translated->SetArgv(ac, av);
	else
		Rpc::SetArgv(ac, av);
}

void
Client::VSetVar( const StrPtr &var, const StrPtr &val )
{
	if (translated != this)
		translated->RemoveVar(var.Text());
	// careful about the order here
	Rpc::VSetVar(var, val);
}

void
Client::SetProg( const char *prog )
{
	programName.Set( prog );
}

void
Client::SetProg( const StrPtr *prog )
{
	programName.Set( prog );
}

void
Client::SetVersion( const char *version )
{
	SetVar( P4Tag::v_version, version );
}

void
Client::SetVersion( const StrPtr *version )
{
	SetVar( P4Tag::v_version, version );
}

StrPtr *
Client::GetProtocol( const StrPtr &var )
{
	// NB: result only valid until next GetProtocol call.

	if( var == P4Tag::v_server2 || var == P4Tag::v_server )
	    protocolBuf.Set( protocolServer );
	else if( var == P4Tag::v_nocase && protocolNocase )
	    protocolBuf.Set( protocolNocase );
	else if( var == P4Tag::v_security )
	    protocolBuf.Set( protocolSecurity );
	else if( var == P4Tag::v_unicode )
	    protocolBuf.Set( protocolUnicode );
	else return 0;

	return &protocolBuf;
}

void
Client::SetupUnicode( Error *e )
{
	const char *charsetVal = GetCharset().Text();

	if( *charsetVal )
	    LateUnicodeSetup( charsetVal, e );
}

void
Client::LearnUnicode( Error *e )
{
	const char *value = "none";

	if( protocolUnicode )
	    value = "auto";

	SetCharset( value );

	if( charsetVar.Length() )
	    enviro->Set( charsetVar.Text(), value, e );

	// ignore any errors trying to save the charset
	e->Clear();
	errors = 0;

	LateUnicodeSetup( value, e );
}

void
Client::LateUnicodeSetup( const char *value, Error *e )
{
	unknownUnicode = 0;

	int cs = CharSetApi::Lookup( value );

	if( cs >= 0 )
	    SetTrans( cs );
	else if( e )
	    e->Set( MsgClient::UnknownCharset ) << value;
}

void
Client::SetEnviroFile( const char *c )
{
	enviro->SetEnviroFile( c );
	enviro->Reload();
}

const StrPtr *
Client::GetEnviroFile()
{
	return enviro->GetEnviroFile();
}
# Change User Description Committed
#1 19472 Liz Lam Initial add of the 2016.1 p4/p4api source code.