Fossil SCM
Merge the experimental ssh:// changes into the trunk.
Commit
0a51263a23b658ab98a7ddb064e81422b7ab02bc
Parent
1a9fe88b66e05b1…
7 files changed
-172
+1
-1
+22
-5
+97
-46
+12
-25
+95
-6
+43
-16
-172
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -851,145 +851,10 @@ | ||
| 851 | 851 | cgi_printf("%s = %s <br />\n", |
| 852 | 852 | htmlize(aParamQP[i].zName, -1), htmlize(aParamQP[i].zValue, -1)); |
| 853 | 853 | } |
| 854 | 854 | } |
| 855 | 855 | |
| 856 | -/* | |
| 857 | -** Write HTML text for an option menu to standard output. zParam | |
| 858 | -** is the query parameter that the option menu sets. zDflt is the | |
| 859 | -** initial value of the option menu. Addition arguments are name/value | |
| 860 | -** pairs that define values on the menu. The list is terminated with | |
| 861 | -** a single NULL argument. | |
| 862 | -*/ | |
| 863 | -void cgi_optionmenu(int in, const char *zP, const char *zD, ...){ | |
| 864 | - va_list ap; | |
| 865 | - char *zName, *zVal; | |
| 866 | - int dfltSeen = 0; | |
| 867 | - cgi_printf("%*s<select size=1 name=\"%s\">\n", in, "", zP); | |
| 868 | - va_start(ap, zD); | |
| 869 | - while( (zName = va_arg(ap, char*))!=0 && (zVal = va_arg(ap, char*))!=0 ){ | |
| 870 | - if( strcmp(zVal,zD)==0 ){ dfltSeen = 1; break; } | |
| 871 | - } | |
| 872 | - va_end(ap); | |
| 873 | - if( !dfltSeen ){ | |
| 874 | - if( zD[0] ){ | |
| 875 | - cgi_printf("%*s<option value=\"%h\" selected>%h</option>\n", | |
| 876 | - in+2, "", zD, zD); | |
| 877 | - }else{ | |
| 878 | - cgi_printf("%*s<option value=\"\" selected> </option>\n", in+2, ""); | |
| 879 | - } | |
| 880 | - } | |
| 881 | - va_start(ap, zD); | |
| 882 | - while( (zName = va_arg(ap, char*))!=0 && (zVal = va_arg(ap, char*))!=0 ){ | |
| 883 | - if( zName[0] ){ | |
| 884 | - cgi_printf("%*s<option value=\"%h\"%s>%h</option>\n", | |
| 885 | - in+2, "", | |
| 886 | - zVal, | |
| 887 | - strcmp(zVal, zD) ? "" : " selected", | |
| 888 | - zName | |
| 889 | - ); | |
| 890 | - }else{ | |
| 891 | - cgi_printf("%*s<option value=\"\"%s> </option>\n", | |
| 892 | - in+2, "", | |
| 893 | - strcmp(zVal, zD) ? "" : " selected" | |
| 894 | - ); | |
| 895 | - } | |
| 896 | - } | |
| 897 | - va_end(ap); | |
| 898 | - cgi_printf("%*s</select>\n", in, ""); | |
| 899 | -} | |
| 900 | - | |
| 901 | -/* | |
| 902 | -** This routine works a lot like cgi_optionmenu() except that the list of | |
| 903 | -** values is contained in an array. Also, the values are just values, not | |
| 904 | -** name/value pairs as in cgi_optionmenu. | |
| 905 | -*/ | |
| 906 | -void cgi_v_optionmenu( | |
| 907 | - int in, /* Indent by this amount */ | |
| 908 | - const char *zP, /* The query parameter name */ | |
| 909 | - const char *zD, /* Default value */ | |
| 910 | - const char **az /* NULL-terminated list of allowed values */ | |
| 911 | -){ | |
| 912 | - const char *zVal; | |
| 913 | - int i; | |
| 914 | - cgi_printf("%*s<select size=1 name=\"%s\">\n", in, "", zP); | |
| 915 | - for(i=0; az[i]; i++){ | |
| 916 | - if( strcmp(az[i],zD)==0 ) break; | |
| 917 | - } | |
| 918 | - if( az[i]==0 ){ | |
| 919 | - if( zD[0]==0 ){ | |
| 920 | - cgi_printf("%*s<option value=\"\" selected> </option>\n", | |
| 921 | - in+2, ""); | |
| 922 | - }else{ | |
| 923 | - cgi_printf("%*s<option value=\"%h\" selected>%h</option>\n", | |
| 924 | - in+2, "", zD, zD); | |
| 925 | - } | |
| 926 | - } | |
| 927 | - while( (zVal = *(az++))!=0 ){ | |
| 928 | - if( zVal[0] ){ | |
| 929 | - cgi_printf("%*s<option value=\"%h\"%s>%h</option>\n", | |
| 930 | - in+2, "", | |
| 931 | - zVal, | |
| 932 | - strcmp(zVal, zD) ? "" : " selected", | |
| 933 | - zVal | |
| 934 | - ); | |
| 935 | - }else{ | |
| 936 | - cgi_printf("%*s<option value=\"\"%s> </option>\n", | |
| 937 | - in+2, "", | |
| 938 | - strcmp(zVal, zD) ? "" : " selected" | |
| 939 | - ); | |
| 940 | - } | |
| 941 | - } | |
| 942 | - cgi_printf("%*s</select>\n", in, ""); | |
| 943 | -} | |
| 944 | - | |
| 945 | -/* | |
| 946 | -** This routine works a lot like cgi_v_optionmenu() except that the list | |
| 947 | -** is a list of pairs. The first element of each pair is the value used | |
| 948 | -** internally and the second element is the value displayed to the user. | |
| 949 | -*/ | |
| 950 | -void cgi_v_optionmenu2( | |
| 951 | - int in, /* Indent by this amount */ | |
| 952 | - const char *zP, /* The query parameter name */ | |
| 953 | - const char *zD, /* Default value */ | |
| 954 | - const char **az /* NULL-terminated list of allowed values */ | |
| 955 | -){ | |
| 956 | - const char *zVal; | |
| 957 | - int i; | |
| 958 | - cgi_printf("%*s<select size=1 name=\"%s\">\n", in, "", zP); | |
| 959 | - for(i=0; az[i]; i+=2){ | |
| 960 | - if( strcmp(az[i],zD)==0 ) break; | |
| 961 | - } | |
| 962 | - if( az[i]==0 ){ | |
| 963 | - if( zD[0]==0 ){ | |
| 964 | - cgi_printf("%*s<option value=\"\" selected> </option>\n", | |
| 965 | - in+2, ""); | |
| 966 | - }else{ | |
| 967 | - cgi_printf("%*s<option value=\"%h\" selected>%h</option>\n", | |
| 968 | - in+2, "", zD, zD); | |
| 969 | - } | |
| 970 | - } | |
| 971 | - while( (zVal = *(az++))!=0 ){ | |
| 972 | - const char *zName = *(az++); | |
| 973 | - if( zName[0] ){ | |
| 974 | - cgi_printf("%*s<option value=\"%h\"%s>%h</option>\n", | |
| 975 | - in+2, "", | |
| 976 | - zVal, | |
| 977 | - strcmp(zVal, zD) ? "" : " selected", | |
| 978 | - zName | |
| 979 | - ); | |
| 980 | - }else{ | |
| 981 | - cgi_printf("%*s<option value=\"%h\"%s> </option>\n", | |
| 982 | - in+2, "", | |
| 983 | - zVal, | |
| 984 | - strcmp(zVal, zD) ? "" : " selected" | |
| 985 | - ); | |
| 986 | - } | |
| 987 | - } | |
| 988 | - cgi_printf("%*s</select>\n", in, ""); | |
| 989 | -} | |
| 990 | - | |
| 991 | 856 | /* |
| 992 | 857 | ** This routine works like "printf" except that it has the |
| 993 | 858 | ** extra formatting capabilities such as %h and %t. |
| 994 | 859 | */ |
| 995 | 860 | void cgi_printf(const char *zFormat, ...){ |
| @@ -1014,22 +879,10 @@ | ||
| 1014 | 879 | static void malformed_request(void){ |
| 1015 | 880 | cgi_set_status(501, "Not Implemented"); |
| 1016 | 881 | cgi_printf( |
| 1017 | 882 | "<html><body>Unrecognized HTTP Request</body></html>\n" |
| 1018 | 883 | ); |
| 1019 | - cgi_reply(); | |
| 1020 | - fossil_exit(0); | |
| 1021 | -} | |
| 1022 | - | |
| 1023 | -/* | |
| 1024 | -** Send a reply indicating that the HTTP request is forbidden | |
| 1025 | -*/ | |
| 1026 | -static void forbidden_request(void){ | |
| 1027 | - cgi_set_status(403, "Forbidden"); | |
| 1028 | - cgi_printf( | |
| 1029 | - "<html><body>Access Denied</body></html>\n" | |
| 1030 | - ); | |
| 1031 | 884 | cgi_reply(); |
| 1032 | 885 | fossil_exit(0); |
| 1033 | 886 | } |
| 1034 | 887 | |
| 1035 | 888 | /* |
| @@ -1085,11 +938,10 @@ | ||
| 1085 | 938 | void cgi_handle_http_request(const char *zIpAddr){ |
| 1086 | 939 | char *z, *zToken; |
| 1087 | 940 | int i; |
| 1088 | 941 | struct sockaddr_in remoteName; |
| 1089 | 942 | size_t size = sizeof(struct sockaddr_in); |
| 1090 | - int accessTokenSeen = 0; | |
| 1091 | 943 | char zLine[2000]; /* A single line of input. */ |
| 1092 | 944 | |
| 1093 | 945 | g.fullHttpReply = 1; |
| 1094 | 946 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1095 | 947 | malformed_request(); |
| @@ -1149,16 +1001,10 @@ | ||
| 1149 | 1001 | cgi_setenv("HTTP_HOST", zVal); |
| 1150 | 1002 | }else if( strcmp(zFieldName,"if-none-match:")==0 ){ |
| 1151 | 1003 | cgi_setenv("HTTP_IF_NONE_MATCH", zVal); |
| 1152 | 1004 | }else if( strcmp(zFieldName,"if-modified-since:")==0 ){ |
| 1153 | 1005 | cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal); |
| 1154 | - }else if( strcmp(zFieldName,"x-fossil-access-token:")==0 ){ | |
| 1155 | - if( g.zAccessToken ){ | |
| 1156 | - if( strcmp(zVal,g.zAccessToken)==0 ){ | |
| 1157 | - accessTokenSeen = 1; | |
| 1158 | - } | |
| 1159 | - } | |
| 1160 | 1006 | } |
| 1161 | 1007 | #if 0 |
| 1162 | 1008 | else if( strcmp(zFieldName,"referer:")==0 ){ |
| 1163 | 1009 | cgi_setenv("HTTP_REFERER", zVal); |
| 1164 | 1010 | }else if( strcmp(zFieldName,"user-agent:")==0 ){ |
| @@ -1165,23 +1011,18 @@ | ||
| 1165 | 1011 | cgi_setenv("HTTP_USER_AGENT", zVal); |
| 1166 | 1012 | } |
| 1167 | 1013 | #endif |
| 1168 | 1014 | } |
| 1169 | 1015 | |
| 1170 | - if( g.zAccessToken && !accessTokenSeen ){ | |
| 1171 | - forbidden_request(); | |
| 1172 | - } | |
| 1173 | - | |
| 1174 | 1016 | cgi_init(); |
| 1175 | 1017 | } |
| 1176 | 1018 | |
| 1177 | 1019 | #if INTERFACE |
| 1178 | 1020 | /* |
| 1179 | 1021 | ** Bitmap values for the flags parameter to cgi_http_server(). |
| 1180 | 1022 | */ |
| 1181 | 1023 | #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */ |
| 1182 | -#define HTTP_SERVER_STDIN 0x0002 /* Monitor stdin for "quit" */ | |
| 1183 | 1024 | |
| 1184 | 1025 | #endif /* INTERFACE */ |
| 1185 | 1026 | |
| 1186 | 1027 | /* |
| 1187 | 1028 | ** Maximum number of child processes that we can have running |
| @@ -1265,24 +1106,11 @@ | ||
| 1265 | 1106 | } |
| 1266 | 1107 | delay.tv_sec = 60; |
| 1267 | 1108 | delay.tv_usec = 0; |
| 1268 | 1109 | FD_ZERO(&readfds); |
| 1269 | 1110 | FD_SET( listener, &readfds); |
| 1270 | - if( flags & HTTP_SERVER_STDIN ){ | |
| 1271 | - FD_SET( 0, &readfds); | |
| 1272 | - } | |
| 1273 | 1111 | select( listener+1, &readfds, 0, 0, &delay); |
| 1274 | - if( FD_ISSET(0, &readfds) ){ | |
| 1275 | - int i; | |
| 1276 | - char zIn[200]; | |
| 1277 | - assert( flags & HTTP_SERVER_STDIN ); | |
| 1278 | - zIn[0] = 0; | |
| 1279 | - fgets(zIn, sizeof(zIn), stdin); | |
| 1280 | - for(i=0; zIn[i] && zIn[i]!='\n'; i++){} | |
| 1281 | - zIn[i] = 0; | |
| 1282 | - if( strcmp(zIn, "quit")==0 || feof(stdin) ) fossil_exit(0); | |
| 1283 | - } | |
| 1284 | 1112 | if( FD_ISSET(listener, &readfds) ){ |
| 1285 | 1113 | lenaddr = sizeof(inaddr); |
| 1286 | 1114 | connection = accept(listener, (struct sockaddr*)&inaddr, |
| 1287 | 1115 | (socklen_t*) &lenaddr); |
| 1288 | 1116 | if( connection>=0 ){ |
| 1289 | 1117 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -851,145 +851,10 @@ | |
| 851 | cgi_printf("%s = %s <br />\n", |
| 852 | htmlize(aParamQP[i].zName, -1), htmlize(aParamQP[i].zValue, -1)); |
| 853 | } |
| 854 | } |
| 855 | |
| 856 | /* |
| 857 | ** Write HTML text for an option menu to standard output. zParam |
| 858 | ** is the query parameter that the option menu sets. zDflt is the |
| 859 | ** initial value of the option menu. Addition arguments are name/value |
| 860 | ** pairs that define values on the menu. The list is terminated with |
| 861 | ** a single NULL argument. |
| 862 | */ |
| 863 | void cgi_optionmenu(int in, const char *zP, const char *zD, ...){ |
| 864 | va_list ap; |
| 865 | char *zName, *zVal; |
| 866 | int dfltSeen = 0; |
| 867 | cgi_printf("%*s<select size=1 name=\"%s\">\n", in, "", zP); |
| 868 | va_start(ap, zD); |
| 869 | while( (zName = va_arg(ap, char*))!=0 && (zVal = va_arg(ap, char*))!=0 ){ |
| 870 | if( strcmp(zVal,zD)==0 ){ dfltSeen = 1; break; } |
| 871 | } |
| 872 | va_end(ap); |
| 873 | if( !dfltSeen ){ |
| 874 | if( zD[0] ){ |
| 875 | cgi_printf("%*s<option value=\"%h\" selected>%h</option>\n", |
| 876 | in+2, "", zD, zD); |
| 877 | }else{ |
| 878 | cgi_printf("%*s<option value=\"\" selected> </option>\n", in+2, ""); |
| 879 | } |
| 880 | } |
| 881 | va_start(ap, zD); |
| 882 | while( (zName = va_arg(ap, char*))!=0 && (zVal = va_arg(ap, char*))!=0 ){ |
| 883 | if( zName[0] ){ |
| 884 | cgi_printf("%*s<option value=\"%h\"%s>%h</option>\n", |
| 885 | in+2, "", |
| 886 | zVal, |
| 887 | strcmp(zVal, zD) ? "" : " selected", |
| 888 | zName |
| 889 | ); |
| 890 | }else{ |
| 891 | cgi_printf("%*s<option value=\"\"%s> </option>\n", |
| 892 | in+2, "", |
| 893 | strcmp(zVal, zD) ? "" : " selected" |
| 894 | ); |
| 895 | } |
| 896 | } |
| 897 | va_end(ap); |
| 898 | cgi_printf("%*s</select>\n", in, ""); |
| 899 | } |
| 900 | |
| 901 | /* |
| 902 | ** This routine works a lot like cgi_optionmenu() except that the list of |
| 903 | ** values is contained in an array. Also, the values are just values, not |
| 904 | ** name/value pairs as in cgi_optionmenu. |
| 905 | */ |
| 906 | void cgi_v_optionmenu( |
| 907 | int in, /* Indent by this amount */ |
| 908 | const char *zP, /* The query parameter name */ |
| 909 | const char *zD, /* Default value */ |
| 910 | const char **az /* NULL-terminated list of allowed values */ |
| 911 | ){ |
| 912 | const char *zVal; |
| 913 | int i; |
| 914 | cgi_printf("%*s<select size=1 name=\"%s\">\n", in, "", zP); |
| 915 | for(i=0; az[i]; i++){ |
| 916 | if( strcmp(az[i],zD)==0 ) break; |
| 917 | } |
| 918 | if( az[i]==0 ){ |
| 919 | if( zD[0]==0 ){ |
| 920 | cgi_printf("%*s<option value=\"\" selected> </option>\n", |
| 921 | in+2, ""); |
| 922 | }else{ |
| 923 | cgi_printf("%*s<option value=\"%h\" selected>%h</option>\n", |
| 924 | in+2, "", zD, zD); |
| 925 | } |
| 926 | } |
| 927 | while( (zVal = *(az++))!=0 ){ |
| 928 | if( zVal[0] ){ |
| 929 | cgi_printf("%*s<option value=\"%h\"%s>%h</option>\n", |
| 930 | in+2, "", |
| 931 | zVal, |
| 932 | strcmp(zVal, zD) ? "" : " selected", |
| 933 | zVal |
| 934 | ); |
| 935 | }else{ |
| 936 | cgi_printf("%*s<option value=\"\"%s> </option>\n", |
| 937 | in+2, "", |
| 938 | strcmp(zVal, zD) ? "" : " selected" |
| 939 | ); |
| 940 | } |
| 941 | } |
| 942 | cgi_printf("%*s</select>\n", in, ""); |
| 943 | } |
| 944 | |
| 945 | /* |
| 946 | ** This routine works a lot like cgi_v_optionmenu() except that the list |
| 947 | ** is a list of pairs. The first element of each pair is the value used |
| 948 | ** internally and the second element is the value displayed to the user. |
| 949 | */ |
| 950 | void cgi_v_optionmenu2( |
| 951 | int in, /* Indent by this amount */ |
| 952 | const char *zP, /* The query parameter name */ |
| 953 | const char *zD, /* Default value */ |
| 954 | const char **az /* NULL-terminated list of allowed values */ |
| 955 | ){ |
| 956 | const char *zVal; |
| 957 | int i; |
| 958 | cgi_printf("%*s<select size=1 name=\"%s\">\n", in, "", zP); |
| 959 | for(i=0; az[i]; i+=2){ |
| 960 | if( strcmp(az[i],zD)==0 ) break; |
| 961 | } |
| 962 | if( az[i]==0 ){ |
| 963 | if( zD[0]==0 ){ |
| 964 | cgi_printf("%*s<option value=\"\" selected> </option>\n", |
| 965 | in+2, ""); |
| 966 | }else{ |
| 967 | cgi_printf("%*s<option value=\"%h\" selected>%h</option>\n", |
| 968 | in+2, "", zD, zD); |
| 969 | } |
| 970 | } |
| 971 | while( (zVal = *(az++))!=0 ){ |
| 972 | const char *zName = *(az++); |
| 973 | if( zName[0] ){ |
| 974 | cgi_printf("%*s<option value=\"%h\"%s>%h</option>\n", |
| 975 | in+2, "", |
| 976 | zVal, |
| 977 | strcmp(zVal, zD) ? "" : " selected", |
| 978 | zName |
| 979 | ); |
| 980 | }else{ |
| 981 | cgi_printf("%*s<option value=\"%h\"%s> </option>\n", |
| 982 | in+2, "", |
| 983 | zVal, |
| 984 | strcmp(zVal, zD) ? "" : " selected" |
| 985 | ); |
| 986 | } |
| 987 | } |
| 988 | cgi_printf("%*s</select>\n", in, ""); |
| 989 | } |
| 990 | |
| 991 | /* |
| 992 | ** This routine works like "printf" except that it has the |
| 993 | ** extra formatting capabilities such as %h and %t. |
| 994 | */ |
| 995 | void cgi_printf(const char *zFormat, ...){ |
| @@ -1014,22 +879,10 @@ | |
| 1014 | static void malformed_request(void){ |
| 1015 | cgi_set_status(501, "Not Implemented"); |
| 1016 | cgi_printf( |
| 1017 | "<html><body>Unrecognized HTTP Request</body></html>\n" |
| 1018 | ); |
| 1019 | cgi_reply(); |
| 1020 | fossil_exit(0); |
| 1021 | } |
| 1022 | |
| 1023 | /* |
| 1024 | ** Send a reply indicating that the HTTP request is forbidden |
| 1025 | */ |
| 1026 | static void forbidden_request(void){ |
| 1027 | cgi_set_status(403, "Forbidden"); |
| 1028 | cgi_printf( |
| 1029 | "<html><body>Access Denied</body></html>\n" |
| 1030 | ); |
| 1031 | cgi_reply(); |
| 1032 | fossil_exit(0); |
| 1033 | } |
| 1034 | |
| 1035 | /* |
| @@ -1085,11 +938,10 @@ | |
| 1085 | void cgi_handle_http_request(const char *zIpAddr){ |
| 1086 | char *z, *zToken; |
| 1087 | int i; |
| 1088 | struct sockaddr_in remoteName; |
| 1089 | size_t size = sizeof(struct sockaddr_in); |
| 1090 | int accessTokenSeen = 0; |
| 1091 | char zLine[2000]; /* A single line of input. */ |
| 1092 | |
| 1093 | g.fullHttpReply = 1; |
| 1094 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1095 | malformed_request(); |
| @@ -1149,16 +1001,10 @@ | |
| 1149 | cgi_setenv("HTTP_HOST", zVal); |
| 1150 | }else if( strcmp(zFieldName,"if-none-match:")==0 ){ |
| 1151 | cgi_setenv("HTTP_IF_NONE_MATCH", zVal); |
| 1152 | }else if( strcmp(zFieldName,"if-modified-since:")==0 ){ |
| 1153 | cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal); |
| 1154 | }else if( strcmp(zFieldName,"x-fossil-access-token:")==0 ){ |
| 1155 | if( g.zAccessToken ){ |
| 1156 | if( strcmp(zVal,g.zAccessToken)==0 ){ |
| 1157 | accessTokenSeen = 1; |
| 1158 | } |
| 1159 | } |
| 1160 | } |
| 1161 | #if 0 |
| 1162 | else if( strcmp(zFieldName,"referer:")==0 ){ |
| 1163 | cgi_setenv("HTTP_REFERER", zVal); |
| 1164 | }else if( strcmp(zFieldName,"user-agent:")==0 ){ |
| @@ -1165,23 +1011,18 @@ | |
| 1165 | cgi_setenv("HTTP_USER_AGENT", zVal); |
| 1166 | } |
| 1167 | #endif |
| 1168 | } |
| 1169 | |
| 1170 | if( g.zAccessToken && !accessTokenSeen ){ |
| 1171 | forbidden_request(); |
| 1172 | } |
| 1173 | |
| 1174 | cgi_init(); |
| 1175 | } |
| 1176 | |
| 1177 | #if INTERFACE |
| 1178 | /* |
| 1179 | ** Bitmap values for the flags parameter to cgi_http_server(). |
| 1180 | */ |
| 1181 | #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */ |
| 1182 | #define HTTP_SERVER_STDIN 0x0002 /* Monitor stdin for "quit" */ |
| 1183 | |
| 1184 | #endif /* INTERFACE */ |
| 1185 | |
| 1186 | /* |
| 1187 | ** Maximum number of child processes that we can have running |
| @@ -1265,24 +1106,11 @@ | |
| 1265 | } |
| 1266 | delay.tv_sec = 60; |
| 1267 | delay.tv_usec = 0; |
| 1268 | FD_ZERO(&readfds); |
| 1269 | FD_SET( listener, &readfds); |
| 1270 | if( flags & HTTP_SERVER_STDIN ){ |
| 1271 | FD_SET( 0, &readfds); |
| 1272 | } |
| 1273 | select( listener+1, &readfds, 0, 0, &delay); |
| 1274 | if( FD_ISSET(0, &readfds) ){ |
| 1275 | int i; |
| 1276 | char zIn[200]; |
| 1277 | assert( flags & HTTP_SERVER_STDIN ); |
| 1278 | zIn[0] = 0; |
| 1279 | fgets(zIn, sizeof(zIn), stdin); |
| 1280 | for(i=0; zIn[i] && zIn[i]!='\n'; i++){} |
| 1281 | zIn[i] = 0; |
| 1282 | if( strcmp(zIn, "quit")==0 || feof(stdin) ) fossil_exit(0); |
| 1283 | } |
| 1284 | if( FD_ISSET(listener, &readfds) ){ |
| 1285 | lenaddr = sizeof(inaddr); |
| 1286 | connection = accept(listener, (struct sockaddr*)&inaddr, |
| 1287 | (socklen_t*) &lenaddr); |
| 1288 | if( connection>=0 ){ |
| 1289 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -851,145 +851,10 @@ | |
| 851 | cgi_printf("%s = %s <br />\n", |
| 852 | htmlize(aParamQP[i].zName, -1), htmlize(aParamQP[i].zValue, -1)); |
| 853 | } |
| 854 | } |
| 855 | |
| 856 | /* |
| 857 | ** This routine works like "printf" except that it has the |
| 858 | ** extra formatting capabilities such as %h and %t. |
| 859 | */ |
| 860 | void cgi_printf(const char *zFormat, ...){ |
| @@ -1014,22 +879,10 @@ | |
| 879 | static void malformed_request(void){ |
| 880 | cgi_set_status(501, "Not Implemented"); |
| 881 | cgi_printf( |
| 882 | "<html><body>Unrecognized HTTP Request</body></html>\n" |
| 883 | ); |
| 884 | cgi_reply(); |
| 885 | fossil_exit(0); |
| 886 | } |
| 887 | |
| 888 | /* |
| @@ -1085,11 +938,10 @@ | |
| 938 | void cgi_handle_http_request(const char *zIpAddr){ |
| 939 | char *z, *zToken; |
| 940 | int i; |
| 941 | struct sockaddr_in remoteName; |
| 942 | size_t size = sizeof(struct sockaddr_in); |
| 943 | char zLine[2000]; /* A single line of input. */ |
| 944 | |
| 945 | g.fullHttpReply = 1; |
| 946 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 947 | malformed_request(); |
| @@ -1149,16 +1001,10 @@ | |
| 1001 | cgi_setenv("HTTP_HOST", zVal); |
| 1002 | }else if( strcmp(zFieldName,"if-none-match:")==0 ){ |
| 1003 | cgi_setenv("HTTP_IF_NONE_MATCH", zVal); |
| 1004 | }else if( strcmp(zFieldName,"if-modified-since:")==0 ){ |
| 1005 | cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal); |
| 1006 | } |
| 1007 | #if 0 |
| 1008 | else if( strcmp(zFieldName,"referer:")==0 ){ |
| 1009 | cgi_setenv("HTTP_REFERER", zVal); |
| 1010 | }else if( strcmp(zFieldName,"user-agent:")==0 ){ |
| @@ -1165,23 +1011,18 @@ | |
| 1011 | cgi_setenv("HTTP_USER_AGENT", zVal); |
| 1012 | } |
| 1013 | #endif |
| 1014 | } |
| 1015 | |
| 1016 | cgi_init(); |
| 1017 | } |
| 1018 | |
| 1019 | #if INTERFACE |
| 1020 | /* |
| 1021 | ** Bitmap values for the flags parameter to cgi_http_server(). |
| 1022 | */ |
| 1023 | #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */ |
| 1024 | |
| 1025 | #endif /* INTERFACE */ |
| 1026 | |
| 1027 | /* |
| 1028 | ** Maximum number of child processes that we can have running |
| @@ -1265,24 +1106,11 @@ | |
| 1106 | } |
| 1107 | delay.tv_sec = 60; |
| 1108 | delay.tv_usec = 0; |
| 1109 | FD_ZERO(&readfds); |
| 1110 | FD_SET( listener, &readfds); |
| 1111 | select( listener+1, &readfds, 0, 0, &delay); |
| 1112 | if( FD_ISSET(listener, &readfds) ){ |
| 1113 | lenaddr = sizeof(inaddr); |
| 1114 | connection = accept(listener, (struct sockaddr*)&inaddr, |
| 1115 | (socklen_t*) &lenaddr); |
| 1116 | if( connection>=0 ){ |
| 1117 |
+1
-1
| --- src/diffcmd.c | ||
| +++ src/diffcmd.c | ||
| @@ -22,11 +22,11 @@ | ||
| 22 | 22 | #include <assert.h> |
| 23 | 23 | |
| 24 | 24 | /* |
| 25 | 25 | ** Shell-escape the given string. Append the result to a blob. |
| 26 | 26 | */ |
| 27 | -static void shell_escape(Blob *pBlob, const char *zIn){ | |
| 27 | +void shell_escape(Blob *pBlob, const char *zIn){ | |
| 28 | 28 | int n = blob_size(pBlob); |
| 29 | 29 | int k = strlen(zIn); |
| 30 | 30 | int i, c; |
| 31 | 31 | char *z; |
| 32 | 32 | for(i=0; (c = zIn[i])!=0; i++){ |
| 33 | 33 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -22,11 +22,11 @@ | |
| 22 | #include <assert.h> |
| 23 | |
| 24 | /* |
| 25 | ** Shell-escape the given string. Append the result to a blob. |
| 26 | */ |
| 27 | static void shell_escape(Blob *pBlob, const char *zIn){ |
| 28 | int n = blob_size(pBlob); |
| 29 | int k = strlen(zIn); |
| 30 | int i, c; |
| 31 | char *z; |
| 32 | for(i=0; (c = zIn[i])!=0; i++){ |
| 33 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -22,11 +22,11 @@ | |
| 22 | #include <assert.h> |
| 23 | |
| 24 | /* |
| 25 | ** Shell-escape the given string. Append the result to a blob. |
| 26 | */ |
| 27 | void shell_escape(Blob *pBlob, const char *zIn){ |
| 28 | int n = blob_size(pBlob); |
| 29 | int k = strlen(zIn); |
| 30 | int i, c; |
| 31 | char *z; |
| 32 | for(i=0; (c = zIn[i])!=0; i++){ |
| 33 |
+22
-5
| --- src/http.c | ||
| +++ src/http.c | ||
| @@ -42,10 +42,13 @@ | ||
| 42 | 42 | |
| 43 | 43 | blob_zero(pLogin); |
| 44 | 44 | if( g.urlUser==0 || strcmp(g.urlUser, "anonymous")==0 ){ |
| 45 | 45 | return; /* If no login card for users "nobody" and "anonymous" */ |
| 46 | 46 | } |
| 47 | + if( g.urlIsSsh ){ | |
| 48 | + return; /* If no login card for SSH: */ | |
| 49 | + } | |
| 47 | 50 | blob_zero(&nonce); |
| 48 | 51 | blob_zero(&pw); |
| 49 | 52 | sha1sum_blob(pPayload, &nonce); |
| 50 | 53 | blob_copy(&pw, &nonce); |
| 51 | 54 | zLogin = g.urlUser; |
| @@ -111,15 +114,11 @@ | ||
| 111 | 114 | if( g.fHttpTrace ){ |
| 112 | 115 | blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); |
| 113 | 116 | }else{ |
| 114 | 117 | blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n"); |
| 115 | 118 | } |
| 116 | - blob_appendf(pHdr, "Content-Length: %d\r\n", blob_size(pPayload)); | |
| 117 | - if( g.zAccessToken ){ | |
| 118 | - blob_appendf(pHdr, "X-Fossil-Access-Token: %s\r\n", g.zAccessToken); | |
| 119 | - } | |
| 120 | - blob_appendf(pHdr, "\r\n"); | |
| 119 | + blob_appendf(pHdr, "Content-Length: %d\r\n\r\n", blob_size(pPayload)); | |
| 121 | 120 | } |
| 122 | 121 | |
| 123 | 122 | /* |
| 124 | 123 | ** Sign the content in pSend, compress it, and send it to the server |
| 125 | 124 | ** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply |
| @@ -138,10 +137,11 @@ | ||
| 138 | 137 | int iLength; /* Length of the reply payload */ |
| 139 | 138 | int rc; /* Result code */ |
| 140 | 139 | int iHttpVersion; /* Which version of HTTP protocol server uses */ |
| 141 | 140 | char *zLine; /* A single line of the reply header */ |
| 142 | 141 | int i; /* Loop counter */ |
| 142 | + int isError = 0; /* True if the reply is an error message */ | |
| 143 | 143 | |
| 144 | 144 | if( transport_open() ){ |
| 145 | 145 | fossil_fatal(transport_errmsg()); |
| 146 | 146 | } |
| 147 | 147 | |
| @@ -194,10 +194,11 @@ | ||
| 194 | 194 | ** Read and interpret the server reply |
| 195 | 195 | */ |
| 196 | 196 | closeConnection = 1; |
| 197 | 197 | iLength = -1; |
| 198 | 198 | while( (zLine = transport_receive_line())!=0 && zLine[0]!=0 ){ |
| 199 | + /* printf("[%s]\n", zLine); fflush(stdout); */ | |
| 199 | 200 | if( strncasecmp(zLine, "http/1.", 7)==0 ){ |
| 200 | 201 | if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err; |
| 201 | 202 | if( rc!=200 && rc!=302 ){ |
| 202 | 203 | int ii; |
| 203 | 204 | for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){} |
| @@ -231,10 +232,12 @@ | ||
| 231 | 232 | fossil_print("redirect to %s\n", &zLine[i]); |
| 232 | 233 | url_parse(&zLine[i]); |
| 233 | 234 | transport_close(); |
| 234 | 235 | http_exchange(pSend, pReply, useLogin); |
| 235 | 236 | return; |
| 237 | + }else if( strncasecmp(zLine, "content-type: text/html", 23)==0 ){ | |
| 238 | + isError = 1; | |
| 236 | 239 | } |
| 237 | 240 | } |
| 238 | 241 | if( rc!=200 ){ |
| 239 | 242 | fossil_fatal("\"location:\" missing from 302 redirect reply"); |
| 240 | 243 | goto write_err; |
| @@ -249,10 +252,24 @@ | ||
| 249 | 252 | } |
| 250 | 253 | blob_zero(pReply); |
| 251 | 254 | blob_resize(pReply, iLength); |
| 252 | 255 | iLength = transport_receive(blob_buffer(pReply), iLength); |
| 253 | 256 | blob_resize(pReply, iLength); |
| 257 | + if( isError ){ | |
| 258 | + char *z; | |
| 259 | + int i, j; | |
| 260 | + z = blob_str(pReply); | |
| 261 | + for(i=j=0; z[i]; i++, j++){ | |
| 262 | + if( z[i]=='<' ){ | |
| 263 | + while( z[i] && z[i]!='>' ) i++; | |
| 264 | + if( z[i]==0 ) break; | |
| 265 | + } | |
| 266 | + z[j] = z[i]; | |
| 267 | + } | |
| 268 | + z[j] = 0; | |
| 269 | + fossil_fatal("server sends error: %s", z); | |
| 270 | + } | |
| 254 | 271 | if( g.fHttpTrace ){ |
| 255 | 272 | printf("HTTP RECEIVE:\n%s\n=======================\n", blob_str(pReply)); |
| 256 | 273 | }else{ |
| 257 | 274 | blob_uncompress(pReply, pReply); |
| 258 | 275 | } |
| 259 | 276 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -42,10 +42,13 @@ | |
| 42 | |
| 43 | blob_zero(pLogin); |
| 44 | if( g.urlUser==0 || strcmp(g.urlUser, "anonymous")==0 ){ |
| 45 | return; /* If no login card for users "nobody" and "anonymous" */ |
| 46 | } |
| 47 | blob_zero(&nonce); |
| 48 | blob_zero(&pw); |
| 49 | sha1sum_blob(pPayload, &nonce); |
| 50 | blob_copy(&pw, &nonce); |
| 51 | zLogin = g.urlUser; |
| @@ -111,15 +114,11 @@ | |
| 111 | if( g.fHttpTrace ){ |
| 112 | blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); |
| 113 | }else{ |
| 114 | blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n"); |
| 115 | } |
| 116 | blob_appendf(pHdr, "Content-Length: %d\r\n", blob_size(pPayload)); |
| 117 | if( g.zAccessToken ){ |
| 118 | blob_appendf(pHdr, "X-Fossil-Access-Token: %s\r\n", g.zAccessToken); |
| 119 | } |
| 120 | blob_appendf(pHdr, "\r\n"); |
| 121 | } |
| 122 | |
| 123 | /* |
| 124 | ** Sign the content in pSend, compress it, and send it to the server |
| 125 | ** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply |
| @@ -138,10 +137,11 @@ | |
| 138 | int iLength; /* Length of the reply payload */ |
| 139 | int rc; /* Result code */ |
| 140 | int iHttpVersion; /* Which version of HTTP protocol server uses */ |
| 141 | char *zLine; /* A single line of the reply header */ |
| 142 | int i; /* Loop counter */ |
| 143 | |
| 144 | if( transport_open() ){ |
| 145 | fossil_fatal(transport_errmsg()); |
| 146 | } |
| 147 | |
| @@ -194,10 +194,11 @@ | |
| 194 | ** Read and interpret the server reply |
| 195 | */ |
| 196 | closeConnection = 1; |
| 197 | iLength = -1; |
| 198 | while( (zLine = transport_receive_line())!=0 && zLine[0]!=0 ){ |
| 199 | if( strncasecmp(zLine, "http/1.", 7)==0 ){ |
| 200 | if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err; |
| 201 | if( rc!=200 && rc!=302 ){ |
| 202 | int ii; |
| 203 | for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){} |
| @@ -231,10 +232,12 @@ | |
| 231 | fossil_print("redirect to %s\n", &zLine[i]); |
| 232 | url_parse(&zLine[i]); |
| 233 | transport_close(); |
| 234 | http_exchange(pSend, pReply, useLogin); |
| 235 | return; |
| 236 | } |
| 237 | } |
| 238 | if( rc!=200 ){ |
| 239 | fossil_fatal("\"location:\" missing from 302 redirect reply"); |
| 240 | goto write_err; |
| @@ -249,10 +252,24 @@ | |
| 249 | } |
| 250 | blob_zero(pReply); |
| 251 | blob_resize(pReply, iLength); |
| 252 | iLength = transport_receive(blob_buffer(pReply), iLength); |
| 253 | blob_resize(pReply, iLength); |
| 254 | if( g.fHttpTrace ){ |
| 255 | printf("HTTP RECEIVE:\n%s\n=======================\n", blob_str(pReply)); |
| 256 | }else{ |
| 257 | blob_uncompress(pReply, pReply); |
| 258 | } |
| 259 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -42,10 +42,13 @@ | |
| 42 | |
| 43 | blob_zero(pLogin); |
| 44 | if( g.urlUser==0 || strcmp(g.urlUser, "anonymous")==0 ){ |
| 45 | return; /* If no login card for users "nobody" and "anonymous" */ |
| 46 | } |
| 47 | if( g.urlIsSsh ){ |
| 48 | return; /* If no login card for SSH: */ |
| 49 | } |
| 50 | blob_zero(&nonce); |
| 51 | blob_zero(&pw); |
| 52 | sha1sum_blob(pPayload, &nonce); |
| 53 | blob_copy(&pw, &nonce); |
| 54 | zLogin = g.urlUser; |
| @@ -111,15 +114,11 @@ | |
| 114 | if( g.fHttpTrace ){ |
| 115 | blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); |
| 116 | }else{ |
| 117 | blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n"); |
| 118 | } |
| 119 | blob_appendf(pHdr, "Content-Length: %d\r\n\r\n", blob_size(pPayload)); |
| 120 | } |
| 121 | |
| 122 | /* |
| 123 | ** Sign the content in pSend, compress it, and send it to the server |
| 124 | ** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply |
| @@ -138,10 +137,11 @@ | |
| 137 | int iLength; /* Length of the reply payload */ |
| 138 | int rc; /* Result code */ |
| 139 | int iHttpVersion; /* Which version of HTTP protocol server uses */ |
| 140 | char *zLine; /* A single line of the reply header */ |
| 141 | int i; /* Loop counter */ |
| 142 | int isError = 0; /* True if the reply is an error message */ |
| 143 | |
| 144 | if( transport_open() ){ |
| 145 | fossil_fatal(transport_errmsg()); |
| 146 | } |
| 147 | |
| @@ -194,10 +194,11 @@ | |
| 194 | ** Read and interpret the server reply |
| 195 | */ |
| 196 | closeConnection = 1; |
| 197 | iLength = -1; |
| 198 | while( (zLine = transport_receive_line())!=0 && zLine[0]!=0 ){ |
| 199 | /* printf("[%s]\n", zLine); fflush(stdout); */ |
| 200 | if( strncasecmp(zLine, "http/1.", 7)==0 ){ |
| 201 | if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err; |
| 202 | if( rc!=200 && rc!=302 ){ |
| 203 | int ii; |
| 204 | for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){} |
| @@ -231,10 +232,12 @@ | |
| 232 | fossil_print("redirect to %s\n", &zLine[i]); |
| 233 | url_parse(&zLine[i]); |
| 234 | transport_close(); |
| 235 | http_exchange(pSend, pReply, useLogin); |
| 236 | return; |
| 237 | }else if( strncasecmp(zLine, "content-type: text/html", 23)==0 ){ |
| 238 | isError = 1; |
| 239 | } |
| 240 | } |
| 241 | if( rc!=200 ){ |
| 242 | fossil_fatal("\"location:\" missing from 302 redirect reply"); |
| 243 | goto write_err; |
| @@ -249,10 +252,24 @@ | |
| 252 | } |
| 253 | blob_zero(pReply); |
| 254 | blob_resize(pReply, iLength); |
| 255 | iLength = transport_receive(blob_buffer(pReply), iLength); |
| 256 | blob_resize(pReply, iLength); |
| 257 | if( isError ){ |
| 258 | char *z; |
| 259 | int i, j; |
| 260 | z = blob_str(pReply); |
| 261 | for(i=j=0; z[i]; i++, j++){ |
| 262 | if( z[i]=='<' ){ |
| 263 | while( z[i] && z[i]!='>' ) i++; |
| 264 | if( z[i]==0 ) break; |
| 265 | } |
| 266 | z[j] = z[i]; |
| 267 | } |
| 268 | z[j] = 0; |
| 269 | fossil_fatal("server sends error: %s", z); |
| 270 | } |
| 271 | if( g.fHttpTrace ){ |
| 272 | printf("HTTP RECEIVE:\n%s\n=======================\n", blob_str(pReply)); |
| 273 | }else{ |
| 274 | blob_uncompress(pReply, pReply); |
| 275 | } |
| 276 |
+97
-46
| --- src/http_transport.c | ||
| +++ src/http_transport.c | ||
| @@ -38,10 +38,19 @@ | ||
| 38 | 38 | char *zOutFile; /* Name of outbound file for FILE: */ |
| 39 | 39 | char *zInFile; /* Name of inbound file for FILE: */ |
| 40 | 40 | } transport = { |
| 41 | 41 | 0, 0, 0, 0, 0, 0, 0 |
| 42 | 42 | }; |
| 43 | + | |
| 44 | +/* | |
| 45 | +** Information about the connection to the SSH subprocess when | |
| 46 | +** using the ssh:// sync method. | |
| 47 | +*/ | |
| 48 | +static int sshPid; /* Process id of ssh subprocess */ | |
| 49 | +static int sshIn; /* From ssh subprocess to this process */ | |
| 50 | +static FILE *sshOut; /* From this to ssh subprocess */ | |
| 51 | + | |
| 43 | 52 | |
| 44 | 53 | /* |
| 45 | 54 | ** Return the current transport error message. |
| 46 | 55 | */ |
| 47 | 56 | const char *transport_errmsg(void){ |
| @@ -63,49 +72,53 @@ | ||
| 63 | 72 | if( resetFlag ){ |
| 64 | 73 | transport.nSent = 0; |
| 65 | 74 | transport.nRcvd = 0; |
| 66 | 75 | } |
| 67 | 76 | } |
| 77 | + | |
| 78 | +/* | |
| 79 | +** Read text from sshIn. Zero-terminate and remove trailing | |
| 80 | +** whitespace. | |
| 81 | +*/ | |
| 82 | +static void sshin_read(char *zBuf, int szBuf){ | |
| 83 | + int got; | |
| 84 | + zBuf[0] = 0; | |
| 85 | + got = read(sshIn, zBuf, szBuf-1); | |
| 86 | + while( got>=0 ){ | |
| 87 | + zBuf[got] = 0; | |
| 88 | + if( got==0 || !isspace(zBuf[got-1]) ) break; | |
| 89 | + got--; | |
| 90 | + } | |
| 91 | +} | |
| 68 | 92 | |
| 69 | 93 | /* |
| 70 | 94 | ** Global initialization of the transport layer |
| 71 | 95 | */ |
| 72 | 96 | void transport_global_startup(void){ |
| 73 | 97 | if( g.urlIsSsh ){ |
| 74 | 98 | char *zCmd; |
| 75 | - int i; | |
| 76 | - char zIn[200]; | |
| 99 | + char zIn[20]; | |
| 77 | 100 | #ifdef __MINGW32__ |
| 78 | 101 | fossil_fatal("the ssh:// sync method is currently only supported on unix"); |
| 79 | 102 | #endif |
| 80 | 103 | if( g.urlUser && g.urlUser[0] ){ |
| 81 | - zCmd = mprintf( | |
| 82 | - "ssh -L127.0.0.1:%d:127.0.0.1:%d %s@%s " | |
| 83 | - "\"fossil server -P %d '%s'\"", | |
| 84 | - g.urlPort, g.urlPort, g.urlUser, g.urlSshHost, g.urlPort, g.urlPath | |
| 85 | - ); | |
| 104 | + zCmd = mprintf("ssh -e none %s@%s", g.urlUser, g.urlName); | |
| 86 | 105 | }else{ |
| 87 | - zCmd = mprintf( | |
| 88 | - "ssh -L127.0.0.1:%d:127.0.0.1:%d %s \"fossil sshd -P %d '%s'\"", | |
| 89 | - g.urlPort, g.urlPort, g.urlSshHost, g.urlPort, g.urlPath | |
| 90 | - ); | |
| 91 | - } | |
| 92 | - printf("%s\n", zCmd); | |
| 93 | - popen2(zCmd, &g.sshIn, &g.sshOut, &g.sshPid); | |
| 94 | - if( g.sshPid==0 ){ | |
| 106 | + zCmd = mprintf("ssh -e none %s", g.urlName); | |
| 107 | + } | |
| 108 | + /* printf("%s\n", zCmd); */ | |
| 109 | + popen2(zCmd, &sshIn, &sshOut, &sshPid); | |
| 110 | + if( sshPid==0 ){ | |
| 95 | 111 | fossil_fatal("cannot start ssh tunnel using [%s]", zCmd); |
| 96 | 112 | } |
| 97 | 113 | free(zCmd); |
| 98 | - zIn[0] = 0; | |
| 99 | - fgets(zIn, sizeof(zIn), g.sshIn); | |
| 100 | - for(i=0; zIn[i] && zIn[i]!='\n'; i++){} | |
| 101 | - zIn[i] = 0; | |
| 102 | - if( memcmp(zIn, "Access-Token: ", 14)!=0 ){ | |
| 103 | - pclose2(g.sshIn, g.sshOut, g.sshPid); | |
| 104 | - fossil_fatal("failed to start ssh tunnel"); | |
| 105 | - } | |
| 106 | - g.zAccessToken = mprintf("%s", &zIn[14]); | |
| 114 | + fprintf(sshOut, "echo test\n"); | |
| 115 | + fflush(sshOut); | |
| 116 | + sshin_read(zIn, sizeof(zIn)); | |
| 117 | + if( memcmp(zIn, "test", 4)!=0 ){ | |
| 118 | + fossil_fatal("ssh connection failed"); | |
| 119 | + } | |
| 107 | 120 | } |
| 108 | 121 | } |
| 109 | 122 | |
| 110 | 123 | /* |
| 111 | 124 | ** Open a connection to the server. The server is defined by the following |
| @@ -118,11 +131,21 @@ | ||
| 118 | 131 | ** Return the number of errors. |
| 119 | 132 | */ |
| 120 | 133 | int transport_open(void){ |
| 121 | 134 | int rc = 0; |
| 122 | 135 | if( transport.isOpen==0 ){ |
| 123 | - if( g.urlIsHttps ){ | |
| 136 | + if( g.urlIsSsh ){ | |
| 137 | + Blob cmd; | |
| 138 | + blob_zero(&cmd); | |
| 139 | + shell_escape(&cmd, g.urlFossil); | |
| 140 | + blob_append(&cmd, " test-http ", -1); | |
| 141 | + shell_escape(&cmd, g.urlPath); | |
| 142 | + /* fprintf(stdout, "%s\n", blob_str(&cmd)); */ | |
| 143 | + fprintf(sshOut, "%s\n", blob_str(&cmd)); | |
| 144 | + fflush(sshOut); | |
| 145 | + blob_reset(&cmd); | |
| 146 | + }else if( g.urlIsHttps ){ | |
| 124 | 147 | #ifdef FOSSIL_ENABLE_SSL |
| 125 | 148 | rc = ssl_open(); |
| 126 | 149 | if( rc==0 ) transport.isOpen = 1; |
| 127 | 150 | #else |
| 128 | 151 | socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support"); |
| @@ -156,11 +179,13 @@ | ||
| 156 | 179 | free(transport.pBuf); |
| 157 | 180 | transport.pBuf = 0; |
| 158 | 181 | transport.nAlloc = 0; |
| 159 | 182 | transport.nUsed = 0; |
| 160 | 183 | transport.iCursor = 0; |
| 161 | - if( g.urlIsHttps ){ | |
| 184 | + if( g.urlIsSsh ){ | |
| 185 | + /* No-op */ | |
| 186 | + }else if( g.urlIsHttps ){ | |
| 162 | 187 | #ifdef FOSSIL_ENABLE_SSL |
| 163 | 188 | ssl_close(); |
| 164 | 189 | #endif |
| 165 | 190 | }else if( g.urlIsFile ){ |
| 166 | 191 | if( transport.pFile ){ |
| @@ -183,11 +208,16 @@ | ||
| 183 | 208 | */ |
| 184 | 209 | void transport_send(Blob *toSend){ |
| 185 | 210 | char *z = blob_buffer(toSend); |
| 186 | 211 | int n = blob_size(toSend); |
| 187 | 212 | transport.nSent += n; |
| 188 | - if( g.urlIsHttps ){ | |
| 213 | + if( g.urlIsSsh ){ | |
| 214 | + int sent; | |
| 215 | + sent = fwrite(z, 1, n, sshOut); | |
| 216 | + fflush(sshOut); | |
| 217 | + /* printf("sent %d of %d bytes\n", sent, n); fflush(stdout); */ | |
| 218 | + }else if( g.urlIsHttps ){ | |
| 189 | 219 | #ifdef FOSSIL_ENABLE_SSL |
| 190 | 220 | int sent; |
| 191 | 221 | while( n>0 ){ |
| 192 | 222 | sent = ssl_send(0, z, n); |
| 193 | 223 | /* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */ |
| @@ -211,11 +241,13 @@ | ||
| 211 | 241 | /* |
| 212 | 242 | ** This routine is called when the outbound message is complete and |
| 213 | 243 | ** it is time to being recieving a reply. |
| 214 | 244 | */ |
| 215 | 245 | void transport_flip(void){ |
| 216 | - if( g.urlIsFile ){ | |
| 246 | + if( g.urlIsSsh ){ | |
| 247 | + fprintf(sshOut, "\n\n"); | |
| 248 | + }else if( g.urlIsFile ){ | |
| 217 | 249 | char *zCmd; |
| 218 | 250 | fclose(transport.pFile); |
| 219 | 251 | zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1", |
| 220 | 252 | g.argv[0], g.urlName, transport.zOutFile, transport.zInFile |
| 221 | 253 | ); |
| @@ -232,10 +264,41 @@ | ||
| 232 | 264 | void transport_rewind(void){ |
| 233 | 265 | if( g.urlIsFile ){ |
| 234 | 266 | transport_close(); |
| 235 | 267 | } |
| 236 | 268 | } |
| 269 | + | |
| 270 | +/* | |
| 271 | +** Read N bytes of content directly from the wire and write into | |
| 272 | +** the buffer. | |
| 273 | +*/ | |
| 274 | +static int transport_fetch(char *zBuf, int N){ | |
| 275 | + int got; | |
| 276 | + if( sshIn ){ | |
| 277 | + int x; | |
| 278 | + int wanted = N; | |
| 279 | + got = 0; | |
| 280 | + while( wanted>0 ){ | |
| 281 | + x = read(sshIn, &zBuf[got], wanted); | |
| 282 | + if( x<=0 ) break; | |
| 283 | + got += x; | |
| 284 | + wanted -= x; | |
| 285 | + } | |
| 286 | + }else if( g.urlIsHttps ){ | |
| 287 | + #ifdef FOSSIL_ENABLE_SSL | |
| 288 | + got = ssl_receive(0, zBuf, N); | |
| 289 | + #else | |
| 290 | + got = 0; | |
| 291 | + #endif | |
| 292 | + }else if( g.urlIsFile ){ | |
| 293 | + got = fread(zBuf, 1, N, transport.pFile); | |
| 294 | + }else{ | |
| 295 | + got = socket_receive(0, zBuf, N); | |
| 296 | + } | |
| 297 | + /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */ | |
| 298 | + return got; | |
| 299 | +} | |
| 237 | 300 | |
| 238 | 301 | /* |
| 239 | 302 | ** Read N bytes of content from the wire and store in the supplied buffer. |
| 240 | 303 | ** Return the number of bytes actually received. |
| 241 | 304 | */ |
| @@ -242,10 +305,11 @@ | ||
| 242 | 305 | int transport_receive(char *zBuf, int N){ |
| 243 | 306 | int onHand; /* Bytes current held in the transport buffer */ |
| 244 | 307 | int nByte = 0; /* Bytes of content received */ |
| 245 | 308 | |
| 246 | 309 | onHand = transport.nUsed - transport.iCursor; |
| 310 | + /* printf("request %d with %d on hand\n", N, onHand); fflush(stdout); */ | |
| 247 | 311 | if( onHand>0 ){ |
| 248 | 312 | int toMove = onHand; |
| 249 | 313 | if( toMove>N ) toMove = N; |
| 250 | 314 | /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */ |
| 251 | 315 | memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove); |
| @@ -257,24 +321,11 @@ | ||
| 257 | 321 | N -= toMove; |
| 258 | 322 | zBuf += toMove; |
| 259 | 323 | nByte += toMove; |
| 260 | 324 | } |
| 261 | 325 | if( N>0 ){ |
| 262 | - int got; | |
| 263 | - if( g.urlIsHttps ){ | |
| 264 | - #ifdef FOSSIL_ENABLE_SSL | |
| 265 | - got = ssl_receive(0, zBuf, N); | |
| 266 | - /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */ | |
| 267 | - #else | |
| 268 | - got = 0; | |
| 269 | - #endif | |
| 270 | - }else if( g.urlIsFile ){ | |
| 271 | - got = fread(zBuf, 1, N, transport.pFile); | |
| 272 | - }else{ | |
| 273 | - got = socket_receive(0, zBuf, N); | |
| 274 | - /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */ | |
| 275 | - } | |
| 326 | + int got = transport_fetch(zBuf, N); | |
| 276 | 327 | if( got>0 ){ |
| 277 | 328 | nByte += got; |
| 278 | 329 | transport.nRcvd += got; |
| 279 | 330 | } |
| 280 | 331 | } |
| @@ -308,11 +359,11 @@ | ||
| 308 | 359 | pNew = realloc(transport.pBuf, transport.nAlloc); |
| 309 | 360 | if( pNew==0 ) fossil_panic("out of memory"); |
| 310 | 361 | transport.pBuf = pNew; |
| 311 | 362 | } |
| 312 | 363 | if( N>0 ){ |
| 313 | - i = transport_receive(&transport.pBuf[transport.nUsed], N); | |
| 364 | + i = transport_fetch(&transport.pBuf[transport.nUsed], N); | |
| 314 | 365 | if( i>0 ){ |
| 315 | 366 | transport.nUsed += i; |
| 316 | 367 | } |
| 317 | 368 | } |
| 318 | 369 | } |
| @@ -354,19 +405,19 @@ | ||
| 354 | 405 | /* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */ |
| 355 | 406 | return &transport.pBuf[iStart]; |
| 356 | 407 | } |
| 357 | 408 | |
| 358 | 409 | void transport_global_shutdown(void){ |
| 359 | - if( g.urlIsSsh && g.sshPid ){ | |
| 410 | + if( g.urlIsSsh && sshPid ){ | |
| 360 | 411 | printf("Closing SSH tunnel: "); |
| 361 | 412 | fflush(stdout); |
| 362 | - pclose2(g.sshIn, g.sshOut, g.sshPid); | |
| 363 | - g.sshPid = 0; | |
| 413 | + pclose2(sshIn, sshOut, sshPid); | |
| 414 | + sshPid = 0; | |
| 364 | 415 | } |
| 365 | 416 | if( g.urlIsHttps ){ |
| 366 | 417 | #ifdef FOSSIL_ENABLE_SSL |
| 367 | 418 | ssl_global_shutdown(); |
| 368 | 419 | #endif |
| 369 | 420 | }else{ |
| 370 | 421 | socket_global_shutdown(); |
| 371 | 422 | } |
| 372 | 423 | } |
| 373 | 424 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -38,10 +38,19 @@ | |
| 38 | char *zOutFile; /* Name of outbound file for FILE: */ |
| 39 | char *zInFile; /* Name of inbound file for FILE: */ |
| 40 | } transport = { |
| 41 | 0, 0, 0, 0, 0, 0, 0 |
| 42 | }; |
| 43 | |
| 44 | /* |
| 45 | ** Return the current transport error message. |
| 46 | */ |
| 47 | const char *transport_errmsg(void){ |
| @@ -63,49 +72,53 @@ | |
| 63 | if( resetFlag ){ |
| 64 | transport.nSent = 0; |
| 65 | transport.nRcvd = 0; |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | /* |
| 70 | ** Global initialization of the transport layer |
| 71 | */ |
| 72 | void transport_global_startup(void){ |
| 73 | if( g.urlIsSsh ){ |
| 74 | char *zCmd; |
| 75 | int i; |
| 76 | char zIn[200]; |
| 77 | #ifdef __MINGW32__ |
| 78 | fossil_fatal("the ssh:// sync method is currently only supported on unix"); |
| 79 | #endif |
| 80 | if( g.urlUser && g.urlUser[0] ){ |
| 81 | zCmd = mprintf( |
| 82 | "ssh -L127.0.0.1:%d:127.0.0.1:%d %s@%s " |
| 83 | "\"fossil server -P %d '%s'\"", |
| 84 | g.urlPort, g.urlPort, g.urlUser, g.urlSshHost, g.urlPort, g.urlPath |
| 85 | ); |
| 86 | }else{ |
| 87 | zCmd = mprintf( |
| 88 | "ssh -L127.0.0.1:%d:127.0.0.1:%d %s \"fossil sshd -P %d '%s'\"", |
| 89 | g.urlPort, g.urlPort, g.urlSshHost, g.urlPort, g.urlPath |
| 90 | ); |
| 91 | } |
| 92 | printf("%s\n", zCmd); |
| 93 | popen2(zCmd, &g.sshIn, &g.sshOut, &g.sshPid); |
| 94 | if( g.sshPid==0 ){ |
| 95 | fossil_fatal("cannot start ssh tunnel using [%s]", zCmd); |
| 96 | } |
| 97 | free(zCmd); |
| 98 | zIn[0] = 0; |
| 99 | fgets(zIn, sizeof(zIn), g.sshIn); |
| 100 | for(i=0; zIn[i] && zIn[i]!='\n'; i++){} |
| 101 | zIn[i] = 0; |
| 102 | if( memcmp(zIn, "Access-Token: ", 14)!=0 ){ |
| 103 | pclose2(g.sshIn, g.sshOut, g.sshPid); |
| 104 | fossil_fatal("failed to start ssh tunnel"); |
| 105 | } |
| 106 | g.zAccessToken = mprintf("%s", &zIn[14]); |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | /* |
| 111 | ** Open a connection to the server. The server is defined by the following |
| @@ -118,11 +131,21 @@ | |
| 118 | ** Return the number of errors. |
| 119 | */ |
| 120 | int transport_open(void){ |
| 121 | int rc = 0; |
| 122 | if( transport.isOpen==0 ){ |
| 123 | if( g.urlIsHttps ){ |
| 124 | #ifdef FOSSIL_ENABLE_SSL |
| 125 | rc = ssl_open(); |
| 126 | if( rc==0 ) transport.isOpen = 1; |
| 127 | #else |
| 128 | socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support"); |
| @@ -156,11 +179,13 @@ | |
| 156 | free(transport.pBuf); |
| 157 | transport.pBuf = 0; |
| 158 | transport.nAlloc = 0; |
| 159 | transport.nUsed = 0; |
| 160 | transport.iCursor = 0; |
| 161 | if( g.urlIsHttps ){ |
| 162 | #ifdef FOSSIL_ENABLE_SSL |
| 163 | ssl_close(); |
| 164 | #endif |
| 165 | }else if( g.urlIsFile ){ |
| 166 | if( transport.pFile ){ |
| @@ -183,11 +208,16 @@ | |
| 183 | */ |
| 184 | void transport_send(Blob *toSend){ |
| 185 | char *z = blob_buffer(toSend); |
| 186 | int n = blob_size(toSend); |
| 187 | transport.nSent += n; |
| 188 | if( g.urlIsHttps ){ |
| 189 | #ifdef FOSSIL_ENABLE_SSL |
| 190 | int sent; |
| 191 | while( n>0 ){ |
| 192 | sent = ssl_send(0, z, n); |
| 193 | /* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */ |
| @@ -211,11 +241,13 @@ | |
| 211 | /* |
| 212 | ** This routine is called when the outbound message is complete and |
| 213 | ** it is time to being recieving a reply. |
| 214 | */ |
| 215 | void transport_flip(void){ |
| 216 | if( g.urlIsFile ){ |
| 217 | char *zCmd; |
| 218 | fclose(transport.pFile); |
| 219 | zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1", |
| 220 | g.argv[0], g.urlName, transport.zOutFile, transport.zInFile |
| 221 | ); |
| @@ -232,10 +264,41 @@ | |
| 232 | void transport_rewind(void){ |
| 233 | if( g.urlIsFile ){ |
| 234 | transport_close(); |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | /* |
| 239 | ** Read N bytes of content from the wire and store in the supplied buffer. |
| 240 | ** Return the number of bytes actually received. |
| 241 | */ |
| @@ -242,10 +305,11 @@ | |
| 242 | int transport_receive(char *zBuf, int N){ |
| 243 | int onHand; /* Bytes current held in the transport buffer */ |
| 244 | int nByte = 0; /* Bytes of content received */ |
| 245 | |
| 246 | onHand = transport.nUsed - transport.iCursor; |
| 247 | if( onHand>0 ){ |
| 248 | int toMove = onHand; |
| 249 | if( toMove>N ) toMove = N; |
| 250 | /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */ |
| 251 | memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove); |
| @@ -257,24 +321,11 @@ | |
| 257 | N -= toMove; |
| 258 | zBuf += toMove; |
| 259 | nByte += toMove; |
| 260 | } |
| 261 | if( N>0 ){ |
| 262 | int got; |
| 263 | if( g.urlIsHttps ){ |
| 264 | #ifdef FOSSIL_ENABLE_SSL |
| 265 | got = ssl_receive(0, zBuf, N); |
| 266 | /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */ |
| 267 | #else |
| 268 | got = 0; |
| 269 | #endif |
| 270 | }else if( g.urlIsFile ){ |
| 271 | got = fread(zBuf, 1, N, transport.pFile); |
| 272 | }else{ |
| 273 | got = socket_receive(0, zBuf, N); |
| 274 | /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */ |
| 275 | } |
| 276 | if( got>0 ){ |
| 277 | nByte += got; |
| 278 | transport.nRcvd += got; |
| 279 | } |
| 280 | } |
| @@ -308,11 +359,11 @@ | |
| 308 | pNew = realloc(transport.pBuf, transport.nAlloc); |
| 309 | if( pNew==0 ) fossil_panic("out of memory"); |
| 310 | transport.pBuf = pNew; |
| 311 | } |
| 312 | if( N>0 ){ |
| 313 | i = transport_receive(&transport.pBuf[transport.nUsed], N); |
| 314 | if( i>0 ){ |
| 315 | transport.nUsed += i; |
| 316 | } |
| 317 | } |
| 318 | } |
| @@ -354,19 +405,19 @@ | |
| 354 | /* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */ |
| 355 | return &transport.pBuf[iStart]; |
| 356 | } |
| 357 | |
| 358 | void transport_global_shutdown(void){ |
| 359 | if( g.urlIsSsh && g.sshPid ){ |
| 360 | printf("Closing SSH tunnel: "); |
| 361 | fflush(stdout); |
| 362 | pclose2(g.sshIn, g.sshOut, g.sshPid); |
| 363 | g.sshPid = 0; |
| 364 | } |
| 365 | if( g.urlIsHttps ){ |
| 366 | #ifdef FOSSIL_ENABLE_SSL |
| 367 | ssl_global_shutdown(); |
| 368 | #endif |
| 369 | }else{ |
| 370 | socket_global_shutdown(); |
| 371 | } |
| 372 | } |
| 373 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -38,10 +38,19 @@ | |
| 38 | char *zOutFile; /* Name of outbound file for FILE: */ |
| 39 | char *zInFile; /* Name of inbound file for FILE: */ |
| 40 | } transport = { |
| 41 | 0, 0, 0, 0, 0, 0, 0 |
| 42 | }; |
| 43 | |
| 44 | /* |
| 45 | ** Information about the connection to the SSH subprocess when |
| 46 | ** using the ssh:// sync method. |
| 47 | */ |
| 48 | static int sshPid; /* Process id of ssh subprocess */ |
| 49 | static int sshIn; /* From ssh subprocess to this process */ |
| 50 | static FILE *sshOut; /* From this to ssh subprocess */ |
| 51 | |
| 52 | |
| 53 | /* |
| 54 | ** Return the current transport error message. |
| 55 | */ |
| 56 | const char *transport_errmsg(void){ |
| @@ -63,49 +72,53 @@ | |
| 72 | if( resetFlag ){ |
| 73 | transport.nSent = 0; |
| 74 | transport.nRcvd = 0; |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | /* |
| 79 | ** Read text from sshIn. Zero-terminate and remove trailing |
| 80 | ** whitespace. |
| 81 | */ |
| 82 | static void sshin_read(char *zBuf, int szBuf){ |
| 83 | int got; |
| 84 | zBuf[0] = 0; |
| 85 | got = read(sshIn, zBuf, szBuf-1); |
| 86 | while( got>=0 ){ |
| 87 | zBuf[got] = 0; |
| 88 | if( got==0 || !isspace(zBuf[got-1]) ) break; |
| 89 | got--; |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | /* |
| 94 | ** Global initialization of the transport layer |
| 95 | */ |
| 96 | void transport_global_startup(void){ |
| 97 | if( g.urlIsSsh ){ |
| 98 | char *zCmd; |
| 99 | char zIn[20]; |
| 100 | #ifdef __MINGW32__ |
| 101 | fossil_fatal("the ssh:// sync method is currently only supported on unix"); |
| 102 | #endif |
| 103 | if( g.urlUser && g.urlUser[0] ){ |
| 104 | zCmd = mprintf("ssh -e none %s@%s", g.urlUser, g.urlName); |
| 105 | }else{ |
| 106 | zCmd = mprintf("ssh -e none %s", g.urlName); |
| 107 | } |
| 108 | /* printf("%s\n", zCmd); */ |
| 109 | popen2(zCmd, &sshIn, &sshOut, &sshPid); |
| 110 | if( sshPid==0 ){ |
| 111 | fossil_fatal("cannot start ssh tunnel using [%s]", zCmd); |
| 112 | } |
| 113 | free(zCmd); |
| 114 | fprintf(sshOut, "echo test\n"); |
| 115 | fflush(sshOut); |
| 116 | sshin_read(zIn, sizeof(zIn)); |
| 117 | if( memcmp(zIn, "test", 4)!=0 ){ |
| 118 | fossil_fatal("ssh connection failed"); |
| 119 | } |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | /* |
| 124 | ** Open a connection to the server. The server is defined by the following |
| @@ -118,11 +131,21 @@ | |
| 131 | ** Return the number of errors. |
| 132 | */ |
| 133 | int transport_open(void){ |
| 134 | int rc = 0; |
| 135 | if( transport.isOpen==0 ){ |
| 136 | if( g.urlIsSsh ){ |
| 137 | Blob cmd; |
| 138 | blob_zero(&cmd); |
| 139 | shell_escape(&cmd, g.urlFossil); |
| 140 | blob_append(&cmd, " test-http ", -1); |
| 141 | shell_escape(&cmd, g.urlPath); |
| 142 | /* fprintf(stdout, "%s\n", blob_str(&cmd)); */ |
| 143 | fprintf(sshOut, "%s\n", blob_str(&cmd)); |
| 144 | fflush(sshOut); |
| 145 | blob_reset(&cmd); |
| 146 | }else if( g.urlIsHttps ){ |
| 147 | #ifdef FOSSIL_ENABLE_SSL |
| 148 | rc = ssl_open(); |
| 149 | if( rc==0 ) transport.isOpen = 1; |
| 150 | #else |
| 151 | socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support"); |
| @@ -156,11 +179,13 @@ | |
| 179 | free(transport.pBuf); |
| 180 | transport.pBuf = 0; |
| 181 | transport.nAlloc = 0; |
| 182 | transport.nUsed = 0; |
| 183 | transport.iCursor = 0; |
| 184 | if( g.urlIsSsh ){ |
| 185 | /* No-op */ |
| 186 | }else if( g.urlIsHttps ){ |
| 187 | #ifdef FOSSIL_ENABLE_SSL |
| 188 | ssl_close(); |
| 189 | #endif |
| 190 | }else if( g.urlIsFile ){ |
| 191 | if( transport.pFile ){ |
| @@ -183,11 +208,16 @@ | |
| 208 | */ |
| 209 | void transport_send(Blob *toSend){ |
| 210 | char *z = blob_buffer(toSend); |
| 211 | int n = blob_size(toSend); |
| 212 | transport.nSent += n; |
| 213 | if( g.urlIsSsh ){ |
| 214 | int sent; |
| 215 | sent = fwrite(z, 1, n, sshOut); |
| 216 | fflush(sshOut); |
| 217 | /* printf("sent %d of %d bytes\n", sent, n); fflush(stdout); */ |
| 218 | }else if( g.urlIsHttps ){ |
| 219 | #ifdef FOSSIL_ENABLE_SSL |
| 220 | int sent; |
| 221 | while( n>0 ){ |
| 222 | sent = ssl_send(0, z, n); |
| 223 | /* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */ |
| @@ -211,11 +241,13 @@ | |
| 241 | /* |
| 242 | ** This routine is called when the outbound message is complete and |
| 243 | ** it is time to being recieving a reply. |
| 244 | */ |
| 245 | void transport_flip(void){ |
| 246 | if( g.urlIsSsh ){ |
| 247 | fprintf(sshOut, "\n\n"); |
| 248 | }else if( g.urlIsFile ){ |
| 249 | char *zCmd; |
| 250 | fclose(transport.pFile); |
| 251 | zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1", |
| 252 | g.argv[0], g.urlName, transport.zOutFile, transport.zInFile |
| 253 | ); |
| @@ -232,10 +264,41 @@ | |
| 264 | void transport_rewind(void){ |
| 265 | if( g.urlIsFile ){ |
| 266 | transport_close(); |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | /* |
| 271 | ** Read N bytes of content directly from the wire and write into |
| 272 | ** the buffer. |
| 273 | */ |
| 274 | static int transport_fetch(char *zBuf, int N){ |
| 275 | int got; |
| 276 | if( sshIn ){ |
| 277 | int x; |
| 278 | int wanted = N; |
| 279 | got = 0; |
| 280 | while( wanted>0 ){ |
| 281 | x = read(sshIn, &zBuf[got], wanted); |
| 282 | if( x<=0 ) break; |
| 283 | got += x; |
| 284 | wanted -= x; |
| 285 | } |
| 286 | }else if( g.urlIsHttps ){ |
| 287 | #ifdef FOSSIL_ENABLE_SSL |
| 288 | got = ssl_receive(0, zBuf, N); |
| 289 | #else |
| 290 | got = 0; |
| 291 | #endif |
| 292 | }else if( g.urlIsFile ){ |
| 293 | got = fread(zBuf, 1, N, transport.pFile); |
| 294 | }else{ |
| 295 | got = socket_receive(0, zBuf, N); |
| 296 | } |
| 297 | /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */ |
| 298 | return got; |
| 299 | } |
| 300 | |
| 301 | /* |
| 302 | ** Read N bytes of content from the wire and store in the supplied buffer. |
| 303 | ** Return the number of bytes actually received. |
| 304 | */ |
| @@ -242,10 +305,11 @@ | |
| 305 | int transport_receive(char *zBuf, int N){ |
| 306 | int onHand; /* Bytes current held in the transport buffer */ |
| 307 | int nByte = 0; /* Bytes of content received */ |
| 308 | |
| 309 | onHand = transport.nUsed - transport.iCursor; |
| 310 | /* printf("request %d with %d on hand\n", N, onHand); fflush(stdout); */ |
| 311 | if( onHand>0 ){ |
| 312 | int toMove = onHand; |
| 313 | if( toMove>N ) toMove = N; |
| 314 | /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */ |
| 315 | memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove); |
| @@ -257,24 +321,11 @@ | |
| 321 | N -= toMove; |
| 322 | zBuf += toMove; |
| 323 | nByte += toMove; |
| 324 | } |
| 325 | if( N>0 ){ |
| 326 | int got = transport_fetch(zBuf, N); |
| 327 | if( got>0 ){ |
| 328 | nByte += got; |
| 329 | transport.nRcvd += got; |
| 330 | } |
| 331 | } |
| @@ -308,11 +359,11 @@ | |
| 359 | pNew = realloc(transport.pBuf, transport.nAlloc); |
| 360 | if( pNew==0 ) fossil_panic("out of memory"); |
| 361 | transport.pBuf = pNew; |
| 362 | } |
| 363 | if( N>0 ){ |
| 364 | i = transport_fetch(&transport.pBuf[transport.nUsed], N); |
| 365 | if( i>0 ){ |
| 366 | transport.nUsed += i; |
| 367 | } |
| 368 | } |
| 369 | } |
| @@ -354,19 +405,19 @@ | |
| 405 | /* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */ |
| 406 | return &transport.pBuf[iStart]; |
| 407 | } |
| 408 | |
| 409 | void transport_global_shutdown(void){ |
| 410 | if( g.urlIsSsh && sshPid ){ |
| 411 | printf("Closing SSH tunnel: "); |
| 412 | fflush(stdout); |
| 413 | pclose2(sshIn, sshOut, sshPid); |
| 414 | sshPid = 0; |
| 415 | } |
| 416 | if( g.urlIsHttps ){ |
| 417 | #ifdef FOSSIL_ENABLE_SSL |
| 418 | ssl_global_shutdown(); |
| 419 | #endif |
| 420 | }else{ |
| 421 | socket_global_shutdown(); |
| 422 | } |
| 423 | } |
| 424 |
+12
-25
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -83,30 +83,25 @@ | ||
| 83 | 83 | FILE *httpOut; /* Send HTTP output here */ |
| 84 | 84 | int xlinkClusterOnly; /* Set when cloning. Only process clusters */ |
| 85 | 85 | int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */ |
| 86 | 86 | int *aCommitFile; /* Array of files to be committed */ |
| 87 | 87 | int markPrivate; /* All new artifacts are private if true */ |
| 88 | - char *zAccessToken; /* X-Fossil-Access-Token HTTP header field */ | |
| 89 | - int sshPid; /* Process id of ssh subprocess */ | |
| 90 | - FILE *sshIn; /* From ssh subprocess to this */ | |
| 91 | - FILE *sshOut; /* From this to ssh subprocess */ | |
| 92 | 88 | |
| 93 | 89 | int urlIsFile; /* True if a "file:" url */ |
| 94 | 90 | int urlIsHttps; /* True if a "https:" url */ |
| 95 | 91 | int urlIsSsh; /* True if an "ssh:" url */ |
| 96 | 92 | char *urlName; /* Hostname for http: or filename for file: */ |
| 97 | - char *urlSshHost; /* Hostname for ssh: tunnels */ | |
| 98 | 93 | char *urlHostname; /* The HOST: parameter on http headers */ |
| 99 | 94 | char *urlProtocol; /* "http" or "https" */ |
| 100 | 95 | int urlPort; /* TCP port number for http: or https: */ |
| 101 | 96 | int urlDfltPort; /* The default port for the given protocol */ |
| 102 | - int urlSshPort; /* TCP port for SSH */ | |
| 103 | 97 | char *urlPath; /* Pathname for http: */ |
| 104 | 98 | char *urlUser; /* User id for http: */ |
| 105 | 99 | char *urlPasswd; /* Password for http: */ |
| 106 | 100 | char *urlCanonical; /* Canonical representation of the URL */ |
| 107 | 101 | char *urlProxyAuth; /* Proxy-Authorizer: string */ |
| 102 | + char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */ | |
| 108 | 103 | int dontKeepUrl; /* Do not persist the URL */ |
| 109 | 104 | |
| 110 | 105 | const char *zLogin; /* Login name. "" if not logged in. */ |
| 111 | 106 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 112 | 107 | int userUid; /* Integer user id */ |
| @@ -921,14 +916,14 @@ | ||
| 921 | 916 | */ |
| 922 | 917 | void cmd_http(void){ |
| 923 | 918 | const char *zIpAddr; |
| 924 | 919 | const char *zNotFound; |
| 925 | 920 | zNotFound = find_option("notfound", 0, 1); |
| 921 | + g.cgiOutput = 1; | |
| 926 | 922 | if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){ |
| 927 | - cgi_panic("no repository specified"); | |
| 923 | + fossil_fatal("no repository specified"); | |
| 928 | 924 | } |
| 929 | - g.cgiOutput = 1; | |
| 930 | 925 | g.fullHttpReply = 1; |
| 931 | 926 | if( g.argc==6 ){ |
| 932 | 927 | g.httpIn = fopen(g.argv[3], "rb"); |
| 933 | 928 | g.httpOut = fopen(g.argv[4], "wb"); |
| 934 | 929 | zIpAddr = g.argv[5]; |
| @@ -942,16 +937,24 @@ | ||
| 942 | 937 | cgi_handle_http_request(zIpAddr); |
| 943 | 938 | process_one_web_page(zNotFound); |
| 944 | 939 | } |
| 945 | 940 | |
| 946 | 941 | /* |
| 942 | +** Note that the following command is used by ssh:// processing. | |
| 943 | +** | |
| 947 | 944 | ** COMMAND: test-http |
| 948 | 945 | ** Works like the http command but gives setup permission to all users. |
| 949 | 946 | */ |
| 950 | 947 | void cmd_test_http(void){ |
| 951 | 948 | login_set_capabilities("s"); |
| 952 | - cmd_http(); | |
| 949 | + g.httpIn = stdin; | |
| 950 | + g.httpOut = stdout; | |
| 951 | + find_server_repository(0); | |
| 952 | + g.cgiOutput = 1; | |
| 953 | + g.fullHttpReply = 1; | |
| 954 | + cgi_handle_http_request(0); | |
| 955 | + process_one_web_page(0); | |
| 953 | 956 | } |
| 954 | 957 | |
| 955 | 958 | #ifndef __MINGW32__ |
| 956 | 959 | #if !defined(__DARWIN__) && !defined(__APPLE__) |
| 957 | 960 | /* |
| @@ -976,11 +979,10 @@ | ||
| 976 | 979 | } |
| 977 | 980 | #endif |
| 978 | 981 | #endif |
| 979 | 982 | |
| 980 | 983 | /* |
| 981 | -** COMMAND: sshd | |
| 982 | 984 | ** COMMAND: server |
| 983 | 985 | ** COMMAND: ui |
| 984 | 986 | ** |
| 985 | 987 | ** Usage: %fossil server ?-P|--port TCPPORT? ?REPOSITORY? |
| 986 | 988 | ** Or: %fossil ui ?-P|--port TCPPORT? ?REPOSITORY? |
| @@ -997,13 +999,10 @@ | ||
| 997 | 999 | ** |
| 998 | 1000 | ** In the "server" command, the REPOSITORY can be a directory (aka folder) |
| 999 | 1001 | ** that contains one or more respositories with names ending in ".fossil". |
| 1000 | 1002 | ** In that case, the first element of the URL is used to select among the |
| 1001 | 1003 | ** various repositories. |
| 1002 | -** | |
| 1003 | -** The "ui" or "server" verb can also be "sshd". This is used internally | |
| 1004 | -** by the ssh:// sync method. | |
| 1005 | 1004 | */ |
| 1006 | 1005 | void cmd_webserver(void){ |
| 1007 | 1006 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| 1008 | 1007 | const char *zPort; /* Value of the --port option */ |
| 1009 | 1008 | char *zBrowser; /* Name of web browser program */ |
| @@ -1031,22 +1030,10 @@ | ||
| 1031 | 1030 | iPort = mxPort = atoi(zPort); |
| 1032 | 1031 | }else{ |
| 1033 | 1032 | iPort = db_get_int("http-port", 8080); |
| 1034 | 1033 | mxPort = iPort+100; |
| 1035 | 1034 | } |
| 1036 | - if( g.argv[1][0]=='s' && g.argv[1][1]=='s' ){ | |
| 1037 | - /* For ssh://, output a random "access token" that must appear in | |
| 1038 | - ** the header of every HTTP request. HTTP requests without the | |
| 1039 | - ** correct access token reply with 403 Forbidden. The access token | |
| 1040 | - ** prevents any clients other than the one client that launched the | |
| 1041 | - ** remote server via SSH from accessing the remote server. | |
| 1042 | - */ | |
| 1043 | - g.zAccessToken = db_text(0, "SELECT lower(hex(randomblob(20)))"); | |
| 1044 | - printf("Access-Token: %s\n", g.zAccessToken); | |
| 1045 | - fflush(stdout); | |
| 1046 | - flags |= HTTP_SERVER_LOCALHOST | HTTP_SERVER_STDIN; | |
| 1047 | - } | |
| 1048 | 1035 | #ifndef __MINGW32__ |
| 1049 | 1036 | /* Unix implementation */ |
| 1050 | 1037 | if( isUiCmd ){ |
| 1051 | 1038 | #if !defined(__DARWIN__) && !defined(__APPLE__) |
| 1052 | 1039 | zBrowser = db_get("web-browser", 0); |
| 1053 | 1040 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -83,30 +83,25 @@ | |
| 83 | FILE *httpOut; /* Send HTTP output here */ |
| 84 | int xlinkClusterOnly; /* Set when cloning. Only process clusters */ |
| 85 | int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */ |
| 86 | int *aCommitFile; /* Array of files to be committed */ |
| 87 | int markPrivate; /* All new artifacts are private if true */ |
| 88 | char *zAccessToken; /* X-Fossil-Access-Token HTTP header field */ |
| 89 | int sshPid; /* Process id of ssh subprocess */ |
| 90 | FILE *sshIn; /* From ssh subprocess to this */ |
| 91 | FILE *sshOut; /* From this to ssh subprocess */ |
| 92 | |
| 93 | int urlIsFile; /* True if a "file:" url */ |
| 94 | int urlIsHttps; /* True if a "https:" url */ |
| 95 | int urlIsSsh; /* True if an "ssh:" url */ |
| 96 | char *urlName; /* Hostname for http: or filename for file: */ |
| 97 | char *urlSshHost; /* Hostname for ssh: tunnels */ |
| 98 | char *urlHostname; /* The HOST: parameter on http headers */ |
| 99 | char *urlProtocol; /* "http" or "https" */ |
| 100 | int urlPort; /* TCP port number for http: or https: */ |
| 101 | int urlDfltPort; /* The default port for the given protocol */ |
| 102 | int urlSshPort; /* TCP port for SSH */ |
| 103 | char *urlPath; /* Pathname for http: */ |
| 104 | char *urlUser; /* User id for http: */ |
| 105 | char *urlPasswd; /* Password for http: */ |
| 106 | char *urlCanonical; /* Canonical representation of the URL */ |
| 107 | char *urlProxyAuth; /* Proxy-Authorizer: string */ |
| 108 | int dontKeepUrl; /* Do not persist the URL */ |
| 109 | |
| 110 | const char *zLogin; /* Login name. "" if not logged in. */ |
| 111 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 112 | int userUid; /* Integer user id */ |
| @@ -921,14 +916,14 @@ | |
| 921 | */ |
| 922 | void cmd_http(void){ |
| 923 | const char *zIpAddr; |
| 924 | const char *zNotFound; |
| 925 | zNotFound = find_option("notfound", 0, 1); |
| 926 | if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){ |
| 927 | cgi_panic("no repository specified"); |
| 928 | } |
| 929 | g.cgiOutput = 1; |
| 930 | g.fullHttpReply = 1; |
| 931 | if( g.argc==6 ){ |
| 932 | g.httpIn = fopen(g.argv[3], "rb"); |
| 933 | g.httpOut = fopen(g.argv[4], "wb"); |
| 934 | zIpAddr = g.argv[5]; |
| @@ -942,16 +937,24 @@ | |
| 942 | cgi_handle_http_request(zIpAddr); |
| 943 | process_one_web_page(zNotFound); |
| 944 | } |
| 945 | |
| 946 | /* |
| 947 | ** COMMAND: test-http |
| 948 | ** Works like the http command but gives setup permission to all users. |
| 949 | */ |
| 950 | void cmd_test_http(void){ |
| 951 | login_set_capabilities("s"); |
| 952 | cmd_http(); |
| 953 | } |
| 954 | |
| 955 | #ifndef __MINGW32__ |
| 956 | #if !defined(__DARWIN__) && !defined(__APPLE__) |
| 957 | /* |
| @@ -976,11 +979,10 @@ | |
| 976 | } |
| 977 | #endif |
| 978 | #endif |
| 979 | |
| 980 | /* |
| 981 | ** COMMAND: sshd |
| 982 | ** COMMAND: server |
| 983 | ** COMMAND: ui |
| 984 | ** |
| 985 | ** Usage: %fossil server ?-P|--port TCPPORT? ?REPOSITORY? |
| 986 | ** Or: %fossil ui ?-P|--port TCPPORT? ?REPOSITORY? |
| @@ -997,13 +999,10 @@ | |
| 997 | ** |
| 998 | ** In the "server" command, the REPOSITORY can be a directory (aka folder) |
| 999 | ** that contains one or more respositories with names ending in ".fossil". |
| 1000 | ** In that case, the first element of the URL is used to select among the |
| 1001 | ** various repositories. |
| 1002 | ** |
| 1003 | ** The "ui" or "server" verb can also be "sshd". This is used internally |
| 1004 | ** by the ssh:// sync method. |
| 1005 | */ |
| 1006 | void cmd_webserver(void){ |
| 1007 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| 1008 | const char *zPort; /* Value of the --port option */ |
| 1009 | char *zBrowser; /* Name of web browser program */ |
| @@ -1031,22 +1030,10 @@ | |
| 1031 | iPort = mxPort = atoi(zPort); |
| 1032 | }else{ |
| 1033 | iPort = db_get_int("http-port", 8080); |
| 1034 | mxPort = iPort+100; |
| 1035 | } |
| 1036 | if( g.argv[1][0]=='s' && g.argv[1][1]=='s' ){ |
| 1037 | /* For ssh://, output a random "access token" that must appear in |
| 1038 | ** the header of every HTTP request. HTTP requests without the |
| 1039 | ** correct access token reply with 403 Forbidden. The access token |
| 1040 | ** prevents any clients other than the one client that launched the |
| 1041 | ** remote server via SSH from accessing the remote server. |
| 1042 | */ |
| 1043 | g.zAccessToken = db_text(0, "SELECT lower(hex(randomblob(20)))"); |
| 1044 | printf("Access-Token: %s\n", g.zAccessToken); |
| 1045 | fflush(stdout); |
| 1046 | flags |= HTTP_SERVER_LOCALHOST | HTTP_SERVER_STDIN; |
| 1047 | } |
| 1048 | #ifndef __MINGW32__ |
| 1049 | /* Unix implementation */ |
| 1050 | if( isUiCmd ){ |
| 1051 | #if !defined(__DARWIN__) && !defined(__APPLE__) |
| 1052 | zBrowser = db_get("web-browser", 0); |
| 1053 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -83,30 +83,25 @@ | |
| 83 | FILE *httpOut; /* Send HTTP output here */ |
| 84 | int xlinkClusterOnly; /* Set when cloning. Only process clusters */ |
| 85 | int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */ |
| 86 | int *aCommitFile; /* Array of files to be committed */ |
| 87 | int markPrivate; /* All new artifacts are private if true */ |
| 88 | |
| 89 | int urlIsFile; /* True if a "file:" url */ |
| 90 | int urlIsHttps; /* True if a "https:" url */ |
| 91 | int urlIsSsh; /* True if an "ssh:" url */ |
| 92 | char *urlName; /* Hostname for http: or filename for file: */ |
| 93 | char *urlHostname; /* The HOST: parameter on http headers */ |
| 94 | char *urlProtocol; /* "http" or "https" */ |
| 95 | int urlPort; /* TCP port number for http: or https: */ |
| 96 | int urlDfltPort; /* The default port for the given protocol */ |
| 97 | char *urlPath; /* Pathname for http: */ |
| 98 | char *urlUser; /* User id for http: */ |
| 99 | char *urlPasswd; /* Password for http: */ |
| 100 | char *urlCanonical; /* Canonical representation of the URL */ |
| 101 | char *urlProxyAuth; /* Proxy-Authorizer: string */ |
| 102 | char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */ |
| 103 | int dontKeepUrl; /* Do not persist the URL */ |
| 104 | |
| 105 | const char *zLogin; /* Login name. "" if not logged in. */ |
| 106 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 107 | int userUid; /* Integer user id */ |
| @@ -921,14 +916,14 @@ | |
| 916 | */ |
| 917 | void cmd_http(void){ |
| 918 | const char *zIpAddr; |
| 919 | const char *zNotFound; |
| 920 | zNotFound = find_option("notfound", 0, 1); |
| 921 | g.cgiOutput = 1; |
| 922 | if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){ |
| 923 | fossil_fatal("no repository specified"); |
| 924 | } |
| 925 | g.fullHttpReply = 1; |
| 926 | if( g.argc==6 ){ |
| 927 | g.httpIn = fopen(g.argv[3], "rb"); |
| 928 | g.httpOut = fopen(g.argv[4], "wb"); |
| 929 | zIpAddr = g.argv[5]; |
| @@ -942,16 +937,24 @@ | |
| 937 | cgi_handle_http_request(zIpAddr); |
| 938 | process_one_web_page(zNotFound); |
| 939 | } |
| 940 | |
| 941 | /* |
| 942 | ** Note that the following command is used by ssh:// processing. |
| 943 | ** |
| 944 | ** COMMAND: test-http |
| 945 | ** Works like the http command but gives setup permission to all users. |
| 946 | */ |
| 947 | void cmd_test_http(void){ |
| 948 | login_set_capabilities("s"); |
| 949 | g.httpIn = stdin; |
| 950 | g.httpOut = stdout; |
| 951 | find_server_repository(0); |
| 952 | g.cgiOutput = 1; |
| 953 | g.fullHttpReply = 1; |
| 954 | cgi_handle_http_request(0); |
| 955 | process_one_web_page(0); |
| 956 | } |
| 957 | |
| 958 | #ifndef __MINGW32__ |
| 959 | #if !defined(__DARWIN__) && !defined(__APPLE__) |
| 960 | /* |
| @@ -976,11 +979,10 @@ | |
| 979 | } |
| 980 | #endif |
| 981 | #endif |
| 982 | |
| 983 | /* |
| 984 | ** COMMAND: server |
| 985 | ** COMMAND: ui |
| 986 | ** |
| 987 | ** Usage: %fossil server ?-P|--port TCPPORT? ?REPOSITORY? |
| 988 | ** Or: %fossil ui ?-P|--port TCPPORT? ?REPOSITORY? |
| @@ -997,13 +999,10 @@ | |
| 999 | ** |
| 1000 | ** In the "server" command, the REPOSITORY can be a directory (aka folder) |
| 1001 | ** that contains one or more respositories with names ending in ".fossil". |
| 1002 | ** In that case, the first element of the URL is used to select among the |
| 1003 | ** various repositories. |
| 1004 | */ |
| 1005 | void cmd_webserver(void){ |
| 1006 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| 1007 | const char *zPort; /* Value of the --port option */ |
| 1008 | char *zBrowser; /* Name of web browser program */ |
| @@ -1031,22 +1030,10 @@ | |
| 1030 | iPort = mxPort = atoi(zPort); |
| 1031 | }else{ |
| 1032 | iPort = db_get_int("http-port", 8080); |
| 1033 | mxPort = iPort+100; |
| 1034 | } |
| 1035 | #ifndef __MINGW32__ |
| 1036 | /* Unix implementation */ |
| 1037 | if( isUiCmd ){ |
| 1038 | #if !defined(__DARWIN__) && !defined(__APPLE__) |
| 1039 | zBrowser = db_get("web-browser", 0); |
| 1040 |
+95
-6
| --- src/popen.c | ||
| +++ src/popen.c | ||
| @@ -18,26 +18,115 @@ | ||
| 18 | 18 | ** This file contains an implementation of a bi-directional popen(). |
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include "popen.h" |
| 22 | 22 | |
| 23 | +#ifdef __MINGW32__ | |
| 24 | +/* | |
| 25 | +** Print a fatal error and quit. | |
| 26 | +*/ | |
| 27 | +static void win32_fatal_error(const char *zMsg){ | |
| 28 | + fossil_fatal("%s"); | |
| 29 | +} | |
| 30 | +#endif | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | +#ifdef __MINGW32__ | |
| 35 | +/* | |
| 36 | +** On windows, create a child process and specify the stdin, stdout, | |
| 37 | +** and stderr channels for that process to use. | |
| 38 | +** | |
| 39 | +** Return the number of errors. | |
| 40 | +*/ | |
| 41 | +static int win32_create_child_process( | |
| 42 | + char *zCmd, /* The command that the child process will run */ | |
| 43 | + HANDLE hIn, /* Standard input */ | |
| 44 | + HANDLE hOut, /* Standard output */ | |
| 45 | + HANDLE hErr, /* Standard error */ | |
| 46 | + DWORD *pChildPid /* OUT: Child process handle */ | |
| 47 | +){ | |
| 48 | + STARTUPINFO si; | |
| 49 | + PROCESS_INFORMATION pi; | |
| 50 | + BOOL rc; | |
| 51 | + | |
| 52 | + memset(&si, 0, sizeof(si)); | |
| 53 | + si.cb = sizeof(si); | |
| 54 | + si.dwFlags = STARTF_USESTDHANDLES; | |
| 55 | + SetHandleInformation(hIn, HANDLE_FLAG_INHERIT, TRUE); | |
| 56 | + si.hStdInput = hIn; | |
| 57 | + SetHandleInformation(hOut, HANDLE_FLAG_INHERIT, TRUE); | |
| 58 | + si.hStdOutput = hOut; | |
| 59 | + SetHandleInformation(hErr, HANDLE_FLAG_INHERIT, TRUE); | |
| 60 | + si.hStdError = hErr; | |
| 61 | + rc = CreateProcess( | |
| 62 | + NULL, /* Application Name */ | |
| 63 | + zCmd, /* Command-line */ | |
| 64 | + NULL, /* Process attributes */ | |
| 65 | + NULL, /* Thread attributes */ | |
| 66 | + TRUE, /* Inherit Handles */ | |
| 67 | + 0, /* Create flags */ | |
| 68 | + NULL, /* Environment */ | |
| 69 | + NULL, /* Current directory */ | |
| 70 | + &si, /* Startup Info */ | |
| 71 | + &pi /* Process Info */ | |
| 72 | + ); | |
| 73 | + if( rc ){ | |
| 74 | + CloseHandle( pi.hProcess ); | |
| 75 | + CloseHandle( pi.hThread ); | |
| 76 | + *pChildPid = pi.dwProcessId; | |
| 77 | + }else{ | |
| 78 | + win32_fatal_error("cannot create child process"); | |
| 79 | + } | |
| 80 | + return rc!=0; | |
| 81 | +} | |
| 82 | +#endif | |
| 83 | + | |
| 23 | 84 | /* |
| 24 | 85 | ** Create a child process running shell command "zCmd". *ppOut is |
| 25 | 86 | ** a FILE that becomes the standard input of the child process. |
| 26 | 87 | ** (The caller writes to *ppOut in order to send text to the child.) |
| 27 | 88 | ** *ppIn is stdout from the child process. (The caller |
| 28 | 89 | ** reads from *ppIn in order to receive input from the child.) |
| 90 | +** Note that *ppIn is an unbuffered file descriptor, not a FILE. | |
| 29 | 91 | ** The process ID of the child is written into *pChildPid. |
| 30 | 92 | ** |
| 31 | 93 | ** Return the number of errors. |
| 32 | 94 | */ |
| 33 | -int popen2(const char *zCmd, FILE **ppIn, FILE **ppOut, int *pChildPid){ | |
| 95 | +int popen2(const char *zCmd, int *pfdIn, FILE **ppOut, int *pChildPid){ | |
| 34 | 96 | #ifdef __MINGW32__ |
| 35 | - return 1; /* Not implemented on windows, yet */ | |
| 97 | + HANDLE hStdinRd, hStdinWr, hStdoutRd, hStdoutWr, hStderr; | |
| 98 | + SECURITY_ATTRIBUTES saAttr; | |
| 99 | + DWORD childPid; | |
| 100 | + int fd; | |
| 101 | + | |
| 102 | + saAttr.nLength = sizeof(saAttr); | |
| 103 | + saAttr.bInheritHandle = TRUE; | |
| 104 | + saAttr.lpSecurityDescriptor = NULL; | |
| 105 | + hStderr = GetStdHandle(STD_ERROR_HANDLE); | |
| 106 | + if( !CreatePipe(&hStdoutRd, &hStdoutWr, &saAttr, 4096) ){ | |
| 107 | + win32_fatal_error("cannot create pipe for stdout"); | |
| 108 | + } | |
| 109 | + SetHandleInformation( hStdoutRd, HANDLE_FLAG_INHERIT, FALSE); | |
| 110 | + | |
| 111 | + if( !CreatePipe(&hStdinRd, &hStdinWr, &saAttr, 4096) ){ | |
| 112 | + win32_fatal_error("cannot create pipe for stdin"); | |
| 113 | + } | |
| 114 | + SetHandleInformation( hStdinWr, HANDLE_FLAG_INHERIT, FALSE); | |
| 115 | + | |
| 116 | + win32_create_child_process((char*)zCmd, | |
| 117 | + hStdinRd, hStdoutWr, hStderr,&childPid); | |
| 118 | + *pChildPid = childPid; | |
| 119 | + *pfdIn = _open_osfhandle(hStdoutRd, 0); | |
| 120 | + fd = _open_osfhandle(hStdoutWr, 0); | |
| 121 | + *ppOut = _fdopen(fd, "w"); | |
| 122 | + CloseHandle(hStdinRd); | |
| 123 | + CloseHandle(hStdoutWr); | |
| 124 | + return 0; | |
| 36 | 125 | #else |
| 37 | 126 | int pin[2], pout[2]; |
| 38 | - *ppIn = 0; | |
| 127 | + *pfdIn = 0; | |
| 39 | 128 | *ppOut = 0; |
| 40 | 129 | *pChildPid = 0; |
| 41 | 130 | |
| 42 | 131 | if( pipe(pin)<0 ){ |
| 43 | 132 | return 1; |
| @@ -69,11 +158,11 @@ | ||
| 69 | 158 | execl("/bin/sh", "/bin/sh", "-c", zCmd, (char*)0); |
| 70 | 159 | return 1; |
| 71 | 160 | }else{ |
| 72 | 161 | /* This is the parent process */ |
| 73 | 162 | close(pin[1]); |
| 74 | - *ppIn = fdopen(pin[0], "r"); | |
| 163 | + *pfdIn = pin[0]; | |
| 75 | 164 | close(pout[0]); |
| 76 | 165 | *ppOut = fdopen(pout[1], "w"); |
| 77 | 166 | return 0; |
| 78 | 167 | } |
| 79 | 168 | #endif |
| @@ -81,14 +170,14 @@ | ||
| 81 | 170 | |
| 82 | 171 | /* |
| 83 | 172 | ** Close the connection to a child process previously created using |
| 84 | 173 | ** popen2(). Kill off the child process, then close the pipes. |
| 85 | 174 | */ |
| 86 | -void pclose2(FILE *pIn, FILE *pOut, int childPid){ | |
| 175 | +void pclose2(int fdIn, FILE *pOut, int childPid){ | |
| 87 | 176 | #ifdef __MINGW32__ |
| 88 | 177 | /* Not implemented, yet */ |
| 89 | 178 | #else |
| 90 | 179 | kill(childPid, SIGINT); |
| 91 | - fclose(pIn); | |
| 180 | + close(fdIn); | |
| 92 | 181 | fclose(pOut); |
| 93 | 182 | #endif |
| 94 | 183 | } |
| 95 | 184 |
| --- src/popen.c | |
| +++ src/popen.c | |
| @@ -18,26 +18,115 @@ | |
| 18 | ** This file contains an implementation of a bi-directional popen(). |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "popen.h" |
| 22 | |
| 23 | /* |
| 24 | ** Create a child process running shell command "zCmd". *ppOut is |
| 25 | ** a FILE that becomes the standard input of the child process. |
| 26 | ** (The caller writes to *ppOut in order to send text to the child.) |
| 27 | ** *ppIn is stdout from the child process. (The caller |
| 28 | ** reads from *ppIn in order to receive input from the child.) |
| 29 | ** The process ID of the child is written into *pChildPid. |
| 30 | ** |
| 31 | ** Return the number of errors. |
| 32 | */ |
| 33 | int popen2(const char *zCmd, FILE **ppIn, FILE **ppOut, int *pChildPid){ |
| 34 | #ifdef __MINGW32__ |
| 35 | return 1; /* Not implemented on windows, yet */ |
| 36 | #else |
| 37 | int pin[2], pout[2]; |
| 38 | *ppIn = 0; |
| 39 | *ppOut = 0; |
| 40 | *pChildPid = 0; |
| 41 | |
| 42 | if( pipe(pin)<0 ){ |
| 43 | return 1; |
| @@ -69,11 +158,11 @@ | |
| 69 | execl("/bin/sh", "/bin/sh", "-c", zCmd, (char*)0); |
| 70 | return 1; |
| 71 | }else{ |
| 72 | /* This is the parent process */ |
| 73 | close(pin[1]); |
| 74 | *ppIn = fdopen(pin[0], "r"); |
| 75 | close(pout[0]); |
| 76 | *ppOut = fdopen(pout[1], "w"); |
| 77 | return 0; |
| 78 | } |
| 79 | #endif |
| @@ -81,14 +170,14 @@ | |
| 81 | |
| 82 | /* |
| 83 | ** Close the connection to a child process previously created using |
| 84 | ** popen2(). Kill off the child process, then close the pipes. |
| 85 | */ |
| 86 | void pclose2(FILE *pIn, FILE *pOut, int childPid){ |
| 87 | #ifdef __MINGW32__ |
| 88 | /* Not implemented, yet */ |
| 89 | #else |
| 90 | kill(childPid, SIGINT); |
| 91 | fclose(pIn); |
| 92 | fclose(pOut); |
| 93 | #endif |
| 94 | } |
| 95 |
| --- src/popen.c | |
| +++ src/popen.c | |
| @@ -18,26 +18,115 @@ | |
| 18 | ** This file contains an implementation of a bi-directional popen(). |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "popen.h" |
| 22 | |
| 23 | #ifdef __MINGW32__ |
| 24 | /* |
| 25 | ** Print a fatal error and quit. |
| 26 | */ |
| 27 | static void win32_fatal_error(const char *zMsg){ |
| 28 | fossil_fatal("%s"); |
| 29 | } |
| 30 | #endif |
| 31 | |
| 32 | |
| 33 | |
| 34 | #ifdef __MINGW32__ |
| 35 | /* |
| 36 | ** On windows, create a child process and specify the stdin, stdout, |
| 37 | ** and stderr channels for that process to use. |
| 38 | ** |
| 39 | ** Return the number of errors. |
| 40 | */ |
| 41 | static int win32_create_child_process( |
| 42 | char *zCmd, /* The command that the child process will run */ |
| 43 | HANDLE hIn, /* Standard input */ |
| 44 | HANDLE hOut, /* Standard output */ |
| 45 | HANDLE hErr, /* Standard error */ |
| 46 | DWORD *pChildPid /* OUT: Child process handle */ |
| 47 | ){ |
| 48 | STARTUPINFO si; |
| 49 | PROCESS_INFORMATION pi; |
| 50 | BOOL rc; |
| 51 | |
| 52 | memset(&si, 0, sizeof(si)); |
| 53 | si.cb = sizeof(si); |
| 54 | si.dwFlags = STARTF_USESTDHANDLES; |
| 55 | SetHandleInformation(hIn, HANDLE_FLAG_INHERIT, TRUE); |
| 56 | si.hStdInput = hIn; |
| 57 | SetHandleInformation(hOut, HANDLE_FLAG_INHERIT, TRUE); |
| 58 | si.hStdOutput = hOut; |
| 59 | SetHandleInformation(hErr, HANDLE_FLAG_INHERIT, TRUE); |
| 60 | si.hStdError = hErr; |
| 61 | rc = CreateProcess( |
| 62 | NULL, /* Application Name */ |
| 63 | zCmd, /* Command-line */ |
| 64 | NULL, /* Process attributes */ |
| 65 | NULL, /* Thread attributes */ |
| 66 | TRUE, /* Inherit Handles */ |
| 67 | 0, /* Create flags */ |
| 68 | NULL, /* Environment */ |
| 69 | NULL, /* Current directory */ |
| 70 | &si, /* Startup Info */ |
| 71 | &pi /* Process Info */ |
| 72 | ); |
| 73 | if( rc ){ |
| 74 | CloseHandle( pi.hProcess ); |
| 75 | CloseHandle( pi.hThread ); |
| 76 | *pChildPid = pi.dwProcessId; |
| 77 | }else{ |
| 78 | win32_fatal_error("cannot create child process"); |
| 79 | } |
| 80 | return rc!=0; |
| 81 | } |
| 82 | #endif |
| 83 | |
| 84 | /* |
| 85 | ** Create a child process running shell command "zCmd". *ppOut is |
| 86 | ** a FILE that becomes the standard input of the child process. |
| 87 | ** (The caller writes to *ppOut in order to send text to the child.) |
| 88 | ** *ppIn is stdout from the child process. (The caller |
| 89 | ** reads from *ppIn in order to receive input from the child.) |
| 90 | ** Note that *ppIn is an unbuffered file descriptor, not a FILE. |
| 91 | ** The process ID of the child is written into *pChildPid. |
| 92 | ** |
| 93 | ** Return the number of errors. |
| 94 | */ |
| 95 | int popen2(const char *zCmd, int *pfdIn, FILE **ppOut, int *pChildPid){ |
| 96 | #ifdef __MINGW32__ |
| 97 | HANDLE hStdinRd, hStdinWr, hStdoutRd, hStdoutWr, hStderr; |
| 98 | SECURITY_ATTRIBUTES saAttr; |
| 99 | DWORD childPid; |
| 100 | int fd; |
| 101 | |
| 102 | saAttr.nLength = sizeof(saAttr); |
| 103 | saAttr.bInheritHandle = TRUE; |
| 104 | saAttr.lpSecurityDescriptor = NULL; |
| 105 | hStderr = GetStdHandle(STD_ERROR_HANDLE); |
| 106 | if( !CreatePipe(&hStdoutRd, &hStdoutWr, &saAttr, 4096) ){ |
| 107 | win32_fatal_error("cannot create pipe for stdout"); |
| 108 | } |
| 109 | SetHandleInformation( hStdoutRd, HANDLE_FLAG_INHERIT, FALSE); |
| 110 | |
| 111 | if( !CreatePipe(&hStdinRd, &hStdinWr, &saAttr, 4096) ){ |
| 112 | win32_fatal_error("cannot create pipe for stdin"); |
| 113 | } |
| 114 | SetHandleInformation( hStdinWr, HANDLE_FLAG_INHERIT, FALSE); |
| 115 | |
| 116 | win32_create_child_process((char*)zCmd, |
| 117 | hStdinRd, hStdoutWr, hStderr,&childPid); |
| 118 | *pChildPid = childPid; |
| 119 | *pfdIn = _open_osfhandle(hStdoutRd, 0); |
| 120 | fd = _open_osfhandle(hStdoutWr, 0); |
| 121 | *ppOut = _fdopen(fd, "w"); |
| 122 | CloseHandle(hStdinRd); |
| 123 | CloseHandle(hStdoutWr); |
| 124 | return 0; |
| 125 | #else |
| 126 | int pin[2], pout[2]; |
| 127 | *pfdIn = 0; |
| 128 | *ppOut = 0; |
| 129 | *pChildPid = 0; |
| 130 | |
| 131 | if( pipe(pin)<0 ){ |
| 132 | return 1; |
| @@ -69,11 +158,11 @@ | |
| 158 | execl("/bin/sh", "/bin/sh", "-c", zCmd, (char*)0); |
| 159 | return 1; |
| 160 | }else{ |
| 161 | /* This is the parent process */ |
| 162 | close(pin[1]); |
| 163 | *pfdIn = pin[0]; |
| 164 | close(pout[0]); |
| 165 | *ppOut = fdopen(pout[1], "w"); |
| 166 | return 0; |
| 167 | } |
| 168 | #endif |
| @@ -81,14 +170,14 @@ | |
| 170 | |
| 171 | /* |
| 172 | ** Close the connection to a child process previously created using |
| 173 | ** popen2(). Kill off the child process, then close the pipes. |
| 174 | */ |
| 175 | void pclose2(int fdIn, FILE *pOut, int childPid){ |
| 176 | #ifdef __MINGW32__ |
| 177 | /* Not implemented, yet */ |
| 178 | #else |
| 179 | kill(childPid, SIGINT); |
| 180 | close(fdIn); |
| 181 | fclose(pOut); |
| 182 | #endif |
| 183 | } |
| 184 |
+43
-16
| --- src/url.c | ||
| +++ src/url.c | ||
| @@ -35,15 +35,13 @@ | ||
| 35 | 35 | ** |
| 36 | 36 | ** g.urlIsFile True if FILE: |
| 37 | 37 | ** g.urlIsHttps True if HTTPS: |
| 38 | 38 | ** g.urlIsSsh True if SSH: |
| 39 | 39 | ** g.urlProtocol "http" or "https" or "file" |
| 40 | -** g.urlName Hostname for HTTP: or HTTPS:. Filename for FILE: | |
| 41 | -** g.urlSshHost Hostname for SSH: tunnel | |
| 40 | +** g.urlName Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE: | |
| 42 | 41 | ** g.urlPort TCP port number for HTTP or HTTPS. |
| 43 | 42 | ** g.urlDfltPort Default TCP port number (80 or 443). |
| 44 | -** g.urlSshPort TCP port for SSH: tunnel | |
| 45 | 43 | ** g.urlPath Path name for HTTP or HTTPS. |
| 46 | 44 | ** g.urlUser Userid. |
| 47 | 45 | ** g.urlPasswd Password. |
| 48 | 46 | ** g.urlHostname HOST:PORT or just HOST if port is the default. |
| 49 | 47 | ** g.urlCanonical The URL in canonical form, omitting the password |
| @@ -52,11 +50,11 @@ | ||
| 52 | 50 | ** |
| 53 | 51 | ** http://userid:password@host:port/path?query#fragment |
| 54 | 52 | ** |
| 55 | 53 | ** SSH url format is: |
| 56 | 54 | ** |
| 57 | -** ssh://userid@host:port/fullpath | |
| 55 | +** ssh://userid@host:port/path?fossil=path/to/fossil.exe | |
| 58 | 56 | ** |
| 59 | 57 | */ |
| 60 | 58 | void url_parse(const char *zUrl){ |
| 61 | 59 | int i, j, c; |
| 62 | 60 | char *zFile = 0; |
| @@ -120,19 +118,19 @@ | ||
| 120 | 118 | ); |
| 121 | 119 | } |
| 122 | 120 | free(zLogin); |
| 123 | 121 | }else if( strncmp(zUrl, "ssh://", 6)==0 ){ |
| 124 | 122 | char *zLogin; |
| 125 | - int r; | |
| 123 | + char *zExe; | |
| 124 | + int i, j; | |
| 126 | 125 | g.urlIsFile = 0; |
| 127 | 126 | g.urlIsSsh = 1; |
| 128 | 127 | g.urlProtocol = "ssh"; |
| 129 | - sqlite3_randomness(sizeof(r), &r); | |
| 130 | - g.urlPort = 18800 + (r & 0x7fffff)%2000; | |
| 131 | - g.urlDfltPort = 80; | |
| 132 | - g.urlName = "127.0.0.1"; | |
| 133 | - g.urlHostname = g.urlName; | |
| 128 | + g.urlPort = 22; | |
| 129 | + g.urlDfltPort = 22; | |
| 130 | + g.urlPasswd = "(not-used)"; | |
| 131 | + g.urlFossil = "fossil"; | |
| 134 | 132 | for(i=6; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){} |
| 135 | 133 | if( c=='@' ){ |
| 136 | 134 | for(j=6; j<i && zUrl[j]!=':'; j++){} |
| 137 | 135 | g.urlUser = mprintf("%.*s", j-6, &zUrl[6]); |
| 138 | 136 | dehttpize(g.urlUser); |
| @@ -139,25 +137,54 @@ | ||
| 139 | 137 | if( j<i ){ |
| 140 | 138 | g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]); |
| 141 | 139 | dehttpize(g.urlPasswd); |
| 142 | 140 | } |
| 143 | 141 | for(j=i+1; (c=zUrl[j])!=0 && c!='/'; j++){} |
| 144 | - g.urlSshHost = mprintf("%.*s", j-i-1, &zUrl[i+1]); | |
| 142 | + g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]); | |
| 145 | 143 | i = j; |
| 146 | 144 | zLogin = mprintf("%t@", g.urlUser); |
| 147 | 145 | }else{ |
| 148 | - g.urlSshHost = mprintf("%.*s", i-6, &zUrl[6]); | |
| 146 | + g.urlName = mprintf("%.*s", i-6, &zUrl[6]); | |
| 149 | 147 | zLogin = mprintf(""); |
| 150 | 148 | } |
| 151 | - url_tolower(g.urlSshHost); | |
| 149 | + url_tolower(g.urlName); | |
| 150 | + g.urlHostname = g.urlName; | |
| 152 | 151 | g.urlPath = mprintf(&zUrl[i+1]); |
| 152 | + for(i=0; g.urlPath[i] && g.urlPath[i]!='?'; i++){} | |
| 153 | + if( g.urlPath[i] ){ | |
| 154 | + g.urlPath[i] = 0; | |
| 155 | + i++; | |
| 156 | + } | |
| 157 | + zExe = mprintf(""); | |
| 158 | + while( g.urlPath[i]!=0 ){ | |
| 159 | + char *zName, *zValue; | |
| 160 | + zName = &g.urlPath[i]; | |
| 161 | + zValue = zName; | |
| 162 | + while( g.urlPath[i] && g.urlPath[i]!='=' ){ i++; } | |
| 163 | + if( g.urlPath[i]=='=' ){ | |
| 164 | + g.urlPath[i] = 0; | |
| 165 | + i++; | |
| 166 | + zValue = &g.urlPath[i]; | |
| 167 | + while( g.urlPath[i] && g.urlPath[i]!='&' ){ i++; } | |
| 168 | + } | |
| 169 | + if( g.urlPath[i] ){ | |
| 170 | + g.urlPath[i] = 0; | |
| 171 | + i++; | |
| 172 | + } | |
| 173 | + if( strcmp(zName,"fossil")==0 ){ | |
| 174 | + g.urlFossil = zValue; | |
| 175 | + dehttpize(g.urlFossil); | |
| 176 | + zExe = mprintf("?fossil=%T", g.urlFossil); | |
| 177 | + } | |
| 178 | + } | |
| 153 | 179 | dehttpize(g.urlPath); |
| 154 | 180 | g.urlCanonical = mprintf( |
| 155 | - "ssh://%s%T/%T", | |
| 156 | - zLogin, g.urlSshHost, g.urlPath | |
| 181 | + "ssh://%s%T/%T%s", | |
| 182 | + zLogin, g.urlName, g.urlPath, zExe | |
| 157 | 183 | ); |
| 158 | 184 | free(zLogin); |
| 185 | + free(zExe); | |
| 159 | 186 | }else if( strncmp(zUrl, "file:", 5)==0 ){ |
| 160 | 187 | g.urlIsFile = 1; |
| 161 | 188 | if( zUrl[5]=='/' && zUrl[6]=='/' ){ |
| 162 | 189 | i = 7; |
| 163 | 190 | }else{ |
| @@ -205,18 +232,18 @@ | ||
| 205 | 232 | printf("g.urlIsFile = %d\n", g.urlIsFile); |
| 206 | 233 | printf("g.urlIsHttps = %d\n", g.urlIsHttps); |
| 207 | 234 | printf("g.urlIsSsh = %d\n", g.urlIsSsh); |
| 208 | 235 | printf("g.urlProtocol = %s\n", g.urlProtocol); |
| 209 | 236 | printf("g.urlName = %s\n", g.urlName); |
| 210 | - printf("g.urlSshHost = %s\n", g.urlSshHost); | |
| 211 | 237 | printf("g.urlPort = %d\n", g.urlPort); |
| 212 | 238 | printf("g.urlDfltPort = %d\n", g.urlDfltPort); |
| 213 | 239 | printf("g.urlHostname = %s\n", g.urlHostname); |
| 214 | 240 | printf("g.urlPath = %s\n", g.urlPath); |
| 215 | 241 | printf("g.urlUser = %s\n", g.urlUser); |
| 216 | 242 | printf("g.urlPasswd = %s\n", g.urlPasswd); |
| 217 | 243 | printf("g.urlCanonical = %s\n", g.urlCanonical); |
| 244 | + printf("g.urlFossil = %s\n", g.urlFossil); | |
| 218 | 245 | if( g.urlIsFile || g.urlIsSsh ) break; |
| 219 | 246 | if( i==0 ){ |
| 220 | 247 | printf("********\n"); |
| 221 | 248 | url_enable_proxy("Using proxy: "); |
| 222 | 249 | } |
| 223 | 250 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -35,15 +35,13 @@ | |
| 35 | ** |
| 36 | ** g.urlIsFile True if FILE: |
| 37 | ** g.urlIsHttps True if HTTPS: |
| 38 | ** g.urlIsSsh True if SSH: |
| 39 | ** g.urlProtocol "http" or "https" or "file" |
| 40 | ** g.urlName Hostname for HTTP: or HTTPS:. Filename for FILE: |
| 41 | ** g.urlSshHost Hostname for SSH: tunnel |
| 42 | ** g.urlPort TCP port number for HTTP or HTTPS. |
| 43 | ** g.urlDfltPort Default TCP port number (80 or 443). |
| 44 | ** g.urlSshPort TCP port for SSH: tunnel |
| 45 | ** g.urlPath Path name for HTTP or HTTPS. |
| 46 | ** g.urlUser Userid. |
| 47 | ** g.urlPasswd Password. |
| 48 | ** g.urlHostname HOST:PORT or just HOST if port is the default. |
| 49 | ** g.urlCanonical The URL in canonical form, omitting the password |
| @@ -52,11 +50,11 @@ | |
| 52 | ** |
| 53 | ** http://userid:password@host:port/path?query#fragment |
| 54 | ** |
| 55 | ** SSH url format is: |
| 56 | ** |
| 57 | ** ssh://userid@host:port/fullpath |
| 58 | ** |
| 59 | */ |
| 60 | void url_parse(const char *zUrl){ |
| 61 | int i, j, c; |
| 62 | char *zFile = 0; |
| @@ -120,19 +118,19 @@ | |
| 120 | ); |
| 121 | } |
| 122 | free(zLogin); |
| 123 | }else if( strncmp(zUrl, "ssh://", 6)==0 ){ |
| 124 | char *zLogin; |
| 125 | int r; |
| 126 | g.urlIsFile = 0; |
| 127 | g.urlIsSsh = 1; |
| 128 | g.urlProtocol = "ssh"; |
| 129 | sqlite3_randomness(sizeof(r), &r); |
| 130 | g.urlPort = 18800 + (r & 0x7fffff)%2000; |
| 131 | g.urlDfltPort = 80; |
| 132 | g.urlName = "127.0.0.1"; |
| 133 | g.urlHostname = g.urlName; |
| 134 | for(i=6; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){} |
| 135 | if( c=='@' ){ |
| 136 | for(j=6; j<i && zUrl[j]!=':'; j++){} |
| 137 | g.urlUser = mprintf("%.*s", j-6, &zUrl[6]); |
| 138 | dehttpize(g.urlUser); |
| @@ -139,25 +137,54 @@ | |
| 139 | if( j<i ){ |
| 140 | g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]); |
| 141 | dehttpize(g.urlPasswd); |
| 142 | } |
| 143 | for(j=i+1; (c=zUrl[j])!=0 && c!='/'; j++){} |
| 144 | g.urlSshHost = mprintf("%.*s", j-i-1, &zUrl[i+1]); |
| 145 | i = j; |
| 146 | zLogin = mprintf("%t@", g.urlUser); |
| 147 | }else{ |
| 148 | g.urlSshHost = mprintf("%.*s", i-6, &zUrl[6]); |
| 149 | zLogin = mprintf(""); |
| 150 | } |
| 151 | url_tolower(g.urlSshHost); |
| 152 | g.urlPath = mprintf(&zUrl[i+1]); |
| 153 | dehttpize(g.urlPath); |
| 154 | g.urlCanonical = mprintf( |
| 155 | "ssh://%s%T/%T", |
| 156 | zLogin, g.urlSshHost, g.urlPath |
| 157 | ); |
| 158 | free(zLogin); |
| 159 | }else if( strncmp(zUrl, "file:", 5)==0 ){ |
| 160 | g.urlIsFile = 1; |
| 161 | if( zUrl[5]=='/' && zUrl[6]=='/' ){ |
| 162 | i = 7; |
| 163 | }else{ |
| @@ -205,18 +232,18 @@ | |
| 205 | printf("g.urlIsFile = %d\n", g.urlIsFile); |
| 206 | printf("g.urlIsHttps = %d\n", g.urlIsHttps); |
| 207 | printf("g.urlIsSsh = %d\n", g.urlIsSsh); |
| 208 | printf("g.urlProtocol = %s\n", g.urlProtocol); |
| 209 | printf("g.urlName = %s\n", g.urlName); |
| 210 | printf("g.urlSshHost = %s\n", g.urlSshHost); |
| 211 | printf("g.urlPort = %d\n", g.urlPort); |
| 212 | printf("g.urlDfltPort = %d\n", g.urlDfltPort); |
| 213 | printf("g.urlHostname = %s\n", g.urlHostname); |
| 214 | printf("g.urlPath = %s\n", g.urlPath); |
| 215 | printf("g.urlUser = %s\n", g.urlUser); |
| 216 | printf("g.urlPasswd = %s\n", g.urlPasswd); |
| 217 | printf("g.urlCanonical = %s\n", g.urlCanonical); |
| 218 | if( g.urlIsFile || g.urlIsSsh ) break; |
| 219 | if( i==0 ){ |
| 220 | printf("********\n"); |
| 221 | url_enable_proxy("Using proxy: "); |
| 222 | } |
| 223 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -35,15 +35,13 @@ | |
| 35 | ** |
| 36 | ** g.urlIsFile True if FILE: |
| 37 | ** g.urlIsHttps True if HTTPS: |
| 38 | ** g.urlIsSsh True if SSH: |
| 39 | ** g.urlProtocol "http" or "https" or "file" |
| 40 | ** g.urlName Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE: |
| 41 | ** g.urlPort TCP port number for HTTP or HTTPS. |
| 42 | ** g.urlDfltPort Default TCP port number (80 or 443). |
| 43 | ** g.urlPath Path name for HTTP or HTTPS. |
| 44 | ** g.urlUser Userid. |
| 45 | ** g.urlPasswd Password. |
| 46 | ** g.urlHostname HOST:PORT or just HOST if port is the default. |
| 47 | ** g.urlCanonical The URL in canonical form, omitting the password |
| @@ -52,11 +50,11 @@ | |
| 50 | ** |
| 51 | ** http://userid:password@host:port/path?query#fragment |
| 52 | ** |
| 53 | ** SSH url format is: |
| 54 | ** |
| 55 | ** ssh://userid@host:port/path?fossil=path/to/fossil.exe |
| 56 | ** |
| 57 | */ |
| 58 | void url_parse(const char *zUrl){ |
| 59 | int i, j, c; |
| 60 | char *zFile = 0; |
| @@ -120,19 +118,19 @@ | |
| 118 | ); |
| 119 | } |
| 120 | free(zLogin); |
| 121 | }else if( strncmp(zUrl, "ssh://", 6)==0 ){ |
| 122 | char *zLogin; |
| 123 | char *zExe; |
| 124 | int i, j; |
| 125 | g.urlIsFile = 0; |
| 126 | g.urlIsSsh = 1; |
| 127 | g.urlProtocol = "ssh"; |
| 128 | g.urlPort = 22; |
| 129 | g.urlDfltPort = 22; |
| 130 | g.urlPasswd = "(not-used)"; |
| 131 | g.urlFossil = "fossil"; |
| 132 | for(i=6; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){} |
| 133 | if( c=='@' ){ |
| 134 | for(j=6; j<i && zUrl[j]!=':'; j++){} |
| 135 | g.urlUser = mprintf("%.*s", j-6, &zUrl[6]); |
| 136 | dehttpize(g.urlUser); |
| @@ -139,25 +137,54 @@ | |
| 137 | if( j<i ){ |
| 138 | g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]); |
| 139 | dehttpize(g.urlPasswd); |
| 140 | } |
| 141 | for(j=i+1; (c=zUrl[j])!=0 && c!='/'; j++){} |
| 142 | g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]); |
| 143 | i = j; |
| 144 | zLogin = mprintf("%t@", g.urlUser); |
| 145 | }else{ |
| 146 | g.urlName = mprintf("%.*s", i-6, &zUrl[6]); |
| 147 | zLogin = mprintf(""); |
| 148 | } |
| 149 | url_tolower(g.urlName); |
| 150 | g.urlHostname = g.urlName; |
| 151 | g.urlPath = mprintf(&zUrl[i+1]); |
| 152 | for(i=0; g.urlPath[i] && g.urlPath[i]!='?'; i++){} |
| 153 | if( g.urlPath[i] ){ |
| 154 | g.urlPath[i] = 0; |
| 155 | i++; |
| 156 | } |
| 157 | zExe = mprintf(""); |
| 158 | while( g.urlPath[i]!=0 ){ |
| 159 | char *zName, *zValue; |
| 160 | zName = &g.urlPath[i]; |
| 161 | zValue = zName; |
| 162 | while( g.urlPath[i] && g.urlPath[i]!='=' ){ i++; } |
| 163 | if( g.urlPath[i]=='=' ){ |
| 164 | g.urlPath[i] = 0; |
| 165 | i++; |
| 166 | zValue = &g.urlPath[i]; |
| 167 | while( g.urlPath[i] && g.urlPath[i]!='&' ){ i++; } |
| 168 | } |
| 169 | if( g.urlPath[i] ){ |
| 170 | g.urlPath[i] = 0; |
| 171 | i++; |
| 172 | } |
| 173 | if( strcmp(zName,"fossil")==0 ){ |
| 174 | g.urlFossil = zValue; |
| 175 | dehttpize(g.urlFossil); |
| 176 | zExe = mprintf("?fossil=%T", g.urlFossil); |
| 177 | } |
| 178 | } |
| 179 | dehttpize(g.urlPath); |
| 180 | g.urlCanonical = mprintf( |
| 181 | "ssh://%s%T/%T%s", |
| 182 | zLogin, g.urlName, g.urlPath, zExe |
| 183 | ); |
| 184 | free(zLogin); |
| 185 | free(zExe); |
| 186 | }else if( strncmp(zUrl, "file:", 5)==0 ){ |
| 187 | g.urlIsFile = 1; |
| 188 | if( zUrl[5]=='/' && zUrl[6]=='/' ){ |
| 189 | i = 7; |
| 190 | }else{ |
| @@ -205,18 +232,18 @@ | |
| 232 | printf("g.urlIsFile = %d\n", g.urlIsFile); |
| 233 | printf("g.urlIsHttps = %d\n", g.urlIsHttps); |
| 234 | printf("g.urlIsSsh = %d\n", g.urlIsSsh); |
| 235 | printf("g.urlProtocol = %s\n", g.urlProtocol); |
| 236 | printf("g.urlName = %s\n", g.urlName); |
| 237 | printf("g.urlPort = %d\n", g.urlPort); |
| 238 | printf("g.urlDfltPort = %d\n", g.urlDfltPort); |
| 239 | printf("g.urlHostname = %s\n", g.urlHostname); |
| 240 | printf("g.urlPath = %s\n", g.urlPath); |
| 241 | printf("g.urlUser = %s\n", g.urlUser); |
| 242 | printf("g.urlPasswd = %s\n", g.urlPasswd); |
| 243 | printf("g.urlCanonical = %s\n", g.urlCanonical); |
| 244 | printf("g.urlFossil = %s\n", g.urlFossil); |
| 245 | if( g.urlIsFile || g.urlIsSsh ) break; |
| 246 | if( i==0 ){ |
| 247 | printf("********\n"); |
| 248 | url_enable_proxy("Using proxy: "); |
| 249 | } |
| 250 |