| | @@ -64,10 +64,11 @@ |
| 64 | 64 | C(ASSERT,"Assertion failed"); |
| 65 | 65 | C(ALLOC,"Resource allocation failed"); |
| 66 | 66 | C(NYI,"Not yet implemented."); |
| 67 | 67 | C(AUTH,"Authentication error"); |
| 68 | 68 | C(LOGIN_FAILED,"Login failed"); |
| 69 | + C(LOGIN_FAILED_NOSEED,"Anonymous login attempt was missing password seed."); |
| 69 | 70 | C(LOGIN_FAILED_NONAME,"Login failed - name not supplied"); |
| 70 | 71 | C(LOGIN_FAILED_NOPW,"Login failed - password not supplied"); |
| 71 | 72 | C(LOGIN_FAILED_NOTFOUND,"Login failed - no match found"); |
| 72 | 73 | C(MISSING_AUTH,"Authentication info missing from request"); |
| 73 | 74 | C(DENIED,"Access denied"); |
| | @@ -652,11 +653,11 @@ |
| 652 | 653 | ** If resultCode is non-zero and payload is not NULL then this |
| 653 | 654 | ** function calls cson_value_free(payload) and does not insert the |
| 654 | 655 | ** payload into the response. |
| 655 | 656 | ** |
| 656 | 657 | */ |
| 657 | | -cson_value * json_response_skeleton( int resultCode, |
| 658 | +cson_value * json_create_response( int resultCode, |
| 658 | 659 | cson_value * payload, |
| 659 | 660 | char const * pMsg ){ |
| 660 | 661 | cson_value * v = NULL; |
| 661 | 662 | cson_value * tmp = NULL; |
| 662 | 663 | cson_object * o = NULL; |
| | @@ -713,19 +714,33 @@ |
| 713 | 714 | tmp = cson_value_new_string(pMsg,strlen(pMsg)); |
| 714 | 715 | SET("resultText"); |
| 715 | 716 | } |
| 716 | 717 | tmp = json_getenv("requestId"); |
| 717 | 718 | if( tmp ) cson_object_set( o, "requestId", tmp ); |
| 719 | + |
| 720 | + if(0){ |
| 721 | + if(g.json.cmd.v){/* this is only intended for my own testing...*/ |
| 722 | + tmp = g.json.cmd.v; |
| 723 | + SET("$commandPath"); |
| 724 | + } |
| 725 | + if(g.json.param.v){/* this is only intended for my own testing...*/ |
| 726 | + tmp = g.json.param.v; |
| 727 | + SET("$params"); |
| 728 | + } |
| 729 | + } |
| 730 | + |
| 731 | + /* Only add the payload to SUCCESS responses. Else delete it. */ |
| 718 | 732 | if( NULL != payload ){ |
| 719 | 733 | if( resultCode ){ |
| 720 | 734 | cson_value_free(payload); |
| 721 | 735 | payload = NULL; |
| 722 | 736 | }else{ |
| 723 | 737 | tmp = payload; |
| 724 | 738 | SET("payload"); |
| 725 | 739 | } |
| 726 | 740 | } |
| 741 | + |
| 727 | 742 | #undef SET |
| 728 | 743 | |
| 729 | 744 | if(0){/*Only for debuggering, add some info to the response.*/ |
| 730 | 745 | tmp = cson_value_new_integer( g.json.cmd.offset ); |
| 731 | 746 | cson_object_set( o, "cmd.offset", tmp ); |
| | @@ -765,11 +780,11 @@ |
| 765 | 780 | cson_value * resp = NULL; |
| 766 | 781 | rc = json_dumbdown_rc(rc); |
| 767 | 782 | if( rc && !msg ){ |
| 768 | 783 | msg = json_err_str(rc); |
| 769 | 784 | } |
| 770 | | - resp = json_response_skeleton(rc, NULL, msg); |
| 785 | + resp = json_create_response(rc, NULL, msg); |
| 771 | 786 | if( g.isCGI ){ |
| 772 | 787 | Blob buf = empty_blob; |
| 773 | 788 | cgi_reset_content(); |
| 774 | 789 | cson_output_Blob( resp, &buf, &g.json.outOpt ); |
| 775 | 790 | cgi_set_content(&buf); |
| | @@ -892,11 +907,11 @@ |
| 892 | 907 | cson_value * json_page_login(void){ |
| 893 | 908 | static char preciseErrors = |
| 894 | 909 | #if 0 |
| 895 | 910 | g.json.errorDetailParanoia ? 0 : 1 |
| 896 | 911 | #else |
| 897 | | - 0 |
| 912 | + 1 |
| 898 | 913 | #endif |
| 899 | 914 | ; |
| 900 | 915 | /* |
| 901 | 916 | FIXME: we want to check the GET/POST args in this order: |
| 902 | 917 | |
| | @@ -912,10 +927,13 @@ |
| 912 | 927 | checks then both forms work. Strangely enough, the |
| 913 | 928 | "p"/"password" check is not affected by this. |
| 914 | 929 | */ |
| 915 | 930 | char const * name = cson_value_get_cstr(json_payload_property("name")); |
| 916 | 931 | char const * pw = NULL; |
| 932 | + char const * anonSeed = NULL; |
| 933 | + cson_value * payload = NULL; |
| 934 | + int uid = 0; |
| 917 | 935 | if( !name ){ |
| 918 | 936 | name = PD("n",NULL); |
| 919 | 937 | if( !name ){ |
| 920 | 938 | name = PD("name",NULL); |
| 921 | 939 | if( !name ){ |
| | @@ -924,11 +942,11 @@ |
| 924 | 942 | : FSL_JSON_E_LOGIN_FAILED; |
| 925 | 943 | return NULL; |
| 926 | 944 | } |
| 927 | 945 | } |
| 928 | 946 | } |
| 929 | | - |
| 947 | + |
| 930 | 948 | pw = cson_value_get_cstr(json_payload_property("password")); |
| 931 | 949 | if( !pw ){ |
| 932 | 950 | pw = PD("p",NULL); |
| 933 | 951 | if( !pw ){ |
| 934 | 952 | pw = PD("password",NULL); |
| | @@ -937,37 +955,78 @@ |
| 937 | 955 | if(!pw){ |
| 938 | 956 | g.json.resultCode = preciseErrors |
| 939 | 957 | ? FSL_JSON_E_LOGIN_FAILED_NOPW |
| 940 | 958 | : FSL_JSON_E_LOGIN_FAILED; |
| 941 | 959 | return NULL; |
| 942 | | - }else{ |
| 943 | | - cson_value * payload = NULL; |
| 944 | | - int uid = 0; |
| 960 | + } |
| 961 | + |
| 962 | + if(0 == strcmp("anonymous",name)){ |
| 963 | + /* check captcha/seed values... */ |
| 964 | + enum { SeedBufLen = 100 /* in some JSON tests i once actually got an |
| 965 | + 80-digit number. |
| 966 | + */ |
| 967 | + }; |
| 968 | + static char seedBuffer[SeedBufLen]; |
| 969 | + seedBuffer[0] = 0; |
| 970 | + cson_value const * jseed = json_getenv("anonymousSeed"); |
| 971 | + if( !jseed ){ |
| 972 | + jseed = json_payload_property("anonymousSeed"); |
| 973 | + if( !jseed ){ |
| 974 | + jseed = json_getenv("cs") /* name used by HTML interface */; |
| 975 | + } |
| 976 | + } |
| 977 | + if(jseed){ |
| 978 | + if( cson_value_is_number(jseed) ){ |
| 979 | + sprintf(seedBuffer, "%"CSON_INT_T_PFMT, cson_value_get_integer(jseed)); |
| 980 | + anonSeed = seedBuffer; |
| 981 | + }else if( cson_value_is_string(jseed) ){ |
| 982 | + anonSeed = cson_string_cstr(cson_value_get_string(jseed)); |
| 983 | + } |
| 984 | + } |
| 985 | + if(!anonSeed){ |
| 986 | + g.json.resultCode = preciseErrors |
| 987 | + ? FSL_JSON_E_LOGIN_FAILED_NOSEED |
| 988 | + : FSL_JSON_E_LOGIN_FAILED; |
| 989 | + return NULL; |
| 990 | + } |
| 991 | + } |
| 992 | + |
| 945 | 993 | #if 0 |
| 994 | + { |
| 946 | 995 | /* only for debugging the PD()-incorrect-result problem */ |
| 947 | 996 | cson_object * o = NULL; |
| 948 | 997 | uid = login_search_uid( name, pw ); |
| 949 | 998 | payload = cson_value_new_object(); |
| 950 | 999 | o = cson_value_get_object(payload); |
| 951 | 1000 | cson_object_set( o, "n", cson_value_new_string(name,strlen(name))); |
| 952 | 1001 | cson_object_set( o, "p", cson_value_new_string(pw,strlen(pw))); |
| 953 | 1002 | return payload; |
| 1003 | + } |
| 954 | 1004 | #else |
| 955 | | - uid = login_search_uid( name, pw ); |
| 956 | | - if( !uid ){ |
| 957 | | - g.json.resultCode = preciseErrors |
| 958 | | - ? FSL_JSON_E_LOGIN_FAILED_NOTFOUND |
| 959 | | - : FSL_JSON_E_LOGIN_FAILED; |
| 1005 | + uid = anonSeed |
| 1006 | + ? login_is_valid_anonymous(name, pw, anonSeed) |
| 1007 | + : login_search_uid(name, pw) |
| 1008 | + ; |
| 1009 | + if( !uid ){ |
| 1010 | + g.json.resultCode = preciseErrors |
| 1011 | + ? FSL_JSON_E_LOGIN_FAILED_NOTFOUND |
| 1012 | + : FSL_JSON_E_LOGIN_FAILED; |
| 1013 | + return NULL; |
| 1014 | + }else{ |
| 1015 | + char * cookie = NULL; |
| 1016 | + if(anonSeed){ |
| 1017 | + login_set_anon_cookie(NULL, &cookie); |
| 960 | 1018 | }else{ |
| 961 | | - char * cookie = NULL; |
| 962 | 1019 | login_set_user_cookie(name, uid, &cookie); |
| 963 | | - payload = cson_value_new_string( cookie, strlen(cookie) ); |
| 964 | | - free(cookie); |
| 965 | 1020 | } |
| 1021 | + payload = cookie |
| 1022 | + ? cson_value_new_string( cookie, strlen(cookie) ) |
| 1023 | + : cson_value_null(); |
| 1024 | + free(cookie); |
| 966 | 1025 | return payload; |
| 967 | | -#endif |
| 968 | 1026 | } |
| 1027 | +#endif |
| 969 | 1028 | } |
| 970 | 1029 | |
| 971 | 1030 | /* |
| 972 | 1031 | ** Impl of /json/logout. |
| 973 | 1032 | ** |
| | @@ -991,10 +1050,28 @@ |
| 991 | 1050 | login_clear_login_data(); |
| 992 | 1051 | g.json.authToken = NULL /* memory is owned elsewhere.*/; |
| 993 | 1052 | } |
| 994 | 1053 | return NULL; |
| 995 | 1054 | } |
| 1055 | + |
| 1056 | +/* |
| 1057 | +** Implementation of the /json/anonymousPassword page. |
| 1058 | +*/ |
| 1059 | +cson_value * json_page_anon_password(void){ |
| 1060 | + cson_value * v = cson_value_new_object(); |
| 1061 | + cson_object * o = cson_value_get_object(v); |
| 1062 | + unsigned const int seed = captcha_seed(); |
| 1063 | + char const * zCaptcha = captcha_decode(seed); |
| 1064 | + cson_object_set(o, "seed", |
| 1065 | + cson_value_new_integer( (cson_int_t)seed ) |
| 1066 | + ); |
| 1067 | + cson_object_set(o, "captcha", |
| 1068 | + cson_value_new_string( zCaptcha, strlen(zCaptcha) ) |
| 1069 | + ); |
| 1070 | + return v; |
| 1071 | +} |
| 1072 | + |
| 996 | 1073 | |
| 997 | 1074 | /* |
| 998 | 1075 | ** Implementation of the /json/stat page/command. |
| 999 | 1076 | ** |
| 1000 | 1077 | */ |
| | @@ -1006,11 +1083,10 @@ |
| 1006 | 1083 | char zBuf[BufLen]; |
| 1007 | 1084 | cson_value * jv = NULL; |
| 1008 | 1085 | cson_object * jo = NULL; |
| 1009 | 1086 | cson_value * jv2 = NULL; |
| 1010 | 1087 | cson_object * jo2 = NULL; |
| 1011 | | - login_check_credentials(); |
| 1012 | 1088 | if( !g.perm.Read ){ |
| 1013 | 1089 | g.json.resultCode = FSL_JSON_E_DENIED; |
| 1014 | 1090 | return NULL; |
| 1015 | 1091 | } |
| 1016 | 1092 | #define SETBUF(O,K) cson_object_set(O, K, cson_value_new_string(zBuf, strlen(zBuf))); |
| | @@ -1116,10 +1192,11 @@ |
| 1116 | 1192 | ** Mapping of names to JSON pages/commands. Each name is a subpath of |
| 1117 | 1193 | ** /json (in CGI mode) or a subcommand of the json command in CLI mode |
| 1118 | 1194 | */ |
| 1119 | 1195 | static const JsonPageDef JsonPageDefs[] = { |
| 1120 | 1196 | /* please keep alphabetically sorted (case-insensitive) for maintenance reasons. */ |
| 1197 | +{"anonymousPassword", json_page_anon_password, 1}, |
| 1121 | 1198 | {"cap", json_page_cap, 0}, |
| 1122 | 1199 | {"HAI",json_page_version,0}, |
| 1123 | 1200 | {"login",json_page_login,1/*should be >0. Only 0 for dev/testing purposes.*/}, |
| 1124 | 1201 | {"logout",json_page_logout,1/*should be >0. Only 0 for dev/testing purposes.*/}, |
| 1125 | 1202 | {"stat",json_page_stat,0}, |
| | @@ -1156,11 +1233,11 @@ |
| 1156 | 1233 | } |
| 1157 | 1234 | if( g.json.resultCode ){ |
| 1158 | 1235 | json_err(g.json.resultCode, NULL, 0); |
| 1159 | 1236 | }else{ |
| 1160 | 1237 | blob_zero(&buf); |
| 1161 | | - root = json_response_skeleton(rc, payload, NULL); |
| 1238 | + root = json_create_response(rc, payload, NULL); |
| 1162 | 1239 | cson_output_Blob( root, &buf, NULL ); |
| 1163 | 1240 | cson_value_free(root); |
| 1164 | 1241 | cgi_set_content(&buf)/*takes ownership of the buf memory*/; |
| 1165 | 1242 | } |
| 1166 | 1243 | } |
| | @@ -1219,11 +1296,11 @@ |
| 1219 | 1296 | payload = (pageDef->func)(); |
| 1220 | 1297 | } |
| 1221 | 1298 | if( g.json.resultCode ){ |
| 1222 | 1299 | json_err(g.json.resultCode, NULL, 1); |
| 1223 | 1300 | }else{ |
| 1224 | | - payload = json_response_skeleton(rc, payload, NULL); |
| 1301 | + payload = json_create_response(rc, payload, NULL); |
| 1225 | 1302 | cson_output_FILE( payload, stdout, &g.json.outOpt ); |
| 1226 | 1303 | cson_value_free( payload ); |
| 1227 | 1304 | if((0 != rc) && !g.isCGI){ |
| 1228 | 1305 | /* FIXME: we need a way of passing this error back |
| 1229 | 1306 | up to the routine which called this callback. |
| 1230 | 1307 | |