Fossil

Check-in [9cd2c42e]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:If the REPOSITORY argument to the "server" or "http" commands is a directory, then use the first element of PATH_INFO as the basename of a repository in that directory.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:9cd2c42e79a2fee98d0ca4932ad3e094199f90d8
User & Date: drh 2010-01-31 20:29:04
References
2010-03-31
18:42
Get chroot jails working correctly when a particular fossil repository is specified on the "http" command. This fixes a problem introduced by the multi-repository feature added by check-in [9cd2c42e79] on 2010-01-31. check-in: 42ba7b97 user: drh tags: trunk
Context
2010-02-01
06:45
fix typo check-in: a61fe7b8 user: ron tags: trunk
2010-01-31
20:29
If the REPOSITORY argument to the "server" or "http" commands is a directory, then use the first element of PATH_INFO as the basename of a repository in that directory. check-in: 9cd2c42e user: drh tags: trunk
17:44
Run autosync before resolving the version name in the "update" command. In that way, if a branch is specified which has been extended by the sync, the latest version of that branch is extracted rather than the version that was latest prior to the sync. check-in: da48c10d user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/file.c.

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
...
227
228
229
230
231
232
233

234
235
236
237
238
239
240
** other than a directory.
*/
int file_isdir(const char *zFilename){
  int rc;

  if( zFilename ){
    char *zFN = mprintf("%s", zFilename);
    file_simplify_name(zFN, strlen(zFN));
    rc = getStat(zFN);
    free(zFN);
  }else{
    rc = getStat(0);
  }
  return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
}
................................................................................
**  * removing /./
**  * removing /A/../
**
** Changes are made in-place.  Return the new name length.
*/
int file_simplify_name(char *z, int n){
  int i, j;

#ifdef __MINGW32__
  for(i=0; i<n; i++){
    if( z[i]=='\\' ) z[i] = '/';
  }
#endif
  while( n>1 && z[n-1]=='/' ){ n--; }
  for(i=j=0; i<n; i++){







|







 







>







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
...
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
** other than a directory.
*/
int file_isdir(const char *zFilename){
  int rc;

  if( zFilename ){
    char *zFN = mprintf("%s", zFilename);
    file_simplify_name(zFN, -1);
    rc = getStat(zFN);
    free(zFN);
  }else{
    rc = getStat(0);
  }
  return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
}
................................................................................
**  * removing /./
**  * removing /A/../
**
** Changes are made in-place.  Return the new name length.
*/
int file_simplify_name(char *z, int n){
  int i, j;
  if( n<0 ) n = strlen(z);
#ifdef __MINGW32__
  for(i=0; i<n; i++){
    if( z[i]=='\\' ) z[i] = '/';
  }
#endif
  while( n>1 && z[n-1]=='/' ){ n--; }
  for(i=j=0; i<n; i++){

Changes to src/main.c.

451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
...
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
...
569
570
571
572
573
574
575
576
577



578
579
580
581
582
583
584
585
586


































587
588
589
590
591
592
593
594
595
596
597
598
599
...
705
706
707
708
709
710
711





























712
713
714
715
716
717
718
...
721
722
723
724
725
726
727




728
729
730
731
732
733
734
...
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
...
815
816
817
818
819
820
821





822
823
824
825
826
827

828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
...
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
      zSpacer = "  ";
    }
    printf("\n");
  }
}

/*
** COM MAND: commands
**
** Usage: %fossil commands
** List all supported commands.
*/
void cmd_cmd_list(void){
  int i, nCmd;
  const char *aCmd[count(aCommand)];
................................................................................
    }
  }
  putchar('\n');
}

/*
** Set the g.zBaseURL value to the full URL for the toplevel of
** the fossil tree.  Set g.zHomeURL to g.zBaseURL without the
** leading "http://" and the host and port.
*/
void set_base_url(void){
  int i;
  const char *zHost = PD("HTTP_HOST","");
  const char *zMode = PD("HTTPS","off");
  const char *zCur = PD("SCRIPT_NAME","/");
................................................................................
void fossil_redirect_home(void){
  cgi_redirectf("%s%s", g.zBaseURL, db_get("index-page", "/index"));
}

/*
** Preconditions:
**
**    * Environment variables are set up according to the CGI standard.
**    * The respository database has been located and opened.



** 
** Process the webpage specified by the PATH_INFO or REQUEST_URI
** environment variable.
*/
static void process_one_web_page(void){
  const char *zPathInfo;
  char *zPath = NULL;
  int idx;
  int i;



































  /* Find the page that the user has requested, construct and deliver that
  ** page.
  */
  set_base_url();
  zPathInfo = P("PATH_INFO");
  if( zPathInfo==0 || zPathInfo[0]==0 
      || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
    fossil_redirect_home();
  }else{
    zPath = mprintf("%s", zPathInfo);
  }

................................................................................
  }
  if( g.db==0 ){
    cgi_panic("Unable to find or open the project repository");
  }
  cgi_init();
  process_one_web_page();
}






























/*
** undocumented format:
**
**        fossil http REPOSITORY INFILE OUTFILE IPADDR
**
** The argv==6 form is used by the win32 server only.
................................................................................
**
** Usage: %fossil http REPOSITORY
**
** Handle a single HTTP request appearing on stdin.  The resulting webpage
** is delivered on stdout.  This method is used to launch an HTTP request
** handler from inetd, for example.  The argument is the name of the 
** repository.




*/
void cmd_http(void){
  const char *zIpAddr;
  if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){
    cgi_panic("no repository specified");
  }
#if !defined(__MINGW32__)
................................................................................
    g.httpOut = fopen(g.argv[4], "wb");
    zIpAddr = g.argv[5];
  }else{
    g.httpIn = stdin;
    g.httpOut = stdout;
    zIpAddr = 0;
  }
  if( g.argc>=3 ){
    db_open_repository(g.argv[2]);
  }else{
    db_must_be_within_tree();
  }
  cgi_handle_http_request(zIpAddr);
  process_one_web_page();
}

/*
** COMMAND: test-http
** Works like the http command but gives setup permission to all users.
................................................................................
** TCP port 8080, or on any other TCP port defined by the -P or
** --port option.  The optional argument is the name of the repository.
** The repository argument may be omitted if the working directory is
** within an open checkout.
**
** The "ui" command automatically starts a web browser after initializing
** the web server.





*/
void cmd_webserver(void){
  int iPort, mxPort;
  const char *zPort;
  char *zBrowser;
  char *zBrowserCmd = 0;


#ifdef __MINGW32__
  const char *zStopperFile;    /* Name of file used to terminate server */
  zStopperFile = find_option("stopper", 0, 1);
#endif

  g.thTrace = find_option("th-trace", 0, 0)!=0;
  if( g.thTrace ){
    blob_zero(&g.thLog);
  }
  zPort = find_option("port", "P", 1);
  if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
  if( g.argc==2 ){
    db_must_be_within_tree();
  }else{
    db_open_repository(g.argv[2]);
  }
  if( zPort ){
    iPort = mxPort = atoi(zPort);
  }else{
    iPort = db_get_int("http-port", 8080);
    mxPort = iPort+100;
  }
#ifndef __MINGW32__
  /* Unix implementation */
  if( g.argv[1][0]=='u' ){
#if !defined(__DARWIN__) && !defined(__APPLE__)
    zBrowser = db_get("web-browser", 0);
    if( zBrowser==0 ){
      static char *azBrowserProg[] = { "xdg-open", "gnome-open", "firefox" };
      int i;
      zBrowser = "echo";
      for(i=0; i<sizeof(azBrowserProg)/sizeof(azBrowserProg[0]); i++){
................................................................................
  }
  g.httpIn = stdin;
  g.httpOut = stdout;
  if( g.fHttpTrace || g.fSqlTrace ){
    fprintf(stderr, "====== SERVER pid %d =======\n", getpid());
  }
  g.cgiPanic = 1;
  if( g.argc==2 ){
    db_must_be_within_tree();
  }else{
    db_open_repository(g.argv[2]);
  }
  cgi_handle_http_request(0);
  process_one_web_page();
#else
  /* Win32 implementation */
  if( g.argv[1][0]=='u' ){
    zBrowser = db_get("web-browser", "start");
    zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser);
  }
  db_close();
  win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile);
#endif
}







|







 







|







 







|
|
>
>
>









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





<







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>







 







<
|
<
<
<







 







>
>
>
>
>






>












|
<
<
|
<








|







 







<
<
<
|
<




|







451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
...
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
...
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628

629
630
631
632
633
634
635
...
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
...
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
...
826
827
828
829
830
831
832

833



834
835
836
837
838
839
840
...
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911


912

913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
...
943
944
945
946
947
948
949



950

951
952
953
954
955
956
957
958
959
960
961
962
      zSpacer = "  ";
    }
    printf("\n");
  }
}

/*
** COM -off- MAND: commands
**
** Usage: %fossil commands
** List all supported commands.
*/
void cmd_cmd_list(void){
  int i, nCmd;
  const char *aCmd[count(aCommand)];
................................................................................
    }
  }
  putchar('\n');
}

/*
** Set the g.zBaseURL value to the full URL for the toplevel of
** the fossil tree.  Set g.zTop to g.zBaseURL without the
** leading "http://" and the host and port.
*/
void set_base_url(void){
  int i;
  const char *zHost = PD("HTTP_HOST","");
  const char *zMode = PD("HTTPS","off");
  const char *zCur = PD("SCRIPT_NAME","/");
................................................................................
void fossil_redirect_home(void){
  cgi_redirectf("%s%s", g.zBaseURL, db_get("index-page", "/index"));
}

/*
** Preconditions:
**
**  * Environment variables are set up according to the CGI standard.
**
** If the repository is known, it has already been opened.  If unknown,
** then g.zRepositoryName holds the directory that contains the repository
** and the actual repository is taken from the first element of PATH_INFO.
** 
** Process the webpage specified by the PATH_INFO or REQUEST_URI
** environment variable.
*/
static void process_one_web_page(void){
  const char *zPathInfo;
  char *zPath = NULL;
  int idx;
  int i;

  /* If the repository has not been opened already, then find the
  ** repository based on the first element of PATH_INFO and open it.
  */
  zPathInfo = P("PATH_INFO");
  if( !g.repositoryOpen ){
    char *zRepo;
    const char *zOldScript = PD("SCRIPT_NAME", "");
    char *zNewScript;
    int j, k;

    i = 1;
    while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
    zRepo = mprintf("%s%.*s.fossil",g.zRepositoryName,i,zPathInfo);

    /* To avoid mischief, make sure the repository basename contains no
    ** characters other than alphanumerics, "-", and "_".
    */
    for(j=strlen(g.zRepositoryName)+1, k=0; k<i-1; j++, k++){
      if( !isalnum(zRepo[j]) && zRepo[j]!='-' ) zRepo[j] = '_';
    }

    if( file_size(zRepo)<1024 ){
      @ <h1>Not Found</h1>
      cgi_set_status(404, "not found");
      cgi_reply();
      return;
    }
    zNewScript = mprintf("%s%.*s", zOldScript, i, zPathInfo);
    cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]);
    zPathInfo += i;
    cgi_replace_parameter("SCRIPT_NAME", zNewScript);
    db_open_repository(zRepo);
  }

  /* Find the page that the user has requested, construct and deliver that
  ** page.
  */
  set_base_url();

  if( zPathInfo==0 || zPathInfo[0]==0 
      || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
    fossil_redirect_home();
  }else{
    zPath = mprintf("%s", zPathInfo);
  }

................................................................................
  }
  if( g.db==0 ){
    cgi_panic("Unable to find or open the project repository");
  }
  cgi_init();
  process_one_web_page();
}

/*
** If g.argv[2] exists then it is either the name of a repository
** that will be used by a server, or else it is a directory that
** contains multiple repositories that can be served.  If g.argv[2]
** is a directory, the repositories it contains must be named
** "*.fossil".  If g.argv[2] does not exists, then we must be within
** a check-out and the repository to be served is the repository of
** that check-out.
**
** Open the respository to be served if it is known.  If g.argv[2] is
** a directory full of repositories, then set g.zRepositoryName to
** the name of that directory and the specific repository will be
** opened later by process_one_web_page() based on the content of
** the PATH_INFO variable.
**
** If disallowDir is set, then the directory full of repositories method
** is disallowed.
*/
static void find_server_repository(int disallowDir){
  if( g.argc<3 ){
    db_must_be_within_tree();
  }else if( !disallowDir && file_isdir(g.argv[2])==1 ){
    g.zRepositoryName = mprintf("%s", g.argv[2]);
    file_simplify_name(g.zRepositoryName, -1);
  }else{
    db_open_repository(g.argv[2]);
  }
}

/*
** undocumented format:
**
**        fossil http REPOSITORY INFILE OUTFILE IPADDR
**
** The argv==6 form is used by the win32 server only.
................................................................................
**
** Usage: %fossil http REPOSITORY
**
** Handle a single HTTP request appearing on stdin.  The resulting webpage
** is delivered on stdout.  This method is used to launch an HTTP request
** handler from inetd, for example.  The argument is the name of the 
** repository.
**
** If REPOSITORY is a directory that contains one or more respositories
** with names of the form "*.fossil" then the first element of the URL
** pathname selects among the various repositories.
*/
void cmd_http(void){
  const char *zIpAddr;
  if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){
    cgi_panic("no repository specified");
  }
#if !defined(__MINGW32__)
................................................................................
    g.httpOut = fopen(g.argv[4], "wb");
    zIpAddr = g.argv[5];
  }else{
    g.httpIn = stdin;
    g.httpOut = stdout;
    zIpAddr = 0;
  }

  find_server_repository(0);



  cgi_handle_http_request(zIpAddr);
  process_one_web_page();
}

/*
** COMMAND: test-http
** Works like the http command but gives setup permission to all users.
................................................................................
** TCP port 8080, or on any other TCP port defined by the -P or
** --port option.  The optional argument is the name of the repository.
** The repository argument may be omitted if the working directory is
** within an open checkout.
**
** The "ui" command automatically starts a web browser after initializing
** the web server.
**
** In the "server" command, the REPOSITORY can be a directory (aka folder)
** that contains one or more respositories with names ending in ".fossil".
** In that case, the first element of the URL is used to select among the
** various repositories.
*/
void cmd_webserver(void){
  int iPort, mxPort;
  const char *zPort;
  char *zBrowser;
  char *zBrowserCmd = 0;
  int isUiCmd;              /* True if command is "ui", not "server' */

#ifdef __MINGW32__
  const char *zStopperFile;    /* Name of file used to terminate server */
  zStopperFile = find_option("stopper", 0, 1);
#endif

  g.thTrace = find_option("th-trace", 0, 0)!=0;
  if( g.thTrace ){
    blob_zero(&g.thLog);
  }
  zPort = find_option("port", "P", 1);
  if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
  isUiCmd = g.argv[1][0]=='u';


  find_server_repository(isUiCmd);

  if( zPort ){
    iPort = mxPort = atoi(zPort);
  }else{
    iPort = db_get_int("http-port", 8080);
    mxPort = iPort+100;
  }
#ifndef __MINGW32__
  /* Unix implementation */
  if( isUiCmd ){
#if !defined(__DARWIN__) && !defined(__APPLE__)
    zBrowser = db_get("web-browser", 0);
    if( zBrowser==0 ){
      static char *azBrowserProg[] = { "xdg-open", "gnome-open", "firefox" };
      int i;
      zBrowser = "echo";
      for(i=0; i<sizeof(azBrowserProg)/sizeof(azBrowserProg[0]); i++){
................................................................................
  }
  g.httpIn = stdin;
  g.httpOut = stdout;
  if( g.fHttpTrace || g.fSqlTrace ){
    fprintf(stderr, "====== SERVER pid %d =======\n", getpid());
  }
  g.cgiPanic = 1;



  find_server_repository(isUiCmd);

  cgi_handle_http_request(0);
  process_one_web_page();
#else
  /* Win32 implementation */
  if( isUiCmd ){
    zBrowser = db_get("web-browser", "start");
    zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser);
  }
  db_close();
  win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile);
#endif
}