tcl.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. /*
  2. * tcl.c -- handles:
  3. * the code for every command eggdrop adds to Tcl
  4. * Tcl initialization
  5. * getting and setting Tcl/eggdrop variables
  6. *
  7. */
  8. #include <stdlib.h> /* getenv() */
  9. #include <locale.h> /* setlocale() */
  10. #include "common.h"
  11. #include "misc.h"
  12. #include "chanprog.h"
  13. #include <sys/stat.h>
  14. #if ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 1)) || (TCL_MAJOR_VERSION > 8)
  15. #define USE_BYTE_ARRAYS
  16. #endif
  17. /* Used for read/write to internal strings */
  18. typedef struct {
  19. char *str; /* Pointer to actual string in eggdrop */
  20. int max; /* max length (negative: read-only var
  21. when protect is on) (0: read-only ALWAYS) */
  22. int flags; /* 1 = directory */
  23. } strinfo;
  24. typedef struct {
  25. int *var;
  26. int ro;
  27. } intinfo;
  28. extern time_t online_since;
  29. extern int backgrd, flood_telnet_thr, flood_telnet_time,
  30. shtime, allow_new_telnets, use_telnet_banner,
  31. default_flags, conmask, connect_timeout,
  32. firewallport, notify_users_at, flood_thr, ignore_time,
  33. reserved_port_min, reserved_port_max, localhub,
  34. enable_simul, dcc_total, debug_output, identtimeout,
  35. protect_telnet, dupwait_timeout, egg_numver, share_unlinks,
  36. dcc_sanitycheck, sort_users, tands, resolve_timeout,
  37. default_uflags, strict_host, userfile_perm;
  38. extern char origbotname[], botuser[], motdfile[], admin[], userfile[],
  39. firewall[], hostname[], hostname6[], myip[], myip6[],
  40. tempdir[], owner[], network[], botnetnick[],
  41. egg_version[], natip[],
  42. dcc_prefix[];
  43. extern struct dcc_t *dcc;
  44. int protect_readonly = 0; /* turn on/off readonly protection */
  45. char whois_fields[1025] = ""; /* fields to display in a .whois */
  46. Tcl_Interp *interp; /* eggdrop always uses the same
  47. interpreter */
  48. int dcc_flood_thr = 3;
  49. int use_invites = 1; /* Jason/drummer */
  50. int use_exempts = 1; /* Jason/drummer */
  51. int force_expire = 0; /* Rufus */
  52. int remote_boots = 2;
  53. int allow_dk_cmds = 1;
  54. int must_be_owner = 1;
  55. int max_dcc = 200; /* needs at least 4 or 5 just to
  56. get started. 20 should be enough */
  57. int quiet_save = 1; /* quiet-save patch by Lucas */
  58. int strtot = 0;
  59. int handlen = HANDLEN;
  60. int utftot = 0;
  61. int clientdata_stuff = 0;
  62. /* Prototypes for tcl */
  63. Tcl_Interp *Tcl_CreateInterp();
  64. static void botnet_change(char *new)
  65. {
  66. if (egg_strcasecmp(botnetnick, new)) {
  67. /* Trying to change bot's nickname */
  68. if (tands > 0) {
  69. putlog(LOG_MISC, "*", "* Tried to change my botnet nick, but I'm still linked to a botnet.");
  70. putlog(LOG_MISC, "*", "* (Unlink and try again.)");
  71. return;
  72. } else {
  73. if (botnetnick[0])
  74. putlog(LOG_MISC, "*", "* IDENTITY CHANGE: %s -> %s", botnetnick, new);
  75. strcpy(botnetnick, new);
  76. }
  77. }
  78. }
  79. /*
  80. * Vars, traces, misc
  81. */
  82. int init_dcc_max();
  83. /* Used for read/write to integer couplets */
  84. typedef struct {
  85. int *left; /* left side of couplet */
  86. int *right; /* right side */
  87. } coupletinfo;
  88. /* Read/write integer couplets (int1:int2) */
  89. #if (((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4)) || (TCL_MAJOR_VERSION > 8))
  90. static char *tcl_eggcouplet(ClientData cdata, Tcl_Interp *irp, CONST char *name1,
  91. CONST char *name2, int flags)
  92. #else
  93. static char *tcl_eggcouplet(ClientData cdata, Tcl_Interp *irp, char *name1,
  94. char *name2, int flags)
  95. #endif
  96. {
  97. char *s, s1[41];
  98. coupletinfo *cp = (coupletinfo *) cdata;
  99. if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) {
  100. egg_snprintf(s1, sizeof s1, "%d:%d", *(cp->left), *(cp->right));
  101. Tcl_SetVar2(interp, name1, name2, s1, TCL_GLOBAL_ONLY);
  102. if (flags & TCL_TRACE_UNSETS)
  103. Tcl_TraceVar(interp, name1,
  104. TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
  105. tcl_eggcouplet, cdata);
  106. } else { /* writes */
  107. s = (char *) Tcl_GetVar2(interp, name1, name2, 0);
  108. if (s != NULL) {
  109. int nr1, nr2;
  110. if (strlen(s) > 40)
  111. s[40] = 0;
  112. sscanf(s, "%d%*c%d", &nr1, &nr2);
  113. *(cp->left) = nr1;
  114. *(cp->right) = nr2;
  115. }
  116. }
  117. return NULL;
  118. }
  119. /* Read or write normal integer.
  120. */
  121. #if (((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4)) || (TCL_MAJOR_VERSION > 8))
  122. static char *tcl_eggint(ClientData cdata, Tcl_Interp *irp, CONST char *name1,
  123. CONST char *name2, int flags)
  124. #else
  125. static char *tcl_eggint(ClientData cdata, Tcl_Interp *irp, char *name1,
  126. char *name2, int flags)
  127. #endif
  128. {
  129. char *s, s1[40];
  130. long l;
  131. intinfo *ii = (intinfo *) cdata;
  132. if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) {
  133. /* Special cases */
  134. if ((int *) ii->var == &conmask)
  135. strcpy(s1, masktype(conmask));
  136. else if ((int *) ii->var == &default_flags) {
  137. struct flag_record fr = {FR_GLOBAL, 0, 0, 0, 0, 0};
  138. fr.global = default_flags;
  139. fr.udef_global = default_uflags;
  140. build_flags(s1, &fr, 0);
  141. } else if ((int *) ii->var == &userfile_perm) {
  142. egg_snprintf(s1, sizeof s1, "0%o", userfile_perm);
  143. } else
  144. egg_snprintf(s1, sizeof s1, "%d", *(int *) ii->var);
  145. Tcl_SetVar2(interp, name1, name2, s1, TCL_GLOBAL_ONLY);
  146. if (flags & TCL_TRACE_UNSETS)
  147. Tcl_TraceVar(interp, name1,
  148. TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
  149. tcl_eggint, cdata);
  150. return NULL;
  151. } else { /* Writes */
  152. s = (char *) Tcl_GetVar2(interp, name1, name2, TCL_GLOBAL_ONLY);
  153. if (s != NULL) {
  154. if ((int *) ii->var == &conmask) {
  155. if (s[0])
  156. conmask = logmodes(s);
  157. else
  158. conmask = LOG_MODES | LOG_MISC | LOG_CMDS;
  159. } else if ((int *) ii->var == &default_flags) {
  160. struct flag_record fr = {FR_GLOBAL, 0, 0, 0, 0, 0};
  161. break_down_flags(s, &fr, 0);
  162. default_flags = sanity_check(fr.global); /* drummer */
  163. default_uflags = fr.udef_global;
  164. } else if ((int *) ii->var == &userfile_perm) {
  165. int p = oatoi(s);
  166. if (p <= 0)
  167. return "invalid userfile permissions";
  168. userfile_perm = p;
  169. } else if ((ii->ro == 2) || ((ii->ro == 1) && protect_readonly)) {
  170. return "read-only variable";
  171. } else {
  172. if (Tcl_ExprLong(interp, s, &l) == TCL_ERROR)
  173. return interp->result;
  174. if ((int *) ii->var == &max_dcc) {
  175. if (l < max_dcc)
  176. return "you can't DECREASE max-dcc";
  177. max_dcc = l;
  178. init_dcc_max();
  179. } else
  180. *(ii->var) = (int) l;
  181. }
  182. }
  183. return NULL;
  184. }
  185. }
  186. /* Read/write normal string variable
  187. */
  188. #if (((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4)) || (TCL_MAJOR_VERSION > 8))
  189. static char *tcl_eggstr(ClientData cdata, Tcl_Interp *irp, CONST char *name1,
  190. CONST char *name2, int flags)
  191. #else
  192. static char *tcl_eggstr(ClientData cdata, Tcl_Interp *irp, char *name1,
  193. char *name2, int flags)
  194. #endif
  195. {
  196. char *s;
  197. strinfo *st = (strinfo *) cdata;
  198. if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) {
  199. if ((st->str == firewall) && (firewall[0])) {
  200. char s1[127];
  201. egg_snprintf(s1, sizeof s1, "%s:%d", firewall, firewallport);
  202. Tcl_SetVar2(interp, name1, name2, s1, TCL_GLOBAL_ONLY);
  203. } else
  204. Tcl_SetVar2(interp, name1, name2, st->str, TCL_GLOBAL_ONLY);
  205. if (flags & TCL_TRACE_UNSETS) {
  206. Tcl_TraceVar(interp, name1, TCL_TRACE_READS | TCL_TRACE_WRITES |
  207. TCL_TRACE_UNSETS, tcl_eggstr, cdata);
  208. if ((st->max <= 0) && (protect_readonly || (st->max == 0)))
  209. return "read-only variable"; /* it won't return the error... */
  210. }
  211. return NULL;
  212. } else { /* writes */
  213. if ((st->max <= 0) && (protect_readonly || (st->max == 0))) {
  214. Tcl_SetVar2(interp, name1, name2, st->str, TCL_GLOBAL_ONLY);
  215. return "read-only variable";
  216. }
  217. #ifdef USE_BYTE_ARRAYS
  218. #undef malloc
  219. #undef free
  220. {
  221. Tcl_Obj *obj;
  222. unsigned char *bytes;
  223. int len;
  224. obj = Tcl_GetVar2Ex(interp, name1, name2, 0);
  225. if (!obj) return(NULL);
  226. len = 0;
  227. bytes = Tcl_GetByteArrayFromObj(obj, &len);
  228. if (!bytes) return(NULL);
  229. s = malloc(len+1);
  230. memcpy(s, bytes, len);
  231. s[len] = 0;
  232. }
  233. #else
  234. s = (char *) Tcl_GetVar2(interp, name1, name2, 0);
  235. #endif
  236. if (s != NULL) {
  237. if (strlen(s) > abs(st->max))
  238. s[abs(st->max)] = 0;
  239. if (st->str == botnetnick)
  240. botnet_change(s);
  241. else if (st->str == firewall) {
  242. splitc(firewall, s, ':');
  243. if (!firewall[0])
  244. strcpy(firewall, s);
  245. else
  246. firewallport = atoi(s);
  247. } else
  248. strcpy(st->str, s);
  249. if ((st->flags) && (s[0])) {
  250. if (st->str[strlen(st->str) - 1] != '/')
  251. strcat(st->str, "/");
  252. }
  253. #ifdef USE_BYTE_ARRAYS
  254. free(s);
  255. #endif
  256. }
  257. return NULL;
  258. }
  259. }
  260. static tcl_coups def_tcl_coups[] =
  261. {
  262. {"telnet-flood", &flood_telnet_thr, &flood_telnet_time},
  263. {"reserved-portrange", &reserved_port_min, &reserved_port_max},
  264. {NULL, NULL, NULL}
  265. };
  266. /* Set up Tcl variables that will hook into eggdrop internal vars via
  267. * trace callbacks.
  268. */
  269. void add_tcl_coups(tcl_coups *); /* prototype */
  270. static void init_traces()
  271. {
  272. add_tcl_coups(def_tcl_coups);
  273. }
  274. /* Not going through Tcl's crazy main() system (what on earth was he
  275. * smoking?!) so we gotta initialize the Tcl interpreter
  276. */
  277. void init_tcl(int argc, char **argv)
  278. {
  279. #if (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 1) || (TCL_MAJOR_VERSION > 8)
  280. const char *encoding;
  281. char *langEnv;
  282. #endif
  283. int j;
  284. char pver[1024] = "";
  285. /* This must be done *BEFORE* Tcl_SetSystemEncoding(),
  286. * or Tcl_SetSystemEncoding() will cause a segfault.
  287. */
  288. /* This is used for 'info nameofexecutable'.
  289. * The filename in argv[0] must exist in a directory listed in
  290. * the environment variable PATH for it to register anything.
  291. */
  292. Tcl_FindExecutable(argv[0]);
  293. /* Initialize the interpreter */
  294. interp = Tcl_CreateInterp();
  295. /* Set Tcl variable tcl_interactive to 0 */
  296. Tcl_SetVar(interp, "tcl_interactive", "0", TCL_GLOBAL_ONLY);
  297. /* Setup script library facility */
  298. Tcl_Init(interp);
  299. /* Code based on Tcl's TclpSetInitialEncodings() */
  300. #if (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 1) || (TCL_MAJOR_VERSION > 8)
  301. /* Determine the current encoding from the LC_* or LANG environment
  302. * variables.
  303. */
  304. langEnv = getenv("LC_ALL");
  305. if (langEnv == NULL || langEnv[0] == '\0') {
  306. langEnv = getenv("LC_CTYPE");
  307. }
  308. if (langEnv == NULL || langEnv[0] == '\0') {
  309. langEnv = getenv("LANG");
  310. }
  311. if (langEnv == NULL || langEnv[0] == '\0') {
  312. langEnv = NULL;
  313. }
  314. encoding = NULL;
  315. if (langEnv != NULL) {
  316. /* There was no mapping in the locale table. If there is an
  317. * encoding subfield, we can try to guess from that.
  318. */
  319. if (encoding == NULL) {
  320. char *p;
  321. for (p = langEnv; *p != '\0'; p++) {
  322. if (*p == '.') {
  323. p++;
  324. break;
  325. }
  326. }
  327. if (*p != '\0') {
  328. Tcl_DString ds;
  329. Tcl_DStringInit(&ds);
  330. Tcl_DStringAppend(&ds, p, -1);
  331. encoding = Tcl_DStringValue(&ds);
  332. Tcl_UtfToLower(Tcl_DStringValue(&ds));
  333. if (Tcl_SetSystemEncoding(NULL, encoding) == TCL_OK) {
  334. Tcl_DStringFree(&ds);
  335. goto resetPath;
  336. }
  337. Tcl_DStringFree(&ds);
  338. encoding = NULL;
  339. }
  340. }
  341. }
  342. if (encoding == NULL) {
  343. encoding = "iso8859-1";
  344. }
  345. Tcl_SetSystemEncoding(NULL, encoding);
  346. resetPath:
  347. /* Initialize the C library's locale subsystem. */
  348. setlocale(LC_CTYPE, "");
  349. /* In case the initial locale is not "C", ensure that the numeric
  350. * processing is done in "C" locale regardless. */
  351. setlocale(LC_NUMERIC, "C");
  352. /* Keep the iso8859-1 encoding preloaded. The IO package uses it for
  353. * gets on a binary channel. */
  354. Tcl_GetEncoding(NULL, "iso8859-1");
  355. #endif
  356. /* Add eggdrop to Tcl's package list */
  357. for (j = 0; j <= strlen(egg_version); j++) {
  358. if ((egg_version[j] == ' ') || (egg_version[j] == '+'))
  359. break;
  360. pver[strlen(pver)] = egg_version[j];
  361. }
  362. Tcl_PkgProvide(interp, "eggdrop", pver);
  363. /* Initialize binds and traces */
  364. Context;
  365. init_traces();
  366. Context;
  367. }
  368. /* Allocate couplet space for tracing couplets
  369. */
  370. void add_tcl_coups(tcl_coups *list)
  371. {
  372. coupletinfo *cp;
  373. int i;
  374. for (i = 0; list[i].name; i++) {
  375. cp = (coupletinfo *) malloc(sizeof(coupletinfo));
  376. strtot += sizeof(coupletinfo);
  377. cp->left = list[i].lptr;
  378. cp->right = list[i].rptr;
  379. tcl_eggcouplet((ClientData) cp, interp, list[i].name, NULL,
  380. TCL_TRACE_WRITES);
  381. tcl_eggcouplet((ClientData) cp, interp, list[i].name, NULL,
  382. TCL_TRACE_READS);
  383. Tcl_TraceVar(interp, list[i].name,
  384. TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
  385. tcl_eggcouplet, (ClientData) cp);
  386. }
  387. }
  388. void rem_tcl_coups(tcl_coups * list)
  389. {
  390. coupletinfo *cp;
  391. int i;
  392. for (i = 0; list[i].name; i++) {
  393. cp = (coupletinfo *) Tcl_VarTraceInfo(interp, list[i].name,
  394. TCL_TRACE_READS |
  395. TCL_TRACE_WRITES |
  396. TCL_TRACE_UNSETS,
  397. tcl_eggcouplet, NULL);
  398. strtot -= sizeof(coupletinfo);
  399. Tcl_UntraceVar(interp, list[i].name,
  400. TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
  401. tcl_eggcouplet, (ClientData) cp);
  402. free(cp);
  403. }
  404. }