some cleanups for the previous commit
[chiroptera.git] / sqbase_experiment / zsq_30_create_twits.d
blob4278dc5aad87a522b4b4e587ac77756419c61b40
1 /* E-Mail Client
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // WARNING! this doesn't perform any sanity checks on "accounts.rc"!
18 module zsq_create_twits is aliced;
20 import chibackend;
22 import iv.sq3;
23 import iv.strex;
24 import iv.timer;
25 import iv.utfutil;
26 import iv.vfs;
27 import iv.vfs.io;
28 import iv.vfs.util;
31 // ////////////////////////////////////////////////////////////////////////// //
32 void main (string[] args) {
33 ChiroTimerEnabled = true;
34 chiroParseCommonCLIArgs(args);
36 // here, i don't have any threads at all
37 if (sqlite3_config(SQLITE_CONFIG_SINGLETHREAD) != SQLITE_OK) throw new Exception("cannot switch SQLite to multi-threaded mode");
39 bool allowed = false;
40 foreach (string s; args[1..$]) {
41 if (s == "force") allowed = true;
43 if (!allowed) throw new Exception("use \"force\" to rebuild");
45 chiroOpenConfDB();
47 // we will re-import them
48 dbConf.execute(`
49 DELETE FROM emailtwits;
50 DELETE FROM msgidtwits;
51 `);
53 auto stInsTwitEMail = dbConf.persistentStatement(`
54 INSERT INTO emailtwits
55 ( tagglob, email, name, title, notes)
56 VALUES(:tagglob,:email,:name,:title,:notes)
57 ;`);
59 auto stInsTwitMsgid = dbConf.persistentStatement(`
60 INSERT INTO msgidtwits
61 ( etwitid, tagglob, msgid, automatic)
62 VALUES(:etwitid,:tagglob,:msgid, 0)
63 ;`);
65 uint hasTwitEMail (const(char)[] email, const(char)[] name) {
66 if (name.length == 0) {
67 if (email.length == 0) return 0;
68 // email only
69 foreach (auto row; dbConf.statement(`SELECT rowid AS twitid FROM emailtwits WHERE email=:email;`).bindConstText(":email", email).range) return row.twitid!uint;
70 } else if (email.length == 0) {
71 // name only
72 foreach (auto row; dbConf.statement(`SELECT rowid AS twitid FROM emailtwits WHERE name=:name;`).bindConstText(":name", name).range) return row.twitid!uint;
73 } else {
74 // both name and email
75 foreach (auto row; dbConf.statement(`SELECT rowid AS twitid FROM emailtwits WHERE email=:email AND name=:name;`)
76 .bindConstText(":email", email).bindConstText(":name", name).range) return row.twitid!uint;
78 return false;
82 bool hasTwitMsgid (const(char)[] msgid) {
83 if (msgid.length == 0) return false;
84 foreach (auto row; dbConf.statement(`SELECT rowid FROM msgidtwits WHERE msgid=:msgid;`).bindConstText(":msgid", msgid).range) return true;
85 return false;
89 dbConf.execute("BEGIN TRANSACTION;");
90 scope(failure) dbConf.execute("ROLLBACK TRANSACTION;");
92 void insertTwit (string[] argv) {
93 string tagglob = null;
94 string msgid = null;
95 string name = null;
96 string email = null;
97 string title = null;
98 string notes = null; /* url */
100 if (argv[0] != "twit_set" && argv[0] != "twit_thread") throw new Exception("expected \"twit_set\", but got \""~argv[0]~"\"");
101 if (argv.length < 2) throw new Exception("\"twit_set\" don't have any arguments");
103 for (usize aidx = 1; aidx < argv.length; ) {
104 string aname = argv[aidx++];
105 switch (aname) {
106 case "foldermask":
107 case "folder_mask":
108 if (tagglob !is null) throw new Exception("duplicate tag glob, old is \""~tagglob~"\", new is \""~argv[aidx+1]~"\"");
109 tagglob = argv[aidx++];
110 if (tagglob.length && tagglob[0].isalpha()) tagglob = "/"~tagglob;
111 break;
112 case "msgid":
113 case "message":
114 case "messageid":
115 case "message_id":
116 if (msgid !is null) throw new Exception("duplicate message id, old is \""~msgid~"\", new is \""~argv[aidx+1]~"\"");
117 msgid = argv[aidx++];
118 if (msgid.length >= 2 && msgid[0] == '<' && msgid[$-1] == '>') msgid = msgid[1..$-1].xstrip;
119 break;
120 case "mail":
121 case "email":
122 case "e-mail":
123 if (email !is null) throw new Exception("duplicate email, old is \""~email~"\", new is \""~argv[aidx+1]~"\"");
124 email = argv[aidx++].toLowerStr;
125 if (!isGoodEmail(email) && email.indexOf('*') < 0) throw new Exception("invalid email \""~email~"\"");
126 break;
127 case "name":
128 if (name !is null) throw new Exception("duplicate name, old is \""~name~"\", new is \""~argv[aidx+1]~"\"");
129 name = argv[aidx++];
130 break;
131 case "title":
132 if (title !is null) throw new Exception("duplicate title, old is \""~title~"\", new is \""~argv[aidx+1]~"\"");
133 title = argv[aidx++];
134 break;
135 case "url":
136 case "href":
137 if (notes !is null) throw new Exception("duplicate notes, old is \""~notes~"\", new is \""~argv[aidx+1]~"\"");
138 notes = argv[aidx++];
139 break;
140 default:
141 throw new Exception("unknown option \""~aname~"\"");
145 while (tagglob.length && tagglob[$-1] == '/') tagglob = tagglob[0..$-1];
146 if (tagglob.length && isalnum(tagglob[0])) tagglob = "/"~tagglob;
148 if (tagglob.length == 0) throw new Exception("missing tag glob");
149 if (email.length == 0 && name.length == 0 && msgid.length == 0) {
150 throw new Exception("missing email, name or msgid");
153 uint etid = hasTwitEMail(email, name);
155 if (hasTwitEMail(email, name)) {
156 writeln("duplicate twit for: email=\"", email, "\"; name=\"", name, "\"");
157 } else
159 if (email.length || name.length) {
160 stInsTwitEMail
161 .bindConstText(":tagglob", tagglob)
162 .bindConstText(":email", email)
163 .bindConstText(":name", name)
164 .bindConstText(":title", title, allowNull:true)
165 .bindConstText(":notes", notes, allowNull:true)
166 .doAll();
167 etid = cast(uint)dbConf.lastRowId;
170 if (msgid.length) {
171 stInsTwitMsgid
172 .bind(":etwitid", etid)
173 .bindConstText(":tagglob", tagglob)
174 .bindConstText(":msgid", msgid, allowNull:true)
175 .doAll();
179 uint count = 0;
180 foreach (string[] argv; loadRCFile("twits.rc")) { insertTwit(argv); ++count; }
181 foreach (string[] argv; loadRCFile("twit_threads.rc")) { insertTwit(argv); ++count; }
182 foreach (string[] argv; loadRCFile("auto_twits.rc")) { insertTwit(argv); ++count; }
183 foreach (string[] argv; loadRCFile("auto_twit_threads.rc")) { insertTwit(argv); ++count; }
184 dbConf.execute("COMMIT TRANSACTION;");
186 writeln(count, " twit entries added.");
188 writeln("closing the db");
189 dbConf.execute("ANALYZE;");
190 dbConf.close();