corrade-lsl-templates – Blame information for rev 42

Subversion Repositories:
Rev:
Rev Author Line No. Line
8 office 1 ///////////////////////////////////////////////////////////////////////////
42 office 2 // Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 //
8 office 3 ///////////////////////////////////////////////////////////////////////////
4 //
5 // A wiki module that can memorize strings and recall them by path.
6 //
7 ///////////////////////////////////////////////////////////////////////////
8  
9 ///////////////////////////////////////////////////////////////////////////
42 office 10 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
8 office 11 ///////////////////////////////////////////////////////////////////////////
12 integer wasIsAlNum(string a) {
13 if(a == "") return FALSE;
42 office 14 integer x = llBase64ToInteger("AAAA" +
8 office 15 llStringToBase64(llGetSubString(a, 0, 0)));
16 return (x >= 65 && x <= 90) || (x >= 97 && x <= 122) ||
17 (x >= 48 && x <= 57);
18 }
41 office 19  
8 office 20 ///////////////////////////////////////////////////////////////////////////
41 office 21 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
8 office 22 ///////////////////////////////////////////////////////////////////////////
23 string wasKeyValueGet(string k, string data) {
24 if(llStringLength(data) == 0) return "";
25 if(llStringLength(k) == 0) return "";
41 office 26 list a = llParseStringKeepNulls(data, ["&", "="], []);
27 integer i = llListFindList(llList2ListStrided(a, 0, -1, 2), [ k ]);
28 if(i != -1) return llList2String(a, 2*i+1);
8 office 29 return "";
30 }
42 office 31  
8 office 32 ///////////////////////////////////////////////////////////////////////////
42 office 33 // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
8 office 34 ///////////////////////////////////////////////////////////////////////////
35 string wasKeyValueEncode(list data) {
36 list k = llList2ListStrided(data, 0, -1, 2);
37 list v = llList2ListStrided(llDeleteSubList(data, 0, 0), 0, -1, 2);
38 data = [];
39 do {
40 data += llList2String(k, 0) + "=" + llList2String(v, 0);
41 k = llDeleteSubList(k, 0, 0);
42 v = llDeleteSubList(v, 0, 0);
43 } while(llGetListLength(k) != 0);
44 return llDumpList2String(data, "&");
45 }
46  
47 ///////////////////////////////////////////////////////////////////////////
42 office 48 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
8 office 49 ///////////////////////////////////////////////////////////////////////////
50 // escapes a string in conformance with RFC1738
51 string wasURLEscape(string i) {
52 string o = "";
53 do {
54 string c = llGetSubString(i, 0, 0);
55 i = llDeleteSubString(i, 0, 0);
56 if(c == "") jump continue;
57 if(c == " ") {
58 o += "+";
59 jump continue;
60 }
61 if(c == "\n") {
62 o += "%0D" + llEscapeURL(c);
63 jump continue;
64 }
65 o += llEscapeURL(c);
66 @continue;
67 } while(i != "");
68 return o;
69 }
70  
71 ///////////////////////////////////////////////////////////////////////////
42 office 72 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
8 office 73 ///////////////////////////////////////////////////////////////////////////
74 list wasCSVToList(string csv) {
75 list l = [];
76 list s = [];
77 string m = "";
78 do {
79 string a = llGetSubString(csv, 0, 0);
80 csv = llDeleteSubString(csv, 0, 0);
81 if(a == ",") {
82 if(llList2String(s, -1) != "\"") {
83 l += m;
84 m = "";
85 jump continue;
86 }
87 m += a;
88 jump continue;
89 }
90 if(a == "\"" && llGetSubString(csv, 0, 0) == a) {
91 m += a;
92 csv = llDeleteSubString(csv, 0, 0);
93 jump continue;
94 }
95 if(a == "\"") {
96 if(llList2String(s, -1) != a) {
97 s += a;
98 jump continue;
99 }
100 s = llDeleteSubList(s, -1, -1);
101 jump continue;
102 }
103 m += a;
104 @continue;
105 } while(csv != "");
106 // postcondition: length(s) = 0
107 return l + m;
108 }
109  
110 ///////////////////////////////////////////////////////////////////////////
42 office 111 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
8 office 112 ///////////////////////////////////////////////////////////////////////////
113 string wasListToCSV(list l) {
114 list v = [];
115 do {
116 string a = llDumpList2String(
117 llParseStringKeepNulls(
118 llList2String(
42 office 119 l,
8 office 120  
42 office 121 ),
122 ["\""],
8 office 123 []
124 ),
125 "\"\""
126 );
127 if(llParseStringKeepNulls(
42 office 128 a,
8 office 129 [" ", ",", "\n", "\""], []
42 office 130 ) !=
8 office 131 (list) a
132 ) a = "\"" + a + "\"";
133 v += a;
134 l = llDeleteSubList(l, 0, 0);
135 } while(l != []);
136 return llDumpList2String(v, ",");
137 }
138  
139 ///////////////////////////////////////////////////////////////////////////
42 office 140 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
8 office 141 ///////////////////////////////////////////////////////////////////////////
142 // unescapes a string in conformance with RFC1738
143 string wasURLUnescape(string i) {
144 return llUnescapeURL(
145 llDumpList2String(
146 llParseString2List(
147 llDumpList2String(
148 llParseString2List(
42 office 149 i,
150 ["+"],
8 office 151 []
42 office 152 ),
8 office 153 " "
42 office 154 ),
155 ["%0D%0A"],
8 office 156 []
42 office 157 ),
8 office 158 "\n"
159 )
160 );
161 }
162  
163 // configuration data
164 string configuration = "";
165 // store message over state.
12 office 166 string path = "";
8 office 167 string data = "";
12 office 168 string action = "";
169 string statement = "";
170 string parameters = "";
8 office 171  
172 default {
173 state_entry() {
174 llOwnerSay("[Wiki] Starting...");
175 llSetTimerEvent(10);
176 }
177 link_message(integer sender, integer num, string message, key id) {
178 if(id != "configuration") return;
179 llOwnerSay("[Wiki] Got configuration...");
180 configuration = message;
12 office 181 action = "create";
42 office 182 state trampoline;
8 office 183 }
184 timer() {
185 llOwnerSay("[Wiki] Requesting configuration...");
186 llMessageLinked(LINK_THIS, 0, "configuration", NULL_KEY);
187 }
188 on_rez(integer num) {
189 llResetScript();
190 }
191 changed(integer change) {
42 office 192 if((change & CHANGED_INVENTORY) ||
193 (change & CHANGED_REGION_START) ||
8 office 194 (change & CHANGED_OWNER)) {
195 llResetScript();
196 }
197 }
198 state_exit() {
199 llSetTimerEvent(0);
200 }
201 }
202  
42 office 203 state trampoline {
8 office 204 state_entry() {
12 office 205 if(action == "create") {
42 office 206 statement = wasURLEscape("CREATE TABLE IF NOT EXISTS \"" +
207 wasKeyValueGet("wiki table", configuration) +
12 office 208 "\" (path text unique collate nocase, data text)");
209 state query;
8 office 210 }
42 office 211  
12 office 212 if(action == "get") {
213 statement = wasURLEscape("SELECT data FROM \"" +
42 office 214 wasKeyValueGet("wiki table", configuration) +
12 office 215 "\" WHERE path=:path");
216 parameters = wasURLEscape(
217 wasListToCSV(
218 [
219 "path",
220 wasURLEscape(path)
221 ]
8 office 222 )
223 );
12 office 224 state query;
8 office 225 }
42 office 226  
12 office 227 if(action == "set") {
228 if(data == "") {
42 office 229 statement = wasURLEscape("DELETE FROM \"" +
230 wasKeyValueGet("wiki table", configuration) +
12 office 231 "\" WHERE path=:path");
232 parameters = wasURLEscape(
233 wasListToCSV(
234 [
235 "path",
236 wasURLEscape(path)
237 ]
238 )
239 );
240 state query;
241 }
242 statement = wasURLEscape("REPLACE INTO \"" +
42 office 243 wasKeyValueGet("wiki table", configuration) +
12 office 244 "\" (path, data) VALUES (:path, :data)");
245 parameters = wasURLEscape(
246 wasListToCSV(
247 [
248 "path",
249 wasURLEscape(path),
250 "data",
251 wasURLEscape(data)
252 ]
253 )
254 );
255 state query;
256 }
42 office 257  
12 office 258 if(action == "dir") {
259 if(path == "/") {
260 path = "";
14 office 261 statement = wasURLEscape(
42 office 262 "SELECT DISTINCT SUBSTR(path, 1, LENGTH(path) - LENGTH(LTRIM(SUBSTR(path,2), 'abcdefghijklmnopqrstuvwxyz'))) AS path FROM \"" +
263 wasKeyValueGet("wiki table", configuration) +
14 office 264 "\" WHERE path LIKE '/%' LIMIT " +
265 wasKeyValueGet("wiki results limit", configuration)
266 );
12 office 267 state query;
268 }
14 office 269 statement = wasURLEscape(
42 office 270 "SELECT DISTINCT SUBSTR(REPLACE(path, :base, ''),1, LENGTH(REPLACE(path, :base, '')) - LENGTH(LTRIM(REPLACE(path, :base, ''), 'abcdefghijklmnopqrstuvwxyz'))) AS path FROM \"" +
271 wasKeyValueGet("wiki table", configuration) +
14 office 272 "\" WHERE path LIKE :path LIMIT " +
273 wasKeyValueGet("wiki results limit", configuration)
274 );
12 office 275 parameters = wasURLEscape(
276 wasListToCSV(
277 [
278 "path",
279 wasURLEscape(path + "/" + "%"),
280 "base",
42 office 281 wasURLEscape("/" +
14 office 282 llDumpList2String(
283 llParseString2List(
284 path,
285 ["/"],
286 []
287 ),
288 "/"
42 office 289 ) +
14 office 290 "/"
291 )
12 office 292 ]
293 )
294 );
295 state query;
296 }
42 office 297  
14 office 298 if(action == "find") {
299 if(data == "") {
300 data = "Command requires two parameters: a path followed by a search term.";
301 state tell;
302 }
303 if(path == "/")
42 office 304 path = "";
14 office 305 statement = wasURLEscape(
42 office 306 "SELECT DISTINCT path FROM \"" +
307 wasKeyValueGet("wiki table", configuration) +
14 office 308 "\" WHERE path LIKE :path AND ( data LIKE :data OR path LIKE :data ) COLLATE NOCASE LIMIT " +
309 wasKeyValueGet("wiki search limit", configuration)
310 );
311 parameters = wasURLEscape(
312 wasListToCSV(
313 [
314 "path",
315 wasURLEscape(path + "/" + "%"),
316 "data",
317 wasURLEscape("%" + data + "%")
318 ]
319 )
320 );
321 state query;
322 }
42 office 323  
12 office 324 // DEBUG
325 llOwnerSay("[Wiki] Jump table corrupted, please contact creator...");
326 llResetScript();
8 office 327 }
328 on_rez(integer num) {
329 llResetScript();
330 }
331 changed(integer change) {
42 office 332 if((change & CHANGED_INVENTORY) ||
333 (change & CHANGED_REGION_START) ||
8 office 334 (change & CHANGED_OWNER)) {
335 llResetScript();
336 }
337 }
338 }
339  
340 state listen_group {
341 state_entry() {
342 // DEBUG
343 llOwnerSay("[Wiki] Waiting for group messages...");
344 }
345 link_message(integer sender, integer num, string message, key id) {
346 // We only care about notifications now.
347 if(id != "notification")
348 return;
42 office 349  
8 office 350 // This script only processes group notifications.
42 office 351 if(wasKeyValueGet("type", message) != "group" ||
352 (wasKeyValueGet("type", message) == "group" &&
353 wasURLUnescape(wasKeyValueGet("group", message)) !=
354 wasKeyValueGet("group", configuration)))
8 office 355 return;
42 office 356  
8 office 357 // Get the sent message.
358 data = wasURLUnescape(
359 wasKeyValueGet(
42 office 360 "message",
8 office 361 message
362 )
363 );
42 office 364  
11 office 365 // Check if this is an eggdrop command.
42 office 366 if(llGetSubString(data, 0, 0) !=
8 office 367 wasKeyValueGet("command", configuration))
368 return;
42 office 369  
11 office 370 // Check if the command matches the current module.
12 office 371 list command = llParseString2List(data, [" "], []);
42 office 372 if(llList2String(command, 0) !=
12 office 373 wasKeyValueGet("command", configuration) + "wiki")
8 office 374 return;
42 office 375  
8 office 376 // Remove command.
377 command = llDeleteSubList(command, 0, 0);
42 office 378  
8 office 379 // Check for supported sub-commands.
42 office 380 if(llList2String(command, 0) != "set" &&
8 office 381 llList2String(command, 0) != "get" &&
14 office 382 llList2String(command, 0) != "dir" &&
383 llList2String(command, 0) != "find") {
384 data = "Subcommands are: get, set, dir or find";
8 office 385 state tell;
386 }
42 office 387  
8 office 388 // Get the sub-command and store it as a jump state.
12 office 389 action = llList2String(command, 0);
42 office 390  
8 office 391 // Remove sub-command.
392 command = llDeleteSubList(command, 0, 0);
42 office 393  
8 office 394 // Get the path parts.
395 list path_parts = llParseString2List(
396 llList2String(command, 0), ["/"], []
397 );
42 office 398  
8 office 399 // Dump the path and store it over states.
400 path = llStringTrim(
401 llDumpList2String(
42 office 402 path_parts,
8 office 403 "/"
42 office 404 ),
8 office 405 STRING_TRIM
406 );
42 office 407  
8 office 408 if(path != "") {
409 integer i = llStringLength(path) - 1;
410 do {
411 string c = llGetSubString(path, i, i);
412 if(c != "/" && !wasIsAlNum(c)) {
413 data = "Only alpha-numerics accepted in the path string.";
414 state tell;
415 }
416 } while(--i > -1);
417 }
42 office 418  
8 office 419 path = "/" + path;
42 office 420  
8 office 421 // Remove path.
422 command = llDeleteSubList(command, 0, 0);
42 office 423  
8 office 424 // Dump the rest of the message.
425 data = llDumpList2String(command, " ");
42 office 426  
427 state trampoline;
8 office 428 }
429 on_rez(integer num) {
430 llResetScript();
431 }
432 changed(integer change) {
42 office 433 if((change & CHANGED_INVENTORY) ||
434 (change & CHANGED_REGION_START) ||
8 office 435 (change & CHANGED_OWNER)) {
436 llResetScript();
437 }
438 }
439 }
440  
12 office 441 state query {
8 office 442 state_entry() {
14 office 443 // Check messge length.
444 string message = wasKeyValueEncode(
445 [
446 "command", "database",
447 "group", wasURLEscape(
448 wasKeyValueGet(
42 office 449 "group",
14 office 450 configuration
451 )
452 ),
453 "password", wasURLEscape(
454 wasKeyValueGet(
42 office 455 "password",
14 office 456 configuration
457 )
458 ),
459 "SQL", statement,
460 "data", parameters,
42 office 461 "callback", wasURLEscape(
462 wasKeyValueGet(
463 "URL",
464 configuration
465 )
466 )
14 office 467 ]
468 );
42 office 469  
14 office 470 // GC - none of these are needed anymore.
471 statement = "";
472 parameters = "";
42 office 473  
474 // Message length check.
14 office 475 if(llStringLength(message) > 1023) {
476 data = "Message length exceeded 1023 characters.";
477 state tell;
478 }
42 office 479  
8 office 480 // DEBUG
12 office 481 llOwnerSay("[Wiki] Executing action: " + action);
8 office 482 llInstantMessage(
483 wasKeyValueGet(
42 office 484 "corrade",
8 office 485 configuration
42 office 486 ),
14 office 487 message
8 office 488 );
14 office 489 // GC
490 message = "";
8 office 491 llSetTimerEvent(60);
492 }
42 office 493 link_message(integer sender, integer num, string body, key id) {
494 // Only process callbacks for the database command.
495 if(id != "callback" || wasKeyValueGet("command", body) != "database")
496 return;
497  
498 if(wasKeyValueGet("success", body) != "True") {
8 office 499 // DEBUG
42 office 500 llOwnerSay("[Wiki] Unable query database: " +
8 office 501 wasURLUnescape(
502 wasKeyValueGet("error", body)
503 )
504 );
505 state listen_group;
506 }
42 office 507  
12 office 508 // Process actions.
509 if(action == "set") {
510 if(data == "") {
511 data = "Deleted from " + path;
512 state tell;
513 }
514 data = "Stored into " + path;
8 office 515 state tell;
516 }
14 office 517  
518 if(action == "find") {
519 data = llDumpList2String(
520 llList2ListStrided(
521 llDeleteSubList(
522 wasCSVToList(
523 wasURLUnescape(
524 wasKeyValueGet("data", body)
525 )
526 ),
527 0,
528  
529 ),
530 0,
531 -1,
532 2
533 ),
534 ","
535 );
536 if(data == "") {
537 data = "Sorry, the term was not found.";
538 state tell;
539 }
540 state tell;
541 }
542  
12 office 543 if(action == "get") {
544 data = llDumpList2String(
545 llDeleteSubList(
546 wasCSVToList(
547 wasURLUnescape(
548 wasKeyValueGet("data", body)
8 office 549 )
550 ),
12 office 551 0,
552  
553 ),
554 ""
8 office 555 );
42 office 556  
12 office 557 if(data == "") {
558 data = "Sorry, that path contains no data.";
559 state tell;
560 }
42 office 561  
12 office 562 data = path + ": " + data;
8 office 563 state tell;
564 }
42 office 565  
12 office 566 if(action == "dir") {
567 list paths = llList2ListStrided(
568 llDeleteSubList(
569 wasCSVToList(
570 wasURLUnescape(
571 wasKeyValueGet("data", body)
8 office 572 )
573 ),
12 office 574 0,
575  
8 office 576 ),
577 0,
12 office 578 -1,
579 2
580 );
42 office 581  
12 office 582 if(llGetListLength(paths) == 0) {
583 data = "Sorry, that path contains no sub-paths.";
584 state tell;
585 }
42 office 586  
12 office 587 // Eliminate path component.
588 if(path == "/")
589 path = "";
42 office 590  
12 office 591 list sibling = [];
592 do {
593 // Get the path part.
594 string part = llList2String(paths, 0);
42 office 595  
12 office 596 // Remove the path component.
597 string child = llStringTrim(
598 llDumpList2String(
599 llParseString2List(
600 part,
42 office 601 [path, "/"],
12 office 602 []
603 ),
604 "/"
8 office 605 ),
12 office 606 STRING_TRIM
42 office 607  
12 office 608 );
42 office 609  
12 office 610 integer i = llSubStringIndex(child, "/");
611 if(i == -1) {
612 sibling += path + "/" + child;
613 jump continue_dir;
614 }
615 child = path + "/" + llDeleteSubString(child, i, -1) + "/";
616 if(llListFindList(sibling, (list)child) == -1)
617 sibling += child;
618 @continue_dir;
619 paths = llDeleteSubList(paths, 0, 0);
620 } while(llGetListLength(paths) != 0);
42 office 621  
12 office 622 data = llList2CSV(sibling);
623 // GC
624 sibling = [];
42 office 625  
12 office 626 state tell;
627 }
42 office 628  
12 office 629 // Don't announce creating table.
630 if(action == "create")
631 state listen_group;
42 office 632  
12 office 633 // DEBUG
634 llOwnerSay("[Wiki] Jump table corrupted, please contact creator...");
635 state listen_group;
8 office 636 }
637 timer() {
638 state listen_group;
639 }
640 on_rez(integer num) {
641 llResetScript();
642 }
643 changed(integer change) {
42 office 644 if((change & CHANGED_INVENTORY) ||
645 (change & CHANGED_REGION_START) ||
8 office 646 (change & CHANGED_OWNER)) {
647 llResetScript();
648 }
649 }
650 state_exit() {
651 llSetTimerEvent(0);
652 }
653 }
654  
655 state tell {
656 state_entry() {
657 // DEBUG
658 llOwnerSay("[Wiki] Sending to group.");
659 llInstantMessage(
660 wasKeyValueGet(
42 office 661 "corrade",
8 office 662 configuration
42 office 663 ),
8 office 664 wasKeyValueEncode(
665 [
666 "command", "tell",
667 "group", wasURLEscape(
668 wasKeyValueGet(
42 office 669 "group",
8 office 670 configuration
671 )
672 ),
673 "password", wasURLEscape(
674 wasKeyValueGet(
42 office 675 "password",
8 office 676 configuration
677 )
678 ),
679 "entity", "group",
680 "message", wasURLEscape(data)
681 ]
682 )
683 );
14 office 684 // GC
685 path = "";
686 data = "";
8 office 687 state listen_group;
688 }
689 }