// // Copyright 2002 Perforce Software. All rights reserved. // // This file is part of Perforce - the FAST SCM System. // // p4wURL: // Used to parse and construct urls based on knowledge of // the p4web url syntax #include <p4wp4.h> #include "p4wStrBuf.h" #include "p4wHtml.h" #include "p4wI18n.h" #include "p4wMenuPane.h" #include "p4wURL.h" // // These methods are used to make the random value that gets stuck // in the URL to defeat caching. // // We just need a string that contains 'safe' chars. Safe chars are // those from '0'-'9', 'a'-'z', and 'A'-'Z'. There are 62 of them. // #define NUM_SAFE_CHARS 62 // this is the length of the string that get used in the value #define NUM_CHARS_FOR_RANDOM_CACHE_VALUE 3 #define MAX_RANDOM_NUMBER 238328 // 62^3 // // Takes a number from 0-61 and maps it to // a 'safe' char // char MapNumberToSafeChar( int num ); char MapNumberToSafeChar( int num ) { char returnVal = 0; num = num % NUM_SAFE_CHARS; if ( num < 10 ) { returnVal = num + '0'; } else if ( num < 36 ) { returnVal = num - 10 + 'a'; } else { returnVal = num - 36 + 'A'; } return returnVal; } // // Takes a number and converts it to a string made of 'safe' // chars. // void MakeSafeString( int num, char *string, int length ); void MakeSafeString( int num, char *string, int length ) { int temp_num = num; int counter = length - 2; string[ length - 1 ] = '\0'; while ( counter > -1 ) { string[ counter ] = MapNumberToSafeChar( temp_num % NUM_SAFE_CHARS ); counter--; temp_num = temp_num / NUM_SAFE_CHARS; } } void p4wURL::ConstructURL( StrBuf & url, const char *path, AllCommands cmd, StrBufDict *cmdArgs, int isUnicode ) { // // Construct an URL path that can be used in an HREF or ACTION. // This URL is the "dynamic" part: the state part is in the BASE. // Also, encode any double quotes in the url. url.Clear(); // // Mime content requests have no ac or cmd arguments so // just escape the path and return. Also, if there are no // command arguments, don't bother with the logic below: // just escape the path, append the ac code, and split. if( cmd == AC_MIMECONTENT || !cmdArgs ) { if( path ) url << p4wStrBuf().EscapeURL( StrRef(path), isUnicode ); if( cmd != AC_MIMECONTENT ) url << "?ac=" << cmd; return; } if( path ) url << path; url << "?ac=" << cmd; StrPtr *flags = cmdArgs->GetVar( "fl" ); StrPtr *args = cmdArgs->GetVar( "arg" ); StrPtr *rev1 = cmdArgs->GetVar( "rev1" ); StrPtr *rev2 = cmdArgs->GetVar( "rev2" ); StrPtr *sr = cmdArgs->GetVar( "sr" ); StrPtr *sr1 = cmdArgs->GetVar( "sr1" ); StrPtr *sr2 = cmdArgs->GetVar( "sr2" ); StrPtr *fil1 = cmdArgs->GetVar( "fil1" ); StrPtr *fil2 = cmdArgs->GetVar( "fil2" ); StrPtr *pw = cmdArgs->GetVar( "pw" ); StrPtr *mx = cmdArgs->GetVar( "mx" ); StrPtr *uch = cmdArgs->GetVar( "uch" ); StrPtr *bv = cmdArgs->GetVar( "bv" ); StrPtr *ft = cmdArgs->GetVar( "ft" ); StrPtr *jasc = cmdArgs->GetVar( "jasc" ); StrPtr *jfp = cmdArgs->GetVar( "jfp" ); StrPtr *jin = cmdArgs->GetVar( "jin" ); StrPtr *cl = cmdArgs->GetVar( "cl" ); StrPtr *u = cmdArgs->GetVar( "u" ); StrPtr *lac = cmdArgs->GetVar( "lac" ); StrPtr *al = cmdArgs->GetVar( "al" ); StrPtr *jb = cmdArgs->GetVar( "jb" ); StrPtr *bfl = cmdArgs->GetVar( "bfl" ); StrPtr *bnm = cmdArgs->GetVar( "bnm" ); StrPtr *bow = cmdArgs->GetVar( "bow" ); StrPtr *bdu = cmdArgs->GetVar( "bdu" ); StrPtr *bda = cmdArgs->GetVar( "bda" ); StrPtr *lfl = cmdArgs->GetVar( "lfl" ); StrPtr *lnm = cmdArgs->GetVar( "lnm" ); StrPtr *low = cmdArgs->GetVar( "low" ); StrPtr *ldu = cmdArgs->GetVar( "ldu" ); StrPtr *lda = cmdArgs->GetVar( "lda" ); StrPtr *cfl = cmdArgs->GetVar( "cfl" ); StrPtr *cnm = cmdArgs->GetVar( "cnm" ); StrPtr *cow = cmdArgs->GetVar( "cow" ); StrPtr *cdu = cmdArgs->GetVar( "cdu" ); StrPtr *cda = cmdArgs->GetVar( "cda" ); StrPtr *cho = cmdArgs->GetVar( "cho" ); StrPtr *unm = cmdArgs->GetVar( "unm" ); StrPtr *udu = cmdArgs->GetVar( "udu" ); StrPtr *uda = cmdArgs->GetVar( "uda" ); StrPtr *pcl = cmdArgs->GetVar( "pcl" ); if( flags ) url << "&fl=" << flags; if( args ) url << "&arg=" << args; if( rev1 ) url << "&rev1=" << rev1; if( rev2 ) url << "&rev2=" << rev2; if( sr ) url << "&sr=" << sr; if( sr1 ) url << "&sr1=" << sr1; if( sr2 ) url << "&sr2=" << sr2; if( fil1 ) url << "&fil1=" << fil1; if( fil2 ) url << "&fil2=" << fil2; if( mx ) url << "&mx=" << mx; if( ft ) { if (strchr(ft->Text(), '&')) { StrBuf temp; temp << ft; char *p = temp.Text(); while (p = strchr(p, '&')) *p++ = ' '; url << "&ft=" << temp; } else url << "&ft=" << ft; } if( jasc ) url << "&jasc=" << jasc; if( jfp ) url << "&jfp=" << jfp; if( jin ) url << "&jin=" << jin; if( bv ) url << "&bv=" << bv; if( uch ) url << "&uch=" << uch; if( u ) url << "&u=" << u; if( cl ) url << "&cl=" << cl; if( lac ) url << "&lac=" << lac; if( al ) url << "&al=" << al; if( jb ) url << "&jb=" << jb; if( bfl ) url << "&bfl=" << bfl; if( bnm ) url << "&bnm=" << bnm; if( bow ) url << "&bow=" << bow; if( bdu ) url << "&bdu=" << bdu; if( bda ) url << "&bda=" << bda; if( lfl ) url << "&lfl=" << lfl; if( lnm ) url << "&lnm=" << lnm; if( low ) url << "&low=" << low; if( ldu ) url << "&ldu=" << ldu; if( lda ) url << "&lda=" << lda; if( cfl ) url << "&cfl=" << cfl; if( cnm ) url << "&cnm=" << cnm; if( cow ) url << "&cow=" << cow; if( cdu ) url << "&cdu=" << cdu; if( cda ) url << "&cda=" << cda; if( cho ) url << "&cho=" << cho; if( unm ) url << "&unm=" << unm; if( udu ) url << "&udu=" << udu; if( uda ) url << "&uda=" << uda; if( pcl ) url << "&pcl=" << pcl; if( pw ) url << "&pw"; // // Encode any special characters in the url url.Set( p4wStrBuf().EscapeURL( url, isUnicode ) ); } const char * p4wURL::GetURLPath( StrPtr & url ) { const char * p = url.Text(); if ( *p != '/' ) { int numSlashes = 0; while ( *p != '\0' && ( numSlashes < 3 ) ) { if ( *p == '/' ) numSlashes++; if ( numSlashes < 3 ) p++; } } return p; } void p4wURL::ConstructIcon( StrBuf & icon, const char *iconName, int height, int width, const char *alt, int suppressBorder, const char* align, const char *id ) { // // Construct the named icon using specified characteristics for use // in HREFs p4wHtml htm; StrBuf name; StrBuf ht; StrBuf wdt; ht << height; wdt << width; name << iconName << "?ac=" << AC_ICON; htm.icon( name.Text(), ht.Text(), wdt.Text(), alt, suppressBorder, 0, 0, align, 0, id ); icon.Set( htm ); } void p4wURL::ConstructLocationURL( StrBuf &url, const char *path, AllCommands cmd, StrBufDict *args, int isUnicode ) { // // Construct location url as appropriate for the desired command StrPtr *value; StrPtr *value2; StrPtr *value3; StrPtr *value4; StrPtr *value5; StrPtr *value6; StrPtr *value7; StrPtr *value8; StrPtr *value9; StrPtr *value10; int i; const StrPtr *p; switch( cmd ) { case AC_FILESMATCHING: value = args->GetVar( "sr" ); url << "?ac=" << cmd; if( value ) url << "&sr=" << value; break; case AC_BROWSEFILE: case AC_FILETEXTDEPOT: case AC_DIFF2: case AC_ANNOTATE: case AC_FULLANNOTATE: case AC_CHGLISTANNOTATE: case AC_CHGLISTFULLANNOTATE: value = args->GetVar( "rev1" ); value2 = args->GetVar( "rev2" ); value3 = args->GetVar( "sr" ); value4 = args->GetVar( "sr2" ); if( path ) url << path; url << "?ac=" << cmd; if( value ) url << "&rev1=" << value; if( value2 ) url << "&rev2=" << value2; if( value3 ) url << "&sr=" << value3; if( value4 ) url << "&sr2=" << value4; break; case AC_DIFF22: case AC_DIFF2DIRSOUTPUT: value = args->GetVar( "rev1" ); value2 = args->GetVar( "rev2" ); value3 = args->GetVar( "sr" ); value4 = args->GetVar( "sr1" ); value5 = args->GetVar( "sr2" ); value6 = args->GetVar( "fil1" ); value7 = args->GetVar( "fil2" ); if( path ) url << path; url << "?ac=" << cmd; if( value ) url << "&rev1=" << value; if( value2 ) url << "&rev2=" << value2; if( value3 ) url << "&sr=" << value3; if( value4 ) url << "&sr1=" << value4; if( value5 ) url << "&sr2=" << value5; if( value6 ) url << "&fil1=" << value6; if( value7 ) url << "&fil2=" << value7; break; case AC_FIXPENDING: value = args->GetVar( "jb" ); if( path ) url << path; url << "?ac=" << cmd; if( value ) url << "&jb=" << value; break; case AC_SUBMITTEDCHANGELISTSFILE: case AC_SUBMITTEDCHANGELISTS: case AC_FIXSUBMITTED: value = args->GetVar( "mx" ); value2 = args->GetVar( "fl" ); value3 = args->GetVar( "rev1" ); value4 = args->GetVar( "rev2" ); value5 = args->GetVar( "sr" ); value6 = args->GetVar( "sr2" ); value7 = args->GetVar( "u" ); value8 = args->GetVar( "cl" ); value9 = args->GetVar( "al" ); value10 = args->GetVar( "jb" ); if( path ) url << path; url << "?ac=" << cmd; if( value ) url << "&mx=" << value; if( value2 ) url << "&fl=" << value2; if( value3 ) url << "&rev1=" << value3; if( value4 ) url << "&rev2=" << value4; if( value5 ) url << "&sr=" << value5; if( value6 ) url << "&sr2=" << value6; if( value7 ) url << "&u=" << value7; if( value8 ) url << "&cl=" << value8; if( value9 ) url << "&al=" << value9; if( value10 ) url << "&jb=" << value10; break; case AC_MIMECONTENT: url << path; break; case AC_PATHBROWSER: case AC_DEPOTPROCESSOR: value = args->GetVar( "sr" ); if( path ) url << path; url << "?ac=" << cmd; if( value ) url << "&sr=" << value; break; case AC_JOBS: case AC_RSSJOBS: value = args->GetVar( "mx" ); value2 = args->GetVar( "ft" ); value3 = args->GetVar( "jasc" ); value4 = args->GetVar( "jfp" ); value5 = args->GetVar( "jin" ); value6 = args->GetVar( "sr" ); value7 = args->GetVar( "pcl" ); if( path ) url << path; url << "?ac=" << cmd; if( value ) url << "&mx=" << value; if( value3 ) url << "&jasc=" << value3; if( value4 ) url << "&jfp=" << value4; if( value5 ) url << "&jin=" << value5; if( value6 ) url << "&sr=" << value6; if( value7 ) url << "&pcl=" << value7; // // jsf (job show fields) can have multiple // values for( i = 0; ( p = args->GetVar( ( StrRef )"jsf", i ) ) != NULL; i++ ) { url << "&jsf=" << *p; } // // The job filter field (ft) may have characters // that have special meaning to p4web (ie "&"). // Consequently, this field needs to have ALL // characters escaped in the url in order to // not break p4web url-parsing. Generally only // characters not special to p4web are escaped. url.Set( p4wStrBuf().EscapeURL( url, isUnicode ) ); if( value2 ) url << "&ft=" << p4wStrBuf().EscapeURLAllChars( *value2, isUnicode ); return; case AC_REMOVEFRM: case AC_DELETEFRM: case AC_EDITFRM: case AC_REVERTFRM: case AC_FILETYPEFRM: case AC_LOCKFRM: case AC_UNLOCKFRM: case AC_RESOLVEFRM: value = args->GetVar( "uch" ); if( path ) url << path; url << "?ac=" << cmd; if( value ) url << "&uch=" << value; break; case AC_CHANGEPENDINGEDIT: value = args->GetVar( "arg" ); value2 = args->GetVar( "uch" ); value3 = args->GetVar( "bp" ); if( path ) url << path; url << "?ac=" << cmd; if( value ) url << "&arg=" << value; if( value3 ) url << "&bp=" << value3; if( value2 ) url << "&uch=" << value2; break; case AC_SYNCLABEL: case AC_SYNCFRM: case AC_SYNCCHANGE: case AC_SYNCCHANGERNG: value = args->GetVar( "sr" ); value2 = args->GetVar( "uch" ); if( path ) url << path; url << "?ac=" << cmd; if( value2 ) url << "&uch=" << value2; // // range value (sr) can contain special chars (like // ampersand) that are special to p4web and need // to be escaped for p4web url parsing to work // correctly. url.Set( p4wStrBuf().EscapeURL( url, isUnicode ) ); if( value ) url << "&sr=" << p4wStrBuf().EscapeURLAllChars( *value, isUnicode ); return; case AC_INTEGRATEBRANCH: value = args->GetVar( "bv" ); if( path ) url << path; url << "?ac=" << cmd; if( value ) url << "&bv=" << value; break; case AC_BRANCHES: if( path ) url << path; url << "?ac=" << cmd; value = args->GetVar( "bfl" ); if( value ) url << "&bfl=" << value; value = args->GetVar( "bnm" ); if( value ) url << "&bnm=" << value; value = args->GetVar( "bow" ); if( value ) url << "&bow=" << value; value = args->GetVar( "bdu" ); if( value ) url << "&bdu=" << value; value = args->GetVar( "bda" ); if( value ) url << "&bda=" << value; break; case AC_CLIENTS: if( path ) url << path; url << "?ac=" << cmd; value = args->GetVar( "cfl" ); if( value ) url << "&cfl=" << value; value = args->GetVar( "cnm" ); if( value ) url << "&cnm=" << value; value = args->GetVar( "cow" ); if( value ) url << "&cow=" << value; value = args->GetVar( "cdu" ); if( value ) url << "&cdu=" << value; value = args->GetVar( "cda" ); if( value ) url << "&cda=" << value; value = args->GetVar( "cho" ); if( value ) url << "&cho=" << value; break; case AC_LABELS: case AC_LABELSPATH: if( path ) url << path; url << "?ac=" << cmd; value = args->GetVar( "lfl" ); if( value ) url << "&lfl=" << value; value = args->GetVar( "lnm" ); if( value ) url << "&lnm=" << value; value = args->GetVar( "low" ); if( value ) url << "&low=" << value; value = args->GetVar( "ldu" ); if( value ) url << "&ldu=" << value; value = args->GetVar( "lda" ); if( value ) url << "&lda=" << value; break; case AC_USERS: if( path ) url << path; url << "?ac=" << cmd; value = args->GetVar( "unm" ); if( value ) url << "&unm=" << value; value = args->GetVar( "udu" ); if( value ) url << "&udu=" << value; value = args->GetVar( "uda" ); if( value ) url << "&uda=" << value; break; case AC_CONFIGURATION: value = args->GetVar( "pw" ); if( path ) url << path; url << "?ac=" << cmd; if( value ) url << "&pw"; break; case AC_MULTIUSER: value = args->GetVar( "err" ); if( path ) url << path; url << "?ac=" << cmd; if( value ) url << "&err=" << value; break; default: if( path ) url << path; url << "?ac=" << cmd; break; } // // Encode any special characters found in this url url.Set( p4wStrBuf().EscapeURL( url, isUnicode ) ); } void p4wURL::ParseURL( const StrPtr &url, StrBufDict *stateParms, StrBufDict *dynParms, int skipConvert ) { /* * p4web URLs: * * /@abc=def&gh=ij+k&l=m@/ghi/jkl/op.qrs@tu/vw/xyz?ab=c&def=gh&i=j+k * |---state parms---| |--path----------------| |-dynamic parms-| * * Other bits: * * %dd encodes any nasty characters * %40 is treated the same as '@' * * The 'path' is saved as the dynParm 'path'. * */ enum { uSTATE, // between the @'s uPATH, // in the path section uDYN // in the dyn parms section } state, nstate ; // Start up! stateParms->Clear(); dynParms->Clear(); const char *p = url.Text(); StrBuf var, val; int skipVar; // // Handle args which need to be stored as an array int nJsf = 0; // Get going if( *p++ != '/' ) return; if( *p == '@' ) { ++p; state = uSTATE; skipVar = 0; } else if( IsEscapedAmpersand( p ) ) { p += 3; state = uSTATE; skipVar = 0; } else { state = uPATH; skipVar = 1; } while( *p ) { // Skip variable if in path processing. if( skipVar ) goto doVal; // Processing variable name. // Past the /@, &, or ? // Go until the =val, or the &next=val, or the ending @. // Note that %40 is treated the same as @. var.Clear(); while( *p && *p != '&' && *p != '=' && *p != '@' && !IsEscapedAmpersand( p ) ) *var.Alloc(1) = *p++; var.Terminate(); if( *p == '=' ) ++p; doVal: // Processing value. // var contains the variable name // Go until the &next=val (STATE, DYN) // or until the ending @ or %40 (STATE) // or the ?var=val (PATH) val.Clear(); while( *p ) { if( *p == '&' && state != uPATH ) { ++p; nstate = state; skipVar = 0; break; } else if( *p == '@' && state == uSTATE ) { ++p; if( *p == '/' ) ++p; // if not?? XXX nstate = uPATH; skipVar = 1; break; } else if( IsEscapedAmpersand( p ) && IsTerminatingAmpersand( p, &var ) && state == uSTATE ) { p += 3; if( *p == '/' ) ++p; // if not?? XXX nstate = uPATH; skipVar = 1; break; } else if( *p == '?' && state == uPATH ) { ++p; nstate = uDYN; skipVar = 0; break; } else if( !skipConvert && *p == '%' && p[1] && p[2] ) { // Hex to char -- no CR's! char buf[3]; buf[0] = p[1]; buf[1] = p[2]; buf[2] = 0; p += 3; char c = strtol(buf, 0, 16); if( c != '\r' ) *val.Alloc(1) = c; } else { // regular value in a char *val.Alloc(1) = *p++; // try to handle malformed URL if (!strcmp((p-1), "?ac=97") && state == uSTATE) { state = uPATH; p--; val.Set(""); } } } // We got a value: stuff accordingly. Handle the special // cases where we expect a variable to have multiple // values. if( state == uSTATE ) stateParms->SetVar( var, val ); else if( state == uPATH ) { if (*(val.Text()) != '/') // hack for Firefox <base> handling workspace path { // If the <base> tag's URL doesn't in in a slash, // then some browsers don't handle adding a bare filename // correctly (they turncate back to the last slash). // So in p4wHtml::base() we made sure the <base> URL // ended in a slash. Now we need to cleanup the incoming // path of things like C:\root\dir\/filename.ext (Firefox) // or C:/root/dir//filename.ext (IE, Opera, Safari) StrBuf valbuf; // must use a temp buffer valbuf.Set(val); *(valbuf.Text() + valbuf.Length()) = '\0'; // nul terminate the buffer char *p = strstr(valbuf.Text(), "\\/"); // from Firefox if (!p) p = strstr(valbuf.Text()+1, "//"); // from IE, Opera, Safari if (p) { strcpy(p+1, p+2); // chop off the extra / we added in p4wHtml::base() valbuf.SetLength(); // make sure the length is now correct } dynParms->SetVar( "path", valbuf ); } else dynParms->SetVar( "path", val ); } else if( state == uDYN && var == "jsf" ) dynParms->SetVar(var, nJsf++, val ); else if( state == uDYN && var == "mu" && val.Length() ) { stateParms->SetVar( "cdf", dynParms->GetVar( "path" ) ); dynParms->SetVar( var, val ); } else if( state == uDYN ) dynParms->SetVar( var, val ); state = nstate; } } void p4wURL::CreateBaseAndNode( StrBuf & base, StrBuf & node, const char * port, const char *path, StrBufDict *state, int useDepot, int noCache, AllCommands ac, int isHTTPS ) { // // Create base using state variables and path. If path includes // file node, save it to the node variable char *p; char *s; base.Clear(); node.Clear(); // // Base needs a full url address if (isHTTPS) base << "https://"; else base << "http://"; base << port; // // State variables are used to construct the state portion of url. // Certain state variables are NOT propagated (ie rev1), so don't // expect to see them here. StrPtr *mode = state->GetVar( "md" ); StrPtr *cd = state->GetVar( "cd" ); StrPtr *cdf = state->GetVar( "cdf" ); StrPtr *cl = state->GetVar( "cl" ); StrPtr *u = state->GetVar( "u" ); StrPtr *pt = state->GetVar( "pt" ); StrPtr *df = state->GetVar( "df" ); StrPtr *cf = state->GetVar( "cf" ); StrPtr *cn = state->GetVar( "cn" ); StrPtr *wr = state->GetVar( "wr" ); StrPtr *sr = state->GetVar( "sr" ); StrPtr *ra = state->GetVar( "ra" ); StrPtr *rc = state->GetVar( "rc" ); StrPtr *rg = state->GetVar( "rg" ); StrPtr *rt = state->GetVar( "rt" ); StrPtr *pc = state->GetVar( "pc" ); StrPtr *pat = state->GetVar( "pat" ); StrPtr *po = state->GetVar( "po" ); StrPtr *dw = state->GetVar( "dw" ); StrPtr *dc = state->GetVar( "dc" ); StrPtr *dl = state->GetVar( "dl" ); StrPtr *rw = state->GetVar( "rw" ); StrPtr *thc = state->GetVar( "thc" ); StrPtr *thz = state->GetVar( "thz" ); StrPtr *thm = state->GetVar( "thm" ); StrPtr *thb = state->GetVar( "thb" ); StrPtr *thx = state->GetVar( "thx" ); StrPtr *thw = state->GetVar( "thw" ); StrPtr *thv = state->GetVar( "thv" ); StrPtr *pb = state->GetVar( "pb" ); StrPtr *pgc = state->GetVar( "pgc" ); StrPtr *jf = state->GetVar( "jf" ); StrPtr *lno = state->GetVar( "lno" ); StrPtr *hd = state->GetVar( "hd" ); StrPtr *bo = state->GetVar( "bo" ); StrPtr *D = state->GetVar( "D" ); StrPtr *M = state->GetVar( "M" ); StrPtr *X = state->GetVar( "X" ); StrPtr *is = state->GetVar( "is" ); StrPtr *sho = state->GetVar( "sho" ); base << "/@md=" << mode << "&cd=" << p4wStrBuf().EscapeAmp( *cd ); // // Insert the random variable that just allows us to put something // in the URL so the browser won't cache it if( noCache ) { srand(time(NULL)); int number = ( rand() % MAX_RANDOM_NUMBER); char number_str[NUM_CHARS_FOR_RANDOM_CACHE_VALUE + 1]; MakeSafeString( number, number_str, NUM_CHARS_FOR_RANDOM_CACHE_VALUE + 1 ); base << "&c=" << number_str; pgc = NULL; } // // The following state parameters are optional... if( cdf ) base << "&cdf=" << p4wStrBuf().EscapeAmp( *cdf ); if( cl ) base << "&cl=" << cl; if( u ) base << "&u=" << u; if( pt ) base << "&pt=" << pt; if( df ) base << "&df=" << df; if( cf ) base << "&cf=" << cf; if( ra ) base << "&ra=" << ra; if( rc ) base << "&rc=" << rc; if( rg ) base << "&rg=" << rg; if( rt ) base << "&rt=" << rt; if( thc ) base << "&thc=" << thc; if( thz ) base << "&thz=" << thz; if( thm ) base << "&thm=" << thm; if( thb ) base << "&thb=" << thb; if( thx ) base << "&thx=" << thx; if( thw ) base << "&thw=" << thw; if( thv ) base << "&thv=" << thv; if( po ) base << "&po=" << po; if( cn ) base << "&cn=" << cn; if( wr ) base << "&wr=" << wr; if( sr ) base << "&sr=" << sr; if( pc ) base << "&pc=" << pc; if( pat ) base << "&pat=" << pat; if( dw ) base << "&dw=" << dw; if( dc ) base << "&dc=" << dc; if( dl ) base << "&dl=" << dl; if( rw ) base << "&rw=" << rw; if( pb ) base << "&pb=" << pb; if( jf ) base << "&jf=" << jf; if( lno ) base << "&lno=" << lno; if( hd ) base << "&hd=" << hd; if( bo ) base << "&bo=" << bo; if( M ) base << "&M=" << M; if( X ) base << "&X=" << X; if( is ) base << "&is=" << is; if( sho ) base << "&sho=" << sho; base << "@"; // // Path & node p = ( char *) path; // // If we are in workspace mode, terminate the state space with / if( !useDepot ) base << "/"; s = GetNodeLocation( p, useDepot, ac ); if( s && *s ) { base.Append( path, s - path ); node.Set( s ); } else { base << p; } } void p4wURL::SetNewBase(StrBuf & newBase, StrBufDict *oldBase, int editBase, const StrPtr & path, const char *var, const char *v, int noCache ) { // // Create base string from old base variables + new value of one // variable. Note that this does not prepend the port arg. char *val = (char *)v; newBase.Clear(); newBase << "/"; // // Insure cdf and cd '&'s in val are escaped, otherwise parsing // will break. StrBuf cdfPath; StrBuf cdPath; StrRef *cd = (StrRef *)oldBase->GetVar( "cd" ); StrRef *cdf = (StrRef *)oldBase->GetVar( "cdf" ); if( !strcmp( var, "cdf" ) && val != NULL ) { cdfPath << p4wStrBuf().EscapeAmp( StrRef(val) ); val = cdfPath.Text(); } else if( !strcmp( var, "cd" ) && val != NULL ) { cdPath << p4wStrBuf().EscapeAmp( StrRef(val) ); val = cdPath.Text(); } // // If getting cdf or cd from oldBase, insure that '&'s are // escaped there too. And get refreshed versions of variables // in case a ReplaceVar was done. if( cdf && strcmp( var, "cdf") ) { cdfPath << p4wStrBuf().EscapeAmp( *cdf ); if( cdfPath != cdf->Text() ) { oldBase->ReplaceVar( "cdf", cdfPath.Text() ); } } cd = (StrRef *)oldBase->GetVar( "cd" ); if( cd && strcmp( var, "cd" ) ) { cdPath << p4wStrBuf().EscapeAmp( *cd ); if( cdPath != cd->Text() ) { oldBase->ReplaceVar( "cd", cdPath.Text() ); } } // // For each state parameter, either use the passed in value if // the var matches, or use the value from the old base. Only // modify oldBase if editBase is true. If v is set to NULL, // don't set the var in newBase, and possibly unset it in oldBase. StrRef *md = (StrRef *)oldBase->GetVar( "md" ); SetOneBaseArg( "md", md, "@", newBase, oldBase, editBase, var, val ); cd = (StrRef *)oldBase->GetVar( "cd" ); SetOneBaseArg( "cd", cd, "&", newBase, oldBase, editBase, var, val ); cdf = (StrRef *)oldBase->GetVar( "cdf" ); SetOneBaseArg( "cdf", cdf, "&", newBase, oldBase, editBase, var, val ); StrRef *cl = (StrRef *)oldBase->GetVar( "cl" ); SetOneBaseArg( "cl", cl, "&", newBase, oldBase, editBase, var, val ); StrRef *u = (StrRef *)oldBase->GetVar( "u" ); SetOneBaseArg( "u", u, "&", newBase, oldBase, editBase, var, val ); StrRef *pt = (StrRef *)oldBase->GetVar( "pt" ); SetOneBaseArg( "pt", pt, "&", newBase, oldBase, editBase, var, val ); StrRef *df = (StrRef *)oldBase->GetVar( "df" ); SetOneBaseArg( "df", df, "&", newBase, oldBase, editBase, var, val ); StrRef *cf = (StrRef *)oldBase->GetVar( "cf" ); SetOneBaseArg( "cf", cf, "&", newBase, oldBase, editBase, var, val ); StrRef *ra = (StrRef *)oldBase->GetVar( "ra" ); SetOneBaseArg( "ra", ra, "&", newBase, oldBase, editBase, var, val ); StrRef *rc = (StrRef *)oldBase->GetVar( "rc" ); SetOneBaseArg( "rc", rc, "&", newBase, oldBase, editBase, var, val ); StrRef *rg = (StrRef *)oldBase->GetVar( "rg" ); SetOneBaseArg( "rg", rg, "&", newBase, oldBase, editBase, var, val ); StrRef *rt = (StrRef *)oldBase->GetVar( "rt" ); SetOneBaseArg( "rt", rt, "&", newBase, oldBase, editBase, var, val ); StrRef *po = (StrRef *)oldBase->GetVar( "po" ); SetOneBaseArg( "po", po, "&", newBase, oldBase, editBase, var, val ); StrRef *cn = (StrRef *)oldBase->GetVar( "cn" ); SetOneBaseArg( "cn", cn, "&", newBase, oldBase, editBase, var, val ); StrRef *wr = (StrRef *)oldBase->GetVar( "wr" ); SetOneBaseArg( "wr", wr, "&", newBase, oldBase, editBase, var, val ); StrRef *sr = (StrRef *)oldBase->GetVar( "sr" ); SetOneBaseArg( "sr", sr, "&", newBase, oldBase, editBase, var, val ); StrRef *rev1 = (StrRef *)oldBase->GetVar( "rev1" ); SetOneBaseArg( "rev1", rev1, "&", newBase, oldBase, editBase, var, val ); StrRef *pc = (StrRef *)oldBase->GetVar( "pc" ); SetOneBaseArg( "pc", pc, "&", newBase, oldBase, editBase, var, val ); StrRef *pat = (StrRef *)oldBase->GetVar( "pat" ); SetOneBaseArg( "pat", pat, "&", newBase, oldBase, editBase, var, val ); StrRef *dw = (StrRef *)oldBase->GetVar( "dw" ); SetOneBaseArg( "dw", dw, "&", newBase, oldBase, editBase, var, val ); StrRef *dc = (StrRef *)oldBase->GetVar( "dc" ); SetOneBaseArg( "dc", dc, "&", newBase, oldBase, editBase, var, val ); StrRef *dl = (StrRef *)oldBase->GetVar( "dl" ); SetOneBaseArg( "dl", dl, "&", newBase, oldBase, editBase, var, val ); StrRef *rw = (StrRef *)oldBase->GetVar( "rw" ); SetOneBaseArg( "rw", rw, "&", newBase, oldBase, editBase, var, val ); StrRef *thc = (StrRef *)oldBase->GetVar( "thc" ); SetOneBaseArg( "thc", thc, "&", newBase, oldBase, editBase, var, val ); StrRef *thz = (StrRef *)oldBase->GetVar( "thz" ); SetOneBaseArg( "thz", thz, "&", newBase, oldBase, editBase, var, val ); StrRef *thm = (StrRef *)oldBase->GetVar( "thm" ); SetOneBaseArg( "thm", thm, "&", newBase, oldBase, editBase, var, val ); StrRef *thb = (StrRef *)oldBase->GetVar( "thb" ); SetOneBaseArg( "thb", thb, "&", newBase, oldBase, editBase, var, val ); StrRef *thx = (StrRef *)oldBase->GetVar( "thx" ); SetOneBaseArg( "thx", thx, "&", newBase, oldBase, editBase, var, val ); StrRef *thw = (StrRef *)oldBase->GetVar( "thw" ); SetOneBaseArg( "thw", thw, "&", newBase, oldBase, editBase, var, val ); StrRef *thv = (StrRef *)oldBase->GetVar( "thv" ); SetOneBaseArg( "thv", thv, "&", newBase, oldBase, editBase, var, val ); StrRef *pb = (StrRef *)oldBase->GetVar( "pb" ); SetOneBaseArg( "pb", pb, "&", newBase, oldBase, editBase, var, val ); StrRef *pgc = noCache ? NULL : (StrRef *)oldBase->GetVar( "pgc" ); SetOneBaseArg( "pgc", pgc, "&", newBase, oldBase, editBase, var, val ); StrRef *jf = (StrRef *)oldBase->GetVar( "jf" ); SetOneBaseArg( "jf", jf, "&", newBase, oldBase, editBase, var, val ); StrRef *lno = (StrRef *)oldBase->GetVar( "lno" ); SetOneBaseArg( "lno", lno, "&", newBase, oldBase, editBase, var, val ); StrRef *hd = (StrRef *)oldBase->GetVar( "hd" ); SetOneBaseArg( "hd", hd, "&", newBase, oldBase, editBase, var, val ); StrRef *bo = (StrRef *)oldBase->GetVar( "bo" ); SetOneBaseArg( "bo", bo, "&", newBase, oldBase, editBase, var, val ); StrRef *D = (StrRef *)oldBase->GetVar( "D" ); SetOneBaseArg( "D", D, "&", newBase, oldBase, editBase, var, val ); StrRef *M = (StrRef *)oldBase->GetVar( "M" ); SetOneBaseArg( "M", M, "&", newBase, oldBase, editBase, var, val ); StrRef *X = (StrRef *)oldBase->GetVar( "X" ); SetOneBaseArg( "X", X, "&", newBase, oldBase, editBase, var, val ); StrRef *is = (StrRef *)oldBase->GetVar( "is" ); SetOneBaseArg( "is", is, "&", newBase, oldBase, editBase, var, val ); StrRef *sho = (StrRef *)oldBase->GetVar( "sho" ); SetOneBaseArg( "sho", sho, "&", newBase, oldBase, editBase, var, val ); // // Insert the random variable that just allows us to put something // in the URL so the browser won't cache it if ( noCache ) { srand(time(NULL)); int number = ( rand() % MAX_RANDOM_NUMBER); char number_str[NUM_CHARS_FOR_RANDOM_CACHE_VALUE + 1]; MakeSafeString( number, number_str, NUM_CHARS_FOR_RANDOM_CACHE_VALUE + 1 ); StrRef temp(number_str); SetOneBaseArg( "c", &temp, "&", newBase, oldBase, editBase, var, val ); } newBase << "@"; // // Path is not in the state parameter space. // Insure that state space is terminated by /. Also, if path // has leading /, insure it has an extra because leading / is // eaten during url parsing. if( !strcmp( var, "path" ) ) { if( val && ( *val != '/' || strncmp( val, "//", 2 ) ) ) newBase << "/"; if( !val ) newBase << "/"; if( val ) newBase << val; } else { if( *path.Text() != '/' || strncmp( path.Text(), "//", 2 ) ) newBase << "/"; newBase << path; } } void p4wURL::SetOneBaseArg( const char *varName, StrRef *varToSet, const char *prefix, StrBuf &newBase, StrBufDict *oldBase, int editBase, const char *var, char *val ) { // // Set var=val in the newBase string, using either the value from // oldBase, or the new value if the var matches the val. If val is // set to NULL, don't set the value in newBase, and possibly unset // it in oldBase. Only modify oldBase if editBase is set. if( !strcmp( var, varName ) ) { if( val ) { newBase << prefix << varName << "=" << val; if( editBase ) { if( varToSet ) oldBase->ReplaceVar( varName, val ); else oldBase->SetVar( varName, val ); } } else if( editBase && varToSet ) { oldBase->RemoveVar( varName ); } } else if( varToSet && varToSet->Length() ) { newBase << prefix << varName << "=" << varToSet; } } void p4wURL::NormalizePath( StrBuf & path, AllCommands cmd, ViewMode vm ) { char *end; if( path == "//" ) { return; } else if( path.Length() == 0 ) { path.Set( "//" ); return; } // // Insure that path has "//" prefix: (no more, no less than 2 /'s), // only if this is not in workspace mode. if( vm != VM_WORKSPACE ) { StrBuf tmp; char *c; int p; tmp.Set(path); c = tmp.Text(); if( *c != '/' ) { path.Set( "//" ); path << tmp; } else { p = strspn( c, "/" ); if( p != 2 ) { path.Set( "//" ); if( *( c + p ) ) path << ( c + p ); } } } // // If cmd is a path browser command, insure that path has a path // terminator. // Or if cmd is a file browser command, insure that path does not // have a trailing forward slash. If we don't know, do nothing. end = path.Text() + strlen( path.Text() ) - 1; if( p4wAllCommands::IsPathBrowser( cmd ) ) { if( vm != VM_WORKSPACE ) { if( *end != '/' ) path << "/"; } else { // // Path in workspace mode. Terminate with system // dependent path delimiter it is isn't there already. char pd; (void)GetSysDirDelim( &pd ); if( *end != pd ) { if( *end == '/' ) path.Set( path.Text(), path.Length() - 1 ); path << GetSysDirDelim( NULL ); } } } else if( p4wAllCommands::IsFileBrowser( cmd ) ) { if( *end == '/' ) path.Set( path.Text(), path.Length() - 1 ); } if( vm == VM_WORKSPACE ) ConvertNTPath( path, vm ); } void p4wURL::ConvertNTPath( StrBuf & path, ViewMode vm ) { // // If we're in workspace mode // and the p4web is on an NT box, // we need to convert all forward slashes to backward slashes. // Certain browsers (MSIE, Opera 6, Mozilla 5) convert the // backslashes to forward slashes, so we have to convert // them back so they display correctly on the page. #ifdef OS_NT if( vm != VM_WORKSPACE ) return; char *s = path.Text(); while( *s ) { if( *s == '/' ) *s = '\\'; ++s; } #endif } char *p4wURL::GetNodeLocation( const char *path, int useDepot, AllCommands ac ) { // // Return pointer to the node portion of a path. const char *s; // // Sometimes the "path" is not a real directory or path, // but an entity name, like a job or label. In these cases, // In these cases (ie clientname), don't parse the "path" // for path delimiters, but instead return pointer to // the entire node entity. if( p4wAllCommands::NodeNotFile( ac ) && ac != AC_ICON && ac != AC_HELP ) { if( useDepot && path ) s = (char *)path + 1; else if( !useDepot && path ) s = (char *)path; else s = (char *)path; return (char *)s; } // // If file is in depot mode syntax, determine location // of node by searching for the last forward slash if( useDepot ) { // @ can have slashes in its pattern string. If this has @, // skip its arg while looking for node. if( ( s = strchr( path, '@' ) ) ) { while ( s > path && *s != '/' ) --s; } else { s = p4wI18n::safeStrrchr( (char *)path, '/' ); } if( s ) ++s; else s = (char *)path; // // If file is in workspace mode, find node, if possible, // using Filesys directives } else { if( !strcmp( path, "//" ) ) { s = (char *)path + 2; return (char *) s; } int notFile = p4wAllCommands::NodeNotFile( ac ); FileSys *f = FileSys::Create( FST_TEXT ); f->Set( path ); FileSysType ftype = f->CheckType(); if( notFile || ftype == FST_MISSING || ftype == FST_CANTTELL ) { // // Filesys check failed us! Make an educated guess // by checking first for the last forward slash, // and then if not found, the system dependent // path delimiter. If found, the node starts after // that character: otherwise assume the entire // path is the node. char pd; (void)GetSysDirDelim( &pd ); s = p4wI18n::safeStrrchr( (char *)path, '/' ); if( s ) { ++s; } else { s = p4wI18n::safeStrrchr( (char *)path, pd ); if( s ) ++s; else s = (char *)path; } } else if( ftype == FST_DIRECTORY ) { if( path ) s = (char *)path + strlen(path); else s = (char *)path; } else { PathSys *pathDir = PathSys::Create(); StrBuf node; pathDir->Set( path ); pathDir->ToParent( &node ); if( !pathDir ) { s = (char *)path; } else { s = (char *)path + pathDir->Length(); s = strstr( s, node.Text() ); } delete pathDir; } delete f; } return (char *)s; } void p4wURL::GetDirLocation( StrBuf &newPath, const char *path, int useDepot, AllCommands ac ) { // // Strip node from path, returning result in newPath char *n = GetNodeLocation( path, useDepot, ac ); if( !n || n == path ) { newPath.Set( path ); } else { --n; newPath.Set( path, n - path + 1 ); } } char *p4wURL::GetFolderLocation( StrBuf &folder, const char *path, int useDepot ) { // // Return pointer to the last node (folder) of a path. // The new folder is returned in folder, and the method // returns a pointer to its text. const char *s; char pd; StrBuf tmp; if( !path ) return NULL; if( useDepot && !strcmp( path, "//" ) ) { folder.Set( path ); return( folder.Text() ); } if( !useDepot && IsRootDir( path ) ) { folder.Set( path ); return( folder.Text() ); } // // If file is in depot mode syntax, the path delimiter // is forward slash. Otherwise, use the os-specific path // delimiter. if( useDepot ) { pd = '/'; } else { (void)GetSysDirDelim( &pd ); } // // Take off trailing delimiter, and then search for the last // delimiter. s = path + strlen( path ) - 2; while( s > path ) { if( *s == pd ) break; --s; } if( s ) ++s; else s = path; folder.Set( s, strlen( s ) - 1 ); s = folder.Text(); return (char *)s; } char *p4wURL::GetSysDirDelim( char *pd ) { // // Return pointer to the os-dependent directory delimiter character. // If pd isn't NULL, set it too. char *sysDirDelim; #ifdef OS_VMS sysDirDelim = "]"; #endif #ifdef OS_NT sysDirDelim = "\\"; #endif #if !defined(OS_VMS) && !defined(OS_NT) sysDirDelim = "/"; #endif if( pd ) *pd = *sysDirDelim; return sysDirDelim; } int p4wURL::IsRootDir( const char *dir ) { // // Return 1 if dir is the root directory on this platform int isRoot = 0; #ifdef OS_VMS // // XXX don't know if this is correct const char *end = dir + strlen(dir) - 1; if( *end == ']' ) ++isRoot; #endif #ifdef OS_NT if( !strcmp( dir + 1, ":\\" ) ) ++isRoot; #endif #if !defined(OS_VMS) && !defined(OS_NT) if( !strcmp( dir, "/" ) ) ++isRoot; #endif return isRoot; } int p4wURL::IsEscapedAmpersand( const char *str ) { // // Return 1 if str begins with url encoding for // the ampersand character (specifically %40). // It is assumed that str is non-empty. if( *str == '%' && str[1] && str[2] && str[1] == '4' && str[2] == '0' ) return 1; return 0; } int p4wURL::IsTerminatingAmpersand( const char *str, StrBuf *var ) { // // Return 1 if no other @ in str // or var is not cd or cdl if (strchr(str, '@') && (!strcmp(var->Text(), "cd") || !strcmp(var->Text(), "cdf"))) return 0; return 1; }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 12234 | Matt Attaway |
Rejigger P4Web project in preparation for official sunsetting The bin directory contains the last official builds of P4Web from the Perforce download site. P4Web is soon to be completely sunsetted; these builds are here for folks who don't want to build their own. To better handle the archived builds the source code has been moved into a separate src directory. |
||
//guest/perforce_software/p4web/Main/p4wURL.cpp | |||||
#1 | 8914 | Matt Attaway | Initial add of the P4Web source code |