// // Copyright 2002 Perforce Software. All rights reserved. // // This file is part of Perforce - the FAST SCM System. // // p4wRequest: // Manages a single browser request from determining the appropriate // request, through generating the p4 requests to get the data #include <stdio.h> #include <string.h> #include <p4wp4.h> #include "p4wStrBuf.h" #include "p4wHtml.h" #include "p4wRequest.h" #include "base64.h" extern int globalCancel; extern int ignoreBrowserAuth; extern StrBuf szInitTime; // // IsAlive() is used to cancel the current server request int P4KeepAlive::IsAlive() { if( globalCancel ) { globalCancel = 0; return 0; } else { return 1; } } ThumbNailTable::~ThumbNailTable() { for( int i = 0; i < Count(); i++ ) delete (ThumbNailItem *)Get(i); } void ThumbNailTable::AddItem( int rev, const StrRef base64thumb ) { ThumbNailItem *t = new ThumbNailItem; t->rev = rev; t->thumbnail.Set( base64thumb ); VarArray::Put( t ); } ThumbNailItem * ThumbNailTable::GetItem( int rev ) { ThumbNailItem *t; for( int i = 0; i < Count(); ++i ) { t = (ThumbNailItem * )Get(i); if( t->rev == rev ) return( t ); } return( ( ThumbNailItem * ) 0 ); } //*********************************************************************** p4wRequest::p4wRequest( ClientApi & client, Web822 * web822, int isBrowseOnly, int javascript, const char *jsvname, const char *jsvpath, int noAuth, int authBrowseOnly, const char *hostPort, const char *progName, const char *group, const char *tabs, int isNTService, int isHTTPS, ErrorLog *reportLog, int securityFlags, Options * opts) : fClient(client), fWeb822(web822), fBrowseOnly(isBrowseOnly), fJavascript(javascript), fJavaScriptViewName(jsvname), fJavaScriptViewPath(jsvpath), fNoAuth(noAuth), fAuthBrowseOnly(authBrowseOnly), fNBytesSent(0), fKByteDefault(1024), fURLHasNoState(0), fP4InitSet(0), fHadClientError(0), fIsNTService(isNTService), fIsHTTPS(isHTTPS), fSecurityFlags(securityFlags), fOpts(opts), fIsUnicode(0), fOutputInfoOK(0), fHeaderProcessed(0), fHasThumbs(0), fThumbColsDefault(4), fThumbNailTab(NULL), fDiffType(-1), fMultiUser(0), fIsTagged(0), fReportLog(reportLog), fSaveDefaults(0), fSaveJobsDefs(0), fSaveChgsDefs(0), fSaveFilterDefs(0), fGroupsRun(0), fClientRun(0), fQuiet(0), fSave4Later(0), fBrowserVersion(0.0), fCr8NewClient(0), fHeadRev(0), fXSS(0), fDidP4Init(0), fIsMac(0), fScreenChunks(-1), fSaveUserPswd(0), fUnknownClient(0) { // // Set the default page content limit in bytes fKBytes = fKByteDefault; fByteLimit = fKBytes * 1024; // // Set the number of thumbnail columns; fThumbCols = fThumbColsDefault; // // Get the root path. fDepotRootPath.Set("/"); // // Clear the client root field fClientRoot.Set(""); // // Save the HTTP port. If the browser passed this back in the // Host variable, use it as it will be the exact address used by the // user. Otherwise, for old browsers use the host:port as detected // at startup by p4web. StrPtr *port = fWeb822->GetVar( "host" ); if ( port ) fHTTPPort.Set( port ); else fHTTPPort.Set( hostPort ); // // Get the path, state & dynamic variables from the URL. ProcessURL(); // // Get the POST data. fPostData = fWeb822->GetBodyData(); fPostLgth = fWeb822->GetBodyLgth(); // // Save the name of the program or service fProgName.Set( progName ); // // Save the name of the group fGroup.Set( group ); // // Save the requested tabd fTabs.Set( tabs[0] ? tabs : "fpsblcuj" ); // // If we are run via inetd, turn on Quiet switch if ( ( *fOpts)[ 'i' ] ) fQuiet = 1; // // Save off any master root given with -r path StrPtr *s = (*fOpts)[ 'r' ]; if (s) fMasterRoot.Set(s); // // Save off default file filter given with -d path StrPtr *t = (*fOpts)[ 'd' ]; if (t) fFileFilter.Set(t); } void p4wRequest::ProcessCustomization() { // // Process a customization file StrPtr *s; if( (fCommand != AC_ICON) && (s = (*fOpts)[ 'f' ]) ) { Error e; StrBuf buf; FileSys *f; f = FileSys::Create( FST_TEXT ); f->Set( s->Text() ); f->Open( FOM_READ, &e ); if(!e.Test()) { while(f->ReadLine( &buf, &e ) && !e.Test()) { char *p = strchr(buf.Text(), '='); if (p) { p++; switch(*(buf.Text())) { case 'B': fcustBackground.Set(p); break; case 'L': fcustLabel.Set(p); break; case 'T': switch(*(buf.Text()+1)) { case 'e': fcustText.Set(p); break; case 'i': fcustTitle.Set(p); break; } break; case 'H': fcustHeader.Set(p); break; case 'S': fcustSubheader.Set(p); break; case 'F': fcustFooter.Set(p); break; case 'G': fcustGraphic.Set(p); default: break; } } } f->Close( &e ); } delete f; } } void p4wRequest::WriteCustHTML(char *filename, int indent) { Error e; FileSys *f = FileSys::Create( FST_TEXT ); f->Set( filename ); f->Open( FOM_READ, &e ); if(!e.Test()) { StrBuf buf; f->ReadWhole( &buf, &e ); if(!e.Test()) { char *p; if ((p = strstr(buf.Text(), "<BODY")) || (p = strstr(buf.Text(), "<body")) || (p = strstr(buf.Text(), "<Body"))) { char ending[8] = "</"; strncpy(ending+2, ++p, 4); if (p = strchr(p, '>')) { while (*++p <= ' ' && *p) ; char *q = strstr(p, ending); if (q) { StrBuf clearIcon; p4wURL urlMaker; do { *q-- = '\0'; } while (*q <= ' ' && p < q); p4wHtml htm; htm.beginTRow(); if (indent) { urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); htm.beginCol( NULL, NULL, NULL, NULL, NULL, "5" ); htm.icon( clearIcon.Text(), "1", "5", "", 1 ); htm.endCol(); } htm.beginCol( NULL, NULL, "8" ); htm << p; htm.endCol(); if (indent) { htm.beginCol( NULL, NULL, NULL, NULL, NULL, "5" ); htm.icon( clearIcon.Text(), "1", "5", "", 1 ); htm.endCol(); } htm.endTRow(); *this << htm; } } } } f->Close( &e ); } delete f; } p4wRequest::~p4wRequest() { if (fThumbNailTab) { delete fThumbNailTab; } } // ------------------------------------- // URL and form processing functions. // void p4wRequest::ProcessURL() { // // Get path, command, state and dynamic variables // from the url. If nothing useful is found in the // url, set some defaults. p4wURL urlMaker; StrPtr *get = fWeb822->GetVar("get"); if( get == NULL ) get = fWeb822->GetVar("post"); if( get ) { char *p; StrBuf robots; StrBuf crossdomain; StrPtr *url = new StrRef( *get ); // Strip off the HTTP/x.x version number if( ( p = strrchr(url->Text(), ' ' ) ) != NULL ) { fHTTPVersion.Set( p ); *p = '\0'; url->SetEnd( p ); } // Redo the URL if it's for RSS feeds or robots.txt/crossdomain.xml p = url->Text(); int len = url->Length(); if ((len > sizeof(RSSEXT)) && !strcmp(p + len - (sizeof(RSSEXT)-1), RSSEXT)) // Hack for RSS feeds { *(p + len - (sizeof(RSSEXT)-1)) = '\0'; url->SetLength(); } else if (!strcmp(url->Text(), "/robots.txt")) // Hack for robots.txt { Enviro enviro; if( !( p = enviro.Get( "P4WEBROBOTS" ) ) ) p = getenv( "P4WEBROBOTS" ); if ( ( p ) ) { if (*p == '/' && *(p+1) == '/') strcpy(p, p+1); robots << p; delete url; url = new StrRef(robots.Text()); } else { delete url; url = new StrRef("/depot/robots.txt"); } } else if (!strcmp(url->Text(), "/crossdomain.xml")) // Hack for crossdomain.xml { Enviro enviro; if( !( p = enviro.Get( "P4WEBCROSSDOMAIN" ) ) ) p = getenv( "P4WEBCROSSDOMAIN" ); if ( ( p ) ) { if (*p == '/' && *(p+1) == '/') strcpy(p, p+1); crossdomain << p; delete url; url = new StrRef(crossdomain.Text()); } else { delete url; url = new StrRef("/depot/crossdomain.xml"); } } // // Parse the URL into state and dynamic dictionaries urlMaker.ParseURL( *url, &fStateParms, &fDynParms ); fFullURL.Set( *url ); // // Save the dynamic part of the url if( ( p = strstr( fFullURL.Text(), "?ac" ) ) ) fDynURL.Set( p, strlen( p ) ); delete url; } // // If there is no state in this URL, we should // try and pick it up from the referer. if ( !fStateParms.GetCount() ) { char *p; StrPtr * referer = fWeb822->GetVar( "referer" ); if ( referer // is there a referer? && (p=strstr(referer->Text(), "/@")) // and does it have state info && (p=strstr(p+2, "@/")) // that we can use? && (p=strstr(p+2, "?ac=")) ) { StrBufDict refererDynamic; urlMaker.ParseURL( StrRef( urlMaker.GetURLPath( *referer ) ), &fStateParms, &refererDynamic ); fURLHasNoState = 1; } } // // Get the command type, flags & args from dynamic parameters StrPtr *cmd = fDynParms.GetVar( "ac" ); StrPtr *flags = fDynParms.GetVar( "fl" ); StrPtr *args = fDynParms.GetVar( "arg" ); if( !cmd ) fCommand = AC_NONE; else fCommand = (AllCommands) atoi( cmd->Text() ); if (fCommand == AC_EDITNEWCLIENT) fCr8NewClient = 1; if( flags ) fFlags.Set( flags ); if( args ) fArgs.Set( args ); // // Get the chunk flags from the sho parameter StrPtr *sho = fStateParms.GetVar( "sho" ); // try state 1st if( !sho ) sho = fDynParms.GetVar( "sho" ); // if not state, try dyn if ( sho ) { fScreenChunks = atoi(sho->Text()); if (!fScreenChunks) { if (strstr(sho->Text(), "logo")) fScreenChunks += SCRN_LOGO; if (strstr(sho->Text(), "head")) fScreenChunks += SCRN_HEADER; else if (strstr(sho->Text(), "info")) fScreenChunks += SCRN_HEADER; if (strstr(sho->Text(), "path")) fScreenChunks += SCRN_PATH; if (strstr(sho->Text(), "tab")) fScreenChunks += SCRN_TABS; if (strstr(sho->Text(), "tool")) fScreenChunks += SCRN_TOOLBAR; if (strstr(sho->Text(), "title")) fScreenChunks += SCRN_TITLE; if (strstr(sho->Text(), "filter")) fScreenChunks += SCRN_FILTER; if (strstr(sho->Text(), "copy")) fScreenChunks += SCRN_COPYRIGHT; } if (!(fScreenChunks & SCRN_BODY)) fScreenChunks += SCRN_BODY; } // // Depot, client or workspace mode StrPtr *mode = fStateParms.GetVar( "md" ); if( mode && *mode == "d" ) { fViewMode = VM_DEPOT; } else if( mode && *mode == "w" ) { fViewMode = VM_WORKSPACE; } else if( mode && *mode == "c" ) { fViewMode = VM_CLIENT; } else if( fBrowseOnly ) { fViewMode = VM_DEPOT; } else { fViewMode = VM_CLIENT; } SetViewMode( fViewMode ); // // Detect javascript mode (0=off, 1=netscape, 2=msie) if mode // was not specified by user at startup. StrPtr *userAgent = fWeb822->GetVar( "user-agent" ); if( userAgent ) { fUserAgent.Set( userAgent ); fIsMac = strstr(userAgent->Text(), "Macintosh") ? 1 : 0; } if( fHTTPVersion.Length() ) fUserAgent << fHTTPVersion; if( fJavascript != 0 && fJavascript != 1 && fJavascript != 2 && fJavascript != 3 ) { fJavascript = DetectBrowser(); } // // Set the path StrPtr *path = fDynParms.GetVar("path"); if( fViewMode != VM_WORKSPACE ) fDepotPath << fDepotRootPath; if( path ) { fDepotPath << path; } else { fDepotPath << "/"; } // // Normalize the path UNLESS we know that it is // not a file or directory. if( p4wAllCommands::NodeNotFile( fCommand ) ) SetPath( fDepotPath.Text(), 0 ); else SetPath( fDepotPath.Text(), 1 ); // // Set the "return to path browser" path, // removing any &'s as they mess up our parsing. StrPtr *cd = fStateParms.GetVar( "cd" ); if( cd ) { fReturnURL.Set( cd ); } else if( fDepotPath.Length() == 0 ) { fReturnURL.Set( "//" ); } else { // // Remove the file node from the path if // present char *b = urlMaker.GetNodeLocation( fDepotPath.Text(), fViewMode != VM_WORKSPACE, fCommand ); fReturnURL.Set( fDepotPath.Text(), b - fDepotPath.Text() ); } SetCdPath( "cd", p4wStrBuf().UnescapeAmp( fReturnURL ).Text(), 1, 1, fReturnURL ); // // Handle any file conversion needed for workspace root if( fViewMode == VM_WORKSPACE ) SetWRPath(); // // If return to file path is set, save this value too, // also removing any ampersands StrPtr *cdf = fStateParms.GetVar( "cdf" ); if( cdf ) SetCdPath( "cdf", p4wStrBuf().UnescapeAmp( *cdf ).Text(), 1, 0, fReturnFileURL ); // // Set the client if (isLocalRequest() || !(fSecurityFlags & FLG_DISALLOW_CHG_CLIENTS)) { StrPtr *cl = fStateParms.GetVar( "cl" ); if( cl ) fClient.SetClient( cl ); } // // If user is passed in url, // set fClient ONLY if this is not browse-only mode. // Unset state variable if this is browse-only mode. StrPtr *u = fStateParms.GetVar( "u" ); if( u ) { if( !fBrowseOnly ) fClient.SetUser( u ); else fStateParms.RemoveVar( "u" ); } // // If port is passed in url, // set fClient ONLY if this is not browse-only mode // and we are bypassing authorization. Unset the // state variable if it won't be used. StrPtr *pt = fStateParms.GetVar( "pt" ); if( pt && !fBrowseOnly && fNoAuth ) { fClient.SetPort( pt ); } else if( pt ) { fStateParms.RemoveVar( "pt" ); } // // Set user preferences from state variables StrPtr *df = fStateParms.GetVar( "df" ); StrPtr *cf = fStateParms.GetVar( "cf" ); StrPtr *ra = fStateParms.GetVar( "ra" ); StrPtr *rc = fStateParms.GetVar( "rc" ); StrPtr *rg = fStateParms.GetVar( "rg" ); StrPtr *rt = fStateParms.GetVar( "rt" ); StrPtr *po = fStateParms.GetVar( "po" ); if( df && ( *df == "h" ) ) fHideDeleted = 1; else if( df && (*df == "s" ) ) fHideDeleted = 0; else fHideDeleted = 1; if( cf && ( *cf == "h" ) ) fHideCurrentDir = 1; else fHideCurrentDir = 0; if( ra && ( *ra == "s" ) ) fHideRecentChanges = 0; else fHideRecentChanges = 1; if( rc && ( *rc == "s" ) ) fHideRawP4Cmds = 0; else fHideRawP4Cmds = 1; if( rg ) fHideGoTo = 0; else fHideGoTo = 1; if( rt && ( *rt == "s" ) ) { const StrPtr *thv = GetStateArg( "thv" ); if ( thv && *thv == "d" ) { fHideThumbnails = 1; fHideDetails = 0; } else { fHideThumbnails = 0; fHideDetails = 1; } } else fHideThumbnails = fHideDetails = 1; StrPtr * thc = fStateParms.GetVar( "thc" ); if ( thc ) { int i = atoi(thc->Text()); if (i < 1 || i > 10) i = fThumbColsDefault; SetThumbnailCols( i ); } if( po && ( *po == "h" ) ) fHideOpened = 1; else fHideOpened = 0; StrPtr * sr = fStateParms.GetVar( "sr" ); if ( sr ) fBITBRev.Set( sr ); else fBITBRev.Clear(); // // Page content limit StrPtr *pc = fStateParms.GetVar( "pc" ); if( pc ) { int pl = atoi( pc->Text() ); if( pl > 0 ) { fKBytes = pl; fByteLimit = fKBytes * 1024; } } // // Default file filter StrPtr *pat = fStateParms.GetVar( "pat" ); StrPtr *pb = fStateParms.GetVar( "pb" ); StrPtr *t = (*fOpts)[ 'd' ]; if ( t && !cf && !pb && !po && !pat ) fStateParms.SetVar( "pat", t->Text() ); // // Enable/disable caching (always disable in read/write mode) StrPtr *pgc = fBrowseOnly ? fStateParms.GetVar( "pgc" ) : NULL; int noCache; // // Has user chosen to enable caching if( pgc && *pgc == "y" ) { noCache = 0; // // We shouldn't disable cache on help, about nor icons } else if( fCommand == AC_HELP || fCommand == AC_ABOUT || fCommand == AC_ICON ) { noCache = 0; // // Default is to disable caching } else { noCache = 1; } // // Collect the state variables and put them into a BASE url, // and set fURL to the node of the path (if there is one) urlMaker.CreateBaseAndNode( fBase, fURL, fHTTPPort.Text(), fDepotPath.Text(), &fStateParms, fViewMode != VM_WORKSPACE, noCache, fCommand, fIsHTTPS ); } int p4wRequest::ProcessForm( Spec & spec, StrBufDict *useDict ) { // Translate a form into StrDict, whose variables are in Spec StrBufDict *dict = (useDict ? useDict : (StrBufDict *)fPostArgs.Dict()); // Forms look a lot like URL: // variable=[value]&... // %dd encodes any nasty characters // + is a space in values // CR's are superfluous // This routines walks the post data, buffering the chars from each // var and val until they can be entered into the dictionary. // Special handling exists for the "Options" field, list entries // with multiple var=value pairs, and list entries with values // separated by %0A (newline). if( !fPostData ) return 0; if( p4debug.GetLevel( DT_NET ) >= 3 ) printf("Post data '%s'\n", fPostData ); StrBuf var, val, optVal; SpecElem *elem = 0; SpecElem *elemList = 0; int listCount = 0; int inVar = 1; char *p = fPostData; dict->Clear(); for(;;) { if( *p == '=' ) { // End of variable's name. // Look it up -- we'll need to know if it is a list. var.Terminate(); elem = spec.Find( var ); if( elem && elem->IsList() && elem != elemList ) elemList = elem, listCount = 0; inVar = 0; ++p; } else if( !*p || *p == '&' ) { // End of a variable's value. val.Terminate(); if( p4debug.GetLevel( DT_NET ) >= 3 ) printf("\"%s\" = \"%s\"\n", var.Text(), val.Text()); // Enter value. // Special "Options" translation: all go on the same // line. XXX // List items have an index. if( var == "Options" ) optVal << " " << val; else if( elem && elem->IsList() ) dict->SetVar( var, listCount++, val ); else dict->SetVar( var, val ); // End of form? We're outta here. if( !*p ) break; // Always starting a new value val.Clear(); // If we stopped at &, we'll start a new variable. // Stopping at \n continues with the same (list) // variable. if( *p++ == '&' ) { var.Clear(); inVar = 1; } } else if( elem && elem->IsList() && p[0] == '%' && p[1] == '0' && p[2] == 'A' ) { // End of line in a list variable's value. val.Terminate(); if( p4debug.GetLevel( DT_NET ) >= 3 ) printf("\"%s\" nl= \"%s\" %d\n", var.Text(), val.Text(), val.Length()); // Enter value. dict->SetVar( var, listCount++, val ); // Always starting a new value val.Clear(); p += 3; } else if( inVar ) { // adding to var name *var.Alloc(1) = *p++; } else if( *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 if( *p == '+' ) { // xlate + to ' ' *val.Alloc(1) = ' '; ++p; } else { // regular value in a char *val.Alloc(1) = *p++; } } // and this little piggy if( optVal.Length() ) dict->SetVar( "Options", optVal ); return 1; } void p4wRequest::DoLocation( const char *newLocation ) { // // Send new page using LOCATION directive to remap URL p4wHtml htm; htm.httpHeader( 301 ); if (fSaveDefaults) { StrBuf defaults; defaults.Set(newLocation); char *p = strchr(defaults.Text(), '@'); if (p) strcpy(defaults.Text(), p); p = strstr(defaults.Text(), "&cd="); if (p) { char * q = p + 3; while (*++q != '&' && *q != '@' && *q) ; strcpy(p, q); } p = strstr(defaults.Text(), "&cdf="); if (p) { char * q = p + 4; while (*++q != '&' && *q != '@' && *q) ; strcpy(p, q); } p = strstr(defaults.Text(), "&sho="); if (p) { char * q = p + 4; while (*++q != '&' && *q != '@' && *q) ; strcpy(p, q); } p = strstr(defaults.Text(), "&bo="); if (p) { char * q = p + 2; while (*++q != '&' && *q != '@' && *q) ; strcpy(p, q); } p = strstr(defaults.Text(), "&c="); if (p) { char * q = p + 2; while (*++q != '&' && *q != '@' && *q) ; strcpy(p, q); } p = strrchr(defaults.Text(), '@'); if (p) *++p = '\0'; p = strchr(defaults.Text(), '?'); if (p) *p = '\0'; defaults.SetLength(); htm.setCookie( "Defaults", defaults.Text(), fSaveDefaults == 2, 0 ); p = (char *)strstr(newLocation, "&D=1"); // &D=1 should never appear in a URL if (p) strcpy(p, p+4); } else if (fSaveJobsDefs) { StrBuf defaults; StrBuf temp; temp.Set(newLocation); defaults << p4wStrBuf().EscapeSpaces(temp); char *p = strstr(defaults.Text(), "?ac=107"); if (p) { strcpy(defaults.Text(), p + sizeof("?ac=107")-1); defaults.SetLength(); htm.setCookie( "JobsDefs", defaults.Text(), fSaveJobsDefs == 2, 0 ); } } else if (fSaveChgsDefs) { StrBuf defaults; StrBuf temp; temp.Set(newLocation); defaults << p4wStrBuf().EscapeSpaces(temp); char *p = strstr(defaults.Text(), "?ac="); if (p) { for (p += sizeof("?ac="); isdigit(*p); p++) ; strcpy(defaults.Text(), p); defaults.SetLength(); htm.setCookie( "ChgsDefs", defaults.Text(), fSaveChgsDefs == 2, 0 ); } } else if (fSaveFilterDefs) { htm.setCookie( "Filters", fDefaults.Text(), fSaveFilterDefs == 2, 0 ); } else if (fSaveUserPswd) { StrBuf userpswd; userpswd << GetUser(); if (*(userpswd.Text()) != '@') { StrPtr pswd = GetPassword(); if (pswd.Length()) { userpswd << ":" << pswd; if (!isLocalRequest()) doHashPswd(userpswd.Text(), userpswd.Length()); } // conver 'userpswd' to Base64 encoding StrBuf b64data; int ln64; int len; int lgth = userpswd.Length(); char *q = b64data.Alloc(len = ((lgth + 2)/ 3 * 4) + 8); memset(q, '\0', len); ln64 = b64_encode(userpswd.Text(), lgth, q, len); if (ln64) userpswd.Set(q); char *p = GetHTTPPort().Text(); p = strchr(p, ':'); htm.setCookie( p ? p+1 : "", userpswd.Text(), 0, 0, !isLocalRequest() ); ignoreBrowserAuth = 1; } } htm.location( newLocation ); *this << htm; } int p4wRequest::RmvDynArgBeginWith(StrPtr & dynbuf, char x) { // returns // 0 not changed // 1 changed - removed flags but others remain // 2 emptied - removed flags and no others remain int b = 0; char str[4] = "&&"; str[1] = x; char *p; fDefaults.Terminate(); while ((p = strstr(dynbuf.Text(), str))) { char *q = strchr(p+2, '&'); if (q) strcpy(p, q); else *p = '\0'; b = 1; } dynbuf.SetLength(); if (b) return dynbuf.Length() ? 1 : 2; return 0; } void p4wRequest::RedirectToURLWithReferrerState() { // // Use existing base and url to construct address // for LOCATION directive, and then redirect to // this new location. StrBuf newLocation; newLocation << p4wStrBuf().NormalizeBase( GetBase() ); switch ( GetCmd() ) { case AC_P4CMD: case AC_P4CMDTAGGED: case AC_P4CMDXML: newLocation << GetFullURL(); break; default: newLocation << GetURL(); break; } DoLocation( newLocation.Text() ); } StrPtr * p4wRequest::GetAddress( int raf_flags ) { return fWeb822->GetAddress( raf_flags ); } // // Miscellaneous setters void p4wRequest::SetViewMode( ViewMode mode ) { // // Set fViewMode and replace or set the value in the // state dictionary StrRef *replaceValue = (StrRef *)fStateParms.GetVar( "md" ); char *md; fViewMode = mode; switch( mode ) { case VM_CLIENT: md = "c"; break; case VM_DEPOT: md = "d"; break; case VM_WORKSPACE: md = "w"; break; default: md = "c"; break; } if( replaceValue ) fStateParms.ReplaceVar( "md", md ); else fStateParms.SetVar( "md", md ); } void p4wRequest::SetCdPath( const char *var, const char *val, int normalize, int isPathBrowser, StrBuf &cdPath ) { // // Set the file or path return url variable (and // optionally normalize the path). // Replace or set the value in the dictionary too. StrRef *replaceValue = (StrRef *)fStateParms.GetVar( var ); AllCommands ac = ( isPathBrowser ? AC_PATHBROWSER : AC_BROWSEFILE ); p4wURL urlMaker; cdPath.Set( val ); if( normalize ) urlMaker.NormalizePath( cdPath, ac, fViewMode ); if( replaceValue ) fStateParms.ReplaceVar( var, cdPath.Text() ); else fStateParms.SetVar( var, cdPath ); } void p4wRequest::SetPath( const char *path, int normalize ) { StrRef *replaceValue = (StrRef *)fDynParms.GetVar( "path" ); // // Set the fDepotPath variable (normalized) and replace // or set the value in the dictionary fDepotPath.Set( path ); if( normalize ) { // // If this is a request for mime content using a // symbolic revision (sr), the path should be // normalized for client syntax even if in workspace // mode since the path in this case uses client // syntax. StrPtr *sr = fStateParms.GetVar( "sr" ); ViewMode vm; p4wURL urlMaker; if( GetCmd() == AC_NONE && fViewMode == VM_WORKSPACE && sr ) vm = VM_CLIENT; else vm = fViewMode; urlMaker.NormalizePath( fDepotPath, GetCmd(), vm ); } if( replaceValue ) fDynParms.ReplaceVar( "path", fDepotPath.Text() ); else fDynParms.SetVar( "path", fDepotPath ); } void p4wRequest::SetWRPath() { // // If the browser is IE on NT, and the p4web is running on // an NT box, we need to replace forward slashes generated // by IE with backward slashes. StrRef *replaceValue = (StrRef *)fStateParms.GetVar( "wr" ); StrBuf wr; p4wURL urlMaker; if( !replaceValue ) return; wr.Set( replaceValue ); urlMaker.ConvertNTPath( wr, fViewMode ); fStateParms.ReplaceVar( "wr", wr.Text() ); } void p4wRequest::UseNewBase( StrBuf & newBase, const char *oldBaseStr, const char *var, const char *val ) { // // Create base string using values from the oldBaseStr + 1 // new var=val setting. // Include variable that prevents browser from caching the page. int doCache = 1; const StrPtr *pgc = GetStateArg( "pgc" ); if( pgc && *pgc == "y" ) doCache = 0; doUseNewBase( newBase, oldBaseStr, var, val, doCache ); } void p4wRequest::UseNewBaseDoCache( StrBuf & newBase, const char *oldBaseStr, const char *var, const char *val ) { // // Create base string using values from the oldBaseStr + 1 // new var=val setting. // Don't include variable that prevents browser from caching the page. doUseNewBase( newBase, oldBaseStr, var, val, 0 ); } void p4wRequest::doUseNewBase( StrBuf & newBase, const char *oldBaseStr, const char *var, const char *val, int noCache ) { // // Create base string using values from the oldBaseStr + 1 // new var=val setting. If oldBaseStr is NULL, use values // from fStateParms. StrBufDict *oldBase; StrBufDict stateParms; StrBufDict dynParms; p4wURL urlMaker; if( oldBaseStr == NULL ) { oldBase = &fStateParms; } else { StrBuf base; base.Set( oldBaseStr ); urlMaker.ParseURL( base, &stateParms, &dynParms, 1 ); oldBase = &stateParms; } // // Insure that the path section of the base does not include // the node StrBuf tPath; urlMaker.GetDirLocation( tPath, fDepotPath.Text(), GetViewMode() != VM_WORKSPACE, fCommand ); urlMaker.SetNewBase( newBase, oldBase, 0, tPath, var, val, noCache ); } void p4wRequest::ReplaceBase( const char *var, const char *val ) { // // Replace value of var with val in the base. Then reset // any variables we might have changed in the state and/or // dynamic dictionaries. StrBuf tBase; const StrPtr *pgc = GetStateArg( "pgc" ); int doCache = 1; if (fIsHTTPS) fBase.Set( "https://" ); else fBase.Set( "http://" ); // // Insure that node part of path is not included in the state // space StrBuf tPath; p4wURL urlMaker; urlMaker.GetDirLocation( tPath, fDepotPath.Text(), GetViewMode() != VM_WORKSPACE, fCommand ); if( fBrowseOnly && pgc && *pgc == "y" ) doCache = 0; urlMaker.SetNewBase( tBase, &fStateParms, 1, tPath, var, val, doCache ); fBase << fHTTPPort << tBase; // // Reset any variables we might have changed in their respective // dictionaries. if( val && !strcmp( var, "md" ) ) { if( !strcmp( val, "c" ) ) SetViewMode( VM_CLIENT ); else if( !strcmp( val, "d" ) ) SetViewMode( VM_DEPOT ); else if( !strcmp( val, "w" ) ) SetViewMode( VM_WORKSPACE ); } else if( val && !strcmp( var, "cd" ) ) { SetCdPath( "cd", p4wStrBuf().UnescapeAmp( StrRef( val ) ).Text(), 0, 1, fReturnURL ); } else if( val && !strcmp( var, "path" ) ) { SetPath( val, 0 ); } else if( val && !strcmp( var, "thc" ) ) { int i = atoi(val); if (i < 1 || i > 10) i = fThumbColsDefault; SetThumbnailCols( i ); } else if( !strcmp( var, "cdf" ) ) { if( !val ) { fReturnFileURL.Clear(); } else { SetCdPath( "cdf", p4wStrBuf().UnescapeAmp( StrRef( val ) ).Text(), 0, 0, fReturnFileURL ); } } } // ------------------------------------- // P4 Request functions. // void p4wRequest::p4( const char * cmd, int argc, const char ** argv, ClientUser * pane ) { // // Save off the command being run if &M=c const StrPtr *M = GetStateArg( "M" ); if( M && strchr(M->Text(), 'c') ) { fShowP4Cmds << "p4 " << cmd; fShowP4Cmds << fShowP4Args; if( argv != NULL ) { int i = -1; while(++i < argc) fShowP4Cmds << " " << argv[i]; } fShowP4Cmds << "<br>"; } fShowP4Args.Clear(); // always clear since args always saved // // Set the program name variable so that monitor show -e // can report it (4.1+: ignored by older servers) fClient.SetVar( "prog", "p4web" ); char v[50]; sprintf(v, "%s/%s/%s", ID_REL, ID_OS, ID_PATCH); fClient.SetVersion(v); if( argv != NULL ) fClient.SetArgv( argc, (char **)argv ); if( fReportLog ) { char time_buffer[255 + 1]; time_buffer[0] = '\0'; time_t iTime = time( NULL ); StrBuf time_format; time_format.Append( "%d %b %H:%M:%S" ); strftime( time_buffer, sizeof( time_buffer ), time_format.Text(), localtime( &iTime ) ); fReportBuf << time_buffer << " " << cmd << "; "; } fClient.Run( (char *)cmd, pane ); // used to be fClient.RunTag() } void p4wRequest::p4Wait() { // here we used to call fClient.WaitTag(); if( fReportLog && *(fReportBuf.Text()) ) { char time_buffer[255 + 1]; time_buffer[0] = '\0'; time_t iTime = time( NULL ); StrBuf time_format; time_format.Append( "%d %b %H:%M:%S" ); strftime( time_buffer, sizeof( time_buffer ), time_format.Text(), localtime( &iTime ) ); fReportBuf << "ended at " << time_buffer << crlf; Error e; e.Set( E_INFO, fReportBuf.Text() ); fReportLog->Report( &e ); fReportBuf.Clear(); } } void p4wRequest::p4Arg( const StrRef &arg ) { fClient.SetVar( StrRef::Null(), arg ); } void p4wRequest::p4Init( Error *e ) { fDidP4Init = 1; switch (fCommand) { case AC_P4CMD: case AC_EDITFILE: case AC_DELETEFILE: case AC_SYNCCMD: case AC_SYNCREV: case AC_SYNCFILE: case AC_SYNCPROCESSOR: case AC_REMOVEFILEFRM: case AC_EDITPROCESSOR: case AC_ADDFILE: case AC_ADDPROCESSOR: case AC_DELETEPROCESSOR: case AC_DELETECONFPROC: case AC_SHOWHIDECOLSPROC: case AC_SHOWHIDEFILECOLS: case AC_CHGLISTANNOTATEPROC: case AC_REVERTCMD: case AC_REVERTFILE: case AC_REVERTUNCHANGEDFILEFRM: case AC_REVERTPROCESSOR: case AC_CHANGECMD: case AC_CHANGEORSUBMITCMD: case AC_FTYPEPROCESSOR: case AC_LOCKPROCESSOR: case AC_LOCKFILEFRM: case AC_UNLOCKPROCESSOR: case AC_UNLOCKFILEFRM: case AC_LABSYNCPROCESSOR: case AC_INTEGPROCESSOR: break; case AC_RESOLVEPROCESSOR: if (isLocalRequest()) break; default: fClient.SetProtocol("tag", ""); fIsTagged = 1; break; } if( GetP4Init() ) return; globalCancel = 0; fClient.Init( e ); if( ! e->Test() ) { SetP4Init( 1 ); fClient.SetBreak( &cancel ); } } void p4wRequest::p4Final( Error *e ) { fClientRun = fGroupsRun = 0; if( !GetP4Init() ) return; fClient.Final( e ); SetP4Init( 0 ); fDidP4Init = 0; } // ------------------------------------- // iostream-style write functions. // p4wRequest& p4wRequest::operator <<(int val) { // Temporary vars. char tempStr[255 + 1]; // Convert the int to a string and write it out. sprintf( tempStr, "%d", val ); Write( tempStr, strlen( tempStr ) ); return *this; } // ------------------------------------- // Read/Write headers and data. // void p4wRequest::Write( const char * buf, int len ) { fNBytesSent += len; fWeb822->Send( (char *)buf, len ); } void p4wRequest::Write( StrBuf & str ) { fNBytesSent += str.Length(); Write( str.Text(), str.Length() ); str.Clear(); } void p4wRequest::WriteLine() { Write( crlf, strlen( crlf ) ); } void p4wRequest::WriteLine( StrBuf & str ) { str << crlf; Write( str ); } // // Misc getters int p4wRequest::DetectBrowser() { // // Parse the userAgent string to determine browser type. If we can't // detect type or type is not supported, return 0 to turn it off. const char *netscape = "Mozilla/"; const char *msIE = "MSIE"; const char *safari = "Safari/"; float netscapeVersion = 4.5; float ieVersion = 4.5; float safariVersion = 85.0; char *s; char *s2; int type; float browserVersion = 0.0; float supportedVersion; // // Search for presence of msIE string FIRST, as the netscape string // may also be present from MSIE browsers. Get the type and the // version. if( !fUserAgent.Length() ) { return 0; } else if( ( s = strstr( fUserAgent.Text(), msIE ) ) ) { supportedVersion = ieVersion; type = 2; s2 = s + strlen( msIE ); } else if( ( s = strstr( fUserAgent.Text(), safari ) ) ) { supportedVersion = safariVersion; type = 3; s2 = s + strlen( safari ); } else if( ( s = strstr( fUserAgent.Text(), netscape ) ) ) { supportedVersion = netscapeVersion; type = 1; s2 = s + strlen( netscape ); } else { return 0; } // // Check for supported browser version if( !s2 || !*s2 ) browserVersion = 0.0; else sscanf( s2, "%f", &browserVersion ); if( browserVersion < supportedVersion ) type = 0; else fBrowserVersion = browserVersion; return type; } AllCommands p4wRequest::GetLastReturnType() { // // Returns correct AC code for the return icon. If our last command // originated from the file browser, return to a file browser, // otherwise return to the path browser. AllCommands ac; ac = ( fStateParms.GetVar( "cdf" ) ? AC_BROWSEFILE : AC_PATHBROWSER ); return ac; } int p4wRequest::isLocalRequest() { if (fIsHTTPS) return 0; StrPtr * address = fWeb822->GetAddress( 0 ); StrPtr * peer = fWeb822->GetPeerAddress( 0 ); return (*address) == (*peer); } int p4wRequest::URLHasNoStateInfo() { return fURLHasNoState; } void p4wRequest::ConstructSafeURL( StrBuf & url, const char *path, AllCommands cmd, StrBufDict *cmdArgs ) { // // Construct an URL path that can be used in an HREF or ACTION. // This version will convert a relative path to an absolute path // if the path contains any unsafe characters (:), and then just // calls p4wURL::ConstructURL to finish constructing the url. p4wURL urlMaker; StrBuf tPath; StrBuf absPath; // // No conversion needed if( !path || *path == '/' || !strchr( path, ':' ) ) { urlMaker.ConstructURL( url, path, cmd, cmdArgs, GetUnicode() ); return; } // // Found unsafe characters, so convert relative path to // absolute path using the base, and call p4wURL::ConstructURL // to finish the job. switch(fCommand) { case AC_DELETELABELCONFIRM: case AC_DELETEBRANCHCONFIRM: tPath << path; break; default: if( GetViewMode() == VM_WORKSPACE ) tPath << path; else { if (fCommand == AC_PATHBROWSER) tPath << fDepotPath; // add the parent dirs tPath << "/" << path; char *p = strstr(tPath.Text()+2, "//"); if (p) { strcpy(p, p+1); tPath.SetLength(); } } } UseNewBase( absPath, NULL, "path", tPath.Text() ); urlMaker.ConstructURL( url, absPath.Text(), cmd, cmdArgs, GetUnicode() ); } void p4wRequest::AddThumbNail( int rev, const StrPtr * base16thumb ) { if (!fThumbNailTab) fThumbNailTab = new ThumbNailTable; int l; StrBuf base64thumb; StrBuf bindata; char *p = bindata.Alloc((base16thumb->Length() + 1)/2 + 2); int lgth = DecodeBase16(base16thumb->Text(), base16thumb->Length(), p); if (lgth == -1) return; // error p = base64thumb.Alloc(l = ((lgth + 2)/ 3 * 4) + 8); memset(p, '\0', l); if (!b64_encode(bindata.Text(), lgth, p, l)) return; // error fThumbNailTab->AddItem( rev, base64thumb ); } ThumbNailItem * p4wRequest::GetThumbNail( int rev ) { if (fThumbNailTab) return( fThumbNailTab->GetItem(rev) ); return( ( ThumbNailItem * ) 0 ); } void p4wRequest::doShowHideBlock(int showHideState, char *alt, char* title, char *msg, int ctr) { p4wHtml htm; p4wURL urlMaker; htm.beginTable(0, 0, "0", "0"); htm.beginTRow(); int showhidecookie = atoi(GetShowHide().Text()) & ~showHideState; htm << "<script language=javascript>" << crlf; htm << "function SetCookie"; if (ctr) htm << ctr; htm << "(cookieValue) {" << crlf; htm << "var today = new Date();" << crlf; htm << "var expire = new Date();" << crlf; htm << "expire.setTime(today.getTime() + 3600000*24*366);" << crlf; htm << "document.cookie = '"; htm << "P4WStates"; htm << "='+escape(cookieValue)+';path=/;expires='+expire.toGMTString();" << crlf; htm << "}" << crlf; htm << "function showhideFilter"; if (ctr) htm << ctr; htm << "()" << crlf; htm << "{" << crlf; htm << "var elem, vis, viss, vish;" << crlf; htm << "var cookie=" << showhidecookie << ";" << crlf; htm << "if( document.getElementById )" << crlf; // this is the way the standards work htm << " elem = document.getElementById( 'showhideBlock"; if (ctr) htm << ctr; htm << "' );" << crlf; htm << "else if( document.all )" << crlf; // this is the way old msie versions work htm << " elem = document.all['showhideBlock"; if (ctr) htm << ctr; htm << "'];" << crlf; htm << "else if( document.layers )" << crlf; // this is the way nn4 works htm << " elem = document.layers['showhideBlock"; if (ctr) htm << ctr; htm << "'];" << crlf; htm << "vis = elem.style;" << crlf; // if the style.display value is blank we try to figure it out here htm << "if(vis.display==''&&elem.offsetWidth!=undefined&&elem.offsetHeight!=undefined)" << crlf; htm << " vis.display = (elem.offsetWidth!=0&&elem.offsetHeight!=0)?'block':'none';" << crlf; htm << "vis.display = (vis.display==''||vis.display=='block')?'none':'block';" << crlf; htm << "if(vis.display=='block')" << crlf; htm << " SetCookie"; if (ctr) htm << ctr; htm << "(cookie);" << crlf; htm << "else" << crlf; htm << " SetCookie"; if (ctr) htm << ctr; htm << "(cookie + " << showHideState << ");" << crlf; htm << "if( document.getElementById )" << crlf; htm << " elem = document.getElementById( 'showBlockIcon"; if (ctr) htm << ctr; htm << "' );" << crlf; htm << "else if( document.all )" << crlf; htm << " elem = document.all['showBlockIcon"; if (ctr) htm << ctr; htm << "'];" << crlf; htm << "else if( document.layers )" << crlf; htm << " elem = document.layers['showBlockIcon"; if (ctr) htm << ctr; htm << "'];" << crlf; htm << "viss = elem.style;" << crlf; htm << "viss.display = (vis.display==''||vis.display=='block')?'none':'inline';" << crlf; htm << "if( document.getElementById )" << crlf; htm << " elem = document.getElementById( 'hideBlockIcon"; if (ctr) htm << ctr; htm << "' );" << crlf; htm << "else if( document.all )" << crlf; htm << " elem = document.all['hideBlockIcon"; if (ctr) htm << ctr; htm << "'];" << crlf; htm << "else if( document.layers )" << crlf; htm << " elem = document.layers['hideBlockIcon"; if (ctr) htm << ctr; htm << "'];" << crlf; htm << "vish = elem.style;" << crlf; htm << "vish.display = (vis.display==''||vis.display=='block')?'inline':'none';" << crlf; htm << "}" << crlf; htm << "document.write(\"<td>\")" << crlf; htm << "document.write(\"<a href='javascript:showhideFilter"; if (ctr) htm << ctr; htm << "();'>\")" << crlf; htm << "document.write(\"<div id='showBlockIcon"; if (ctr) htm << ctr; htm << "'>\")" << crlf; htm << "document.write(\"<img src='/plusBoxIcon?ac=20' height='20' width='20' border='0' alt='Show "; htm << alt << "' title='Show "; htm << alt << "'>\")" << crlf; htm << "document.write(\"</div>\")" << crlf; htm << "document.write(\"<div id='hideBlockIcon"; if (ctr) htm << ctr; htm << "'>\")" << crlf; htm << "document.write(\"<img src='/minusBoxIcon?ac=20' height='20' width='20' border='0' alt='Hide "; htm << alt << "' title='Hide "; htm << alt << "'>\")" << crlf; htm << "document.write(\"</div>\")" << crlf; htm << "document.write(\"</a></td>\")" << crlf; htm << "</script>" << crlf; htm.beginCol( NULL, "left", "2", 0,0,0,0, 1, "fSmall" ); htm << "<script language=javascript>" << crlf; htm << "document.write(\"<a href='javascript:showhideFilter"; if (ctr) htm << ctr; htm << "();' title='Show/Hide "; htm << alt << "'>\")" << crlf; htm << "</script>" << crlf; htm << title; htm << "<script language=javascript>" << crlf; htm << "document.write(\"</a>\")" << crlf; if (msg && *msg) htm << "document.write(\"<span class='remote'> " << msg << "</span>\")" << crlf; htm << "</script>" << crlf; htm.endCol(); htm.endTRow(); htm.endTable(); *this << htm; } void p4wRequest::doHashPswd(char *userpswd, int lgth) { int len; StrBuf hash; StrBuf user; StrBuf pswd; user << userpswd; char *p = user.Text(); for (len = lgth; len--; p++) { if (*p == ':') break;; } if (len < 0) return; *p = '\0'; pswd << ++p; hash << szInitTime; char *q = user.Text() + (user.Length()/2); for (p = hash.Text(); *p; ) { *p++ ^= *q++; if (!*q) q = user.Text(); } q = hash.Text(); for (p = pswd.Text(); len--; ) { *p++ ^= *q++; if (!*q) q = hash.Text(); } p = userpswd; for (len = lgth; len--; p++) { if (*p == ':') break;; } if (len < 0) return; memmove(p+1, pswd.Text(), len); } void p4wRequest::unHashPswd(char *userpswd, int lgth) { doHashPswd(userpswd, lgth); } const StrPtr * p4wRequest::argEncoding( const char *var, StrPtr *arg, HTMLEncoding encoding ) { if ( !arg || encoding == HE_None ) return arg; p4wStrBuf argBuf; if ( encoding == HE_Page ) { StrPtr rargs = argBuf.EscapeHTML(*arg, GetUnicode() ); fEscStateParms.ReplaceVar(var, rargs.Text()); } else if ( encoding == HE_URL ) { StrPtr rargs = argBuf.EscapeURL(*arg, GetUnicode() ); fEscStateParms.ReplaceVar(var, rargs.Text()); } return fEscStateParms.GetVar(var); }
# | 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/p4wRequest.cpp | |||||
#1 | 8914 | Matt Attaway | Initial add of the P4Web source code |