Fossil SCM
Refactor how /attachadd reads POSTed files so that we can reuse it in the new forum editor.
Commit
4a8bebc81c0237a5dca3d5d305f4dac9bed47d8653ab7c029c8ac63867c40559
Parent
c02e4fcb5cb165e…
1 file changed
+67
-27
+67
-27
| --- src/attach.c | ||
| +++ src/attach.c | ||
| @@ -780,18 +780,12 @@ | ||
| 780 | 780 | void attachadd_ajax_post(void){ |
| 781 | 781 | const char *zTarget; |
| 782 | 782 | char *zExtraFree = 0; |
| 783 | 783 | int eTgtType = 0; |
| 784 | 784 | int bNeedsModeration = 0; |
| 785 | - int i; | |
| 786 | 785 | int goodCaptcha = 1; |
| 787 | - int szLimit; /* attachment-max-size setting */ | |
| 788 | 786 | int bRollback = 0; /* Roll back if true. */ |
| 789 | - char aKeyPrefix[20]; /* Buffer for key "file%d" */ | |
| 790 | - char aKeySize[30]; /* Buffer for key "file%d:bytes" */ | |
| 791 | - char aKeyName[30]; /* Buffer for key "file%d:filename" */ | |
| 792 | - char aKeyDesc[30]; /* Buffer for key "file%d_desc" */ | |
| 793 | 787 | |
| 794 | 788 | if( ! ajax_route_bootstrap(0, 1) ){ |
| 795 | 789 | return; |
| 796 | 790 | }else if( !(goodCaptcha = captcha_is_correct(0)) ){ |
| 797 | 791 | goto ajax_post_403; |
| @@ -869,14 +863,65 @@ | ||
| 869 | 863 | bNeedsModeration = wiki_need_moderation(0); |
| 870 | 864 | break; |
| 871 | 865 | } |
| 872 | 866 | } |
| 873 | 867 | |
| 868 | + if( attachments_from_POST_ajax(zTarget, bNeedsModeration)>=0 ){ | |
| 869 | + CX("}"); | |
| 870 | + if( atoi(PD("dryrun","0"))>0 ){ | |
| 871 | + bRollback = 1; | |
| 872 | + } | |
| 873 | + }/*else error response was set up*/ | |
| 874 | + fossil_free(zExtraFree); | |
| 875 | + db_end_transaction(bRollback); | |
| 876 | + return; | |
| 877 | +ajax_post_403: | |
| 878 | + db_rollback_transaction(); | |
| 879 | + ajax_route_error(403, "Permission denied."); | |
| 880 | + return; | |
| 881 | +ajax_post_404: | |
| 882 | + db_rollback_transaction(); | |
| 883 | + ajax_route_error(404, "Target not found."); | |
| 884 | + return; | |
| 885 | +} | |
| 886 | + | |
| 887 | +/* | |
| 888 | +** A helper for AJAX-style routines which accept file attachments via | |
| 889 | +** POST. zTarget must be a full attachment target. bNeedsModeration | |
| 890 | +** must be true if the attachment requires moderation. | |
| 891 | +** | |
| 892 | +** It is up to the caller to have validated all security measures | |
| 893 | +** before calling this. | |
| 894 | +** | |
| 895 | +** This looks for POSTed files names "file1".."fileN", stopping when | |
| 896 | +** it finds no entry. Returns the number of entries attached to the | |
| 897 | +** target or a negative value on error (in which case the current db | |
| 898 | +** transaction will be in a rollback state). | |
| 899 | +** | |
| 900 | +** The only errors are currently attachment size limit violations: | |
| 901 | +** attachments must have a non-0 size and if the attachment-size-limit | |
| 902 | +** setting is >0 then each file's size must be <= that. | |
| 903 | +** | |
| 904 | +** If this returns a negative value, it will have populated an error | |
| 905 | +** response using ajax_route_error(). On success it produces no | |
| 906 | +** output. | |
| 907 | +*/ | |
| 908 | +int attachments_from_POST_ajax(const char *zTarget, int bNeedsModeration){ | |
| 909 | + int i; | |
| 910 | + int rc = 0; | |
| 911 | + int n = 0; | |
| 912 | + int szLimit; /* attachment-max-size setting */ | |
| 913 | + char aKeyPrefix[20]; /* Buffer for key "file%d" */ | |
| 914 | + char aKeySize[30]; /* Buffer for key "file%d:bytes" */ | |
| 915 | + char aKeyName[30]; /* Buffer for key "file%d:filename" */ | |
| 916 | + char aKeyDesc[30]; /* Buffer for key "file%d_desc" */ | |
| 917 | + db_begin_transaction(); | |
| 874 | 918 | szLimit = db_get_int("attachment-size-limit", 0); |
| 875 | - for(i = 1; !bRollback; ++i){ | |
| 919 | + for(i = 1; ; ++i, ++n){ | |
| 876 | 920 | /* Look for P("fileN"), where N=1..n */ |
| 877 | 921 | const char *zContent; |
| 922 | + const char *zFilename; | |
| 878 | 923 | int szContent; |
| 879 | 924 | sqlite3_snprintf(sizeof(aKeyPrefix), aKeyPrefix, "file%d", i); |
| 880 | 925 | zContent = P(aKeyPrefix); |
| 881 | 926 | if( !zContent ){ |
| 882 | 927 | /* End of the list. */ |
| @@ -884,43 +929,38 @@ | ||
| 884 | 929 | } |
| 885 | 930 | sqlite3_snprintf(sizeof(aKeySize), aKeySize, "%s:bytes", |
| 886 | 931 | aKeyPrefix); |
| 887 | 932 | szContent = atoi(PD(aKeySize,"-1")); |
| 888 | 933 | if( szContent<=0 ){ |
| 889 | - bRollback = 1; | |
| 934 | + rc = -1; | |
| 890 | 935 | ajax_route_error(400,"Invalid file size: %d", szContent); |
| 891 | 936 | break; |
| 892 | 937 | }else if( szLimit>0 && szContent>szLimit ){ |
| 893 | - bRollback = 1; | |
| 938 | + rc = -2; | |
| 894 | 939 | ajax_route_error(400, "File size limit is %d bytes.", szLimit); |
| 895 | 940 | break; |
| 896 | 941 | }else{ |
| 897 | 942 | sqlite3_snprintf(sizeof(aKeyName), aKeyName, "%s:filename", |
| 898 | 943 | aKeyPrefix); |
| 899 | 944 | sqlite3_snprintf(sizeof(aKeyDesc), aKeyDesc, "%s_desc", |
| 900 | 945 | aKeyPrefix); |
| 901 | - attach_commit(P(aKeyName), zTarget, zContent, szContent, | |
| 946 | + if( 0==(zFilename=P(aKeyName)) ){ | |
| 947 | + rc = -3; | |
| 948 | + ajax_route_error(400, "Missing filename."); | |
| 949 | + break; | |
| 950 | + } | |
| 951 | + attach_commit(zFilename, zTarget, zContent, szContent, | |
| 902 | 952 | bNeedsModeration, P(aKeyDesc)); |
| 903 | 953 | } |
| 904 | 954 | } |
| 905 | - fossil_free(zExtraFree); | |
| 906 | - if( !bRollback ){ | |
| 907 | - CX("}"); | |
| 908 | - if( atoi(PD("dryrun","0"))>0 ){ | |
| 909 | - bRollback = 1; | |
| 910 | - } | |
| 911 | - } | |
| 912 | - db_end_transaction(bRollback); | |
| 913 | - return; | |
| 914 | -ajax_post_403: | |
| 915 | - db_rollback_transaction(); | |
| 916 | - ajax_route_error(403, "Permission denied."); | |
| 917 | - return; | |
| 918 | -ajax_post_404: | |
| 919 | - db_rollback_transaction(); | |
| 920 | - ajax_route_error(404, "Target not found."); | |
| 921 | - return; | |
| 955 | + if( rc<0 ){ | |
| 956 | + db_rollback_transaction(); | |
| 957 | + return rc; | |
| 958 | + }else{ | |
| 959 | + db_commit_transaction(); | |
| 960 | + return n; | |
| 961 | + } | |
| 922 | 962 | } |
| 923 | 963 | |
| 924 | 964 | /* |
| 925 | 965 | ** Proxy for /attachadd?target=X |
| 926 | 966 | ** |
| 927 | 967 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -780,18 +780,12 @@ | |
| 780 | void attachadd_ajax_post(void){ |
| 781 | const char *zTarget; |
| 782 | char *zExtraFree = 0; |
| 783 | int eTgtType = 0; |
| 784 | int bNeedsModeration = 0; |
| 785 | int i; |
| 786 | int goodCaptcha = 1; |
| 787 | int szLimit; /* attachment-max-size setting */ |
| 788 | int bRollback = 0; /* Roll back if true. */ |
| 789 | char aKeyPrefix[20]; /* Buffer for key "file%d" */ |
| 790 | char aKeySize[30]; /* Buffer for key "file%d:bytes" */ |
| 791 | char aKeyName[30]; /* Buffer for key "file%d:filename" */ |
| 792 | char aKeyDesc[30]; /* Buffer for key "file%d_desc" */ |
| 793 | |
| 794 | if( ! ajax_route_bootstrap(0, 1) ){ |
| 795 | return; |
| 796 | }else if( !(goodCaptcha = captcha_is_correct(0)) ){ |
| 797 | goto ajax_post_403; |
| @@ -869,14 +863,65 @@ | |
| 869 | bNeedsModeration = wiki_need_moderation(0); |
| 870 | break; |
| 871 | } |
| 872 | } |
| 873 | |
| 874 | szLimit = db_get_int("attachment-size-limit", 0); |
| 875 | for(i = 1; !bRollback; ++i){ |
| 876 | /* Look for P("fileN"), where N=1..n */ |
| 877 | const char *zContent; |
| 878 | int szContent; |
| 879 | sqlite3_snprintf(sizeof(aKeyPrefix), aKeyPrefix, "file%d", i); |
| 880 | zContent = P(aKeyPrefix); |
| 881 | if( !zContent ){ |
| 882 | /* End of the list. */ |
| @@ -884,43 +929,38 @@ | |
| 884 | } |
| 885 | sqlite3_snprintf(sizeof(aKeySize), aKeySize, "%s:bytes", |
| 886 | aKeyPrefix); |
| 887 | szContent = atoi(PD(aKeySize,"-1")); |
| 888 | if( szContent<=0 ){ |
| 889 | bRollback = 1; |
| 890 | ajax_route_error(400,"Invalid file size: %d", szContent); |
| 891 | break; |
| 892 | }else if( szLimit>0 && szContent>szLimit ){ |
| 893 | bRollback = 1; |
| 894 | ajax_route_error(400, "File size limit is %d bytes.", szLimit); |
| 895 | break; |
| 896 | }else{ |
| 897 | sqlite3_snprintf(sizeof(aKeyName), aKeyName, "%s:filename", |
| 898 | aKeyPrefix); |
| 899 | sqlite3_snprintf(sizeof(aKeyDesc), aKeyDesc, "%s_desc", |
| 900 | aKeyPrefix); |
| 901 | attach_commit(P(aKeyName), zTarget, zContent, szContent, |
| 902 | bNeedsModeration, P(aKeyDesc)); |
| 903 | } |
| 904 | } |
| 905 | fossil_free(zExtraFree); |
| 906 | if( !bRollback ){ |
| 907 | CX("}"); |
| 908 | if( atoi(PD("dryrun","0"))>0 ){ |
| 909 | bRollback = 1; |
| 910 | } |
| 911 | } |
| 912 | db_end_transaction(bRollback); |
| 913 | return; |
| 914 | ajax_post_403: |
| 915 | db_rollback_transaction(); |
| 916 | ajax_route_error(403, "Permission denied."); |
| 917 | return; |
| 918 | ajax_post_404: |
| 919 | db_rollback_transaction(); |
| 920 | ajax_route_error(404, "Target not found."); |
| 921 | return; |
| 922 | } |
| 923 | |
| 924 | /* |
| 925 | ** Proxy for /attachadd?target=X |
| 926 | ** |
| 927 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -780,18 +780,12 @@ | |
| 780 | void attachadd_ajax_post(void){ |
| 781 | const char *zTarget; |
| 782 | char *zExtraFree = 0; |
| 783 | int eTgtType = 0; |
| 784 | int bNeedsModeration = 0; |
| 785 | int goodCaptcha = 1; |
| 786 | int bRollback = 0; /* Roll back if true. */ |
| 787 | |
| 788 | if( ! ajax_route_bootstrap(0, 1) ){ |
| 789 | return; |
| 790 | }else if( !(goodCaptcha = captcha_is_correct(0)) ){ |
| 791 | goto ajax_post_403; |
| @@ -869,14 +863,65 @@ | |
| 863 | bNeedsModeration = wiki_need_moderation(0); |
| 864 | break; |
| 865 | } |
| 866 | } |
| 867 | |
| 868 | if( attachments_from_POST_ajax(zTarget, bNeedsModeration)>=0 ){ |
| 869 | CX("}"); |
| 870 | if( atoi(PD("dryrun","0"))>0 ){ |
| 871 | bRollback = 1; |
| 872 | } |
| 873 | }/*else error response was set up*/ |
| 874 | fossil_free(zExtraFree); |
| 875 | db_end_transaction(bRollback); |
| 876 | return; |
| 877 | ajax_post_403: |
| 878 | db_rollback_transaction(); |
| 879 | ajax_route_error(403, "Permission denied."); |
| 880 | return; |
| 881 | ajax_post_404: |
| 882 | db_rollback_transaction(); |
| 883 | ajax_route_error(404, "Target not found."); |
| 884 | return; |
| 885 | } |
| 886 | |
| 887 | /* |
| 888 | ** A helper for AJAX-style routines which accept file attachments via |
| 889 | ** POST. zTarget must be a full attachment target. bNeedsModeration |
| 890 | ** must be true if the attachment requires moderation. |
| 891 | ** |
| 892 | ** It is up to the caller to have validated all security measures |
| 893 | ** before calling this. |
| 894 | ** |
| 895 | ** This looks for POSTed files names "file1".."fileN", stopping when |
| 896 | ** it finds no entry. Returns the number of entries attached to the |
| 897 | ** target or a negative value on error (in which case the current db |
| 898 | ** transaction will be in a rollback state). |
| 899 | ** |
| 900 | ** The only errors are currently attachment size limit violations: |
| 901 | ** attachments must have a non-0 size and if the attachment-size-limit |
| 902 | ** setting is >0 then each file's size must be <= that. |
| 903 | ** |
| 904 | ** If this returns a negative value, it will have populated an error |
| 905 | ** response using ajax_route_error(). On success it produces no |
| 906 | ** output. |
| 907 | */ |
| 908 | int attachments_from_POST_ajax(const char *zTarget, int bNeedsModeration){ |
| 909 | int i; |
| 910 | int rc = 0; |
| 911 | int n = 0; |
| 912 | int szLimit; /* attachment-max-size setting */ |
| 913 | char aKeyPrefix[20]; /* Buffer for key "file%d" */ |
| 914 | char aKeySize[30]; /* Buffer for key "file%d:bytes" */ |
| 915 | char aKeyName[30]; /* Buffer for key "file%d:filename" */ |
| 916 | char aKeyDesc[30]; /* Buffer for key "file%d_desc" */ |
| 917 | db_begin_transaction(); |
| 918 | szLimit = db_get_int("attachment-size-limit", 0); |
| 919 | for(i = 1; ; ++i, ++n){ |
| 920 | /* Look for P("fileN"), where N=1..n */ |
| 921 | const char *zContent; |
| 922 | const char *zFilename; |
| 923 | int szContent; |
| 924 | sqlite3_snprintf(sizeof(aKeyPrefix), aKeyPrefix, "file%d", i); |
| 925 | zContent = P(aKeyPrefix); |
| 926 | if( !zContent ){ |
| 927 | /* End of the list. */ |
| @@ -884,43 +929,38 @@ | |
| 929 | } |
| 930 | sqlite3_snprintf(sizeof(aKeySize), aKeySize, "%s:bytes", |
| 931 | aKeyPrefix); |
| 932 | szContent = atoi(PD(aKeySize,"-1")); |
| 933 | if( szContent<=0 ){ |
| 934 | rc = -1; |
| 935 | ajax_route_error(400,"Invalid file size: %d", szContent); |
| 936 | break; |
| 937 | }else if( szLimit>0 && szContent>szLimit ){ |
| 938 | rc = -2; |
| 939 | ajax_route_error(400, "File size limit is %d bytes.", szLimit); |
| 940 | break; |
| 941 | }else{ |
| 942 | sqlite3_snprintf(sizeof(aKeyName), aKeyName, "%s:filename", |
| 943 | aKeyPrefix); |
| 944 | sqlite3_snprintf(sizeof(aKeyDesc), aKeyDesc, "%s_desc", |
| 945 | aKeyPrefix); |
| 946 | if( 0==(zFilename=P(aKeyName)) ){ |
| 947 | rc = -3; |
| 948 | ajax_route_error(400, "Missing filename."); |
| 949 | break; |
| 950 | } |
| 951 | attach_commit(zFilename, zTarget, zContent, szContent, |
| 952 | bNeedsModeration, P(aKeyDesc)); |
| 953 | } |
| 954 | } |
| 955 | if( rc<0 ){ |
| 956 | db_rollback_transaction(); |
| 957 | return rc; |
| 958 | }else{ |
| 959 | db_commit_transaction(); |
| 960 | return n; |
| 961 | } |
| 962 | } |
| 963 | |
| 964 | /* |
| 965 | ** Proxy for /attachadd?target=X |
| 966 | ** |
| 967 |