Fossil SCM
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.
Commit
9cd2c42e79a2fee98d0ca4932ad3e094199f90d8
Parent
da48c10d66d7f7b…
2 files changed
+2
-1
+86
-22
+2
-1
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -101,11 +101,11 @@ | ||
| 101 | 101 | int file_isdir(const char *zFilename){ |
| 102 | 102 | int rc; |
| 103 | 103 | |
| 104 | 104 | if( zFilename ){ |
| 105 | 105 | char *zFN = mprintf("%s", zFilename); |
| 106 | - file_simplify_name(zFN, strlen(zFN)); | |
| 106 | + file_simplify_name(zFN, -1); | |
| 107 | 107 | rc = getStat(zFN); |
| 108 | 108 | free(zFN); |
| 109 | 109 | }else{ |
| 110 | 110 | rc = getStat(0); |
| 111 | 111 | } |
| @@ -229,10 +229,11 @@ | ||
| 229 | 229 | ** |
| 230 | 230 | ** Changes are made in-place. Return the new name length. |
| 231 | 231 | */ |
| 232 | 232 | int file_simplify_name(char *z, int n){ |
| 233 | 233 | int i, j; |
| 234 | + if( n<0 ) n = strlen(z); | |
| 234 | 235 | #ifdef __MINGW32__ |
| 235 | 236 | for(i=0; i<n; i++){ |
| 236 | 237 | if( z[i]=='\\' ) z[i] = '/'; |
| 237 | 238 | } |
| 238 | 239 | #endif |
| 239 | 240 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -101,11 +101,11 @@ | |
| 101 | int file_isdir(const char *zFilename){ |
| 102 | int rc; |
| 103 | |
| 104 | if( zFilename ){ |
| 105 | char *zFN = mprintf("%s", zFilename); |
| 106 | file_simplify_name(zFN, strlen(zFN)); |
| 107 | rc = getStat(zFN); |
| 108 | free(zFN); |
| 109 | }else{ |
| 110 | rc = getStat(0); |
| 111 | } |
| @@ -229,10 +229,11 @@ | |
| 229 | ** |
| 230 | ** Changes are made in-place. Return the new name length. |
| 231 | */ |
| 232 | int file_simplify_name(char *z, int n){ |
| 233 | int i, j; |
| 234 | #ifdef __MINGW32__ |
| 235 | for(i=0; i<n; i++){ |
| 236 | if( z[i]=='\\' ) z[i] = '/'; |
| 237 | } |
| 238 | #endif |
| 239 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -101,11 +101,11 @@ | |
| 101 | int file_isdir(const char *zFilename){ |
| 102 | int rc; |
| 103 | |
| 104 | if( zFilename ){ |
| 105 | char *zFN = mprintf("%s", zFilename); |
| 106 | file_simplify_name(zFN, -1); |
| 107 | rc = getStat(zFN); |
| 108 | free(zFN); |
| 109 | }else{ |
| 110 | rc = getStat(0); |
| 111 | } |
| @@ -229,10 +229,11 @@ | |
| 229 | ** |
| 230 | ** Changes are made in-place. Return the new name length. |
| 231 | */ |
| 232 | int file_simplify_name(char *z, int n){ |
| 233 | int i, j; |
| 234 | if( n<0 ) n = strlen(z); |
| 235 | #ifdef __MINGW32__ |
| 236 | for(i=0; i<n; i++){ |
| 237 | if( z[i]=='\\' ) z[i] = '/'; |
| 238 | } |
| 239 | #endif |
| 240 |
+86
-22
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -453,11 +453,11 @@ | ||
| 453 | 453 | printf("\n"); |
| 454 | 454 | } |
| 455 | 455 | } |
| 456 | 456 | |
| 457 | 457 | /* |
| 458 | -** COM MAND: commands | |
| 458 | +** COM -off- MAND: commands | |
| 459 | 459 | ** |
| 460 | 460 | ** Usage: %fossil commands |
| 461 | 461 | ** List all supported commands. |
| 462 | 462 | */ |
| 463 | 463 | void cmd_cmd_list(void){ |
| @@ -541,11 +541,11 @@ | ||
| 541 | 541 | putchar('\n'); |
| 542 | 542 | } |
| 543 | 543 | |
| 544 | 544 | /* |
| 545 | 545 | ** Set the g.zBaseURL value to the full URL for the toplevel of |
| 546 | -** the fossil tree. Set g.zHomeURL to g.zBaseURL without the | |
| 546 | +** the fossil tree. Set g.zTop to g.zBaseURL without the | |
| 547 | 547 | ** leading "http://" and the host and port. |
| 548 | 548 | */ |
| 549 | 549 | void set_base_url(void){ |
| 550 | 550 | int i; |
| 551 | 551 | const char *zHost = PD("HTTP_HOST",""); |
| @@ -571,27 +571,63 @@ | ||
| 571 | 571 | } |
| 572 | 572 | |
| 573 | 573 | /* |
| 574 | 574 | ** Preconditions: |
| 575 | 575 | ** |
| 576 | -** * Environment variables are set up according to the CGI standard. | |
| 577 | -** * The respository database has been located and opened. | |
| 576 | +** * Environment variables are set up according to the CGI standard. | |
| 577 | +** | |
| 578 | +** If the repository is known, it has already been opened. If unknown, | |
| 579 | +** then g.zRepositoryName holds the directory that contains the repository | |
| 580 | +** and the actual repository is taken from the first element of PATH_INFO. | |
| 578 | 581 | ** |
| 579 | 582 | ** Process the webpage specified by the PATH_INFO or REQUEST_URI |
| 580 | 583 | ** environment variable. |
| 581 | 584 | */ |
| 582 | 585 | static void process_one_web_page(void){ |
| 583 | 586 | const char *zPathInfo; |
| 584 | 587 | char *zPath = NULL; |
| 585 | 588 | int idx; |
| 586 | 589 | int i; |
| 590 | + | |
| 591 | + /* If the repository has not been opened already, then find the | |
| 592 | + ** repository based on the first element of PATH_INFO and open it. | |
| 593 | + */ | |
| 594 | + zPathInfo = P("PATH_INFO"); | |
| 595 | + if( !g.repositoryOpen ){ | |
| 596 | + char *zRepo; | |
| 597 | + const char *zOldScript = PD("SCRIPT_NAME", ""); | |
| 598 | + char *zNewScript; | |
| 599 | + int j, k; | |
| 600 | + | |
| 601 | + i = 1; | |
| 602 | + while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; } | |
| 603 | + zRepo = mprintf("%s%.*s.fossil",g.zRepositoryName,i,zPathInfo); | |
| 604 | + | |
| 605 | + /* To avoid mischief, make sure the repository basename contains no | |
| 606 | + ** characters other than alphanumerics, "-", and "_". | |
| 607 | + */ | |
| 608 | + for(j=strlen(g.zRepositoryName)+1, k=0; k<i-1; j++, k++){ | |
| 609 | + if( !isalnum(zRepo[j]) && zRepo[j]!='-' ) zRepo[j] = '_'; | |
| 610 | + } | |
| 611 | + | |
| 612 | + if( file_size(zRepo)<1024 ){ | |
| 613 | + @ <h1>Not Found</h1> | |
| 614 | + cgi_set_status(404, "not found"); | |
| 615 | + cgi_reply(); | |
| 616 | + return; | |
| 617 | + } | |
| 618 | + zNewScript = mprintf("%s%.*s", zOldScript, i, zPathInfo); | |
| 619 | + cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]); | |
| 620 | + zPathInfo += i; | |
| 621 | + cgi_replace_parameter("SCRIPT_NAME", zNewScript); | |
| 622 | + db_open_repository(zRepo); | |
| 623 | + } | |
| 587 | 624 | |
| 588 | 625 | /* Find the page that the user has requested, construct and deliver that |
| 589 | 626 | ** page. |
| 590 | 627 | */ |
| 591 | 628 | set_base_url(); |
| 592 | - zPathInfo = P("PATH_INFO"); | |
| 593 | 629 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 594 | 630 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| 595 | 631 | fossil_redirect_home(); |
| 596 | 632 | }else{ |
| 597 | 633 | zPath = mprintf("%s", zPathInfo); |
| @@ -707,10 +743,39 @@ | ||
| 707 | 743 | cgi_panic("Unable to find or open the project repository"); |
| 708 | 744 | } |
| 709 | 745 | cgi_init(); |
| 710 | 746 | process_one_web_page(); |
| 711 | 747 | } |
| 748 | + | |
| 749 | +/* | |
| 750 | +** If g.argv[2] exists then it is either the name of a repository | |
| 751 | +** that will be used by a server, or else it is a directory that | |
| 752 | +** contains multiple repositories that can be served. If g.argv[2] | |
| 753 | +** is a directory, the repositories it contains must be named | |
| 754 | +** "*.fossil". If g.argv[2] does not exists, then we must be within | |
| 755 | +** a check-out and the repository to be served is the repository of | |
| 756 | +** that check-out. | |
| 757 | +** | |
| 758 | +** Open the respository to be served if it is known. If g.argv[2] is | |
| 759 | +** a directory full of repositories, then set g.zRepositoryName to | |
| 760 | +** the name of that directory and the specific repository will be | |
| 761 | +** opened later by process_one_web_page() based on the content of | |
| 762 | +** the PATH_INFO variable. | |
| 763 | +** | |
| 764 | +** If disallowDir is set, then the directory full of repositories method | |
| 765 | +** is disallowed. | |
| 766 | +*/ | |
| 767 | +static void find_server_repository(int disallowDir){ | |
| 768 | + if( g.argc<3 ){ | |
| 769 | + db_must_be_within_tree(); | |
| 770 | + }else if( !disallowDir && file_isdir(g.argv[2])==1 ){ | |
| 771 | + g.zRepositoryName = mprintf("%s", g.argv[2]); | |
| 772 | + file_simplify_name(g.zRepositoryName, -1); | |
| 773 | + }else{ | |
| 774 | + db_open_repository(g.argv[2]); | |
| 775 | + } | |
| 776 | +} | |
| 712 | 777 | |
| 713 | 778 | /* |
| 714 | 779 | ** undocumented format: |
| 715 | 780 | ** |
| 716 | 781 | ** fossil http REPOSITORY INFILE OUTFILE IPADDR |
| @@ -723,10 +788,14 @@ | ||
| 723 | 788 | ** |
| 724 | 789 | ** Handle a single HTTP request appearing on stdin. The resulting webpage |
| 725 | 790 | ** is delivered on stdout. This method is used to launch an HTTP request |
| 726 | 791 | ** handler from inetd, for example. The argument is the name of the |
| 727 | 792 | ** repository. |
| 793 | +** | |
| 794 | +** If REPOSITORY is a directory that contains one or more respositories | |
| 795 | +** with names of the form "*.fossil" then the first element of the URL | |
| 796 | +** pathname selects among the various repositories. | |
| 728 | 797 | */ |
| 729 | 798 | void cmd_http(void){ |
| 730 | 799 | const char *zIpAddr; |
| 731 | 800 | if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){ |
| 732 | 801 | cgi_panic("no repository specified"); |
| @@ -759,15 +828,11 @@ | ||
| 759 | 828 | }else{ |
| 760 | 829 | g.httpIn = stdin; |
| 761 | 830 | g.httpOut = stdout; |
| 762 | 831 | zIpAddr = 0; |
| 763 | 832 | } |
| 764 | - if( g.argc>=3 ){ | |
| 765 | - db_open_repository(g.argv[2]); | |
| 766 | - }else{ | |
| 767 | - db_must_be_within_tree(); | |
| 768 | - } | |
| 833 | + find_server_repository(0); | |
| 769 | 834 | cgi_handle_http_request(zIpAddr); |
| 770 | 835 | process_one_web_page(); |
| 771 | 836 | } |
| 772 | 837 | |
| 773 | 838 | /* |
| @@ -817,16 +882,22 @@ | ||
| 817 | 882 | ** The repository argument may be omitted if the working directory is |
| 818 | 883 | ** within an open checkout. |
| 819 | 884 | ** |
| 820 | 885 | ** The "ui" command automatically starts a web browser after initializing |
| 821 | 886 | ** the web server. |
| 887 | +** | |
| 888 | +** In the "server" command, the REPOSITORY can be a directory (aka folder) | |
| 889 | +** that contains one or more respositories with names ending in ".fossil". | |
| 890 | +** In that case, the first element of the URL is used to select among the | |
| 891 | +** various repositories. | |
| 822 | 892 | */ |
| 823 | 893 | void cmd_webserver(void){ |
| 824 | 894 | int iPort, mxPort; |
| 825 | 895 | const char *zPort; |
| 826 | 896 | char *zBrowser; |
| 827 | 897 | char *zBrowserCmd = 0; |
| 898 | + int isUiCmd; /* True if command is "ui", not "server' */ | |
| 828 | 899 | |
| 829 | 900 | #ifdef __MINGW32__ |
| 830 | 901 | const char *zStopperFile; /* Name of file used to terminate server */ |
| 831 | 902 | zStopperFile = find_option("stopper", 0, 1); |
| 832 | 903 | #endif |
| @@ -835,24 +906,21 @@ | ||
| 835 | 906 | if( g.thTrace ){ |
| 836 | 907 | blob_zero(&g.thLog); |
| 837 | 908 | } |
| 838 | 909 | zPort = find_option("port", "P", 1); |
| 839 | 910 | if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); |
| 840 | - if( g.argc==2 ){ | |
| 841 | - db_must_be_within_tree(); | |
| 842 | - }else{ | |
| 843 | - db_open_repository(g.argv[2]); | |
| 844 | - } | |
| 911 | + isUiCmd = g.argv[1][0]=='u'; | |
| 912 | + find_server_repository(isUiCmd); | |
| 845 | 913 | if( zPort ){ |
| 846 | 914 | iPort = mxPort = atoi(zPort); |
| 847 | 915 | }else{ |
| 848 | 916 | iPort = db_get_int("http-port", 8080); |
| 849 | 917 | mxPort = iPort+100; |
| 850 | 918 | } |
| 851 | 919 | #ifndef __MINGW32__ |
| 852 | 920 | /* Unix implementation */ |
| 853 | - if( g.argv[1][0]=='u' ){ | |
| 921 | + if( isUiCmd ){ | |
| 854 | 922 | #if !defined(__DARWIN__) && !defined(__APPLE__) |
| 855 | 923 | zBrowser = db_get("web-browser", 0); |
| 856 | 924 | if( zBrowser==0 ){ |
| 857 | 925 | static char *azBrowserProg[] = { "xdg-open", "gnome-open", "firefox" }; |
| 858 | 926 | int i; |
| @@ -877,22 +945,18 @@ | ||
| 877 | 945 | g.httpOut = stdout; |
| 878 | 946 | if( g.fHttpTrace || g.fSqlTrace ){ |
| 879 | 947 | fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
| 880 | 948 | } |
| 881 | 949 | g.cgiPanic = 1; |
| 882 | - if( g.argc==2 ){ | |
| 883 | - db_must_be_within_tree(); | |
| 884 | - }else{ | |
| 885 | - db_open_repository(g.argv[2]); | |
| 886 | - } | |
| 950 | + find_server_repository(isUiCmd); | |
| 887 | 951 | cgi_handle_http_request(0); |
| 888 | 952 | process_one_web_page(); |
| 889 | 953 | #else |
| 890 | 954 | /* Win32 implementation */ |
| 891 | - if( g.argv[1][0]=='u' ){ | |
| 955 | + if( isUiCmd ){ | |
| 892 | 956 | zBrowser = db_get("web-browser", "start"); |
| 893 | 957 | zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser); |
| 894 | 958 | } |
| 895 | 959 | db_close(); |
| 896 | 960 | win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile); |
| 897 | 961 | #endif |
| 898 | 962 | } |
| 899 | 963 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -453,11 +453,11 @@ | |
| 453 | printf("\n"); |
| 454 | } |
| 455 | } |
| 456 | |
| 457 | /* |
| 458 | ** COM MAND: commands |
| 459 | ** |
| 460 | ** Usage: %fossil commands |
| 461 | ** List all supported commands. |
| 462 | */ |
| 463 | void cmd_cmd_list(void){ |
| @@ -541,11 +541,11 @@ | |
| 541 | putchar('\n'); |
| 542 | } |
| 543 | |
| 544 | /* |
| 545 | ** Set the g.zBaseURL value to the full URL for the toplevel of |
| 546 | ** the fossil tree. Set g.zHomeURL to g.zBaseURL without the |
| 547 | ** leading "http://" and the host and port. |
| 548 | */ |
| 549 | void set_base_url(void){ |
| 550 | int i; |
| 551 | const char *zHost = PD("HTTP_HOST",""); |
| @@ -571,27 +571,63 @@ | |
| 571 | } |
| 572 | |
| 573 | /* |
| 574 | ** Preconditions: |
| 575 | ** |
| 576 | ** * Environment variables are set up according to the CGI standard. |
| 577 | ** * The respository database has been located and opened. |
| 578 | ** |
| 579 | ** Process the webpage specified by the PATH_INFO or REQUEST_URI |
| 580 | ** environment variable. |
| 581 | */ |
| 582 | static void process_one_web_page(void){ |
| 583 | const char *zPathInfo; |
| 584 | char *zPath = NULL; |
| 585 | int idx; |
| 586 | int i; |
| 587 | |
| 588 | /* Find the page that the user has requested, construct and deliver that |
| 589 | ** page. |
| 590 | */ |
| 591 | set_base_url(); |
| 592 | zPathInfo = P("PATH_INFO"); |
| 593 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 594 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| 595 | fossil_redirect_home(); |
| 596 | }else{ |
| 597 | zPath = mprintf("%s", zPathInfo); |
| @@ -707,10 +743,39 @@ | |
| 707 | cgi_panic("Unable to find or open the project repository"); |
| 708 | } |
| 709 | cgi_init(); |
| 710 | process_one_web_page(); |
| 711 | } |
| 712 | |
| 713 | /* |
| 714 | ** undocumented format: |
| 715 | ** |
| 716 | ** fossil http REPOSITORY INFILE OUTFILE IPADDR |
| @@ -723,10 +788,14 @@ | |
| 723 | ** |
| 724 | ** Handle a single HTTP request appearing on stdin. The resulting webpage |
| 725 | ** is delivered on stdout. This method is used to launch an HTTP request |
| 726 | ** handler from inetd, for example. The argument is the name of the |
| 727 | ** repository. |
| 728 | */ |
| 729 | void cmd_http(void){ |
| 730 | const char *zIpAddr; |
| 731 | if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){ |
| 732 | cgi_panic("no repository specified"); |
| @@ -759,15 +828,11 @@ | |
| 759 | }else{ |
| 760 | g.httpIn = stdin; |
| 761 | g.httpOut = stdout; |
| 762 | zIpAddr = 0; |
| 763 | } |
| 764 | if( g.argc>=3 ){ |
| 765 | db_open_repository(g.argv[2]); |
| 766 | }else{ |
| 767 | db_must_be_within_tree(); |
| 768 | } |
| 769 | cgi_handle_http_request(zIpAddr); |
| 770 | process_one_web_page(); |
| 771 | } |
| 772 | |
| 773 | /* |
| @@ -817,16 +882,22 @@ | |
| 817 | ** The repository argument may be omitted if the working directory is |
| 818 | ** within an open checkout. |
| 819 | ** |
| 820 | ** The "ui" command automatically starts a web browser after initializing |
| 821 | ** the web server. |
| 822 | */ |
| 823 | void cmd_webserver(void){ |
| 824 | int iPort, mxPort; |
| 825 | const char *zPort; |
| 826 | char *zBrowser; |
| 827 | char *zBrowserCmd = 0; |
| 828 | |
| 829 | #ifdef __MINGW32__ |
| 830 | const char *zStopperFile; /* Name of file used to terminate server */ |
| 831 | zStopperFile = find_option("stopper", 0, 1); |
| 832 | #endif |
| @@ -835,24 +906,21 @@ | |
| 835 | if( g.thTrace ){ |
| 836 | blob_zero(&g.thLog); |
| 837 | } |
| 838 | zPort = find_option("port", "P", 1); |
| 839 | if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); |
| 840 | if( g.argc==2 ){ |
| 841 | db_must_be_within_tree(); |
| 842 | }else{ |
| 843 | db_open_repository(g.argv[2]); |
| 844 | } |
| 845 | if( zPort ){ |
| 846 | iPort = mxPort = atoi(zPort); |
| 847 | }else{ |
| 848 | iPort = db_get_int("http-port", 8080); |
| 849 | mxPort = iPort+100; |
| 850 | } |
| 851 | #ifndef __MINGW32__ |
| 852 | /* Unix implementation */ |
| 853 | if( g.argv[1][0]=='u' ){ |
| 854 | #if !defined(__DARWIN__) && !defined(__APPLE__) |
| 855 | zBrowser = db_get("web-browser", 0); |
| 856 | if( zBrowser==0 ){ |
| 857 | static char *azBrowserProg[] = { "xdg-open", "gnome-open", "firefox" }; |
| 858 | int i; |
| @@ -877,22 +945,18 @@ | |
| 877 | g.httpOut = stdout; |
| 878 | if( g.fHttpTrace || g.fSqlTrace ){ |
| 879 | fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
| 880 | } |
| 881 | g.cgiPanic = 1; |
| 882 | if( g.argc==2 ){ |
| 883 | db_must_be_within_tree(); |
| 884 | }else{ |
| 885 | db_open_repository(g.argv[2]); |
| 886 | } |
| 887 | cgi_handle_http_request(0); |
| 888 | process_one_web_page(); |
| 889 | #else |
| 890 | /* Win32 implementation */ |
| 891 | if( g.argv[1][0]=='u' ){ |
| 892 | zBrowser = db_get("web-browser", "start"); |
| 893 | zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser); |
| 894 | } |
| 895 | db_close(); |
| 896 | win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile); |
| 897 | #endif |
| 898 | } |
| 899 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -453,11 +453,11 @@ | |
| 453 | printf("\n"); |
| 454 | } |
| 455 | } |
| 456 | |
| 457 | /* |
| 458 | ** COM -off- MAND: commands |
| 459 | ** |
| 460 | ** Usage: %fossil commands |
| 461 | ** List all supported commands. |
| 462 | */ |
| 463 | void cmd_cmd_list(void){ |
| @@ -541,11 +541,11 @@ | |
| 541 | putchar('\n'); |
| 542 | } |
| 543 | |
| 544 | /* |
| 545 | ** Set the g.zBaseURL value to the full URL for the toplevel of |
| 546 | ** the fossil tree. Set g.zTop to g.zBaseURL without the |
| 547 | ** leading "http://" and the host and port. |
| 548 | */ |
| 549 | void set_base_url(void){ |
| 550 | int i; |
| 551 | const char *zHost = PD("HTTP_HOST",""); |
| @@ -571,27 +571,63 @@ | |
| 571 | } |
| 572 | |
| 573 | /* |
| 574 | ** Preconditions: |
| 575 | ** |
| 576 | ** * Environment variables are set up according to the CGI standard. |
| 577 | ** |
| 578 | ** If the repository is known, it has already been opened. If unknown, |
| 579 | ** then g.zRepositoryName holds the directory that contains the repository |
| 580 | ** and the actual repository is taken from the first element of PATH_INFO. |
| 581 | ** |
| 582 | ** Process the webpage specified by the PATH_INFO or REQUEST_URI |
| 583 | ** environment variable. |
| 584 | */ |
| 585 | static void process_one_web_page(void){ |
| 586 | const char *zPathInfo; |
| 587 | char *zPath = NULL; |
| 588 | int idx; |
| 589 | int i; |
| 590 | |
| 591 | /* If the repository has not been opened already, then find the |
| 592 | ** repository based on the first element of PATH_INFO and open it. |
| 593 | */ |
| 594 | zPathInfo = P("PATH_INFO"); |
| 595 | if( !g.repositoryOpen ){ |
| 596 | char *zRepo; |
| 597 | const char *zOldScript = PD("SCRIPT_NAME", ""); |
| 598 | char *zNewScript; |
| 599 | int j, k; |
| 600 | |
| 601 | i = 1; |
| 602 | while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; } |
| 603 | zRepo = mprintf("%s%.*s.fossil",g.zRepositoryName,i,zPathInfo); |
| 604 | |
| 605 | /* To avoid mischief, make sure the repository basename contains no |
| 606 | ** characters other than alphanumerics, "-", and "_". |
| 607 | */ |
| 608 | for(j=strlen(g.zRepositoryName)+1, k=0; k<i-1; j++, k++){ |
| 609 | if( !isalnum(zRepo[j]) && zRepo[j]!='-' ) zRepo[j] = '_'; |
| 610 | } |
| 611 | |
| 612 | if( file_size(zRepo)<1024 ){ |
| 613 | @ <h1>Not Found</h1> |
| 614 | cgi_set_status(404, "not found"); |
| 615 | cgi_reply(); |
| 616 | return; |
| 617 | } |
| 618 | zNewScript = mprintf("%s%.*s", zOldScript, i, zPathInfo); |
| 619 | cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]); |
| 620 | zPathInfo += i; |
| 621 | cgi_replace_parameter("SCRIPT_NAME", zNewScript); |
| 622 | db_open_repository(zRepo); |
| 623 | } |
| 624 | |
| 625 | /* Find the page that the user has requested, construct and deliver that |
| 626 | ** page. |
| 627 | */ |
| 628 | set_base_url(); |
| 629 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 630 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| 631 | fossil_redirect_home(); |
| 632 | }else{ |
| 633 | zPath = mprintf("%s", zPathInfo); |
| @@ -707,10 +743,39 @@ | |
| 743 | cgi_panic("Unable to find or open the project repository"); |
| 744 | } |
| 745 | cgi_init(); |
| 746 | process_one_web_page(); |
| 747 | } |
| 748 | |
| 749 | /* |
| 750 | ** If g.argv[2] exists then it is either the name of a repository |
| 751 | ** that will be used by a server, or else it is a directory that |
| 752 | ** contains multiple repositories that can be served. If g.argv[2] |
| 753 | ** is a directory, the repositories it contains must be named |
| 754 | ** "*.fossil". If g.argv[2] does not exists, then we must be within |
| 755 | ** a check-out and the repository to be served is the repository of |
| 756 | ** that check-out. |
| 757 | ** |
| 758 | ** Open the respository to be served if it is known. If g.argv[2] is |
| 759 | ** a directory full of repositories, then set g.zRepositoryName to |
| 760 | ** the name of that directory and the specific repository will be |
| 761 | ** opened later by process_one_web_page() based on the content of |
| 762 | ** the PATH_INFO variable. |
| 763 | ** |
| 764 | ** If disallowDir is set, then the directory full of repositories method |
| 765 | ** is disallowed. |
| 766 | */ |
| 767 | static void find_server_repository(int disallowDir){ |
| 768 | if( g.argc<3 ){ |
| 769 | db_must_be_within_tree(); |
| 770 | }else if( !disallowDir && file_isdir(g.argv[2])==1 ){ |
| 771 | g.zRepositoryName = mprintf("%s", g.argv[2]); |
| 772 | file_simplify_name(g.zRepositoryName, -1); |
| 773 | }else{ |
| 774 | db_open_repository(g.argv[2]); |
| 775 | } |
| 776 | } |
| 777 | |
| 778 | /* |
| 779 | ** undocumented format: |
| 780 | ** |
| 781 | ** fossil http REPOSITORY INFILE OUTFILE IPADDR |
| @@ -723,10 +788,14 @@ | |
| 788 | ** |
| 789 | ** Handle a single HTTP request appearing on stdin. The resulting webpage |
| 790 | ** is delivered on stdout. This method is used to launch an HTTP request |
| 791 | ** handler from inetd, for example. The argument is the name of the |
| 792 | ** repository. |
| 793 | ** |
| 794 | ** If REPOSITORY is a directory that contains one or more respositories |
| 795 | ** with names of the form "*.fossil" then the first element of the URL |
| 796 | ** pathname selects among the various repositories. |
| 797 | */ |
| 798 | void cmd_http(void){ |
| 799 | const char *zIpAddr; |
| 800 | if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){ |
| 801 | cgi_panic("no repository specified"); |
| @@ -759,15 +828,11 @@ | |
| 828 | }else{ |
| 829 | g.httpIn = stdin; |
| 830 | g.httpOut = stdout; |
| 831 | zIpAddr = 0; |
| 832 | } |
| 833 | find_server_repository(0); |
| 834 | cgi_handle_http_request(zIpAddr); |
| 835 | process_one_web_page(); |
| 836 | } |
| 837 | |
| 838 | /* |
| @@ -817,16 +882,22 @@ | |
| 882 | ** The repository argument may be omitted if the working directory is |
| 883 | ** within an open checkout. |
| 884 | ** |
| 885 | ** The "ui" command automatically starts a web browser after initializing |
| 886 | ** the web server. |
| 887 | ** |
| 888 | ** In the "server" command, the REPOSITORY can be a directory (aka folder) |
| 889 | ** that contains one or more respositories with names ending in ".fossil". |
| 890 | ** In that case, the first element of the URL is used to select among the |
| 891 | ** various repositories. |
| 892 | */ |
| 893 | void cmd_webserver(void){ |
| 894 | int iPort, mxPort; |
| 895 | const char *zPort; |
| 896 | char *zBrowser; |
| 897 | char *zBrowserCmd = 0; |
| 898 | int isUiCmd; /* True if command is "ui", not "server' */ |
| 899 | |
| 900 | #ifdef __MINGW32__ |
| 901 | const char *zStopperFile; /* Name of file used to terminate server */ |
| 902 | zStopperFile = find_option("stopper", 0, 1); |
| 903 | #endif |
| @@ -835,24 +906,21 @@ | |
| 906 | if( g.thTrace ){ |
| 907 | blob_zero(&g.thLog); |
| 908 | } |
| 909 | zPort = find_option("port", "P", 1); |
| 910 | if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); |
| 911 | isUiCmd = g.argv[1][0]=='u'; |
| 912 | find_server_repository(isUiCmd); |
| 913 | if( zPort ){ |
| 914 | iPort = mxPort = atoi(zPort); |
| 915 | }else{ |
| 916 | iPort = db_get_int("http-port", 8080); |
| 917 | mxPort = iPort+100; |
| 918 | } |
| 919 | #ifndef __MINGW32__ |
| 920 | /* Unix implementation */ |
| 921 | if( isUiCmd ){ |
| 922 | #if !defined(__DARWIN__) && !defined(__APPLE__) |
| 923 | zBrowser = db_get("web-browser", 0); |
| 924 | if( zBrowser==0 ){ |
| 925 | static char *azBrowserProg[] = { "xdg-open", "gnome-open", "firefox" }; |
| 926 | int i; |
| @@ -877,22 +945,18 @@ | |
| 945 | g.httpOut = stdout; |
| 946 | if( g.fHttpTrace || g.fSqlTrace ){ |
| 947 | fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
| 948 | } |
| 949 | g.cgiPanic = 1; |
| 950 | find_server_repository(isUiCmd); |
| 951 | cgi_handle_http_request(0); |
| 952 | process_one_web_page(); |
| 953 | #else |
| 954 | /* Win32 implementation */ |
| 955 | if( isUiCmd ){ |
| 956 | zBrowser = db_get("web-browser", "start"); |
| 957 | zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser); |
| 958 | } |
| 959 | db_close(); |
| 960 | win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile); |
| 961 | #endif |
| 962 | } |
| 963 |