notes.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992
  1. /*
  2. * notes.c -- part of notes.mod
  3. * reading and sending notes
  4. * killing old notes and changing the destinations
  5. * note cmds
  6. * note ignores
  7. *
  8. */
  9. #define MODULE_NAME "notes"
  10. #define MAKING_NOTES
  11. #include <fcntl.h>
  12. #include <sys/stat.h> /* chmod(..) */
  13. #include "src/mod/module.h"
  14. #include "src/tandem.h"
  15. #undef global
  16. #include "notes.h"
  17. static int maxnotes = 50; /* Maximum number of notes to allow stored
  18. * for each user */
  19. static int note_life = 60; /* Number of DAYS a note lives */
  20. static char notefile[121] = ".n"; /* Name of the notefile */
  21. static int allow_fwd = 1; /* Allow note forwarding */
  22. static int notify_users = 1; /* Notify users they have notes every hour? */
  23. static Function *global = NULL; /* DAMN fcntl.h */
  24. static struct user_entry_type USERENTRY_FWD =
  25. {
  26. NULL, /* always 0 ;) */
  27. NULL,
  28. NULL,
  29. NULL,
  30. NULL,
  31. NULL,
  32. NULL,
  33. NULL,
  34. NULL,
  35. NULL,
  36. NULL,
  37. NULL,
  38. fwd_display,
  39. "FWD"
  40. };
  41. #include "cmdsnote.c"
  42. static void fwd_display(int idx, struct user_entry *e, struct userrec *u)
  43. {
  44. if (dcc[idx].user && (dcc[idx].user->flags & USER_MASTER))
  45. dprintf(idx, NOTES_FORWARD_TO, e->u.string);
  46. }
  47. /* Determine how many notes are waiting for a user.
  48. */
  49. static int num_notes(char *user)
  50. {
  51. int tot = 0;
  52. FILE *f;
  53. char s[513], *to, *s1;
  54. if (!notefile[0])
  55. return 0;
  56. f = fopen(notefile, "r");
  57. if (f == NULL)
  58. return 0;
  59. while (!feof(f)) {
  60. fgets(s, 512, f);
  61. if (!feof(f)) {
  62. if (s[strlen(s) - 1] == '\n')
  63. s[strlen(s) - 1] = 0;
  64. rmspace(s);
  65. if ((s[0]) && (s[0] != '#') && (s[0] != ';')) { /* Not comment */
  66. s1 = s;
  67. to = newsplit(&s1);
  68. if (!egg_strcasecmp(to, user))
  69. tot++;
  70. }
  71. }
  72. }
  73. fclose(f);
  74. return tot;
  75. }
  76. /* Change someone's handle.
  77. */
  78. static void notes_change(char *oldnick, char *newnick)
  79. {
  80. FILE *f, *g;
  81. char s[513], *to, *s1;
  82. int tot = 0;
  83. if (!egg_strcasecmp(oldnick, newnick))
  84. return;
  85. if (!notefile[0])
  86. return;
  87. f = fopen(notefile, "r");
  88. if (f == NULL)
  89. return;
  90. sprintf(s, "%s~new", notefile);
  91. g = fopen(s, "w");
  92. if (g == NULL) {
  93. fclose(f);
  94. return;
  95. }
  96. chmod(s, userfile_perm); /* Use userfile permissions. */
  97. while (!feof(f)) {
  98. fgets(s, 512, f);
  99. if (!feof(f)) {
  100. if (s[strlen(s) - 1] == '\n')
  101. s[strlen(s) - 1] = 0;
  102. rmspace(s);
  103. if ((s[0]) && (s[0] != '#') && (s[0] != ';')) { /* Not comment */
  104. s1 = s;
  105. to = newsplit(&s1);
  106. if (!egg_strcasecmp(to, oldnick)) {
  107. tot++;
  108. fprintf(g, "%s %s\n", newnick, s1);
  109. } else
  110. fprintf(g, "%s %s\n", to, s1);
  111. } else
  112. fprintf(g, "%s\n", s);
  113. }
  114. }
  115. fclose(f);
  116. fclose(g);
  117. unlink(notefile);
  118. sprintf(s, "%s~new", notefile);
  119. movefile(s, notefile);
  120. putlog(LOG_MISC, "*", NOTES_SWITCHED_NOTES, tot, tot == 1 ? "" : "s",
  121. oldnick, newnick);
  122. }
  123. /* Get rid of old useless notes.
  124. */
  125. static void expire_notes()
  126. {
  127. FILE *f, *g;
  128. char s[513], *to, *from, *ts, *s1;
  129. int tot = 0, lapse;
  130. if (!notefile[0])
  131. return;
  132. f = fopen(notefile, "r");
  133. if (f == NULL)
  134. return;
  135. sprintf(s, "%s~new", notefile);
  136. g = fopen(s, "w");
  137. if (g == NULL) {
  138. fclose(f);
  139. return;
  140. }
  141. chmod(s, userfile_perm); /* Use userfile permissions. */
  142. while (!feof(f)) {
  143. fgets(s, 512, f);
  144. if (!feof(f)) {
  145. if (s[strlen(s) - 1] == '\n')
  146. s[strlen(s) - 1] = 0;
  147. rmspace(s);
  148. if ((s[0]) && (s[0] != '#') && (s[0] != ';')) { /* Not comment */
  149. s1 = s;
  150. to = newsplit(&s1);
  151. from = newsplit(&s1);
  152. ts = newsplit(&s1);
  153. lapse = (now - (time_t) atoi(ts)) / 86400;
  154. if (lapse > note_life)
  155. tot++;
  156. else if (!get_user_by_handle(userlist, to))
  157. tot++;
  158. else
  159. fprintf(g, "%s %s %s %s\n", to, from, ts, s1);
  160. } else
  161. fprintf(g, "%s\n", s);
  162. }
  163. }
  164. fclose(f);
  165. fclose(g);
  166. unlink(notefile);
  167. sprintf(s, "%s~new", notefile);
  168. movefile(s, notefile);
  169. if (tot > 0)
  170. putlog(LOG_MISC, "*", NOTES_EXPIRED, tot, tot == 1 ? "" : "s");
  171. }
  172. /* Add note to notefile.
  173. */
  174. static int storenote(char *argv1, char *argv2, char *argv3, int idx, char *who, int bufsize)
  175. {
  176. FILE *f;
  177. char u[20], *f1, *to = NULL, work[1024];
  178. struct userrec *ur;
  179. struct userrec *ur2;
  180. idx = findanyidx(idx);
  181. if (who && bufsize > 0) who[0] = 0;
  182. ur = get_user_by_handle(userlist, argv2);
  183. if (ur && allow_fwd && (f1 = get_user(&USERENTRY_FWD, ur))) {
  184. char fwd[161], fwd2[161], *f2, *p, *q, *r;
  185. int ok = 1;
  186. /* User is valid & has a valid forwarding address */
  187. strcpy(fwd, f1); /* Only 40 bytes are stored in the userfile */
  188. p = strchr(fwd, '@');
  189. if (p && !egg_strcasecmp(p + 1, botnetnick)) {
  190. *p = 0;
  191. if (!egg_strcasecmp(fwd, argv2))
  192. /* They're forwarding to themselves on the same bot, llama's */
  193. ok = 0;
  194. strcpy(fwd2, fwd);
  195. splitc(fwd2, fwd2, '@');
  196. /* Get the user record of the user that we're forwarding to locally */
  197. ur2 = get_user_by_handle(userlist, fwd2);
  198. if (!ur2)
  199. ok = 0;
  200. if ((f2 = get_user(&USERENTRY_FWD, ur2))) {
  201. strcpy(fwd2, f2);
  202. splitc(fwd2, fwd2, '@');
  203. if (!egg_strcasecmp(fwd2, argv2))
  204. /* They're forwarding to someone who forwards back to them! */
  205. ok = 0;
  206. }
  207. p = NULL;
  208. }
  209. if ((argv1[0] != '@') && ((argv3[0] == '<') || (argv3[0] == '>')))
  210. ok = 0; /* Probablly fake pre 1.3 hax0r */
  211. if (ok && (!p || in_chain(p + 1))) {
  212. if (p)
  213. p++;
  214. q = argv3;
  215. while (ok && q && (q = strchr(q, '<'))) {
  216. q++;
  217. if ((r = strchr(q, ' '))) {
  218. *r = 0;
  219. if (!egg_strcasecmp(fwd, q))
  220. ok = 0;
  221. *r = ' ';
  222. }
  223. }
  224. if (ok) {
  225. if (p && strchr(argv1, '@')) {
  226. simple_sprintf(work, "<%s@%s >%s %s", argv2, botnetnick,
  227. argv1, argv3);
  228. simple_sprintf(u, "@%s", botnetnick);
  229. p = u;
  230. } else {
  231. simple_sprintf(work, "<%s@%s %s", argv2, botnetnick,
  232. argv3);
  233. p = argv1;
  234. }
  235. }
  236. } else
  237. ok = 0;
  238. if (ok) {
  239. if ((add_note(fwd, p, work, idx, 0) == NOTE_OK) && (idx >= 0))
  240. dprintf(idx, "Not online; forwarded to %s.\n", f1);
  241. if (who) strncpy(who, f1, bufsize);
  242. to = NULL;
  243. } else {
  244. strcpy(work, argv3);
  245. to = argv2;
  246. }
  247. } else
  248. to = argv2;
  249. if (to) {
  250. if (notefile[0] == 0) {
  251. if (idx >= 0)
  252. dprintf(idx, "%s\n", "Notes are not supported by this bot.");
  253. } else if (num_notes(to) >= maxnotes) {
  254. if (idx >= 0)
  255. dprintf(idx, "%s\n", "Sorry, that user has too many notes already.");
  256. } else { /* Time to unpack it meaningfully */
  257. f = fopen(notefile, "a");
  258. if (f == NULL)
  259. f = fopen(notefile, "w");
  260. if (f == NULL) {
  261. if (idx >= 0)
  262. dprintf(idx, "%s\n", "Cant create notefile. Sorry.");
  263. putlog(LOG_MISC, "*", "%s", "Notefile unreachable!");
  264. } else {
  265. char *p, *from = argv1;
  266. int l = 0;
  267. chmod(notefile, userfile_perm); /* Use userfile permissions. */
  268. while ((argv3[0] == '<') || (argv3[0] == '>')) {
  269. p = newsplit(&(argv3));
  270. if (*p == '<')
  271. l += simple_sprintf(work + l, "via %s, ", p + 1);
  272. else if (argv1[0] == '@')
  273. from = p + 1;
  274. }
  275. fprintf(f, "%s %s %lu %s%s\n", to, from, now,
  276. l ? work : "", argv3);
  277. fclose(f);
  278. if (idx >= 0)
  279. dprintf(idx, "%s.\n", "Stored message");
  280. }
  281. }
  282. }
  283. return 0;
  284. }
  285. /* Convert a string like "2-4;8;16-"
  286. * in an array {2, 4, 8, 8, 16, maxnotes, -1}
  287. */
  288. static void notes_parse(int dl[], char *s)
  289. {
  290. int i = 0;
  291. int idl = 0;
  292. do {
  293. while (s[i] == ';')
  294. i++;
  295. if (s[i]) {
  296. if (s[i] == '-')
  297. dl[idl] = 1;
  298. else
  299. dl[idl] = atoi(s + i);
  300. idl++;
  301. while ((s[i]) && (s[i] != '-') && (s[i] != ';'))
  302. i++;
  303. if (s[i] == '-') {
  304. dl[idl] = atoi(s + i + 1); /* Will be 0 if not a number */
  305. if (dl[idl] == 0)
  306. dl[idl] = maxnotes;
  307. } else
  308. dl[idl] = dl[idl - 1];
  309. idl++;
  310. while ((s[i]) && (s[i] != ';'))
  311. i++;
  312. }
  313. }
  314. while ((s[i]) && (idl < 124));
  315. dl[idl] = -1;
  316. }
  317. /* Return true if 'in' is in intervals of 'dl'
  318. */
  319. static int notes_in(int dl[], int in)
  320. {
  321. int i = 0;
  322. while (dl[i] != -1) {
  323. if ((dl[i] <= in) && (in <= dl[i + 1]))
  324. return 1;
  325. i += 2;
  326. }
  327. return 0;
  328. }
  329. /*
  330. * srd="+" : index
  331. * srd="-" : read all msgs
  332. * else : read msg in list : (ex: .notes read 5-9;12;13;18-)
  333. * idx=-1 : /msg
  334. */
  335. static void notes_read(char *hand, char *nick, char *srd, int idx)
  336. {
  337. FILE *f;
  338. char s[601], *to, *dt, *from, *s1, wt[100];
  339. time_t tt;
  340. int ix = 1;
  341. int ir = 0;
  342. int rd[128]; /* Is it enough ? */
  343. int i;
  344. if (srd[0] == 0)
  345. srd = "-";
  346. if (!notefile[0]) {
  347. if (idx >= 0)
  348. dprintf(idx, "%s.\n", NOTES_NO_MESSAGES);
  349. else
  350. dprintf(DP_HELP, "NOTICE %s :%s.\n", nick, NOTES_NO_MESSAGES);
  351. return;
  352. }
  353. f = fopen(notefile, "r");
  354. if (f == NULL) {
  355. if (idx >= 0)
  356. dprintf(idx, "%s.\n", NOTES_NO_MESSAGES);
  357. else
  358. dprintf(DP_HELP, "NOTICE %s :%s.\n", nick, NOTES_NO_MESSAGES);
  359. return;
  360. }
  361. notes_parse(rd, srd);
  362. while (!feof(f)) {
  363. fgets(s, 600, f);
  364. i = strlen(s);
  365. if (i > 0 && s[i - 1] == '\n')
  366. s[i - 1] = 0;
  367. if (!feof(f)) {
  368. rmspace(s);
  369. if ((s[0]) && (s[0] != '#') && (s[0] != ';')) { /* Not comment */
  370. s1 = s;
  371. to = newsplit(&s1);
  372. if (!egg_strcasecmp(to, hand)) {
  373. int lapse;
  374. from = newsplit(&s1);
  375. dt = newsplit(&s1);
  376. tt = atoi(dt);
  377. #ifdef S_UTCTIME
  378. egg_strftime(wt, sizeof wt, "%b %d %H:%M %Z", gmtime(&tt));
  379. #else /* !S_UTCTIME */
  380. egg_strftime(wt, sizeof wt, "%b %d %H:%M %Z", localtime(&tt));
  381. #endif /* S_UTCTIME */
  382. dt = wt;
  383. lapse = (int) ((now - tt) / 86400);
  384. if (lapse > note_life - 7) {
  385. if (lapse >= note_life)
  386. strcat(dt, NOTES_EXPIRE_TODAY);
  387. else
  388. sprintf(&dt[strlen(dt)], NOTES_EXPIRE_XDAYS, note_life - lapse,
  389. (note_life - lapse) == 1 ? "" : "S");
  390. }
  391. if (srd[0] == '+') {
  392. if (idx >= 0) {
  393. if (ix == 1)
  394. dprintf(idx, "### %s:\n", NOTES_WAITING);
  395. dprintf(idx, " %2d. %s (%s)\n", ix, from, dt);
  396. } else {
  397. dprintf(DP_HELP, "NOTICE %s :%2d. %s (%s)\n",
  398. nick, ix, from, dt);
  399. }
  400. } else if (notes_in(rd, ix)) {
  401. if (idx >= 0)
  402. dprintf(idx, "%2d. %s (%s): %s\n", ix, from, dt, s1);
  403. else
  404. dprintf(DP_HELP, "NOTICE %s :%2d. %s (%s): %s\n",
  405. nick, ix, from, dt, s1);
  406. ir++;
  407. }
  408. ix++;
  409. }
  410. }
  411. }
  412. }
  413. fclose(f);
  414. if ((srd[0] != '+') && (ir == 0) && (ix > 1)) {
  415. if (idx >= 0)
  416. dprintf(idx, "%s.\n", NOTES_NOT_THAT_MANY);
  417. else
  418. dprintf(DP_HELP, "NOTICE %s :%s.\n", nick, NOTES_NOT_THAT_MANY);
  419. }
  420. if (srd[0] == '+') {
  421. if (ix == 1) {
  422. if (idx >= 0)
  423. dprintf(idx, "%s.\n", NOTES_NO_MESSAGES);
  424. else
  425. dprintf(DP_HELP, "NOTICE %s :%s.\n", nick, NOTES_NO_MESSAGES);
  426. } else {
  427. if (idx >= 0)
  428. dprintf(idx, "### %s.\n", NOTES_DCC_USAGE_READ);
  429. else
  430. dprintf(DP_HELP, "NOTICE %s :(%d %s)\n", nick, ix - 1, MISC_TOTAL);
  431. }
  432. } else if ((ir == 0) && (ix == 1)) {
  433. if (idx >= 0)
  434. dprintf(idx, "%s.\n", NOTES_NO_MESSAGES);
  435. else
  436. dprintf(DP_HELP, "NOTICE %s :%s.\n", nick, NOTES_NO_MESSAGES);
  437. }
  438. }
  439. /*
  440. * sdl="-" : erase all msgs
  441. * else : erase msg in list : (ex: .notes erase 2-4;8;16-)
  442. * idx=-1 : /msg
  443. */
  444. static void notes_del(char *hand, char *nick, char *sdl, int idx)
  445. {
  446. FILE *f, *g;
  447. char s[513], *to, *s1;
  448. int in = 1;
  449. int er = 0;
  450. int dl[128]; /* Is it enough ? */
  451. if (sdl[0] == 0)
  452. sdl = "-";
  453. if (!notefile[0]) {
  454. if (idx >= 0)
  455. dprintf(idx, "%s.\n", NOTES_NO_MESSAGES);
  456. else
  457. dprintf(DP_HELP, "NOTICE %s :%s.\n", nick, NOTES_NO_MESSAGES);
  458. return;
  459. }
  460. f = fopen(notefile, "r");
  461. if (f == NULL) {
  462. if (idx >= 0)
  463. dprintf(idx, "%s.\n", NOTES_NO_MESSAGES);
  464. else
  465. dprintf(DP_HELP, "NOTICE %s :%s.\n", nick, NOTES_NO_MESSAGES);
  466. return;
  467. }
  468. sprintf(s, "%s~new", notefile);
  469. g = fopen(s, "w");
  470. if (g == NULL) {
  471. if (idx >= 0)
  472. dprintf(idx, "%s. :(\n", NOTES_FAILED_CHMOD);
  473. else
  474. dprintf(DP_HELP, "NOTICE %s :%s. :(\n", nick, NOTES_FAILED_CHMOD);
  475. fclose(f);
  476. return;
  477. }
  478. chmod(s, userfile_perm); /* Use userfile permissions. */
  479. notes_parse(dl, sdl);
  480. while (!feof(f)) {
  481. fgets(s, 512, f);
  482. if (s[strlen(s) - 1] == '\n')
  483. s[strlen(s) - 1] = 0;
  484. if (!feof(f)) {
  485. rmspace(s);
  486. if ((s[0]) && (s[0] != '#') && (s[0] != ';')) { /* Not comment */
  487. s1 = s;
  488. to = newsplit(&s1);
  489. if (!egg_strcasecmp(to, hand)) {
  490. if (!notes_in(dl, in))
  491. fprintf(g, "%s %s\n", to, s1);
  492. else
  493. er++;
  494. in++;
  495. } else
  496. fprintf(g, "%s %s\n", to, s1);
  497. } else
  498. fprintf(g, "%s\n", s);
  499. }
  500. }
  501. fclose(f);
  502. fclose(g);
  503. unlink(notefile);
  504. sprintf(s, "%s~new", notefile);
  505. movefile(s, notefile);
  506. if ((er == 0) && (in > 1)) {
  507. if (idx >= 0)
  508. dprintf(idx, "%s.\n", NOTES_NOT_THAT_MANY);
  509. else
  510. dprintf(DP_HELP, "NOTICE %s :%s.\n", nick, NOTES_NOT_THAT_MANY);
  511. } else if (in == 1) {
  512. if (idx >= 0)
  513. dprintf(idx, "%s.\n", NOTES_NO_MESSAGES);
  514. else
  515. dprintf(DP_HELP, "NOTICE %s :%s.\n", nick, NOTES_NO_MESSAGES);
  516. } else {
  517. if (er == (in - 1)) {
  518. if (idx >= 0)
  519. dprintf(idx, "%s.\n", NOTES_ERASED_ALL);
  520. else
  521. dprintf(DP_HELP, "NOTICE %s :%s.\n", nick, NOTES_ERASED_ALL);
  522. } else {
  523. if (idx >= 0)
  524. dprintf(idx, "%s %d note%s; %d %s.\n", NOTES_ERASED, er,
  525. (er != 1) ? "s" : "", in - 1 - er, NOTES_LEFT);
  526. else
  527. dprintf(DP_HELP, "NOTICE %s :%s %d note%s; %d %s.\n", nick, MISC_ERASED,
  528. er, (er != 1) ? "s" : "", in - 1 - er, NOTES_LEFT);
  529. }
  530. }
  531. }
  532. /* notes <pass> <func>
  533. */
  534. static int msg_notes(char *nick, char *host, struct userrec *u, char *par)
  535. {
  536. char *pwd, *fcn;
  537. if (!u)
  538. return 0;
  539. if (u->flags & (USER_BOT))
  540. return 1;
  541. if (!par[0]) {
  542. dprintf(DP_HELP, "NOTICE %s :%s: NOTES <pass> INDEX\n", nick, NOTES_USAGE);
  543. dprintf(DP_HELP, "NOTICE %s : NOTES <pass> TO <hand> <msg>\n", nick);
  544. dprintf(DP_HELP, "NOTICE %s : NOTES <pass> READ <# or ALL>\n", nick);
  545. dprintf(DP_HELP, "NOTICE %s : NOTES <pass> ERASE <# or ALL>\n", nick);
  546. dprintf(DP_HELP, "NOTICE %s : %s\n", nick, NOTES_MAYBE);
  547. dprintf(DP_HELP, "NOTICE %s : ex: NOTES mypass ERASE 2-4;8;16-\n", nick);
  548. return 1;
  549. }
  550. if (!u_pass_match(u, "-")) {
  551. /* they have a password set */
  552. pwd = newsplit(&par);
  553. if (!u_pass_match(u, pwd))
  554. return 0;
  555. }
  556. fcn = newsplit(&par);
  557. if (!egg_strcasecmp(fcn, "INDEX"))
  558. notes_read(u->handle, nick, "+", -1);
  559. else if (!egg_strcasecmp(fcn, "READ")) {
  560. if (!egg_strcasecmp(par, "ALL"))
  561. notes_read(u->handle, nick, "-", -1);
  562. else
  563. notes_read(u->handle, nick, par, -1);
  564. } else if (!egg_strcasecmp(fcn, "ERASE")) {
  565. if (!egg_strcasecmp(par, "ALL"))
  566. notes_del(u->handle, nick, "-", -1);
  567. else
  568. notes_del(u->handle, nick, par, -1);
  569. } else if (!egg_strcasecmp(fcn, "TO")) {
  570. char *to;
  571. int i;
  572. FILE *f;
  573. struct userrec *u2;
  574. to = newsplit(&par);
  575. if (!par[0]) {
  576. dprintf(DP_HELP, "NOTICE %s :%s: NOTES <pass> TO <hand> <message>\n",
  577. nick, NOTES_USAGE);
  578. return 0;
  579. }
  580. u2 = get_user_by_handle(userlist, to);
  581. if (!u2) {
  582. dprintf(DP_HELP, "NOTICE %s :%s\n", nick, NOTES_USERF_UNKNOWN);
  583. return 1;
  584. } else if (is_bot(u2)) {
  585. dprintf(DP_HELP, "NOTICE %s :%s\n", nick, NOTES_NOTTO_BOT);
  586. return 1;
  587. }
  588. for (i = 0; i < dcc_total; i++) {
  589. if ((!egg_strcasecmp(dcc[i].nick, to)) &&
  590. (dcc[i].type->flags & DCT_GETNOTES)) {
  591. int aok = 1;
  592. if (dcc[i].type->flags & DCT_CHAT)
  593. if (dcc[i].u.chat->away != NULL)
  594. aok = 0;
  595. if (!(dcc[i].type->flags & DCT_CHAT))
  596. aok = 0; /* Assume non dcc-chat == something weird, so
  597. * store notes for later */
  598. if (aok) {
  599. dprintf(i, "\007%s [%s]: %s\n", u->handle, NOTES_OUTSIDE, par);
  600. dprintf(DP_HELP, "NOTICE %s :%s\n", nick, NOTES_DELIVERED);
  601. return 1;
  602. }
  603. }
  604. }
  605. if (notefile[0] == 0) {
  606. dprintf(DP_HELP, "NOTICE %s :%s\n", nick, NOTES_UNSUPPORTED);
  607. return 1;
  608. }
  609. f = fopen(notefile, "a");
  610. if (f == NULL)
  611. f = fopen(notefile, "w");
  612. if (f == NULL) {
  613. dprintf(DP_HELP, "NOTICE %s :%s", nick, NOTES_NOTEFILE_FAILED);
  614. putlog(LOG_MISC, "*", "* %s", NOTES_NOTEFILE_UNREACHABLE);
  615. return 1;
  616. }
  617. chmod(notefile, userfile_perm); /* Use userfile permissions. */
  618. fprintf(f, "%s %s %lu %s\n", to, u->handle, now, par);
  619. fclose(f);
  620. dprintf(DP_HELP, "NOTICE %s :%s\n", nick, NOTES_DELIVERED);
  621. return 1;
  622. } else
  623. dprintf(DP_HELP, "NOTICE %s :%s INDEX, READ, ERASE, TO\n",
  624. nick, NOTES_DCC_USAGE_READ);
  625. putlog(LOG_CMDS, "*", "(%s!%s) !%s! NOTES %s %s", nick, host, u->handle, fcn,
  626. par[0] ? "..." : "");
  627. return 1;
  628. }
  629. static void notes_hourly()
  630. {
  631. expire_notes();
  632. if (notify_users) {
  633. register struct chanset_t *chan;
  634. register memberlist *m;
  635. int k;
  636. register int l;
  637. char s1[256];
  638. struct userrec *u;
  639. for (chan = chanset; chan; chan = chan->next) {
  640. for (m = chan->channel.member; m && m->nick[0]; m = m->next) {
  641. sprintf(s1, "%s!%s", m->nick, m->userhost);
  642. u = get_user_by_host(s1);
  643. if (u) {
  644. k = num_notes(u->handle);
  645. for (l = 0; l < dcc_total; l++)
  646. if ((dcc[l].type->flags & DCT_CHAT) &&
  647. !egg_strcasecmp(dcc[l].nick, u->handle)) {
  648. k = 0; /* They already know they have notes */
  649. break;
  650. }
  651. if (k) {
  652. dprintf(DP_HELP, "NOTICE %s :You have %d note%s waiting on %s.\n",
  653. m->nick, k, k == 1 ? "" : "s", botname);
  654. dprintf(DP_HELP, "NOTICE %s :%s /MSG %s NOTES <pass> INDEX\n",
  655. m->nick, NOTES_FORLIST, botname);
  656. }
  657. }
  658. }
  659. }
  660. for (l = 0; l < dcc_total; l++) {
  661. k = num_notes(dcc[l].nick);
  662. if ((k > 0) && (dcc[l].type->flags & DCT_CHAT)) {
  663. dprintf(l, NOTES_WAITING2, k, k == 1 ? "" : "s");
  664. dprintf(l, NOTES_DCC_USAGE_READ2);
  665. }
  666. }
  667. }
  668. }
  669. static void away_notes(char *bot, int sock, char *msg)
  670. {
  671. int idx = findanyidx(sock);
  672. if (egg_strcasecmp(bot, botnetnick))
  673. return;
  674. if (msg && msg[0])
  675. dprintf(idx, "%s\n", NOTES_STORED);
  676. else
  677. notes_read(dcc[idx].nick, 0, "+", idx);
  678. }
  679. static int chon_notes(char *nick, int idx)
  680. {
  681. if (dcc[idx].type == &DCC_CHAT)
  682. notes_read(nick, 0, "+", idx);
  683. return 0;
  684. }
  685. /* Return either NULL or a pointer to the xtra_key structure
  686. * where the not ignores are kept.
  687. */
  688. static struct xtra_key *getnotesentry(struct userrec *u)
  689. {
  690. struct user_entry *ue = find_user_entry(&USERENTRY_XTRA, u);
  691. struct xtra_key *xk, *nxk = NULL;
  692. if (!ue)
  693. return NULL;
  694. /* Search for the notes ignore list entry */
  695. for (xk = ue->u.extra; xk; xk = xk->next)
  696. if (xk->key && !egg_strcasecmp(xk->key, NOTES_IGNKEY)) {
  697. nxk = xk;
  698. break;
  699. }
  700. if (!nxk || !nxk->data || !(nxk->data[0]))
  701. return NULL;
  702. return nxk;
  703. }
  704. /* Parse the NOTES_IGNKEY xtra field. You must free the memory allocated
  705. * in here: the buffer 'ignores[0]' and the array 'ignores'.
  706. */
  707. int get_note_ignores(struct userrec *u, char ***ignores)
  708. {
  709. struct xtra_key *xk;
  710. char *buf, *p;
  711. int ignoresn;
  712. /* Hullo? sanity? */
  713. if (!u)
  714. return 0;
  715. xk = getnotesentry(u);
  716. if (!xk)
  717. return 0;
  718. rmspace(xk->data);
  719. buf = user_malloc(strlen(xk->data) + 1);
  720. strcpy(buf, xk->data);
  721. p = buf;
  722. /* Split up the string into small parts */
  723. *ignores = nmalloc(sizeof(char *) + 100);
  724. **ignores = p;
  725. ignoresn = 1;
  726. while ((p = strchr(p, ' ')) != NULL) {
  727. *ignores = nrealloc(*ignores, sizeof(char *) * (ignoresn+1));
  728. (*ignores)[ignoresn] = p + 1;
  729. ignoresn++;
  730. *p = 0;
  731. p++;
  732. }
  733. return ignoresn;
  734. }
  735. int add_note_ignore(struct userrec *u, char *mask)
  736. {
  737. struct xtra_key *xk;
  738. char **ignores;
  739. int ignoresn, i;
  740. ignoresn = get_note_ignores(u, &ignores);
  741. if (ignoresn > 0) {
  742. /* Search for existing mask */
  743. for (i = 0; i < ignoresn; i++)
  744. if (!strcmp(ignores[i], mask)) {
  745. nfree(ignores[0]); /* Free the string buffer */
  746. nfree(ignores); /* Free the ptr array */
  747. /* The mask already exists, exit. */
  748. return 0;
  749. }
  750. nfree(ignores[0]); /* Free the string buffer */
  751. nfree(ignores); /* Free the ptr array */
  752. }
  753. xk = getnotesentry(u);
  754. /* First entry? */
  755. if (!xk) {
  756. struct xtra_key *mxk = user_malloc(sizeof(struct xtra_key));
  757. struct user_entry *ue = find_user_entry(&USERENTRY_XTRA, u);
  758. if (!ue)
  759. return 0;
  760. mxk->next = 0;
  761. mxk->data = user_malloc(strlen(mask) + 1);
  762. strcpy(mxk->data, mask);
  763. mxk->key = user_malloc(strlen(NOTES_IGNKEY) + 1);
  764. strcpy(mxk->key, NOTES_IGNKEY);
  765. xtra_set(u, ue, mxk);
  766. } else { /* ... else, we already have other entries. */
  767. xk->data = user_realloc(xk->data, strlen(xk->data) + strlen(mask) + 2);
  768. strcat(xk->data, " ");
  769. strcat(xk->data, mask);
  770. }
  771. return 1;
  772. }
  773. int del_note_ignore(struct userrec *u, char *mask)
  774. {
  775. struct user_entry *ue;
  776. struct xtra_key *xk;
  777. char **ignores, *buf = NULL;
  778. int ignoresn, i, size = 0, foundit = 0;
  779. ignoresn = get_note_ignores(u, &ignores);
  780. if (!ignoresn)
  781. return 0;
  782. buf = user_malloc(1);
  783. buf[0] = 0;
  784. for (i = 0; i < ignoresn; i++) {
  785. if (strcmp(ignores[i], mask)) {
  786. size += strlen(ignores[i]);
  787. if (buf[0])
  788. size++;
  789. buf = user_realloc(buf, size+1);
  790. if (buf[0])
  791. strcat(buf, " ");
  792. strcat(buf, ignores[i]);
  793. } else
  794. foundit = 1;
  795. }
  796. nfree(ignores[0]); /* Free the string buffer */
  797. nfree(ignores); /* Free the ptr array */
  798. /* Entry not found */
  799. if (!foundit) {
  800. nfree(buf);
  801. return 0;
  802. }
  803. ue = find_user_entry(&USERENTRY_XTRA, u);
  804. /* Delete the entry if the buffer is empty */
  805. xk = user_malloc(sizeof(struct xtra_key));
  806. xk->key = user_malloc(strlen(NOTES_IGNKEY)+1);
  807. xk->next = 0;
  808. if (!buf[0]) {
  809. nfree(buf); /* The allocated byte needs to be free'd too */
  810. strcpy(xk->key, NOTES_IGNKEY);
  811. xk->data = 0;
  812. } else {
  813. xk->data = buf;
  814. strcpy(xk->key, NOTES_IGNKEY);
  815. }
  816. xtra_set(u, ue, xk);
  817. return 1;
  818. }
  819. /* Returns 1 if the user u has an note ignore which
  820. * matches from
  821. */
  822. int match_note_ignore(struct userrec *u, char *from)
  823. {
  824. char **ignores;
  825. int ignoresn, i;
  826. ignoresn = get_note_ignores(u, &ignores);
  827. if (!ignoresn)
  828. return 0;
  829. for (i = 0; i < ignoresn; i++)
  830. if (wild_match(ignores[i], from)) {
  831. nfree(ignores[0]);
  832. nfree(ignores);
  833. return 1;
  834. }
  835. nfree(ignores[0]); /* Free the string buffer */
  836. nfree(ignores); /* Free the ptr array */
  837. return 0;
  838. }
  839. static cmd_t notes_nkch[] =
  840. {
  841. {"*", "", (Function) notes_change, "notes"},
  842. {NULL, NULL, NULL, NULL}
  843. };
  844. static cmd_t notes_away[] =
  845. {
  846. {"*", "", (Function) away_notes, "notes"},
  847. {NULL, NULL, NULL, NULL}
  848. };
  849. static cmd_t notes_chon[] =
  850. {
  851. {"*", "", (Function) chon_notes, "notes"},
  852. {NULL, NULL, NULL, NULL}
  853. };
  854. static cmd_t notes_msgs[] =
  855. {
  856. {"notes", "", (Function) msg_notes, NULL},
  857. {NULL, NULL, NULL, NULL}
  858. };
  859. static tcl_ints notes_ints[] =
  860. {
  861. {"note-life", &note_life},
  862. {"max-notes", &maxnotes},
  863. {"allow-fwd", &allow_fwd},
  864. {"notify-users", &notify_users},
  865. {NULL, NULL}
  866. };
  867. static tcl_strings notes_strings[] =
  868. {
  869. {"notefile", notefile, 120, 0},
  870. {NULL, NULL, 0, 0}
  871. };
  872. static int notes_server_setup(char *mod)
  873. {
  874. add_builtins("msg", notes_msgs);
  875. return 0;
  876. }
  877. static cmd_t notes_load[] =
  878. {
  879. {"server", "", notes_server_setup, "notes:server"},
  880. {NULL, NULL, NULL, NULL}
  881. };
  882. static int notes_expmem()
  883. {
  884. return 0;
  885. }
  886. static void notes_report(int idx, int details)
  887. {
  888. if (details) {
  889. if (notefile[0])
  890. dprintf(idx, " Notes can be stored, in: %s\n", notefile);
  891. else
  892. dprintf(idx, " Notes can not be stored.\n");
  893. }
  894. }
  895. EXPORT_SCOPE char *notes_start();
  896. static Function notes_table[] =
  897. {
  898. (Function) notes_start,
  899. (Function) NULL,
  900. (Function) notes_expmem,
  901. (Function) notes_report,
  902. (Function) cmd_note,
  903. };
  904. char *notes_start(Function * global_funcs)
  905. {
  906. global = global_funcs;
  907. notefile[0] = 0;
  908. module_register(MODULE_NAME, notes_table, 2, 1);
  909. add_hook(HOOK_HOURLY, (Function) notes_hourly);
  910. add_hook(HOOK_MATCH_NOTEREJ, (Function) match_note_ignore);
  911. add_hook(HOOK_STORENOTE, (Function) storenote);
  912. add_tcl_ints(notes_ints);
  913. add_tcl_strings(notes_strings);
  914. add_builtins("dcc", notes_cmds);
  915. add_builtins("load", notes_load);
  916. add_builtins("away", notes_away);
  917. add_builtins("chon", notes_chon);
  918. add_builtins("nkch", notes_nkch);
  919. notes_server_setup(0);
  920. my_memcpy(&USERENTRY_FWD, &USERENTRY_INFO, sizeof(void *) * 12);
  921. add_entry_type(&USERENTRY_FWD);
  922. return NULL;
  923. }