Fossil SCM
Add the "fossil chat send" command.
Commit
1e81049063cc8833cb2f73e2b9ef4a1fc4cf0e22938a3c1638d35b7ff1393916
Parent
2146a13df1b27aa…
2 files changed
+120
-24
+19
+120
-24
| --- src/chat.c | ||
| +++ src/chat.c | ||
| @@ -264,11 +264,11 @@ | ||
| 264 | 264 | if( !g.perm.Chat ) { |
| 265 | 265 | chat_emit_permissions_error(0); |
| 266 | 266 | return; |
| 267 | 267 | } |
| 268 | 268 | chat_create_tables(); |
| 269 | - nByte = atoi(PD("file:bytes",0)); | |
| 269 | + nByte = atoi(PD("file:bytes","0")); | |
| 270 | 270 | zMsg = PD("msg",""); |
| 271 | 271 | db_begin_write(); |
| 272 | 272 | chat_purge(); |
| 273 | 273 | if( nByte==0 ){ |
| 274 | 274 | if( zMsg[0] ){ |
| @@ -677,43 +677,139 @@ | ||
| 677 | 677 | } |
| 678 | 678 | |
| 679 | 679 | /* |
| 680 | 680 | ** COMMAND: chat |
| 681 | 681 | ** |
| 682 | -** Usage: %fossil chat | |
| 682 | +** Usage: %fossil chat [SUBCOMMAND] [--remote URL] [ARGS...] | |
| 683 | +** | |
| 684 | +** This command performs actions associated with the /chat instance | |
| 685 | +** on the default remote Fossil repository (the Fossil repository whose | |
| 686 | +** URL shows when you run the "fossil remote" command) or to the URL | |
| 687 | +** specified by the --remote option. If there is no default remote | |
| 688 | +** Fossil repository and the --remote option is omitted, then this | |
| 689 | +** command fails with an error. | |
| 690 | +** | |
| 691 | +** When there is no SUBCOMMAND (when this command is simply "fossil chat") | |
| 692 | +** the response is to bring up a web-browser window to the chatroom | |
| 693 | +** on the default system web-browser. You can accomplish the same by | |
| 694 | +** typing the appropriate URL into the web-browser yourself. This | |
| 695 | +** command is merely a convenience for command-line oriented peope. | |
| 696 | +** | |
| 697 | +** The following subcommands are supported: | |
| 698 | +** | |
| 699 | +** > fossil chat send [ARGUMENTS] | |
| 700 | +** | |
| 701 | +** This command sends a new message to the chatroom. The message | |
| 702 | +** to be sent is determined by arguments as follows: | |
| 703 | +** | |
| 704 | +** -m|--message TEXT Text of the chat message | |
| 705 | +** -f|--file FILENAME File to attach to the message | |
| 683 | 706 | ** |
| 684 | -** Bring up a web-browser window to the chatroom of the default | |
| 685 | -** remote Fossil repository. | |
| 707 | +** Additional subcommands may be added in the future. | |
| 686 | 708 | */ |
| 687 | 709 | void chat_command(void){ |
| 688 | - const char *zUrl; | |
| 689 | - const char *zBrowser; | |
| 690 | - char *zCmd; | |
| 710 | + const char *zUrl = find_option("remote",0,1); | |
| 711 | + int urlFlags = 0; | |
| 712 | + int isDefaultUrl = 0; | |
| 713 | + int i; | |
| 714 | + | |
| 691 | 715 | db_find_and_open_repository(0,0); |
| 692 | - if( g.argc!=2 ){ | |
| 693 | - usage(""); | |
| 694 | - } | |
| 695 | - zUrl = db_get("last-sync-url",0); | |
| 696 | - if( zUrl==0 ){ | |
| 697 | - fossil_fatal("no \"remote\" repository defined"); | |
| 698 | - } | |
| 699 | - url_parse(zUrl, 0); | |
| 716 | + if( zUrl ){ | |
| 717 | + urlFlags = URL_PROMPT_PW; | |
| 718 | + }else{ | |
| 719 | + zUrl = db_get("last-sync-url",0); | |
| 720 | + if( zUrl==0 ){ | |
| 721 | + fossil_fatal("no \"remote\" repository defined"); | |
| 722 | + }else{ | |
| 723 | + isDefaultUrl = 1; | |
| 724 | + } | |
| 725 | + } | |
| 726 | + url_parse(zUrl, urlFlags); | |
| 727 | + if( g.url.isFile || g.url.isSsh ){ | |
| 728 | + fossil_fatal("chat only works for http:// and https:// URLs"); | |
| 729 | + } | |
| 730 | + i = (int)strlen(g.url.path); | |
| 731 | + while( i>0 && g.url.path[i-1]=='/' ) i--; | |
| 700 | 732 | if( g.url.port==g.url.dfltPort ){ |
| 701 | 733 | zUrl = mprintf( |
| 702 | - "%s://%T%T", | |
| 703 | - g.url.protocol, g.url.name, g.url.path | |
| 734 | + "%s://%T%.*T", | |
| 735 | + g.url.protocol, g.url.name, i, g.url.path | |
| 704 | 736 | ); |
| 705 | 737 | }else{ |
| 706 | 738 | zUrl = mprintf( |
| 707 | - "%s://%T:%d%T", | |
| 708 | - g.url.protocol, g.url.name, g.url.port, g.url.path | |
| 739 | + "%s://%T:%d%.*T", | |
| 740 | + g.url.protocol, g.url.name, g.url.port, i, g.url.path | |
| 709 | 741 | ); |
| 710 | 742 | } |
| 711 | - zBrowser = fossil_web_browser(); | |
| 712 | - if( zBrowser==0 ) return; | |
| 743 | + if( g.argc==2 ){ | |
| 744 | + const char *zBrowser = fossil_web_browser(); | |
| 745 | + char *zCmd; | |
| 746 | + if( zBrowser==0 ) return; | |
| 713 | 747 | #ifdef _WIN32 |
| 714 | - zCmd = mprintf("%s %s/chat?cli &", zBrowser, zUrl); | |
| 748 | + zCmd = mprintf("%s %s/chat?cli &", zBrowser, zUrl); | |
| 715 | 749 | #else |
| 716 | - zCmd = mprintf("%s \"%s/chat?cli\" &", zBrowser, zUrl); | |
| 750 | + zCmd = mprintf("%s \"%s/chat?cli\" &", zBrowser, zUrl); | |
| 717 | 751 | #endif |
| 718 | - fossil_system(zCmd); | |
| 752 | + fossil_system(zCmd); | |
| 753 | + }else if( strcmp(g.argv[2],"send")==0 ){ | |
| 754 | + const char *zFilename = find_option("file","r",1); | |
| 755 | + const char *zMsg = find_option("message","m",1); | |
| 756 | + const int mFlags = HTTP_GENERIC | HTTP_QUIET | HTTP_NOCOMPRESS; | |
| 757 | + int i; | |
| 758 | + const char *zPw; | |
| 759 | + Blob up, down, fcontent; | |
| 760 | + char zBoundary[80]; | |
| 761 | + sqlite3_uint64 r[3]; | |
| 762 | + if( zFilename==0 && zMsg==0 ){ | |
| 763 | + fossil_fatal("must have --message or --file or both"); | |
| 764 | + } | |
| 765 | + i = (int)strlen(g.url.path); | |
| 766 | + while( i>0 && g.url.path[i-1]=='/' ) i--; | |
| 767 | + g.url.path = mprintf("%.*s/chat-send", i, g.url.path); | |
| 768 | + blob_init(&up, 0, 0); | |
| 769 | + blob_init(&down, 0, 0); | |
| 770 | + sqlite3_randomness(sizeof(r),r); | |
| 771 | + sqlite3_snprintf(sizeof(zBoundary),zBoundary, | |
| 772 | + "--------%016llu%016llu%016llu", r[0], r[1], r[2]); | |
| 773 | + blob_appendf(&up, "%s", zBoundary); | |
| 774 | + if( g.url.user && g.url.user[0] ){ | |
| 775 | + blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"resid\"\r\n" | |
| 776 | + "\r\n%z\r\n%s", obscure(g.url.user), zBoundary); | |
| 777 | + } | |
| 778 | + zPw = g.url.passwd; | |
| 779 | + if( zPw==0 && isDefaultUrl ) zPw = unobscure(db_get("last-sync-pw", 0)); | |
| 780 | + if( zPw && zPw[0] ){ | |
| 781 | + blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"token\"\r\n" | |
| 782 | + "\r\n%z\r\n%s", obscure(zPw), zBoundary); | |
| 783 | + } | |
| 784 | + if( zMsg && zMsg[0] ){ | |
| 785 | + blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"msg\"\r\n" | |
| 786 | + "\r\n%s\r\n%s", zMsg, zBoundary); | |
| 787 | + } | |
| 788 | + if( zFilename && blob_read_from_file(&fcontent, zFilename, ExtFILE)>0 ){ | |
| 789 | + char *zFN = mprintf("%s", file_tail(zFilename)); | |
| 790 | + int i; | |
| 791 | + const char *zMime = mimetype_from_name(zFilename); | |
| 792 | + for(i=0; zFN[i]; i++){ | |
| 793 | + char c = zFN[i]; | |
| 794 | + if( fossil_isalnum(c) ) continue; | |
| 795 | + if( c=='.' ) continue; | |
| 796 | + if( c=='-' ) continue; | |
| 797 | + zFN[i] = '_'; | |
| 798 | + } | |
| 799 | + blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"file\";" | |
| 800 | + " filename=\"%s\"\r\n", zFN); | |
| 801 | + blob_appendf(&up,"Content-Type: %s\r\n\r\n", zMime); | |
| 802 | + blob_append(&up, fcontent.aData, fcontent.nUsed); | |
| 803 | + blob_appendf(&up,"\r\n%s", zBoundary); | |
| 804 | + } | |
| 805 | + blob_append(&up,"--\r\n", 4); | |
| 806 | + http_exchange(&up, &down, mFlags, 4, "multipart/form-data"); | |
| 807 | + blob_reset(&up); | |
| 808 | + blob_reset(&down); | |
| 809 | + }else if( strcmp(g.argv[2],"url")==0 ){ | |
| 810 | + /* Undocumented command. Show the URL to access chat. */ | |
| 811 | + fossil_print("%s/chat\n", zUrl); | |
| 812 | + }else{ | |
| 813 | + fossil_fatal("no such subcommand \"%s\". Use --help for help", g.argv[2]); | |
| 814 | + } | |
| 719 | 815 | } |
| 720 | 816 |
| --- src/chat.c | |
| +++ src/chat.c | |
| @@ -264,11 +264,11 @@ | |
| 264 | if( !g.perm.Chat ) { |
| 265 | chat_emit_permissions_error(0); |
| 266 | return; |
| 267 | } |
| 268 | chat_create_tables(); |
| 269 | nByte = atoi(PD("file:bytes",0)); |
| 270 | zMsg = PD("msg",""); |
| 271 | db_begin_write(); |
| 272 | chat_purge(); |
| 273 | if( nByte==0 ){ |
| 274 | if( zMsg[0] ){ |
| @@ -677,43 +677,139 @@ | |
| 677 | } |
| 678 | |
| 679 | /* |
| 680 | ** COMMAND: chat |
| 681 | ** |
| 682 | ** Usage: %fossil chat |
| 683 | ** |
| 684 | ** Bring up a web-browser window to the chatroom of the default |
| 685 | ** remote Fossil repository. |
| 686 | */ |
| 687 | void chat_command(void){ |
| 688 | const char *zUrl; |
| 689 | const char *zBrowser; |
| 690 | char *zCmd; |
| 691 | db_find_and_open_repository(0,0); |
| 692 | if( g.argc!=2 ){ |
| 693 | usage(""); |
| 694 | } |
| 695 | zUrl = db_get("last-sync-url",0); |
| 696 | if( zUrl==0 ){ |
| 697 | fossil_fatal("no \"remote\" repository defined"); |
| 698 | } |
| 699 | url_parse(zUrl, 0); |
| 700 | if( g.url.port==g.url.dfltPort ){ |
| 701 | zUrl = mprintf( |
| 702 | "%s://%T%T", |
| 703 | g.url.protocol, g.url.name, g.url.path |
| 704 | ); |
| 705 | }else{ |
| 706 | zUrl = mprintf( |
| 707 | "%s://%T:%d%T", |
| 708 | g.url.protocol, g.url.name, g.url.port, g.url.path |
| 709 | ); |
| 710 | } |
| 711 | zBrowser = fossil_web_browser(); |
| 712 | if( zBrowser==0 ) return; |
| 713 | #ifdef _WIN32 |
| 714 | zCmd = mprintf("%s %s/chat?cli &", zBrowser, zUrl); |
| 715 | #else |
| 716 | zCmd = mprintf("%s \"%s/chat?cli\" &", zBrowser, zUrl); |
| 717 | #endif |
| 718 | fossil_system(zCmd); |
| 719 | } |
| 720 |
| --- src/chat.c | |
| +++ src/chat.c | |
| @@ -264,11 +264,11 @@ | |
| 264 | if( !g.perm.Chat ) { |
| 265 | chat_emit_permissions_error(0); |
| 266 | return; |
| 267 | } |
| 268 | chat_create_tables(); |
| 269 | nByte = atoi(PD("file:bytes","0")); |
| 270 | zMsg = PD("msg",""); |
| 271 | db_begin_write(); |
| 272 | chat_purge(); |
| 273 | if( nByte==0 ){ |
| 274 | if( zMsg[0] ){ |
| @@ -677,43 +677,139 @@ | |
| 677 | } |
| 678 | |
| 679 | /* |
| 680 | ** COMMAND: chat |
| 681 | ** |
| 682 | ** Usage: %fossil chat [SUBCOMMAND] [--remote URL] [ARGS...] |
| 683 | ** |
| 684 | ** This command performs actions associated with the /chat instance |
| 685 | ** on the default remote Fossil repository (the Fossil repository whose |
| 686 | ** URL shows when you run the "fossil remote" command) or to the URL |
| 687 | ** specified by the --remote option. If there is no default remote |
| 688 | ** Fossil repository and the --remote option is omitted, then this |
| 689 | ** command fails with an error. |
| 690 | ** |
| 691 | ** When there is no SUBCOMMAND (when this command is simply "fossil chat") |
| 692 | ** the response is to bring up a web-browser window to the chatroom |
| 693 | ** on the default system web-browser. You can accomplish the same by |
| 694 | ** typing the appropriate URL into the web-browser yourself. This |
| 695 | ** command is merely a convenience for command-line oriented peope. |
| 696 | ** |
| 697 | ** The following subcommands are supported: |
| 698 | ** |
| 699 | ** > fossil chat send [ARGUMENTS] |
| 700 | ** |
| 701 | ** This command sends a new message to the chatroom. The message |
| 702 | ** to be sent is determined by arguments as follows: |
| 703 | ** |
| 704 | ** -m|--message TEXT Text of the chat message |
| 705 | ** -f|--file FILENAME File to attach to the message |
| 706 | ** |
| 707 | ** Additional subcommands may be added in the future. |
| 708 | */ |
| 709 | void chat_command(void){ |
| 710 | const char *zUrl = find_option("remote",0,1); |
| 711 | int urlFlags = 0; |
| 712 | int isDefaultUrl = 0; |
| 713 | int i; |
| 714 | |
| 715 | db_find_and_open_repository(0,0); |
| 716 | if( zUrl ){ |
| 717 | urlFlags = URL_PROMPT_PW; |
| 718 | }else{ |
| 719 | zUrl = db_get("last-sync-url",0); |
| 720 | if( zUrl==0 ){ |
| 721 | fossil_fatal("no \"remote\" repository defined"); |
| 722 | }else{ |
| 723 | isDefaultUrl = 1; |
| 724 | } |
| 725 | } |
| 726 | url_parse(zUrl, urlFlags); |
| 727 | if( g.url.isFile || g.url.isSsh ){ |
| 728 | fossil_fatal("chat only works for http:// and https:// URLs"); |
| 729 | } |
| 730 | i = (int)strlen(g.url.path); |
| 731 | while( i>0 && g.url.path[i-1]=='/' ) i--; |
| 732 | if( g.url.port==g.url.dfltPort ){ |
| 733 | zUrl = mprintf( |
| 734 | "%s://%T%.*T", |
| 735 | g.url.protocol, g.url.name, i, g.url.path |
| 736 | ); |
| 737 | }else{ |
| 738 | zUrl = mprintf( |
| 739 | "%s://%T:%d%.*T", |
| 740 | g.url.protocol, g.url.name, g.url.port, i, g.url.path |
| 741 | ); |
| 742 | } |
| 743 | if( g.argc==2 ){ |
| 744 | const char *zBrowser = fossil_web_browser(); |
| 745 | char *zCmd; |
| 746 | if( zBrowser==0 ) return; |
| 747 | #ifdef _WIN32 |
| 748 | zCmd = mprintf("%s %s/chat?cli &", zBrowser, zUrl); |
| 749 | #else |
| 750 | zCmd = mprintf("%s \"%s/chat?cli\" &", zBrowser, zUrl); |
| 751 | #endif |
| 752 | fossil_system(zCmd); |
| 753 | }else if( strcmp(g.argv[2],"send")==0 ){ |
| 754 | const char *zFilename = find_option("file","r",1); |
| 755 | const char *zMsg = find_option("message","m",1); |
| 756 | const int mFlags = HTTP_GENERIC | HTTP_QUIET | HTTP_NOCOMPRESS; |
| 757 | int i; |
| 758 | const char *zPw; |
| 759 | Blob up, down, fcontent; |
| 760 | char zBoundary[80]; |
| 761 | sqlite3_uint64 r[3]; |
| 762 | if( zFilename==0 && zMsg==0 ){ |
| 763 | fossil_fatal("must have --message or --file or both"); |
| 764 | } |
| 765 | i = (int)strlen(g.url.path); |
| 766 | while( i>0 && g.url.path[i-1]=='/' ) i--; |
| 767 | g.url.path = mprintf("%.*s/chat-send", i, g.url.path); |
| 768 | blob_init(&up, 0, 0); |
| 769 | blob_init(&down, 0, 0); |
| 770 | sqlite3_randomness(sizeof(r),r); |
| 771 | sqlite3_snprintf(sizeof(zBoundary),zBoundary, |
| 772 | "--------%016llu%016llu%016llu", r[0], r[1], r[2]); |
| 773 | blob_appendf(&up, "%s", zBoundary); |
| 774 | if( g.url.user && g.url.user[0] ){ |
| 775 | blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"resid\"\r\n" |
| 776 | "\r\n%z\r\n%s", obscure(g.url.user), zBoundary); |
| 777 | } |
| 778 | zPw = g.url.passwd; |
| 779 | if( zPw==0 && isDefaultUrl ) zPw = unobscure(db_get("last-sync-pw", 0)); |
| 780 | if( zPw && zPw[0] ){ |
| 781 | blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"token\"\r\n" |
| 782 | "\r\n%z\r\n%s", obscure(zPw), zBoundary); |
| 783 | } |
| 784 | if( zMsg && zMsg[0] ){ |
| 785 | blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"msg\"\r\n" |
| 786 | "\r\n%s\r\n%s", zMsg, zBoundary); |
| 787 | } |
| 788 | if( zFilename && blob_read_from_file(&fcontent, zFilename, ExtFILE)>0 ){ |
| 789 | char *zFN = mprintf("%s", file_tail(zFilename)); |
| 790 | int i; |
| 791 | const char *zMime = mimetype_from_name(zFilename); |
| 792 | for(i=0; zFN[i]; i++){ |
| 793 | char c = zFN[i]; |
| 794 | if( fossil_isalnum(c) ) continue; |
| 795 | if( c=='.' ) continue; |
| 796 | if( c=='-' ) continue; |
| 797 | zFN[i] = '_'; |
| 798 | } |
| 799 | blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"file\";" |
| 800 | " filename=\"%s\"\r\n", zFN); |
| 801 | blob_appendf(&up,"Content-Type: %s\r\n\r\n", zMime); |
| 802 | blob_append(&up, fcontent.aData, fcontent.nUsed); |
| 803 | blob_appendf(&up,"\r\n%s", zBoundary); |
| 804 | } |
| 805 | blob_append(&up,"--\r\n", 4); |
| 806 | http_exchange(&up, &down, mFlags, 4, "multipart/form-data"); |
| 807 | blob_reset(&up); |
| 808 | blob_reset(&down); |
| 809 | }else if( strcmp(g.argv[2],"url")==0 ){ |
| 810 | /* Undocumented command. Show the URL to access chat. */ |
| 811 | fossil_print("%s/chat\n", zUrl); |
| 812 | }else{ |
| 813 | fossil_fatal("no such subcommand \"%s\". Use --help for help", g.argv[2]); |
| 814 | } |
| 815 | } |
| 816 |
+19
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -1078,10 +1078,29 @@ | ||
| 1078 | 1078 | ** see if those credentials are valid for a known user. |
| 1079 | 1079 | */ |
| 1080 | 1080 | if( uid==0 && db_get_boolean("http_authentication_ok",0) ){ |
| 1081 | 1081 | uid = login_basic_authentication(zIpAddr); |
| 1082 | 1082 | } |
| 1083 | + | |
| 1084 | + /* Check for magic query parameters "resid" (for the username) and | |
| 1085 | + ** "token" for the password. Both values (if they exist) will be | |
| 1086 | + ** obfuscated. | |
| 1087 | + */ | |
| 1088 | + if( uid==0 ){ | |
| 1089 | + char *zUsr, *zPW; | |
| 1090 | + if( (zUsr = unobscure(P("resid")))!=0 | |
| 1091 | + && (zPW = unobscure(P("token")))!=0 | |
| 1092 | + ){ | |
| 1093 | + char *zSha1Pw = sha1_shared_secret(zPW, zUsr, 0); | |
| 1094 | + uid = db_int(0, "SELECT uid FROM user" | |
| 1095 | + " WHERE login=%Q" | |
| 1096 | + " AND (constant_time_cmp(pw,%Q)=0" | |
| 1097 | + " OR constant_time_cmp(pw,%Q)=0)", | |
| 1098 | + zUsr, zSha1Pw, zPW); | |
| 1099 | + fossil_free(zSha1Pw); | |
| 1100 | + } | |
| 1101 | + } | |
| 1083 | 1102 | |
| 1084 | 1103 | /* If no user found yet, try to log in as "nobody" */ |
| 1085 | 1104 | if( uid==0 ){ |
| 1086 | 1105 | uid = db_int(0, "SELECT uid FROM user WHERE login='nobody'"); |
| 1087 | 1106 | if( uid==0 ){ |
| 1088 | 1107 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -1078,10 +1078,29 @@ | |
| 1078 | ** see if those credentials are valid for a known user. |
| 1079 | */ |
| 1080 | if( uid==0 && db_get_boolean("http_authentication_ok",0) ){ |
| 1081 | uid = login_basic_authentication(zIpAddr); |
| 1082 | } |
| 1083 | |
| 1084 | /* If no user found yet, try to log in as "nobody" */ |
| 1085 | if( uid==0 ){ |
| 1086 | uid = db_int(0, "SELECT uid FROM user WHERE login='nobody'"); |
| 1087 | if( uid==0 ){ |
| 1088 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -1078,10 +1078,29 @@ | |
| 1078 | ** see if those credentials are valid for a known user. |
| 1079 | */ |
| 1080 | if( uid==0 && db_get_boolean("http_authentication_ok",0) ){ |
| 1081 | uid = login_basic_authentication(zIpAddr); |
| 1082 | } |
| 1083 | |
| 1084 | /* Check for magic query parameters "resid" (for the username) and |
| 1085 | ** "token" for the password. Both values (if they exist) will be |
| 1086 | ** obfuscated. |
| 1087 | */ |
| 1088 | if( uid==0 ){ |
| 1089 | char *zUsr, *zPW; |
| 1090 | if( (zUsr = unobscure(P("resid")))!=0 |
| 1091 | && (zPW = unobscure(P("token")))!=0 |
| 1092 | ){ |
| 1093 | char *zSha1Pw = sha1_shared_secret(zPW, zUsr, 0); |
| 1094 | uid = db_int(0, "SELECT uid FROM user" |
| 1095 | " WHERE login=%Q" |
| 1096 | " AND (constant_time_cmp(pw,%Q)=0" |
| 1097 | " OR constant_time_cmp(pw,%Q)=0)", |
| 1098 | zUsr, zSha1Pw, zPW); |
| 1099 | fossil_free(zSha1Pw); |
| 1100 | } |
| 1101 | } |
| 1102 | |
| 1103 | /* If no user found yet, try to log in as "nobody" */ |
| 1104 | if( uid==0 ){ |
| 1105 | uid = db_int(0, "SELECT uid FROM user WHERE login='nobody'"); |
| 1106 | if( uid==0 ){ |
| 1107 |