tclhash.c 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202
  1. /*
  2. * tclhash.c -- handles:
  3. * bind and unbind
  4. * checking and triggering the various in-bot bindings
  5. * listing current bindings
  6. * adding/removing new binding tables
  7. * (non-Tcl) procedure lookups for msg/dcc/file commands
  8. * (Tcl) binding internal procedures to msg/dcc/file commands
  9. *
  10. */
  11. #include "main.h"
  12. #include "chan.h"
  13. #include "users.h"
  14. #include "match.c"
  15. extern Tcl_Interp *interp;
  16. extern struct dcc_t *dcc;
  17. extern struct userrec *userlist;
  18. extern int dcc_total;
  19. extern time_t now;
  20. extern mycmds cmds[];
  21. extern int cmdi;
  22. extern char dcc_prefix[];
  23. p_tcl_bind_list bind_table_list;
  24. p_tcl_bind_list H_chat, H_act, H_bcst, H_chon, H_chof,
  25. H_load, H_unld, H_link, H_disc, H_dcc, H_chjn, H_chpt,
  26. H_bot, H_time, H_nkch, H_away, H_note, H_filt, H_event;
  27. static int builtin_2char();
  28. static int builtin_3char();
  29. static int builtin_5int();
  30. static int builtin_char();
  31. static int builtin_chpt();
  32. static int builtin_chjn();
  33. static int builtin_idxchar();
  34. static int builtin_charidx();
  35. static int builtin_chat();
  36. static int builtin_dcc();
  37. /* Allocate and initialise a chunk of memory.
  38. */
  39. static inline void *n_malloc_null(int size, const char *file, int line)
  40. {
  41. #ifdef DEBUG_MEM
  42. # define nmalloc_null(size) n_malloc_null(size, __FILE__, __LINE__)
  43. void *ptr = n_malloc(size, file, line);
  44. #else
  45. # define nmalloc_null(size) n_malloc_null(size, NULL, 0)
  46. void *ptr = nmalloc(size);
  47. #endif
  48. egg_memset(ptr, 0, size);
  49. return ptr;
  50. }
  51. /* Delete trigger/command.
  52. */
  53. static inline void tcl_cmd_delete(tcl_cmd_t *tc)
  54. {
  55. nfree(tc->func_name);
  56. nfree(tc);
  57. }
  58. /* Delete bind and its elements.
  59. */
  60. static inline void tcl_bind_mask_delete(tcl_bind_mask_t *tm)
  61. {
  62. tcl_cmd_t *tc, *tc_next;
  63. for (tc = tm->first; tc; tc = tc_next) {
  64. tc_next = tc->next;
  65. tcl_cmd_delete(tc);
  66. }
  67. nfree(tm->mask);
  68. nfree(tm);
  69. }
  70. /* Delete bind list and its elements.
  71. */
  72. static inline void tcl_bind_list_delete(tcl_bind_list_t *tl)
  73. {
  74. tcl_bind_mask_t *tm, *tm_next;
  75. for (tm = tl->first; tm; tm = tm_next) {
  76. tm_next = tm->next;
  77. tcl_bind_mask_delete(tm);
  78. }
  79. nfree(tl);
  80. }
  81. inline void garbage_collect_tclhash(void)
  82. {
  83. tcl_bind_list_t *tl, *tl_next, *tl_prev;
  84. tcl_bind_mask_t *tm, *tm_next, *tm_prev;
  85. tcl_cmd_t *tc, *tc_next, *tc_prev;
  86. for (tl = bind_table_list, tl_prev = NULL; tl; tl = tl_next) {
  87. tl_next = tl->next;
  88. if (tl->flags & HT_DELETED) {
  89. if (tl_prev)
  90. tl_prev->next = tl->next;
  91. else
  92. bind_table_list = tl->next;
  93. tcl_bind_list_delete(tl);
  94. } else {
  95. for (tm = tl->first, tm_prev = NULL; tm; tm = tm_next) {
  96. tm_next = tm->next;
  97. if (!(tm->flags & TBM_DELETED)) {
  98. for (tc = tm->first, tc_prev = NULL; tc; tc = tc_next) {
  99. tc_next = tc->next;
  100. if (tc->attributes & TC_DELETED) {
  101. if (tc_prev)
  102. tc_prev->next = tc->next;
  103. else
  104. tm->first = tc->next;
  105. tcl_cmd_delete(tc);
  106. } else
  107. tc_prev = tc;
  108. }
  109. }
  110. /* Delete the bind when it's marked as deleted or
  111. when it's empty. */
  112. if ((tm->flags & TBM_DELETED) || tm->first == NULL) {
  113. if (tm_prev)
  114. tm_prev->next = tm->next;
  115. else
  116. tl->first = tm_next;
  117. tcl_bind_mask_delete(tm);
  118. } else
  119. tm_prev = tm;
  120. }
  121. tl_prev = tl;
  122. }
  123. }
  124. }
  125. static inline int tcl_cmd_expmem(tcl_cmd_t *tc)
  126. {
  127. int tot;
  128. tot = sizeof(*tc);
  129. if (tc->func_name)
  130. tot += strlen(tc->func_name) + 1;
  131. return tot;
  132. }
  133. static inline int tcl_bind_mask_expmem(tcl_bind_mask_t *tm)
  134. {
  135. int tot = 0;
  136. tcl_cmd_t *tc;
  137. for (tc = tm->first; tc; tc = tc->next)
  138. tot += tcl_cmd_expmem(tc);
  139. if (tm->mask)
  140. tot += strlen(tm->mask) + 1;
  141. tot += sizeof(*tm);
  142. return tot;
  143. }
  144. static inline int tcl_bind_list_expmem(tcl_bind_list_t *tl)
  145. {
  146. int tot = 0;
  147. tcl_bind_mask_t *tm;
  148. for (tm = tl->first; tm; tm = tm->next)
  149. tot += tcl_bind_mask_expmem(tm);
  150. tot += sizeof(*tl);
  151. return tot;
  152. }
  153. int expmem_tclhash(void)
  154. {
  155. int tot = 0;
  156. tcl_bind_list_t *tl;
  157. for (tl = bind_table_list; tl; tl = tl->next)
  158. tot += tcl_bind_list_expmem(tl);
  159. return tot;
  160. }
  161. extern cmd_t C_dcc[];
  162. static int tcl_bind();
  163. static cd_tcl_cmd cd_cmd_table[] = {
  164. {"bind", tcl_bind, (void *)0},
  165. {"unbind", tcl_bind, (void *)1},
  166. {0}
  167. };
  168. void init_bind(void)
  169. {
  170. bind_table_list = NULL;
  171. Context;
  172. add_cd_tcl_cmds(cd_cmd_table);
  173. H_unld = add_bind_table("unld", HT_STACKABLE, builtin_char);
  174. H_time = add_bind_table("time", HT_STACKABLE, builtin_5int);
  175. H_note = add_bind_table("note", 0, builtin_3char);
  176. H_nkch = add_bind_table("nkch", HT_STACKABLE, builtin_2char);
  177. H_load = add_bind_table("load", HT_STACKABLE, builtin_char);
  178. H_link = add_bind_table("link", HT_STACKABLE, builtin_2char);
  179. H_filt = add_bind_table("filt", HT_STACKABLE, builtin_idxchar);
  180. H_disc = add_bind_table("disc", HT_STACKABLE, builtin_char);
  181. H_dcc = add_bind_table("dcc", 0, builtin_dcc);
  182. H_chpt = add_bind_table("chpt", HT_STACKABLE, builtin_chpt);
  183. H_chon = add_bind_table("chon", HT_STACKABLE, builtin_charidx);
  184. H_chof = add_bind_table("chof", HT_STACKABLE, builtin_charidx);
  185. H_chjn = add_bind_table("chjn", HT_STACKABLE, builtin_chjn);
  186. H_chat = add_bind_table("chat", HT_STACKABLE, builtin_chat);
  187. H_bot = add_bind_table("bot", 0, builtin_3char);
  188. H_bcst = add_bind_table("bcst", HT_STACKABLE, builtin_chat);
  189. H_away = add_bind_table("away", HT_STACKABLE, builtin_chat);
  190. H_act = add_bind_table("act", HT_STACKABLE, builtin_chat);
  191. H_event = add_bind_table("evnt", HT_STACKABLE, builtin_char);
  192. add_builtins(H_dcc, C_dcc);
  193. Context;
  194. }
  195. void kill_bind(void)
  196. {
  197. tcl_bind_list_t *tl, *tl_next;
  198. rem_builtins(H_dcc, C_dcc);
  199. for (tl = bind_table_list; tl; tl = tl_next) {
  200. tl_next = tl->next;
  201. if (!(tl->flags |= HT_DELETED))
  202. putlog(LOG_DEBUG, "*", "De-Allocated bind table %s", tl->name);
  203. tcl_bind_list_delete(tl);
  204. }
  205. bind_table_list = NULL;
  206. }
  207. tcl_bind_list_t *add_bind_table(const char *nme, int flg, Function func)
  208. {
  209. tcl_bind_list_t *tl, *tl_prev;
  210. int v;
  211. /* Do not allow coders to use bind table names longer than
  212. 4 characters. */
  213. Assert(strlen(nme) <= 4);
  214. for (tl = bind_table_list, tl_prev = NULL; tl; tl_prev = tl, tl = tl->next) {
  215. if (tl->flags & HT_DELETED)
  216. continue;
  217. v = egg_strcasecmp(tl->name, nme);
  218. if (!v)
  219. return tl; /* Duplicate, just return old value. */
  220. if (v > 0)
  221. break; /* New. Insert at start of list. */
  222. }
  223. tl = nmalloc_null(sizeof(tcl_bind_list_t));
  224. strcpy(tl->name, nme);
  225. tl->flags = flg;
  226. tl->func = func;
  227. if (tl_prev) {
  228. tl->next = tl_prev->next;
  229. tl_prev->next = tl;
  230. } else {
  231. tl->next = bind_table_list;
  232. bind_table_list = tl;
  233. }
  234. putlog(LOG_DEBUG, "*", "Allocated bind table %s (flags %d)", nme, flg);
  235. return tl;
  236. }
  237. void del_bind_table(tcl_bind_list_t *tl_which)
  238. {
  239. tcl_bind_list_t *tl;
  240. for (tl = bind_table_list; tl; tl = tl->next) {
  241. if (tl->flags & HT_DELETED)
  242. continue;
  243. if (tl == tl_which) {
  244. tl->flags |= HT_DELETED;
  245. putlog(LOG_DEBUG, "*", "De-Allocated bind table %s", tl->name);
  246. return;
  247. }
  248. }
  249. putlog(LOG_DEBUG, "*", "??? Tried to delete not listed bind table ???");
  250. }
  251. tcl_bind_list_t *find_bind_table(const char *nme)
  252. {
  253. tcl_bind_list_t *tl;
  254. int v;
  255. for (tl = bind_table_list; tl; tl = tl->next) {
  256. if (tl->flags & HT_DELETED)
  257. continue;
  258. v = egg_strcasecmp(tl->name, nme);
  259. if (!v)
  260. return tl;
  261. if (v > 0)
  262. return NULL;
  263. }
  264. return NULL;
  265. }
  266. static void dump_bind_tables(Tcl_Interp *irp)
  267. {
  268. tcl_bind_list_t *tl;
  269. u_8bit_t i;
  270. for (tl = bind_table_list, i = 0; tl; tl = tl->next) {
  271. if (tl->flags & HT_DELETED)
  272. continue;
  273. if (i)
  274. Tcl_AppendResult(irp, ", ", NULL);
  275. else
  276. i = 1;
  277. Tcl_AppendResult(irp, tl->name, NULL);
  278. }
  279. }
  280. static int unbind_bind_entry(tcl_bind_list_t *tl, const char *flags,
  281. const char *cmd, const char *proc)
  282. {
  283. tcl_bind_mask_t *tm;
  284. /* Search for matching bind in bind list. */
  285. for (tm = tl->first; tm; tm = tm->next) {
  286. if (tm->flags & TBM_DELETED)
  287. continue;
  288. if (!strcmp(cmd, tm->mask))
  289. break; /* Found it! fall out! */
  290. }
  291. if (tm) {
  292. tcl_cmd_t *tc;
  293. /* Search for matching proc in bind. */
  294. for (tc = tm->first; tc; tc = tc->next) {
  295. if (tc->attributes & TC_DELETED)
  296. continue;
  297. if (!egg_strcasecmp(tc->func_name, proc)) {
  298. /* Erase proc regardless of flags. */
  299. tc->attributes |= TC_DELETED;
  300. return 1; /* Match. */
  301. }
  302. }
  303. }
  304. return 0; /* No match. */
  305. }
  306. /* Add command (remove old one if necessary)
  307. */
  308. static int bind_bind_entry(tcl_bind_list_t *tl, const char *flags,
  309. const char *cmd, const char *proc)
  310. {
  311. tcl_cmd_t *tc;
  312. tcl_bind_mask_t *tm, *tm_last;
  313. Context;
  314. /* Search for matching bind in bind list. */
  315. for (tm = tl->first, tm_last = NULL; tm; tm_last = tm, tm = tm->next) {
  316. if (tm->flags & TBM_DELETED)
  317. continue;
  318. if (!strcmp(cmd, tm->mask))
  319. break; /* Found it! fall out! */
  320. }
  321. /* Create bind if it doesn't exist yet. */
  322. if (!tm) {
  323. tm = nmalloc_null(sizeof(tcl_bind_mask_t));
  324. tm->mask = nmalloc(strlen(cmd) + 1);
  325. strcpy(tm->mask, cmd);
  326. /* Link into linked list of binds. */
  327. tm->next = tl->first;
  328. tl->first = tm;
  329. }
  330. /* Proc already defined? If so, replace. */
  331. for (tc = tm->first; tc; tc = tc->next) {
  332. if (tc->attributes & TC_DELETED)
  333. continue;
  334. if (!egg_strcasecmp(tc->func_name, proc)) {
  335. tc->flags.match = FR_GLOBAL | FR_CHAN;
  336. break_down_flags(flags, &(tc->flags), NULL);
  337. return 1;
  338. }
  339. }
  340. /* If this bind list is not stackable, remove the
  341. old entry from this bind. */
  342. if (!(tl->flags & HT_STACKABLE)) {
  343. for (tc = tm->first; tc; tc = tc->next) {
  344. if (tc->attributes & TC_DELETED)
  345. continue;
  346. /* NOTE: We assume there's only one not-yet-deleted
  347. entry. */
  348. tc->attributes |= TC_DELETED;
  349. break;
  350. }
  351. }
  352. tc = nmalloc_null(sizeof(tcl_cmd_t));
  353. tc->flags.match = FR_GLOBAL | FR_CHAN;
  354. break_down_flags(flags, &(tc->flags), NULL);
  355. tc->func_name = nmalloc(strlen(proc) + 1);
  356. strcpy(tc->func_name, proc);
  357. /* Link into linked list of the bind's command list. */
  358. tc->next = tm->first;
  359. tm->first = tc;
  360. return 1;
  361. }
  362. static int tcl_getbinds(tcl_bind_list_t *tl_kind, const char *name)
  363. {
  364. tcl_bind_mask_t *tm;
  365. for (tm = tl_kind->first; tm; tm = tm->next) {
  366. if (tm->flags & TBM_DELETED)
  367. continue;
  368. if (!egg_strcasecmp(tm->mask, name)) {
  369. tcl_cmd_t *tc;
  370. for (tc = tm->first; tc; tc = tc->next) {
  371. if (tc->attributes & TC_DELETED)
  372. continue;
  373. Tcl_AppendElement(interp, tc->func_name);
  374. }
  375. break;
  376. }
  377. }
  378. return TCL_OK;
  379. }
  380. static int tcl_bind STDVAR
  381. {
  382. tcl_bind_list_t *tl;
  383. /* Note: `cd' defines what tcl_bind is supposed do: 0 stands for
  384. bind and 1 stands for unbind. */
  385. if ((long int) cd == 1)
  386. BADARGS(5, 5, " type flags cmd/mask procname");
  387. else
  388. BADARGS(4, 5, " type flags cmd/mask ?procname?");
  389. tl = find_bind_table(argv[1]);
  390. if (!tl) {
  391. Tcl_AppendResult(irp, "bad type, should be one of: ", NULL);
  392. dump_bind_tables(irp);
  393. return TCL_ERROR;
  394. }
  395. if ((long int) cd == 1) {
  396. if (!unbind_bind_entry(tl, argv[2], argv[3], argv[4])) {
  397. /* Don't error if trying to re-unbind a builtin */
  398. if (argv[4][0] != '*' || argv[4][4] != ':' ||
  399. strcmp(argv[3], &argv[4][5]) || strncmp(argv[1], &argv[4][1], 3)) {
  400. Tcl_AppendResult(irp, "no such binding", NULL);
  401. return TCL_ERROR;
  402. }
  403. }
  404. } else {
  405. if (argc == 4)
  406. return tcl_getbinds(tl, argv[3]);
  407. bind_bind_entry(tl, argv[2], argv[3], argv[4]);
  408. }
  409. Tcl_AppendResult(irp, argv[3], NULL);
  410. return TCL_OK;
  411. }
  412. int check_validity(char *nme, Function func)
  413. {
  414. char *p;
  415. tcl_bind_list_t *tl;
  416. if (*nme != '*')
  417. return 0;
  418. p = strchr(nme + 1, ':');
  419. if (p == NULL)
  420. return 0;
  421. *p = 0;
  422. tl = find_bind_table(nme + 1);
  423. *p = ':';
  424. if (!tl)
  425. return 0;
  426. if (tl->func != func)
  427. return 0;
  428. return 1;
  429. }
  430. static int builtin_3char STDVAR
  431. {
  432. Function F = (Function) cd;
  433. BADARGS(4, 4, " from to args");
  434. CHECKVALIDITY(builtin_3char);
  435. F(argv[1], argv[2], argv[3]);
  436. return TCL_OK;
  437. }
  438. static int builtin_2char STDVAR
  439. {
  440. Function F = (Function) cd;
  441. BADARGS(3, 3, " nick msg");
  442. CHECKVALIDITY(builtin_2char);
  443. F(argv[1], argv[2]);
  444. return TCL_OK;
  445. }
  446. static int builtin_5int STDVAR
  447. {
  448. Function F = (Function) cd;
  449. BADARGS(6, 6, " min hrs dom mon year");
  450. CHECKVALIDITY(builtin_5int);
  451. F(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), atoi(argv[5]));
  452. return TCL_OK;
  453. }
  454. static int builtin_char STDVAR
  455. {
  456. Function F = (Function) cd;
  457. BADARGS(2, 2, " handle");
  458. CHECKVALIDITY(builtin_char);
  459. F(argv[1]);
  460. return TCL_OK;
  461. }
  462. static int builtin_chpt STDVAR
  463. {
  464. Function F = (Function) cd;
  465. BADARGS(3, 3, " bot nick sock");
  466. CHECKVALIDITY(builtin_chpt);
  467. F(argv[1], argv[2], atoi(argv[3]));
  468. return TCL_OK;
  469. }
  470. static int builtin_chjn STDVAR
  471. {
  472. Function F = (Function) cd;
  473. BADARGS(6, 6, " bot nick chan# flag&sock host");
  474. CHECKVALIDITY(builtin_chjn);
  475. F(argv[1], argv[2], atoi(argv[3]), argv[4][0],
  476. argv[4][0] ? atoi(argv[4] + 1) : 0, argv[5]);
  477. return TCL_OK;
  478. }
  479. static int builtin_idxchar STDVAR
  480. {
  481. Function F = (Function) cd;
  482. int idx;
  483. char *r;
  484. Context;
  485. BADARGS(3, 3, " idx args");
  486. CHECKVALIDITY(builtin_idxchar);
  487. if (atoi(argv[1]) < 0) { /* this is a remote-simul idx */
  488. idx = atoi(argv[1]) * -1;
  489. } else {
  490. idx = findidx(atoi(argv[1]));
  491. }
  492. if (idx < 0) {
  493. Tcl_AppendResult(irp, "invalid idx", NULL);
  494. return TCL_ERROR;
  495. }
  496. r = (((char *(*)()) F) (idx, argv[2]));
  497. Tcl_ResetResult(irp);
  498. Tcl_AppendResult(irp, r, NULL);
  499. return TCL_OK;
  500. }
  501. int findanyidx(register int z)
  502. {
  503. register int j;
  504. for (j = 0; j < dcc_total; j++)
  505. if (dcc[j].sock == z)
  506. return j;
  507. return -1;
  508. }
  509. static int builtin_charidx STDVAR
  510. {
  511. Function F = (Function) cd;
  512. int idx;
  513. BADARGS(3, 3, " handle idx");
  514. CHECKVALIDITY(builtin_charidx);
  515. idx = findanyidx(atoi(argv[2]));
  516. if (idx < 0) {
  517. Tcl_AppendResult(irp, "invalid idx", NULL);
  518. return TCL_ERROR;
  519. }
  520. Tcl_AppendResult(irp, int_to_base10(F(argv[1], idx)), NULL);
  521. return TCL_OK;
  522. }
  523. static int builtin_chat STDVAR
  524. {
  525. Function F = (Function) cd;
  526. int ch;
  527. BADARGS(4, 4, " handle idx text");
  528. CHECKVALIDITY(builtin_chat);
  529. ch = atoi(argv[2]);
  530. F(argv[1], ch, argv[3]);
  531. return TCL_OK;
  532. }
  533. static int builtin_dcc STDVAR
  534. {
  535. int idx;
  536. Function F = (Function) cd;
  537. Context;
  538. BADARGS(4, 4, " hand idx param");
  539. if (atoi(argv[2]) < 0) { /* this is a remote-simul idx */
  540. idx = atoi(argv[2]) * -1;
  541. } else {
  542. idx = findidx(atoi(argv[2]));
  543. }
  544. if (idx < 0) {
  545. Tcl_AppendResult(irp, "invalid idx", NULL);
  546. return TCL_ERROR;
  547. }
  548. if (F == NULL) {
  549. Tcl_AppendResult(irp, "break", NULL);
  550. return TCL_OK;
  551. }
  552. /* Check if it's a password change, if so, don't show the password. We
  553. * don't need pretty formats here, as it's only for debugging purposes.
  554. */
  555. debug4(STR("tcl: builtin dcc call: %s %s %s %s"), argv[0], argv[1], argv[2],
  556. (!strcmp(argv[0] + 5, "newpass") ||
  557. !strcmp(argv[0] + 5, "chpass")) ? "[something]" : argv[3]);
  558. (F) (dcc[idx].user, idx, argv[3]);
  559. Tcl_ResetResult(irp);
  560. Tcl_AppendResult(irp, "0", NULL);
  561. return TCL_OK;
  562. }
  563. /* trigger (execute) a proc */
  564. static int trigger_bind(const char *proc, const char *param)
  565. {
  566. int x;
  567. /* We now try to debug the Tcl_VarEval() call below by remembering both
  568. * the called proc name and it's parameters. This should render us a bit
  569. * less helpless when we see context dumps.
  570. */
  571. {
  572. const char *msg = "Tcl proc: %s, param: %s";
  573. char *buf;
  574. Context;
  575. buf = nmalloc(strlen(msg) + (proc ? strlen(proc) : 6)
  576. + (param ? strlen(param) : 6) + 1);
  577. sprintf(buf, msg, proc ? proc : "<null>", param ? param : "<null>");
  578. ContextNote(buf);
  579. nfree(buf);
  580. }
  581. x = Tcl_VarEval(interp, proc, param, NULL);
  582. Context;
  583. if (x == TCL_ERROR) {
  584. if (strlen(interp->result) > 400)
  585. interp->result[400] = 0;
  586. putlog(LOG_MISC, "*", "Tcl error [%s]: %s", proc, interp->result);
  587. return BIND_EXECUTED;
  588. } else {
  589. if (!strcmp(interp->result, "break"))
  590. return BIND_EXEC_BRK;
  591. return (atoi(interp->result) > 0) ? BIND_EXEC_LOG : BIND_EXECUTED;
  592. }
  593. }
  594. int check_tcl_bind(tcl_bind_list_t *tl, const char *match,
  595. struct flag_record *atr, const char *param, int match_type)
  596. {
  597. tcl_bind_mask_t *tm, *tm_last = NULL, *tm_p = NULL;
  598. int cnt = 0;
  599. char *proc = NULL, *fullmatch = NULL;
  600. tcl_cmd_t *tc, *htc = NULL;
  601. int finish = 0, atrok, x, ok;
  602. for (tm = tl->first; tm && !finish; tm_last = tm, tm = tm->next) {
  603. if (tm->flags & TBM_DELETED)
  604. continue;
  605. /* Find out whether this bind matches the mask or provides
  606. the the requested atcributes, depending on the specified
  607. requirements. */
  608. switch (match_type & 0x03) {
  609. case MATCH_PARTIAL:
  610. ok = !egg_strncasecmp(match, tm->mask, strlen(match));
  611. break;
  612. case MATCH_EXACT:
  613. ok = !egg_strcasecmp(match, tm->mask);
  614. break;
  615. case MATCH_CASE:
  616. ok = !strcmp(match, tm->mask);
  617. break;
  618. case MATCH_MASK:
  619. ok = wild_match_per((unsigned char *) tm->mask, (unsigned char *) match);
  620. break;
  621. default:
  622. ok = 0;
  623. }
  624. if (!ok)
  625. continue; /* This bind does not match. */
  626. if (match_type & BIND_STACKABLE) {
  627. /* Could be multiple commands/triggers. */
  628. for (tc = tm->first; tc; tc = tc->next) {
  629. if (match_type & BIND_USE_ATTR) {
  630. /* Check whether the provided flags suffice for
  631. this command/trigger. */
  632. if (match_type & BIND_HAS_BUILTINS)
  633. atrok = flagrec_ok(&tc->flags, atr);
  634. else
  635. atrok = flagrec_eq(&tc->flags, atr);
  636. } else
  637. atrok = 1;
  638. if (atrok) {
  639. cnt++;
  640. tc->hits++;
  641. tm_p = tm_last;
  642. Tcl_SetVar(interp, "lastbind", (char *) tm->mask, TCL_GLOBAL_ONLY);
  643. x = trigger_bind(tc->func_name, param);
  644. Context;
  645. if (match_type & BIND_ALTER_ARGS) {
  646. if (interp->result == NULL || !interp->result[0])
  647. return x;
  648. } else if ((match_type & BIND_WANTRET) && x == BIND_EXEC_LOG)
  649. return x;
  650. }
  651. }
  652. /* Apart from MATCH_MASK, currently no match type allows us to match
  653. against more than one bind. So if this isn't MATCH_MASK then exit
  654. the loop now. */
  655. /* This will suffice until we have stackable partials. */
  656. if ((match_type & 3) != MATCH_MASK)
  657. finish = 1;
  658. } else {
  659. /* Search for valid entry. */
  660. for (tc = tm->first; tc; tc = tc->next)
  661. if (!(tc->attributes & TC_DELETED))
  662. break;
  663. if (tc) {
  664. /* Check whether the provided flags suffice for this
  665. command/trigger. */
  666. if (match_type & BIND_USE_ATTR) {
  667. if (match_type & BIND_HAS_BUILTINS)
  668. atrok = flagrec_ok(&tc->flags, atr);
  669. else
  670. atrok = flagrec_eq(&tc->flags, atr);
  671. } else
  672. atrok = 1;
  673. if (atrok) {
  674. cnt++;
  675. /* Remember information about this bind and its only
  676. command/trigger. */
  677. proc = tc->func_name;
  678. fullmatch = tm->mask;
  679. htc = tc;
  680. tm_p = tm_last;
  681. /* Either this is a non-partial match, which means we
  682. only want to execute _one_ bind ... */
  683. if ((match_type & 3) != MATCH_PARTIAL ||
  684. /* ... or this is happens to be an exact match. */
  685. !egg_strcasecmp(match, tm->mask))
  686. cnt = finish = 1;
  687. }
  688. }
  689. }
  690. }
  691. if (!cnt)
  692. return BIND_NOMATCH;
  693. if ((match_type & 0x03) == MATCH_MASK ||
  694. (match_type & 0x03) == MATCH_CASE)
  695. return BIND_EXECUTED;
  696. /* Now that we have found at least one bind, we can update the
  697. preferred entries information. */
  698. if (htc)
  699. htc->hits++;
  700. if (tm_p) {
  701. /* Move mask to front of bind's mask list. */
  702. tm = tm_p->next;
  703. tm_p->next = tm->next; /* Unlink mask from list. */
  704. tm->next = tl->first; /* Readd mask to front of list. */
  705. tl->first = tm;
  706. }
  707. if (cnt > 1)
  708. return BIND_AMBIGUOUS;
  709. Tcl_SetVar(interp, "lastbind", (char *) fullmatch, TCL_GLOBAL_ONLY);
  710. Context;
  711. return trigger_bind(proc, param);
  712. }
  713. /* Check for tcl-bound dcc command, return 1 if found
  714. * dcc: proc-name <handle> <sock> <args...>
  715. */
  716. int check_tcl_dcc(char *cmd, int idx, char *args)
  717. {
  718. struct tcl_bind_mask_b *hm;
  719. struct flag_record fr = {FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0};
  720. int x;
  721. #ifdef S_DCCPASS
  722. int found = 0;
  723. #endif
  724. char s[11];
  725. Context;
  726. get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan);
  727. if (dcc[idx].simul) {
  728. egg_snprintf(s, sizeof s, "-%d", idx);
  729. } else {
  730. egg_snprintf(s, sizeof s, "%ld", dcc[idx].sock);
  731. }
  732. #ifdef S_DCCPASS
  733. for (hm = H_dcc->first; hm; hm = hm->next) {
  734. if (!egg_strcasecmp(cmd, hm->mask)) {
  735. found = 1;
  736. break;
  737. }
  738. }
  739. if (found) {
  740. if (has_cmd_pass(cmd)) {
  741. char *p,
  742. work[1024],
  743. pass[128];
  744. p = strchr(args, ' ');
  745. if (p)
  746. *p = 0;
  747. strncpy0(pass, args, sizeof(pass));
  748. if (check_cmd_pass(cmd, pass)) {
  749. if (p)
  750. *p = ' ';
  751. strncpy0(work, args, sizeof(work));
  752. p = work;
  753. newsplit(&p);
  754. strcpy(args, p);
  755. } else {
  756. dprintf(idx, "Invalid command password. Use %scommand password arguments\n", dcc_prefix);
  757. putlog(LOG_MISC, "*", "%s attempted %s%s with missing or incorrect command password", dcc[idx].nick, dcc_prefix, cmd);
  758. return 0;
  759. }
  760. }
  761. }
  762. #endif
  763. Tcl_SetVar(interp, "_dcc1", (char *) dcc[idx].nick, 0);
  764. Tcl_SetVar(interp, "_dcc2", (char *) s, 0);
  765. Tcl_SetVar(interp, "_dcc3", (char *) args, 0);
  766. x = check_tcl_bind(H_dcc, cmd, &fr, " $_dcc1 $_dcc2 $_dcc3",
  767. MATCH_PARTIAL | BIND_USE_ATTR | BIND_HAS_BUILTINS);
  768. if (x == BIND_AMBIGUOUS) {
  769. dprintf(idx, MISC_AMBIGUOUS);
  770. return 0;
  771. }
  772. if (x == BIND_NOMATCH) {
  773. dprintf(idx, "What? You need '%shelp'\n", dcc_prefix);
  774. return 0;
  775. }
  776. if (x == BIND_EXEC_BRK)
  777. return 1; /* quit */
  778. if (x == BIND_EXEC_LOG)
  779. putlog(LOG_CMDS, "*", "#%s# %s %s", dcc[idx].nick, cmd, args);
  780. return 0;
  781. }
  782. void check_tcl_bot(const char *nick, const char *code, const char *param)
  783. {
  784. Tcl_SetVar(interp, "_bot1", (char *) nick, 0);
  785. Tcl_SetVar(interp, "_bot2", (char *) code, 0);
  786. Tcl_SetVar(interp, "_bot3", (char *) param, 0);
  787. check_tcl_bind(H_bot, code, 0, " $_bot1 $_bot2 $_bot3", MATCH_EXACT);
  788. }
  789. void check_tcl_chonof(char *hand, int sock, tcl_bind_list_t *tl)
  790. {
  791. struct flag_record fr = {FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0};
  792. char s[11];
  793. struct userrec *u;
  794. u = get_user_by_handle(userlist, hand);
  795. touch_laston(u, "partyline", now);
  796. get_user_flagrec(u, &fr, NULL);
  797. Tcl_SetVar(interp, "_chonof1", (char *) hand, 0);
  798. egg_snprintf(s, sizeof s, "%d", sock);
  799. Tcl_SetVar(interp, "_chonof2", (char *) s, 0);
  800. check_tcl_bind(tl, hand, &fr, " $_chonof1 $_chonof2", MATCH_MASK |
  801. BIND_USE_ATTR | BIND_STACKABLE | BIND_WANTRET);
  802. }
  803. void check_tcl_chatactbcst(const char *from, int chan, const char *text,
  804. tcl_bind_list_t *tl)
  805. {
  806. char s[11];
  807. egg_snprintf(s, sizeof s, "%d", chan);
  808. Tcl_SetVar(interp, "_cab1", (char *) from, 0);
  809. Tcl_SetVar(interp, "_cab2", (char *) s, 0);
  810. Tcl_SetVar(interp, "_cab3", (char *) text, 0);
  811. check_tcl_bind(tl, text, 0, " $_cab1 $_cab2 $_cab3",
  812. MATCH_MASK | BIND_STACKABLE);
  813. }
  814. void check_tcl_nkch(const char *ohand, const char *nhand)
  815. {
  816. Tcl_SetVar(interp, "_nkch1", (char *) ohand, 0);
  817. Tcl_SetVar(interp, "_nkch2", (char *) nhand, 0);
  818. check_tcl_bind(H_nkch, ohand, 0, " $_nkch1 $_nkch2",
  819. MATCH_MASK | BIND_STACKABLE);
  820. }
  821. void check_tcl_link(const char *bot, const char *via)
  822. {
  823. Tcl_SetVar(interp, "_link1", (char *) bot, 0);
  824. Tcl_SetVar(interp, "_link2", (char *) via, 0);
  825. check_tcl_bind(H_link, bot, 0, " $_link1 $_link2",
  826. MATCH_MASK | BIND_STACKABLE);
  827. }
  828. void check_tcl_disc(const char *bot)
  829. {
  830. Tcl_SetVar(interp, "_disc1", (char *) bot, 0);
  831. check_tcl_bind(H_disc, bot, 0, " $_disc1", MATCH_MASK | BIND_STACKABLE);
  832. }
  833. void check_tcl_loadunld(const char *mod, tcl_bind_list_t *tl)
  834. {
  835. Tcl_SetVar(interp, "_lu1", (char *) mod, 0);
  836. check_tcl_bind(tl, mod, 0, " $_lu1", MATCH_MASK | BIND_STACKABLE);
  837. }
  838. const char *check_tcl_filt(int idx, const char *text)
  839. {
  840. char s[11];
  841. int x;
  842. struct flag_record fr = {FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0};
  843. egg_snprintf(s, sizeof s, "%ld", dcc[idx].sock);
  844. get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan);
  845. Tcl_SetVar(interp, "_filt1", (char *) s, 0);
  846. Tcl_SetVar(interp, "_filt2", (char *) text, 0);
  847. x = check_tcl_bind(H_filt, text, &fr, " $_filt1 $_filt2",
  848. MATCH_MASK | BIND_USE_ATTR | BIND_STACKABLE |
  849. BIND_WANTRET | BIND_ALTER_ARGS);
  850. if (x == BIND_EXECUTED || x == BIND_EXEC_LOG) {
  851. if (interp->result == NULL || !interp->result[0])
  852. return "";
  853. else
  854. return interp->result;
  855. } else
  856. return text;
  857. }
  858. int check_tcl_note(const char *from, const char *to, const char *text)
  859. {
  860. int x;
  861. Tcl_SetVar(interp, "_note1", (char *) from, 0);
  862. Tcl_SetVar(interp, "_note2", (char *) to, 0);
  863. Tcl_SetVar(interp, "_note3", (char *) text, 0);
  864. x = check_tcl_bind(H_note, to, 0, " $_note1 $_note2 $_note3", MATCH_EXACT);
  865. return (x == BIND_MATCHED || x == BIND_EXECUTED || x == BIND_EXEC_LOG);
  866. }
  867. void check_tcl_listen(const char *cmd, int idx)
  868. {
  869. char s[11];
  870. int x;
  871. egg_snprintf(s, sizeof s, "%d", idx);
  872. Tcl_SetVar(interp, "_n", (char *) s, 0);
  873. x = Tcl_VarEval(interp, cmd, " $_n", NULL);
  874. if (x == TCL_ERROR)
  875. putlog(LOG_MISC, "*", "error on listen: %s", interp->result);
  876. }
  877. void check_tcl_chjn(const char *bot, const char *nick, int chan,
  878. const char type, int sock, const char *host)
  879. {
  880. struct flag_record fr = {FR_GLOBAL, 0, 0, 0, 0, 0};
  881. char s[11], t[2], u[11];
  882. t[0] = type;
  883. t[1] = 0;
  884. switch (type) {
  885. case '^':
  886. fr.global = USER_ADMIN;
  887. break;
  888. case '*':
  889. fr.global = USER_OWNER;
  890. break;
  891. case '+':
  892. fr.global = USER_MASTER;
  893. break;
  894. case '@':
  895. fr.global = USER_OP;
  896. break;
  897. }
  898. egg_snprintf(s, sizeof s, "%d", chan);
  899. egg_snprintf(u, sizeof u, "%d", sock);
  900. Tcl_SetVar(interp, "_chjn1", (char *) bot, 0);
  901. Tcl_SetVar(interp, "_chjn2", (char *) nick, 0);
  902. Tcl_SetVar(interp, "_chjn3", (char *) s, 0);
  903. Tcl_SetVar(interp, "_chjn4", (char *) t, 0);
  904. Tcl_SetVar(interp, "_chjn5", (char *) u, 0);
  905. Tcl_SetVar(interp, "_chjn6", (char *) host, 0);
  906. check_tcl_bind(H_chjn, s, &fr,
  907. " $_chjn1 $_chjn2 $_chjn3 $_chjn4 $_chjn5 $_chjn6",
  908. MATCH_MASK | BIND_STACKABLE);
  909. }
  910. void check_tcl_chpt(const char *bot, const char *hand, int sock, int chan)
  911. {
  912. char u[11], v[11];
  913. egg_snprintf(u, sizeof u, "%d", sock);
  914. egg_snprintf(v, sizeof v, "%d", chan);
  915. Tcl_SetVar(interp, "_chpt1", (char *) bot, 0);
  916. Tcl_SetVar(interp, "_chpt2", (char *) hand, 0);
  917. Tcl_SetVar(interp, "_chpt3", (char *) u, 0);
  918. Tcl_SetVar(interp, "_chpt4", (char *) v, 0);
  919. check_tcl_bind(H_chpt, v, 0, " $_chpt1 $_chpt2 $_chpt3 $_chpt4",
  920. MATCH_MASK | BIND_STACKABLE);
  921. }
  922. void check_tcl_away(const char *bot, int idx, const char *msg)
  923. {
  924. char u[11];
  925. egg_snprintf(u, sizeof u, "%d", idx);
  926. Tcl_SetVar(interp, "_away1", (char *) bot, 0);
  927. Tcl_SetVar(interp, "_away2", (char *) u, 0);
  928. Tcl_SetVar(interp, "_away3", msg ? (char *) msg : "", 0);
  929. check_tcl_bind(H_away, bot, 0, " $_away1 $_away2 $_away3",
  930. MATCH_MASK | BIND_STACKABLE);
  931. }
  932. void check_tcl_time(struct tm *tm)
  933. {
  934. char y[18];
  935. egg_snprintf(y, sizeof y, "%02d", tm->tm_min);
  936. Tcl_SetVar(interp, "_time1", (char *) y, 0);
  937. egg_snprintf(y, sizeof y, "%02d", tm->tm_hour);
  938. Tcl_SetVar(interp, "_time2", (char *) y, 0);
  939. egg_snprintf(y, sizeof y, "%02d", tm->tm_mday);
  940. Tcl_SetVar(interp, "_time3", (char *) y, 0);
  941. egg_snprintf(y, sizeof y, "%02d", tm->tm_mon);
  942. Tcl_SetVar(interp, "_time4", (char *) y, 0);
  943. egg_snprintf(y, sizeof y, "%04d", tm->tm_year + 1900);
  944. Tcl_SetVar(interp, "_time5", (char *) y, 0);
  945. egg_snprintf(y, sizeof y, "%02d %02d %02d %02d %04d", tm->tm_min, tm->tm_hour,
  946. tm->tm_mday, tm->tm_mon, tm->tm_year + 1900);
  947. check_tcl_bind(H_time, y, 0,
  948. " $_time1 $_time2 $_time3 $_time4 $_time5",
  949. MATCH_MASK | BIND_STACKABLE);
  950. }
  951. void check_tcl_event(const char *event)
  952. {
  953. Tcl_SetVar(interp, "_event1", (char *) event, 0);
  954. check_tcl_bind(H_event, event, 0, " $_event1", MATCH_EXACT | BIND_STACKABLE);
  955. }
  956. void tell_binds(int idx, char *par)
  957. {
  958. tcl_bind_list_t *tl, *tl_kind;
  959. tcl_bind_mask_t *tm;
  960. int fnd = 0, showall = 0, patmatc = 0;
  961. tcl_cmd_t *tc;
  962. char *name, *proc, *s, flg[100];
  963. if (par[0])
  964. name = newsplit(&par);
  965. else
  966. name = NULL;
  967. if (par[0])
  968. s = newsplit(&par);
  969. else
  970. s = NULL;
  971. if (name)
  972. tl_kind = find_bind_table(name);
  973. else
  974. tl_kind = NULL;
  975. if ((name && name[0] && !egg_strcasecmp(name, "all")) || (s && s[0] && !egg_strcasecmp(s, "all")))
  976. showall = 1;
  977. if (tl_kind == NULL && name && name[0] && egg_strcasecmp(name, "all"))
  978. patmatc = 1;
  979. dprintf(idx, MISC_CMDBINDS);
  980. dprintf(idx, " TYPE FLGS COMMAND HITS BINDING (TCL)\n");
  981. for (tl = tl_kind ? tl_kind : bind_table_list; tl;
  982. tl = tl_kind ? 0 : tl->next) {
  983. if (tl->flags & HT_DELETED)
  984. continue;
  985. for (tm = tl->first; tm; tm = tm->next) {
  986. if (tm->flags & TBM_DELETED)
  987. continue;
  988. for (tc = tm->first; tc; tc = tc->next) {
  989. if (tc->attributes & TC_DELETED)
  990. continue;
  991. proc = tc->func_name;
  992. build_flags(flg, &(tc->flags), NULL);
  993. if (showall || proc[0] != '*') {
  994. int ok = 0;
  995. if (patmatc == 1) {
  996. if (wild_match_per(name, tl->name) ||
  997. wild_match_per(name, tm->mask) ||
  998. wild_match_per(name, tc->func_name))
  999. ok = 1;
  1000. } else
  1001. ok = 1;
  1002. if (ok) {
  1003. dprintf(idx, " %-4s %-8s %-20s %4d %s\n", tl->name, flg, tm->mask,
  1004. tc->hits, tc->func_name);
  1005. fnd = 1;
  1006. }
  1007. }
  1008. }
  1009. }
  1010. }
  1011. if (!fnd) {
  1012. if (patmatc)
  1013. dprintf(idx, "No command bindings found that match %s\n", name);
  1014. else if (tl_kind)
  1015. dprintf(idx, "No command bindings for type: %s.\n", name);
  1016. else
  1017. dprintf(idx, "No command bindings exist.\n");
  1018. }
  1019. }
  1020. /* Bring the default msg/dcc/fil commands into the Tcl interpreter */
  1021. void add_builtins(tcl_bind_list_t *tl, cmd_t *cc)
  1022. {
  1023. int k, i;
  1024. char p[1024], *l;
  1025. cd_tcl_cmd table[2];
  1026. table[0].name = p;
  1027. table[0].callback = tl->func;
  1028. table[1].name = NULL;
  1029. for (i = 0; cc[i].name; i++) {
  1030. /* add H_dcc cmds to cmds[] :: add to the help system.. */
  1031. if (tl == H_dcc) {
  1032. cmds[cmdi].name = cc[i].name;
  1033. cmds[cmdi].flags.match = FR_GLOBAL | FR_CHAN;
  1034. break_down_flags(cc[i].flags, &(cmds[cmdi].flags), NULL);
  1035. cmdi++;
  1036. }
  1037. egg_snprintf(p, sizeof p, "*%s:%s", tl->name,
  1038. cc[i].funcname ? cc[i].funcname : cc[i].name);
  1039. l = (char *) nmalloc(Tcl_ScanElement(p, &k));
  1040. Tcl_ConvertElement(p, l, k | TCL_DONT_USE_BRACES);
  1041. table[0].cdata = (void *)cc[i].func;
  1042. add_cd_tcl_cmds(table);
  1043. bind_bind_entry(tl, cc[i].flags, cc[i].name, l);
  1044. nfree(l);
  1045. }
  1046. }
  1047. /* Remove the default msg/dcc/fil commands from the Tcl interpreter */
  1048. void rem_builtins(tcl_bind_list_t *table, cmd_t *cc)
  1049. {
  1050. int k, i;
  1051. char p[1024], *l;
  1052. for (i = 0; cc[i].name; i++) {
  1053. egg_snprintf(p, sizeof p, "*%s:%s", table->name,
  1054. cc[i].funcname ? cc[i].funcname : cc[i].name);
  1055. l = (char *) nmalloc(Tcl_ScanElement(p, &k));
  1056. Tcl_ConvertElement(p, l, k | TCL_DONT_USE_BRACES);
  1057. Tcl_DeleteCommand(interp, p);
  1058. unbind_bind_entry(table, cc[i].flags, cc[i].name, l);
  1059. nfree(l);
  1060. }
  1061. }