Fossil SCM
Add the "fossil chat backup" command and the "/chat-backup" webpage to support it.
Commit
1827a314878fecb2d105e5fe3c67839bbd9e7c3969039e0b1bc6d1ff0ad3234c
Parent
868d1608382a9bb…
1 file changed
+107
+107
| --- src/chat.c | ||
| +++ src/chat.c | ||
| @@ -815,10 +815,40 @@ | ||
| 815 | 815 | " VALUES(julianday('now'), %Q, %d);\n" |
| 816 | 816 | "COMMIT;", |
| 817 | 817 | mdel, g.zLogin, mdel |
| 818 | 818 | ); |
| 819 | 819 | } |
| 820 | + | |
| 821 | +/* | |
| 822 | +** WEBPAGE: chat-backup hidden | |
| 823 | +** | |
| 824 | +** Download an SQLite database containing all chat content with a | |
| 825 | +** message-id larger than the "msgid" query parameter. Setup | |
| 826 | +** privilege is required to use this URL. | |
| 827 | +*/ | |
| 828 | +void chat_backup_webpage(void){ | |
| 829 | + int msgid; | |
| 830 | + unsigned char *pDb = 0; | |
| 831 | + sqlite3_int64 szDb = 0; | |
| 832 | + Blob chatDb; | |
| 833 | + login_check_credentials(); | |
| 834 | + if( !g.perm.Setup ) return; | |
| 835 | + msgid = atoi(PD("msgid","0")); | |
| 836 | + db_multi_exec( | |
| 837 | + "ATTACH ':memory:' AS mem1;\n" | |
| 838 | + "PRAGMA mem1.page_size=512;\n" | |
| 839 | + "CREATE TABLE mem1.chat AS SELECT * FROM repository.chat WHERE msgid>%d;\n", | |
| 840 | + msgid | |
| 841 | + ); | |
| 842 | + pDb = sqlite3_serialize(g.db, "mem1", &szDb, 0); | |
| 843 | + if( pDb==0 ){ | |
| 844 | + fossil_fatal("Out of memory"); | |
| 845 | + } | |
| 846 | + blob_init(&chatDb, (const char*)pDb, (int)szDb); | |
| 847 | + cgi_set_content_type("application/x-sqlite3"); | |
| 848 | + cgi_set_content(&chatDb); | |
| 849 | +} | |
| 820 | 850 | |
| 821 | 851 | /* |
| 822 | 852 | ** COMMAND: chat |
| 823 | 853 | ** |
| 824 | 854 | ** Usage: %fossil chat [SUBCOMMAND] [--remote URL] [ARGS...] |
| @@ -838,10 +868,22 @@ | ||
| 838 | 868 | ** the response is to bring up a web-browser window to the chatroom |
| 839 | 869 | ** on the default system web-browser. You can accomplish the same by |
| 840 | 870 | ** typing the appropriate URL into the web-browser yourself. This |
| 841 | 871 | ** command is merely a convenience for command-line oriented people. |
| 842 | 872 | ** |
| 873 | +** > fossil chat backup | |
| 874 | +** | |
| 875 | +** Copy chat content from the server down into the local clone, | |
| 876 | +** as a backup. Setup privilege is required on the server. | |
| 877 | +** | |
| 878 | +** --all Download all chat content. Normally only | |
| 879 | +** previously undownloaded content is retrieved. | |
| 880 | +** --debug Additional debugging output. | |
| 881 | +** --out DATABASE Store CHAT table in separate database file | |
| 882 | +** DATABASE rather that adding to local clone | |
| 883 | +** --unsafe Allow the use of unencrypted http:// | |
| 884 | +** | |
| 843 | 885 | ** > fossil chat send [ARGUMENTS] |
| 844 | 886 | ** |
| 845 | 887 | ** This command sends a new message to the chatroom. The message |
| 846 | 888 | ** to be sent is determined by arguments as follows: |
| 847 | 889 | ** |
| @@ -981,12 +1023,77 @@ | ||
| 981 | 1023 | fossil_print("ERROR: %s\n", blob_str(&down)); |
| 982 | 1024 | } |
| 983 | 1025 | fossil_fatal("unable to send the chat message"); |
| 984 | 1026 | } |
| 985 | 1027 | blob_reset(&down); |
| 1028 | + }else if( strcmp(g.argv[2],"backup")==0 ){ | |
| 1029 | + /* Pull the CHAT table from the default server down into the repository | |
| 1030 | + ** here on the local side */ | |
| 1031 | + int allowUnsafe = find_option("unsafe",0,0)!=0; | |
| 1032 | + int bDebug = find_option("debug",0,0)!=0; | |
| 1033 | + const char *zOut = find_option("out",0,1); | |
| 1034 | + int bAll = find_option("all",0,0)!=0; | |
| 1035 | + int mFlags = HTTP_GENERIC | HTTP_QUIET | HTTP_NOCOMPRESS; | |
| 1036 | + int msgid; | |
| 1037 | + Blob reqUri; /* The REQUEST_URI: .../chat-backup?msgid=... */ | |
| 1038 | + char *zObs; | |
| 1039 | + const char *zPw; | |
| 1040 | + Blob up, down; | |
| 1041 | + int nChat; | |
| 1042 | + int rc; | |
| 1043 | + verify_all_options(); | |
| 1044 | + chat_create_tables(); | |
| 1045 | + msgid = bAll ? 0 : db_int(0,"SELECT max(msgid) FROM chat"); | |
| 1046 | + if( !g.url.isHttps && !allowUnsafe ){ | |
| 1047 | + fossil_fatal("URL \"%s\" is unencrypted. Use https:// instead", zUrl); | |
| 1048 | + } | |
| 1049 | + blob_init(&reqUri, g.url.path, -1); | |
| 1050 | + blob_appendf(&reqUri, "/chat-backup?msgid=%d", msgid); | |
| 1051 | + if( g.url.user && g.url.user[0] ){ | |
| 1052 | + zObs = obscure(g.url.user); | |
| 1053 | + blob_appendf(&reqUri, "&resid=%t", zObs); | |
| 1054 | + fossil_free(zObs); | |
| 1055 | + } | |
| 1056 | + zPw = g.url.passwd; | |
| 1057 | + if( zPw==0 && isDefaultUrl ) zPw = unobscure(db_get("last-sync-pw", 0)); | |
| 1058 | + if( zPw && zPw[0] ){ | |
| 1059 | + zObs = obscure(zPw); | |
| 1060 | + blob_appendf(&reqUri, "&token=%t", zObs); | |
| 1061 | + fossil_free(zObs); | |
| 1062 | + } | |
| 1063 | + g.url.path = blob_str(&reqUri); | |
| 1064 | + if( bDebug ){ | |
| 1065 | + fossil_print("REQUEST_URI: %s\n", g.url.path); | |
| 1066 | + mFlags &= ~HTTP_QUIET; | |
| 1067 | + mFlags |= HTTP_VERBOSE; | |
| 1068 | + } | |
| 1069 | + blob_init(&up, 0, 0); | |
| 1070 | + blob_init(&down, 0, 0); | |
| 1071 | + http_exchange(&up, &down, mFlags, 4, 0); | |
| 1072 | + if( zOut ){ | |
| 1073 | + blob_write_to_file(&down, zOut); | |
| 1074 | + fossil_print("Chat database at %s is %d bytes\n", zOut, blob_size(&down)); | |
| 1075 | + }else{ | |
| 1076 | + db_multi_exec("ATTACH ':memory:' AS chatbu;"); | |
| 1077 | + if( g.fSqlTrace ){ | |
| 1078 | + fossil_trace("-- deserialize(\"chatbu\", pData, %d);\n", | |
| 1079 | + blob_size(&down)); | |
| 1080 | + } | |
| 1081 | + rc = sqlite3_deserialize(g.db, "chatbu", | |
| 1082 | + (unsigned char*)blob_buffer(&down), | |
| 1083 | + blob_size(&down), blob_size(&down), 0); | |
| 1084 | + if( rc ){ | |
| 1085 | + fossil_fatal("cannot open patch database: %s", sqlite3_errmsg(g.db)); | |
| 1086 | + } | |
| 1087 | + nChat = db_int(0, "SELECT count(*) FROM chatbu.chat"); | |
| 1088 | + fossil_print("Got %d new records, %d bytes\n", nChat, blob_size(&down)); | |
| 1089 | + db_multi_exec( | |
| 1090 | + "REPLACE INTO repository.chat SELECT * FROM chatbu.chat;" | |
| 1091 | + ); | |
| 1092 | + } | |
| 986 | 1093 | }else if( strcmp(g.argv[2],"url")==0 ){ |
| 987 | 1094 | /* Show the URL to access chat. */ |
| 988 | 1095 | fossil_print("%s/chat\n", zUrl); |
| 989 | 1096 | }else{ |
| 990 | 1097 | fossil_fatal("no such subcommand \"%s\". Use --help for help", g.argv[2]); |
| 991 | 1098 | } |
| 992 | 1099 | } |
| 993 | 1100 |
| --- src/chat.c | |
| +++ src/chat.c | |
| @@ -815,10 +815,40 @@ | |
| 815 | " VALUES(julianday('now'), %Q, %d);\n" |
| 816 | "COMMIT;", |
| 817 | mdel, g.zLogin, mdel |
| 818 | ); |
| 819 | } |
| 820 | |
| 821 | /* |
| 822 | ** COMMAND: chat |
| 823 | ** |
| 824 | ** Usage: %fossil chat [SUBCOMMAND] [--remote URL] [ARGS...] |
| @@ -838,10 +868,22 @@ | |
| 838 | ** the response is to bring up a web-browser window to the chatroom |
| 839 | ** on the default system web-browser. You can accomplish the same by |
| 840 | ** typing the appropriate URL into the web-browser yourself. This |
| 841 | ** command is merely a convenience for command-line oriented people. |
| 842 | ** |
| 843 | ** > fossil chat send [ARGUMENTS] |
| 844 | ** |
| 845 | ** This command sends a new message to the chatroom. The message |
| 846 | ** to be sent is determined by arguments as follows: |
| 847 | ** |
| @@ -981,12 +1023,77 @@ | |
| 981 | fossil_print("ERROR: %s\n", blob_str(&down)); |
| 982 | } |
| 983 | fossil_fatal("unable to send the chat message"); |
| 984 | } |
| 985 | blob_reset(&down); |
| 986 | }else if( strcmp(g.argv[2],"url")==0 ){ |
| 987 | /* Show the URL to access chat. */ |
| 988 | fossil_print("%s/chat\n", zUrl); |
| 989 | }else{ |
| 990 | fossil_fatal("no such subcommand \"%s\". Use --help for help", g.argv[2]); |
| 991 | } |
| 992 | } |
| 993 |
| --- src/chat.c | |
| +++ src/chat.c | |
| @@ -815,10 +815,40 @@ | |
| 815 | " VALUES(julianday('now'), %Q, %d);\n" |
| 816 | "COMMIT;", |
| 817 | mdel, g.zLogin, mdel |
| 818 | ); |
| 819 | } |
| 820 | |
| 821 | /* |
| 822 | ** WEBPAGE: chat-backup hidden |
| 823 | ** |
| 824 | ** Download an SQLite database containing all chat content with a |
| 825 | ** message-id larger than the "msgid" query parameter. Setup |
| 826 | ** privilege is required to use this URL. |
| 827 | */ |
| 828 | void chat_backup_webpage(void){ |
| 829 | int msgid; |
| 830 | unsigned char *pDb = 0; |
| 831 | sqlite3_int64 szDb = 0; |
| 832 | Blob chatDb; |
| 833 | login_check_credentials(); |
| 834 | if( !g.perm.Setup ) return; |
| 835 | msgid = atoi(PD("msgid","0")); |
| 836 | db_multi_exec( |
| 837 | "ATTACH ':memory:' AS mem1;\n" |
| 838 | "PRAGMA mem1.page_size=512;\n" |
| 839 | "CREATE TABLE mem1.chat AS SELECT * FROM repository.chat WHERE msgid>%d;\n", |
| 840 | msgid |
| 841 | ); |
| 842 | pDb = sqlite3_serialize(g.db, "mem1", &szDb, 0); |
| 843 | if( pDb==0 ){ |
| 844 | fossil_fatal("Out of memory"); |
| 845 | } |
| 846 | blob_init(&chatDb, (const char*)pDb, (int)szDb); |
| 847 | cgi_set_content_type("application/x-sqlite3"); |
| 848 | cgi_set_content(&chatDb); |
| 849 | } |
| 850 | |
| 851 | /* |
| 852 | ** COMMAND: chat |
| 853 | ** |
| 854 | ** Usage: %fossil chat [SUBCOMMAND] [--remote URL] [ARGS...] |
| @@ -838,10 +868,22 @@ | |
| 868 | ** the response is to bring up a web-browser window to the chatroom |
| 869 | ** on the default system web-browser. You can accomplish the same by |
| 870 | ** typing the appropriate URL into the web-browser yourself. This |
| 871 | ** command is merely a convenience for command-line oriented people. |
| 872 | ** |
| 873 | ** > fossil chat backup |
| 874 | ** |
| 875 | ** Copy chat content from the server down into the local clone, |
| 876 | ** as a backup. Setup privilege is required on the server. |
| 877 | ** |
| 878 | ** --all Download all chat content. Normally only |
| 879 | ** previously undownloaded content is retrieved. |
| 880 | ** --debug Additional debugging output. |
| 881 | ** --out DATABASE Store CHAT table in separate database file |
| 882 | ** DATABASE rather that adding to local clone |
| 883 | ** --unsafe Allow the use of unencrypted http:// |
| 884 | ** |
| 885 | ** > fossil chat send [ARGUMENTS] |
| 886 | ** |
| 887 | ** This command sends a new message to the chatroom. The message |
| 888 | ** to be sent is determined by arguments as follows: |
| 889 | ** |
| @@ -981,12 +1023,77 @@ | |
| 1023 | fossil_print("ERROR: %s\n", blob_str(&down)); |
| 1024 | } |
| 1025 | fossil_fatal("unable to send the chat message"); |
| 1026 | } |
| 1027 | blob_reset(&down); |
| 1028 | }else if( strcmp(g.argv[2],"backup")==0 ){ |
| 1029 | /* Pull the CHAT table from the default server down into the repository |
| 1030 | ** here on the local side */ |
| 1031 | int allowUnsafe = find_option("unsafe",0,0)!=0; |
| 1032 | int bDebug = find_option("debug",0,0)!=0; |
| 1033 | const char *zOut = find_option("out",0,1); |
| 1034 | int bAll = find_option("all",0,0)!=0; |
| 1035 | int mFlags = HTTP_GENERIC | HTTP_QUIET | HTTP_NOCOMPRESS; |
| 1036 | int msgid; |
| 1037 | Blob reqUri; /* The REQUEST_URI: .../chat-backup?msgid=... */ |
| 1038 | char *zObs; |
| 1039 | const char *zPw; |
| 1040 | Blob up, down; |
| 1041 | int nChat; |
| 1042 | int rc; |
| 1043 | verify_all_options(); |
| 1044 | chat_create_tables(); |
| 1045 | msgid = bAll ? 0 : db_int(0,"SELECT max(msgid) FROM chat"); |
| 1046 | if( !g.url.isHttps && !allowUnsafe ){ |
| 1047 | fossil_fatal("URL \"%s\" is unencrypted. Use https:// instead", zUrl); |
| 1048 | } |
| 1049 | blob_init(&reqUri, g.url.path, -1); |
| 1050 | blob_appendf(&reqUri, "/chat-backup?msgid=%d", msgid); |
| 1051 | if( g.url.user && g.url.user[0] ){ |
| 1052 | zObs = obscure(g.url.user); |
| 1053 | blob_appendf(&reqUri, "&resid=%t", zObs); |
| 1054 | fossil_free(zObs); |
| 1055 | } |
| 1056 | zPw = g.url.passwd; |
| 1057 | if( zPw==0 && isDefaultUrl ) zPw = unobscure(db_get("last-sync-pw", 0)); |
| 1058 | if( zPw && zPw[0] ){ |
| 1059 | zObs = obscure(zPw); |
| 1060 | blob_appendf(&reqUri, "&token=%t", zObs); |
| 1061 | fossil_free(zObs); |
| 1062 | } |
| 1063 | g.url.path = blob_str(&reqUri); |
| 1064 | if( bDebug ){ |
| 1065 | fossil_print("REQUEST_URI: %s\n", g.url.path); |
| 1066 | mFlags &= ~HTTP_QUIET; |
| 1067 | mFlags |= HTTP_VERBOSE; |
| 1068 | } |
| 1069 | blob_init(&up, 0, 0); |
| 1070 | blob_init(&down, 0, 0); |
| 1071 | http_exchange(&up, &down, mFlags, 4, 0); |
| 1072 | if( zOut ){ |
| 1073 | blob_write_to_file(&down, zOut); |
| 1074 | fossil_print("Chat database at %s is %d bytes\n", zOut, blob_size(&down)); |
| 1075 | }else{ |
| 1076 | db_multi_exec("ATTACH ':memory:' AS chatbu;"); |
| 1077 | if( g.fSqlTrace ){ |
| 1078 | fossil_trace("-- deserialize(\"chatbu\", pData, %d);\n", |
| 1079 | blob_size(&down)); |
| 1080 | } |
| 1081 | rc = sqlite3_deserialize(g.db, "chatbu", |
| 1082 | (unsigned char*)blob_buffer(&down), |
| 1083 | blob_size(&down), blob_size(&down), 0); |
| 1084 | if( rc ){ |
| 1085 | fossil_fatal("cannot open patch database: %s", sqlite3_errmsg(g.db)); |
| 1086 | } |
| 1087 | nChat = db_int(0, "SELECT count(*) FROM chatbu.chat"); |
| 1088 | fossil_print("Got %d new records, %d bytes\n", nChat, blob_size(&down)); |
| 1089 | db_multi_exec( |
| 1090 | "REPLACE INTO repository.chat SELECT * FROM chatbu.chat;" |
| 1091 | ); |
| 1092 | } |
| 1093 | }else if( strcmp(g.argv[2],"url")==0 ){ |
| 1094 | /* Show the URL to access chat. */ |
| 1095 | fossil_print("%s/chat\n", zUrl); |
| 1096 | }else{ |
| 1097 | fossil_fatal("no such subcommand \"%s\". Use --help for help", g.argv[2]); |
| 1098 | } |
| 1099 | } |
| 1100 |