Beispiel #1
0
        private ArrayList getUnpublishedFiles(String path)
        {
            this.Cursor = Cursors.WaitCursor;

            if (scan == false)
            {
                int trimIndex = path.IndexOf("art");
                path = path.Substring(trimIndex, path.Length - trimIndex);
                path = "//depot/phoenix/xbox/published/" + path.Replace("\\", "/");
            }

            p4 = new P4Connection();
            p4.Connect();
            p4I = new P4Interface();

            ArrayList unpublished = new ArrayList();

            String[]    args   = { "-n", "-d", "-i", "-t", "-v", "-b", "phx_published", path };
            P4RecordSet record = p4I.Run("integrate", args);

            foreach (P4Record entry in record)
            {
                unpublished.Add(entry.Fields["fromFile"]);
            }
            this.Cursor = Cursors.Default;
            return(unpublished);

            p4.Disconnect();
        }
Beispiel #2
0
        public P4Service(string server, string user, string password, string workspace, bool useP4Config, string path, Map map)
        {
            _map = map;
            if (_map == null)
            {
                _map = new Map(true);
            }
            _p4 = new P4Connection();
            var tmp1 = _p4.CallingVersion;

            _p4.Api = 65; // 2009.1, to support P4 Move. See http://kb.perforce.com/article/512/perforce-protocol-levels

            // Before we set up the connection to Perforce, set the CWD so we pick up any changes to P4Config
            if (useP4Config)
            {
                if (!string.IsNullOrEmpty(path))
                {
                    _p4.CWD = path;
                }
                _p4.Port     = "";
                _p4.User     = "";
                _p4.Password = "";
                _p4.Client   = "";
            }
            else
            {
                _p4.Port     = server;
                _p4.User     = user;
                _p4.Password = password;
                _p4.Client   = workspace;
            }

            //Log.Information(String.Format(Resources.Connected_To, _p4.Port, _p4.User, _p4.Client));
        }
Beispiel #3
0
        /// <summary>
        /// Execute the task.
        /// </summary>
        /// <param name="Job">Information about the current job</param>
        /// <param name="BuildProducts">Set of build products produced by this node.</param>
        /// <param name="TagNameToFileSet">Mapping from tag names to the set of files they include</param>
        public override void Execute(JobContext Job, HashSet <FileReference> BuildProducts, Dictionary <string, HashSet <FileReference> > TagNameToFileSet)
        {
            HashSet <FileReference> Files = ResolveFilespec(CommandUtils.RootDirectory, Parameters.Files, TagNameToFileSet);

            if (CommandUtils.AllowSubmit && Files.Count > 0)
            {
                // Get the connection that we're going to submit with
                P4Connection SubmitP4 = CommandUtils.P4;
                if (Parameters.Workspace != null)
                {
                    // Create a brand new workspace
                    P4ClientInfo Client = new P4ClientInfo();
                    Client.Owner    = CommandUtils.P4Env.User;
                    Client.Host     = Environment.MachineName;
                    Client.Stream   = Parameters.Stream ?? CommandUtils.P4Env.Branch;
                    Client.RootPath = Parameters.RootDir ?? CommandUtils.RootDirectory.FullName;
                    Client.Name     = Parameters.Workspace;
                    Client.Options  = P4ClientOption.NoAllWrite | P4ClientOption.Clobber | P4ClientOption.NoCompress | P4ClientOption.Unlocked | P4ClientOption.NoModTime | P4ClientOption.RmDir;
                    Client.LineEnd  = P4LineEnd.Local;
                    CommandUtils.P4.CreateClient(Client, AllowSpew: false);

                    // Create a new connection for it
                    SubmitP4 = new P4Connection(Client.Owner, Client.Name);
                }

                // Get the latest version of it
                int NewCL = SubmitP4.CreateChange(Description: Parameters.Description);
                foreach (FileReference File in Files)
                {
                    SubmitP4.Revert(String.Format("-k \"{0}\"", File.FullName));
                    SubmitP4.Sync(String.Format("-k \"{0}\"", File.FullName), AllowSpew: false);
                    SubmitP4.Add(NewCL, String.Format("\"{0}\"", File.FullName));
                    SubmitP4.Edit(NewCL, String.Format("\"{0}\"", File.FullName));
                    if (Parameters.FileType != null)
                    {
                        SubmitP4.P4(String.Format("reopen -t \"{0}\" \"{1}\"", Parameters.FileType, File.FullName), AllowSpew: false);
                    }
                }

                // Revert any unchanged files
                if (Parameters.RevertUnchanged)
                {
                    SubmitP4.RevertUnchanged(NewCL);
                    if (SubmitP4.TryDeleteEmptyChange(NewCL))
                    {
                        CommandUtils.Log("No files to submit; ignored.");
                        return;
                    }
                }

                // Submit it
                int SubmittedCL;
                SubmitP4.Submit(NewCL, out SubmittedCL);
                if (SubmittedCL <= 0)
                {
                    throw new AutomationException("Submit failed.");
                }
                CommandUtils.Log("Submitted in changelist {0}", SubmittedCL);
            }
        }
Beispiel #4
0
 public void Dispose()
 {
     if (_p4 != null)
     {
         _p4.Dispose();
         _p4 = null;
     }
 }
Beispiel #5
0
        //=========================================================================================
        // constructor / init / connection
        //

        //-----------------------------------------------------------------------------------------
        public P4API(string _configFile = null)
        {
            m_connection = new P4Connection();

            if (_configFile != null)
            {
                m_configFile = _configFile;
            }
            loadConfig();
        }
Beispiel #6
0
        public void Test001_SetupConnection()
        {
            p4                = new P4Connection();
            p4.Port           = _port;
            p4.CallingProgram = "P4.Net Test Harness";
            p4.CallingVersion = "1.0";
            p4.Connect();

            p4.Client = "TestClient1";
            p4.User   = "******";

            P4Record s = p4.Run("info")[0];

            Assert.AreEqual(s["userName"], "TestUser1", "User name should be known.  This test requires StandardTest to be executed first");
            Assert.AreEqual(s["clientName"], "TestClient1", "Client spec should be known.  This test requires StandardTest to be executed first");
        }
Beispiel #7
0
 /// <summary>
 /// </summary>
 public static int Main(string[] args)
 {
     using (var c = new P4Connection())
     {
         try
         {
             c.Connect();
         }
         catch (Exception ex)
         {
             Console.ForegroundColor = ConsoleColor.Red;
             Console.Write(ex.GetType().FullName);
             Console.Write(": ");
             Console.WriteLine(ex.Message);
             Console.WriteLine(ex.StackTrace);
             Console.ResetColor();
         }
     }
     return 0;
 }
Beispiel #8
0
 /// <summary>
 /// </summary>
 public static int Main(string[] args)
 {
     using (var c = new P4Connection())
     {
         try
         {
             c.Connect();
         }
         catch (Exception ex)
         {
             Console.ForegroundColor = ConsoleColor.Red;
             Console.Write(ex.GetType().FullName);
             Console.Write(": ");
             Console.WriteLine(ex.Message);
             Console.WriteLine(ex.StackTrace);
             Console.ResetColor();
         }
     }
     return(0);
 }
Beispiel #9
0
        private static string executeP4Cmd(string cmd, string [] args)
        {
            string results = "";

            try
            {
                P4Connection p4 = new P4Connection();
                p4.Connect();

                P4RecordSet recSet = p4.Run(cmd, args);

                foreach (object obj in recSet.Messages)
                {
                    results += String.Format("{0}\r\n", (string)obj);
                }

                foreach (P4Record rec in recSet)
                {
                    FieldDictionary fd = rec.Fields;
                    if (cmd.Equals("opened"))
                    {
                        results += fd["clientFile"];
                    }
                    else
                    {
                        foreach (string key in fd.Keys)
                        {
                            results += String.Format("{0}\t\t{1}\r\n", key, fd[key]);
                        }
                    }
                    results += "\r\n";
                }
                p4.Disconnect();
            }
            catch (Exception ex)
            {
                return(ex.Message);
            }
            return(results);
        }
Beispiel #10
0
        public Form1()
        {
            InitializeComponent();


            appSettings = new Settings("data.dat");

            p4 = new P4Connection();
            p4.ExceptionLevel = P4ExceptionLevels.ExceptionOnBothErrorsAndWarnings;
            p4.Connect();

            if (!p4.IsValidConnection(true, true))
            {
                MessageBox.Show("No valid p4 connection.");
            }

            p4I = new P4Interface();
            p4I.Connect();

            if (!p4I.IsValidConnection())
            {
                MessageBox.Show("No valid p4I connection.");
            }

            checkedNodes = new ArrayList();

            foreach (String user in p4I.GetUsers())
            {
                comboBoxUsers.Items.Add(user.ToLower());
            }
            comboBoxUsers.SelectedItem = p4.User.ToLower().ToString();

            BuildLocalPaths();
            MarkUnpublishedFolders(getUnpublishedFiles());
            ExpandNode(treeView1.Nodes[0]);

            p4.Disconnect();
        }
Beispiel #11
0
        protected void InitP4()
        {
            _p4 = new P4Connection();
            if (_p4Port != null)
            {
                _p4.Port = _p4Port;
            }
            if (_p4Client != null)
            {
                _p4.Client = _p4Client;
            }
            if (_p4CWD != null)
            {
                _p4.CWD = _p4CWD;
            }
            if (_p4Host != null)
            {
                _p4.Host = _p4Host;
            }
            if (_p4User != null)
            {
                _p4.User = _p4User;
            }
            if (_p4Passwd != null)
            {
                _p4.Password = _p4Passwd;
            }

            _p4.ExceptionLevel = P4ExceptionLevels.NoExceptionOnErrors;
            _p4.CallingProgram = "P4MSBuildTasks";
            _p4.CallingVersion = "1.0";

            _p4.Connect();
            if (_useLogin && _p4Passwd != null)
            {
                _p4.Login(_p4Passwd);
            }
        }
Beispiel #12
0
        private void BuildLabel(string JobName, P4Connection p4, EventLog log)
        {
            string LabelName = string.Format(_labelNameFormat, JobName);

            // delete the label... it may not exist, but finding that out is worse performance
            // than just deleting and assuming it doesn't exist.
            // The '-f' flag may cause problems if the p4 account running this command isn't a
            // super user.  You should be able to remove the flag... so long as no one
            // manually monkey's with the labels.
            P4UnParsedRecordSet labelDel = p4.RunUnParsed("label", "-f", "-d", LabelName);

            List <int> JobChanges = new List <int>();

            // Run a fixes to get all the changelists we need to add
            P4RecordSet fixes = p4.Run("fixes", "-j", JobName);

            // Spin the recordset to build a unique list of changelists
            foreach (P4Record fix in fixes)
            {
                JobChanges.Add(int.Parse(fix["Change"]));
            }

            // Sort them to be certain they are ascending
            JobChanges.Sort();

            // only build the label if there are indeed fixes
            if (JobChanges.Count > 0)
            {
                //re-create the label
                P4Form labelForm = p4.Fetch_Form("label", "-t", _labelTemplate, LabelName);

                // make sure the form is unlocked
                labelForm["Options"] = "unlocked";

                // make sure we're the owner
                labelForm["Owner"] = _p4user;
                p4.Save_Form(labelForm);

                int ChangesAdded = 0;

                //now need to labelsync to all latest changes
                foreach (int change in JobChanges)
                {
                    // using tag here so a valid client spec is not needed.
                    // for older servers, you could substitue for labelsync,
                    // but you'd need to pass in and set a valid client spec
                    P4UnParsedRecordSet ls = p4.RunUnParsed("tag", "-l", LabelName, string.Format("@={0}", change));

                    // this is why we set exception level to NoExceptionOnErrors
                    if (ls.HasErrors())
                    {
                        if (ls.ErrorMessage.StartsWith("Can't use a pending changelist number for this command."))
                        {
                            // Nothing to worry about.  p4 fixes returns fix records for pending changelists,
                            // but we don't know that going into this.  It's likely more performant to just
                            // assume it is a submitted changelist and ignore this error.
                        }
                        else
                        {
                            // Something's gone ary.  Should throw an Exception.
                            // But I'm lazy so we just log the error.
                            log.WriteEntry(ls.ErrorMessage);
                        }
                    }
                    else
                    {
                        ChangesAdded++;
                    }
                }

                // If ChangesAdded is still 0, then we should delete the label since
                // there are no files anyway
                if (ChangesAdded == 0)
                {
                    p4.RunUnParsed("label", "-f", "-d", LabelName);
                }
                else
                {
                    // now lock the label
                    labelForm["Options"] = "locked";
                    p4.Save_Form(labelForm);

                    // Note this trick.  You can re-save a form as many times as you need,
                    // no need to fetch before each save.  In fact you can use the form to save
                    // new objects... like:
                    //   labelForm["Label"] = "newLabelName";
                    //   p4.Save_Form(labelForm);
                    // and you created a new label named newLabelName.
                    // Cool, huh?  Well, I don't deserve the credit... it's the way the
                    // native C++ API works.
                }
            }
        }
Beispiel #13
0
        private ArrayList getUnpublishedFiles()
        {
            this.Cursor = Cursors.WaitCursor;
            Scanning scan = new Scanning();

            scan.StartPosition = FormStartPosition.CenterParent;
            scan.Show();
            scan.Refresh();

            p4 = new P4Connection();
            p4.Connect();
            p4I = new P4Interface();

            ArrayList unpublished = new ArrayList();

            String[] args = { "-n", "-d", "-i", "-t", "-v", "-b", "phx_published", "//depot/phoenix/xbox/published/..." };

            // logging
            String tmp = "";

            foreach (String t in args)
            {
                tmp += t;
                tmp += " ";
            }
            LogLine(String.Concat("Running: Integrate ", tmp));

            P4RecordSet record = p4I.Run("integrate", args);

            LogLine(String.Concat("Total Records: ", record.Records.Length));
            foreach (P4Record entry in record)
            {
                if (checkBoxFilter.Checked)
                {
                    String file      = entry.Fields["depotFile"];
                    String delim     = "/";
                    int    trimIndex = -1;
                    if (file.Split(delim.ToCharArray())[6] == "art")
                    {
                        trimIndex = file.IndexOf("art");
                    }
                    else if (file.Split(delim.ToCharArray())[6] == "scenario")
                    {
                        trimIndex = file.IndexOf("scenario");
                    }

                    file = file.Substring(trimIndex, file.Length - trimIndex);
                    file = "//depot/phoenix/xbox/published/" + file.Replace("\\", "/");

                    String[]    args2   = { file.Replace("//depot/phoenix/xbox/published", localPath).Replace("\\", "/") };
                    P4RecordSet changes = p4I.Run("changes", args2);

                    if (changes.Records.Length > 0 && changes[0].Fields["user"] == comboBoxUsers.SelectedItem.ToString())
                    {
                        LogLine(String.Concat("Record: ", entry.Fields["depotFile"]));
                        unpublished.Add(entry.Fields["fromFile"]);
                    }
                }
                else
                {
                    unpublished.Add(entry.Fields["fromFile"]);
                }
            }
            scan.Close();
            this.Cursor = Cursors.Default;
            return(unpublished);

            p4.Disconnect();
        }
    void ExecuteInner(string Dir, int PR)
    {
        string PRNum = PR.ToString();

        // Discard any old changes we may have committed
        RunGit("reset --hard");

        // show-ref is just a double check that the PR exists
        //var Refs = RunGit("show-ref");

        /*if (!Refs.Contains("refs/remotes/origin/pr/" + PRNum))
         * {
         *      throw new AutomationException("This is not among the refs: refs/remotes/origin/pr/{0}", PRNum);
         * }*/
        RunGit(String.Format("fetch origin refs/pull/{0}/head:pr/{1}", PRNum, PRNum));
        RunGit(String.Format("checkout pr/{0} --", PRNum));

        int    CLBase;
        string DepotBase;

        ScanForBranchAndCL_BaseVersion(String.Format("log --author=TimSweeney --author=UnrealBot -100 pr/{0} --", PRNum), out DepotBase, out CLBase);


        int    CLLive;
        string DepotLive;

        ScanForBranchAndCL_LiveVersion(String.Format("log -100 pr/{0} --", PRNum), out DepotLive, out CLLive);

        if (CLLive == 0 && CLBase == 0)
        {
            throw new AutomationException("Could not find a base change and branch using either method.");
        }

        int    CL    = 0;
        string Depot = "";

        if (CLBase > CLLive)
        {
            CL    = CLBase;
            Depot = DepotBase;
        }
        else
        {
            CL    = CLLive;
            Depot = DepotLive;
        }
        if (CL < 2000000 || String.IsNullOrWhiteSpace(Depot))
        {
            throw new AutomationException("Could not find a base change and branch using either method.");
        }


        P4ClientInfo NewClient = P4.GetClientInfo(P4Env.Client);

        foreach (var p in NewClient.View)
        {
            LogInformation("{0} = {1}", p.Key, p.Value);
        }

        string TestClient = P4Env.User + "_" + Environment.MachineName + "_PullRequests_" + Depot.Replace("/", "_");

        NewClient.Owner    = P4Env.User;
        NewClient.Host     = Environment.MachineName;
        NewClient.RootPath = Dir;
        NewClient.Name     = TestClient;
        NewClient.View     = new List <KeyValuePair <string, string> >();
        NewClient.View.Add(new KeyValuePair <string, string>(Depot + "/...", "/..."));
        NewClient.Stream = null;
        if (!P4.DoesClientExist(TestClient))
        {
            P4.CreateClient(NewClient);
        }

        var P4Sub = new P4Connection(P4Env.User, TestClient, P4Env.ServerAndPort);

        P4Sub.Sync(String.Format("-f -k -q {0}/...@{1}", Depot, CL));

        var Change = P4Sub.CreateChange(null, String.Format("GitHub pull request #{0}", PRNum));

        P4Sub.ReconcileNoDeletes(Change, CommandUtils.MakePathSafeToUseWithCommandLine(CombinePaths(Dir, bDoingUT ? "UnrealTournament" : "Engine", "...")));
        P4Sub.Shelve(Change);
        P4Sub.Revert(Change, "-k //...");
    }
		/// <summary>
		/// Execute the task.
		/// </summary>
		/// <param name="Job">Information about the current job</param>
		/// <param name="BuildProducts">Set of build products produced by this node.</param>
		/// <param name="TagNameToFileSet">Mapping from tag names to the set of files they include</param>
		/// <returns>True if the task succeeded</returns>
		public override bool Execute(JobContext Job, HashSet<FileReference> BuildProducts, Dictionary<string, HashSet<FileReference>> TagNameToFileSet)
		{
			HashSet<FileReference> Files = ResolveFilespec(CommandUtils.RootDirectory, Parameters.Files, TagNameToFileSet);
			if (CommandUtils.AllowSubmit && Files.Count > 0)
			{
				// Get the connection that we're going to submit with
				P4Connection SubmitP4 = CommandUtils.P4;
				if (Parameters.Workspace != null)
				{
					// Create a brand new workspace
					P4ClientInfo Client = new P4ClientInfo();
					Client.Owner = Environment.UserName;
					Client.Host = Environment.MachineName;
					Client.Stream = Parameters.Stream ?? CommandUtils.P4Env.BuildRootP4;
					Client.RootPath = Parameters.RootDir ?? CommandUtils.RootDirectory.FullName;
					Client.Name = Parameters.Workspace;
					Client.Options = P4ClientOption.NoAllWrite | P4ClientOption.Clobber | P4ClientOption.NoCompress | P4ClientOption.Unlocked | P4ClientOption.NoModTime | P4ClientOption.RmDir;
					Client.LineEnd = P4LineEnd.Local;
					CommandUtils.P4.CreateClient(Client, AllowSpew: false);

					// Create a new connection for it
					SubmitP4 = new P4Connection(Client.Owner, Client.Name);
				}

				// Get the latest version of it
				int NewCL = SubmitP4.CreateChange(Description: Parameters.Description);
				foreach(FileReference File in Files)
				{
					SubmitP4.Revert(String.Format("-k \"{0}\"", File.FullName));
					SubmitP4.Sync(String.Format("-k \"{0}\"", File.FullName), AllowSpew: false);
					SubmitP4.Add(NewCL, String.Format("\"{0}\"", File.FullName));
					SubmitP4.Edit(NewCL, String.Format("\"{0}\"", File.FullName));
					if (Parameters.FileType != null)
					{
						SubmitP4.P4(String.Format("reopen -t \"{0}\" \"{1}\"", Parameters.FileType, File.FullName), AllowSpew: false);
					}
				}

				// Submit it
				int SubmittedCL;
				SubmitP4.Submit(NewCL, out SubmittedCL);
				if (SubmittedCL <= 0)
				{
					throw new AutomationException("Submit failed.");
				}
				CommandUtils.Log("Submitted in changelist {0}", SubmittedCL);
			}
			return true;
		}
Beispiel #16
0
        //Guts of the logic.  This is where the app will dynamicaly build the job labels
        public void RunLabeler(EventLog log)
        {
            P4Connection p4 = null;

            try
            {
                p4      = new P4Connection();
                p4.Port = _p4port;
                p4.User = _p4user;
                if (!_useLogin)
                {
                    p4.Password = _p4password;
                }
                p4.Connect();
                if (_useLogin)
                {
                    p4.Login(_p4password);
                }

                // Don't throw an exception on a Perforce error.  We handle these manually.
                p4.ExceptionLevel = P4ExceptionLevels.NoExceptionOnErrors;

                //Use unparsed b/c older server versions do not support
                //parsed output for the counter command.  And, it's easy
                //to parse by hand.
                P4UnParsedRecordSet counter = p4.RunUnParsed("counter", _p4counter);

                int           counterValue       = int.Parse(counter[0]);
                int           LastSequenceNumber = 0;
                List <string> JobList            = new List <string>();

                //Run p4 logger to find all the changes and jobs since the last run
                P4RecordSet loggers = p4.Run("logger", "-c", counterValue.ToString());

                //spin the results, and get a unique list of jobs
                foreach (P4Record r in loggers)
                {
                    if (r.Fields["key"] == "job")
                    {
                        string JobName = r.Fields["attr"];
                        if (!JobList.Contains(JobName))
                        {
                            JobList.Add(JobName);
                        }
                    }
                    LastSequenceNumber = int.Parse(r.Fields["sequence"]);
                }

                // We're done with loggers, so lets remove the reference and
                // give the GC a chance to clean memory.  If it's been a long time
                // since the last run, it could be thousands of jobs to catch up.
                loggers = null;

                // Now spin all the jobs and build the label
                foreach (string JobName in JobList)
                {
                    BuildLabel(JobName, p4, log);
                }

                //Now we update the counter to the last sequence number from logger.
                p4.RunUnParsed("counter", _p4counter, LastSequenceNumber.ToString());
            }
            catch (Exception e)
            {
                // rethrow... b/c I'm lazy :-)
                throw e;
            }
            finally
            {
                // All done here... time to disconnect.
                p4.Disconnect();
            }
        }
Beispiel #17
0
        public void ProcessRequest(HttpContext context)
        {
            // Don't allow this response to be cached by the browser.
            context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            context.Response.Cache.SetNoStore();
            context.Response.Cache.SetExpires(DateTime.MinValue);

            // Create a new p4 connection, and set the appropriate properties
            P4Connection      p4          = new P4Connection();
            AppSettingsReader appSettings = new AppSettingsReader();

            p4.Port     = (string)appSettings.GetValue("P4PORT", typeof(string));
            p4.User     = (string)appSettings.GetValue("P4USER", typeof(string));
            p4.Client   = (string)appSettings.GetValue("P4CLIENT", typeof(string));
            p4.Password = (string)appSettings.GetValue("P4PASSWD", typeof(string));

            try
            {
                p4.Connect();

                //Figure out the clientPath for the file
                string clientPath = string.Format("//{0}/{1}", p4.Client, context.Request.AppRelativeCurrentExecutionFilePath.Substring(2));

                if (!clientPath.EndsWith("/"))
                {
                    // We have a path to a file

                    // find the MIME type and set it
                    string ext      = Path.GetExtension(clientPath);
                    string mimeType = getMimeType(ext);
                    context.Response.ContentType = mimeType;

                    //stream the results ... will throw an exception if the path isn't found
                    try
                    {
                        p4.PrintStream(context.Response.OutputStream, clientPath);
                        context.Response.OutputStream.Flush();
                    }
                    catch (P4API.Exceptions.FileNotFound)
                    {
                        context.Response.StatusCode = 404;
                    }
                }
                else
                {
                    // we have a directory... let's look for a default "index" file and redirect

                    // My Rule for a default page is:
                    // :: "index.htm" or "index.html" in the current directory (case insensitive)
                    //
                    // I don't rely on the Perforce server to be case insensitive, so I will run an fstat for
                    // all files in the directory and see if there are any "index.htm*" files
                    P4RecordSet rs = p4.Run("fstat", "-Op", clientPath + "*");
                    foreach (P4Record r in rs)
                    {
                        if (r["depotFile"].ToLower().EndsWith("index.html") || r["depotFile"].ToLower().EndsWith("index.htm"))
                        {
                            // the -Op switch means client file will be //<clientname>/<clientpath>
                            clientPath = r["clientFile"];
                            break;
                        }
                    }
                    if (clientPath.EndsWith("/"))
                    {
                        // clientPath not updated, means we can't find a default page

                        // For now, just 404... in the future we could allow directory browsing
                        // (which we be a lot bigger than a sample application ;-)
                        context.Response.StatusCode = 404;
                    }
                    else
                    {
                        // redirect to the index page
                        string redirect = "~" + clientPath.Substring(p4.Client.Length + 2);
                        context.Response.Redirect(redirect, false);
                    }
                }
            }
            catch (Exception e)
            {
                // unhandled exception... send a 500 to the browser
                System.Diagnostics.Trace.WriteLine(e.StackTrace);
                context.Response.StatusCode = 500;
            }
            finally
            {
                p4.Disconnect();
                context.Response.End();
            }
        }