m_allowctcp_opers.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. /* Copyright (C) All Rights Reserved
  2. ** Written by Gottem <support@gottem.nl>
  3. ** Website: https://gitgud.malvager.net/Wazakindjes/unrealircd_mods
  4. ** License: https://gitgud.malvager.net/Wazakindjes/unrealircd_mods/raw/master/LICENSE
  5. */
  6. // One include for all cross-platform compatibility thangs
  7. #include "unrealircd.h"
  8. // Hooktype to use
  9. #define MYHEWK HOOKTYPE_PRE_USERMSG
  10. // Muh macros
  11. #define IsMDErr(x, y, z) \
  12. do { \
  13. if(!(x)) { \
  14. config_error("A critical error occurred when registering ModData for %s: %s", MOD_HEADER(y).name, ModuleGetErrorStr((z)->handle)); \
  15. return MOD_FAILED; \
  16. } \
  17. } while(0)
  18. #define BACKPORT_CONNECTTIMEOUT (UNREAL_VERSION_GENERATION == 4 && UNREAL_VERSION_MAJOR == 0 && UNREAL_VERSION_MINOR <= 15)
  19. #define BACKPORT (UNREAL_VERSION_GENERATION == 4 && UNREAL_VERSION_MAJOR == 0 && UNREAL_VERSION_MINOR < 12)
  20. // Big hecks go here
  21. typedef struct t_ctcpOverride cOverride;
  22. struct t_ctcpOverride {
  23. cOverride *next, *prev;
  24. char *uid;
  25. time_t when;
  26. };
  27. // Quality fowod declarations
  28. char *allowctcp_opers_preusermsg(aClient *sptr, aClient *to, char *text, int notice);
  29. void override_md_free(ModData *md);
  30. void check_stale_overrides(aClient *sptr);
  31. cOverride *match_override(aClient *sptr, char *uid);
  32. void add_override(aClient *sptr, aClient *to);
  33. void delete_override(aClient *sptr, cOverride *covr);
  34. void free_override(cOverride *covr);
  35. static int IsACTCP(char *s);
  36. #if BACKPORT
  37. long find_user_mode(char flag);
  38. int has_user_mode(aClient *acptr, char mode);
  39. #endif
  40. // Muh globals
  41. ModDataInfo *COvrMDI; // To store some shit with the user's aClient pointur ;]
  42. static ModuleInfo *allowctcp_opersMI = NULL; // Store ModuleInfo so we can use it to check for errors in MOD_LOAD
  43. Hook *preUMsgHook = NULL;
  44. // Dat dere module header
  45. ModuleHeader MOD_HEADER(m_allowctcp_opers) = {
  46. "m_allowctcp_opers", // Module name
  47. "$Id: v1.11 2017/11/11 Gottem$", // Version
  48. "Allows opers to override someone's umode +T (noctcp)", // Description
  49. "3.2-b8-1", // Modversion, not sure wat do
  50. NULL
  51. };
  52. // Initialisation routine (register hooks, commands and modes or create structs etc)
  53. MOD_INIT(m_allowctcp_opers) {
  54. preUMsgHook = HookAddPChar(modinfo->handle, MYHEWK, -1, allowctcp_opers_preusermsg); // Add a hook with higher priority than noctcp_preusermsg()
  55. // Request moddata for storing the actual overrides
  56. ModDataInfo mreq;
  57. memset(&mreq, 0, sizeof(mreq));
  58. mreq.type = MODDATATYPE_CLIENT; // Apply to users only (CLIENT actually includes servers but we'll disregard that here =])
  59. mreq.name = "allowctcp_opers"; // Name it
  60. mreq.free = override_md_free; // Function to free 'em
  61. COvrMDI = ModDataAdd(modinfo->handle, mreq);
  62. IsMDErr(COvrMDI, m_allowctcp_opers, modinfo);
  63. allowctcp_opersMI = modinfo; // Store module info yo
  64. return MOD_SUCCESS; // Let MOD_LOAD handle errors and shyte
  65. }
  66. // Actually load the module here
  67. MOD_LOAD(m_allowctcp_opers) {
  68. // Check if module handle is available, also check for hooks that weren't added for some raisin
  69. if(ModuleGetError(allowctcp_opersMI->handle) != MODERR_NOERROR || !preUMsgHook) {
  70. // Display error string kek
  71. config_error("A critical error occurred when loading module %s: %s", MOD_HEADER(m_allowctcp_opers).name, ModuleGetErrorStr(allowctcp_opersMI->handle));
  72. return MOD_FAILED; // No good
  73. }
  74. return MOD_SUCCESS; // We good
  75. }
  76. // Called on unload/rehash obv
  77. MOD_UNLOAD(m_allowctcp_opers) {
  78. return MOD_SUCCESS; // We good
  79. }
  80. // Actual hewk function m8
  81. char *allowctcp_opers_preusermsg(aClient *sptr, aClient *to, char *text, int notice) {
  82. /* Arguments depend on hook type used obv
  83. **
  84. ** sptr: Pointer to user executing command
  85. ** to: Same sort of pointer to the target client
  86. ** text: Actual text obv
  87. ** notice: Was it a notice?
  88. */
  89. cOverride *covr = NULL; // To store the matching entry for een bit
  90. if(MyClient(sptr) && IsACTCP(text) && IsOper(sptr)) { // If an oper is sending a CTCP
  91. if(has_user_mode(sptr, 'T') && !IsOper(to)) { // If oper has +T themselves and target is not an oper (override is not necessary in that case ;])
  92. check_stale_overrides(sptr); // Ayyy lmao
  93. add_override(sptr, to); // Add override for the reply yo
  94. }
  95. if(has_user_mode(to, 'T')) { // Target has +T, so bypass that shit
  96. sendto_one(to, ":%s %s %s :%s", sptr->name, (notice ? "NOTICE" : "PRIVMSG"), to->name, text); // Dirty hack goes here lol
  97. return NULL; // Don't process other hooks (i.e. usermodes/noctcp)
  98. }
  99. return text; // No further action required
  100. }
  101. if(has_user_mode(to, 'T') && IsACTCP(text) && IsOper(to)) { // If an oper is receiving a CTCP reply but has +T also
  102. if(MyClient(sptr) && !MyClient(to)) { // Target is on different server, so bypass that shit
  103. sendto_one(to, ":%s %s %s :%s", sptr->name, (notice ? "NOTICE" : "PRIVMSG"), to->name, text); // Dirty hack goes here lol
  104. return NULL; // Don't process other hooks (i.e. usermodes/noctcp)
  105. }
  106. if(MyClient(to)) { // If the target is on our server
  107. check_stale_overrides(to); // Flush stale overrides first
  108. if(IsOper(sptr) || (covr = match_override(to, sptr->id))) { // Checkem override
  109. delete_override(to, covr); // Clear entry first lol
  110. if(MyClient(sptr)) //{ // Only need to bypass here if it's local2local client
  111. sendto_one(to, ":%s %s %s :%s", sptr->name, (notice ? "NOTICE" : "PRIVMSG"), to->name, text); // Ayyy we good
  112. }
  113. else { // No override found, unsolicited CTCP
  114. if(!notice)
  115. sendto_one(sptr, err_str(ERR_NOCTCP), me.name, sptr->name, to->name);
  116. return NULL; // Drop 'em
  117. }
  118. }
  119. }
  120. return text; // Process normally
  121. }
  122. void override_md_free(ModData *md) {
  123. if(md->ptr) { // r u insaiyan?
  124. cOverride *covrList, *covr, *next; // Sum iter8ors lol
  125. covrList = md->ptr; // Get pointur to head of teh list
  126. for(covr = covrList; covr; covr = next) { // Let's do all entries yo
  127. next = covr->next; // Get next entry in advance lol
  128. free_override(covr); // Free 'em imo
  129. }
  130. md->ptr = NULL; // Shit rip's if we don't kek
  131. }
  132. }
  133. void check_stale_overrides(aClient *sptr) {
  134. int ping; // Expiration time is the same as ping timeouts ;]
  135. cOverride *covrList, *covr, *next; // Sum iter8ors lol
  136. if(!sptr || !sptr->local) // r u insaiyan?
  137. return;
  138. if((covrList = moddata_client(sptr, COvrMDI).ptr)) { // One of the for loops MIGHT have cleared the entire list ;]
  139. #if BACKPORT_CONNECTTIMEOUT
  140. ping = (sptr->local->class ? sptr->local->class->pingfreq : CONNECTTIMEOUT);
  141. #else
  142. ping = (sptr->local->class ? sptr->local->class->pingfreq : iConf.handshake_timeout);
  143. #endif
  144. for(covr = covrList; covr; covr = next) { // Iterate em lol
  145. next = covr->next; // Next one imo
  146. if(ping > (TStime() - sptr->local->lasttime)) // Still good
  147. continue;
  148. // Doubly linked lists ftw yo
  149. if(covr->prev) // Is anything but the FIRST entry
  150. covr->prev->next = covr->next; // Previous entry should skip over dis one
  151. else { // Is the first entry
  152. moddata_client(sptr, COvrMDI).ptr = covr->next; // So make the moddata thingy point to the second one
  153. covrList = covr->next; // Really just for the if below (outside the lewp) =]
  154. }
  155. if(covr->next) // If anything but the LAST entry
  156. covr->next->prev = covr->prev; // Next entry should skip over dis one
  157. next = covr->next; // Next one imo
  158. free_override(covr); // Free 'em lol
  159. }
  160. if(!covrList) // We empty nao?
  161. moddata_client(sptr, COvrMDI).ptr = NULL; // Cuz inb4ripperoni
  162. }
  163. }
  164. cOverride *match_override(aClient *sptr, char *uid) {
  165. cOverride *covrList, *covr; // Some iter80rs lol
  166. if(!sptr || !uid || !uid[0]) // Sanity checks
  167. return NULL; // Lolnope
  168. if((covrList = moddata_client(sptr, COvrMDI).ptr)) { // Something st0red?
  169. for(covr = covrList; covr; covr = covr->next) { // Iter8 em
  170. if(!strcmp(covr->uid, uid)) // Checkem UID
  171. return covr; // Gottem
  172. }
  173. }
  174. return NULL; // Loln0pe
  175. }
  176. void add_override(aClient *sptr, aClient *to) {
  177. cOverride *covrList, *last, *cur; // Sum iterators famalam
  178. cOverride *covr; // New entry etc
  179. if(!sptr || !to || !to->id) // Sanity checks
  180. return; // kbai
  181. covr = (cOverride *)MyMallocEx(sizeof(cOverride)); // Alloc8 new entry pls
  182. covr->uid = strdup(to->id); // Set 'em UID
  183. covr->next = NULL; // Inb4rip
  184. covr->prev = NULL; // ditt0
  185. covr->when = TStime(); // Set timestamp lol
  186. if(!(covrList = moddata_client(sptr, COvrMDI).ptr)) { // One of the for loops MIGHT have cleared the entire list ;]
  187. moddata_client(sptr, COvrMDI).ptr = covr; // Necessary to properly st0re that shit
  188. return; // We good
  189. }
  190. // Dirty shit to get the last entry lol
  191. for(cur = covrList; cur; cur = cur->next)
  192. last = cur; // cur will end up pointing to NULL, so let's use the entry just before ;];]
  193. covr->prev = last; // The new entry's prev should point to the actual last entry
  194. last->next = covr; // And the last entry's next should be the new one obv =]
  195. }
  196. void delete_override(aClient *sptr, cOverride *covr) {
  197. cOverride *covrList, *last; // Sum iter8ors lol
  198. if(!covr || !covr->uid) // r u insaiyan?
  199. return;
  200. if(sptr && (covrList = moddata_client(sptr, COvrMDI).ptr)) { // One of the for loops MIGHT have cleared the entire list ;]
  201. for(last = covrList; last; last = last->next) { // Iterate em lol
  202. if(last == covr) { // We gottem match?
  203. // Doubly linked lists ftw yo
  204. if(last->prev) // Is anything but the FIRST entry
  205. last->prev->next = last->next; // Previous entry should skip over dis one
  206. else { // Is the first entry
  207. moddata_client(sptr, COvrMDI).ptr = last->next; // So make the moddata thingy point to the second one
  208. covrList = last->next; // Really just for the if below =]
  209. }
  210. if(last->next) // If anything but the LAST entry
  211. last->next->prev = last->prev; // Next entry should skip over dis one
  212. free_override(last); // Free 'em lol
  213. break; // Gtfo imo tbh famlammlmflma
  214. }
  215. }
  216. if(!covrList) // We empty nao?
  217. moddata_client(sptr, COvrMDI).ptr = NULL; // Cuz inb4ripperoni
  218. }
  219. }
  220. void free_override(cOverride *covr) {
  221. if(!covr) // LOLNOPE
  222. return;
  223. if(covr->uid) // Sanity cheqq lol
  224. free(covr->uid); // Free
  225. free(covr); // 'em
  226. }
  227. // Ripped from src/modules/usermodes/noctcp.c =]
  228. static int IsACTCP(char *s) {
  229. if(!s)
  230. return 0;
  231. if((*s == '\001') && strncmp(&s[1], "ACTION ", 7) && strncmp(&s[1], "DCC ", 4))
  232. return 1;
  233. return 0;
  234. }
  235. #if BACKPORT
  236. // Both functions were ripped from src/umodes.c in U4.0.12 =]
  237. long find_user_mode(char flag) {
  238. int i;
  239. for (i = 0; i < UMODETABLESZ; i++) {
  240. if((Usermode_Table[i].flag == flag) && !(Usermode_Table[i].unloaded))
  241. return Usermode_Table[i].mode;
  242. }
  243. return 0;
  244. }
  245. int has_user_mode(aClient *acptr, char mode) {
  246. long m = find_user_mode(mode);
  247. if(acptr->umodes & m)
  248. return 1; // We gottem
  249. return 0;
  250. }
  251. #endif