ZNC Modules for SQLite and MySQL
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1341 lines
53 KiB

13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
  1. //
  2. // logmysql.cpp
  3. // LogSQL
  4. //
  5. // Created by Mr. Gecko on 2/10/12.
  6. // Copyright (c) 2012 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/
  7. //
  8. // Permission to use, copy, modify, and/or distribute this software for any purpose
  9. // with or without fee is hereby granted, provided that the above copyright notice
  10. // and this permission notice appear in all copies.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  13. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  14. // FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
  15. // OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
  16. // DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  17. // ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  18. //
  19. #include <znc/Chan.h>
  20. #include <znc/User.h>
  21. #include <znc/Client.h>
  22. #include <znc/IRCNetwork.h>
  23. #include <znc/Modules.h>
  24. #include <time.h>
  25. #import <mysql.h>
  26. class CLogMySQL : public CModule {
  27. public:
  28. MODCONSTRUCTOR(CLogMySQL) {
  29. AddHelpCommand();
  30. AddCommand("Host", static_cast<CModCommand::ModCmdFunc>(&CLogMySQL::HostCommand), "[a-z0-9.]+", "MySQL host.");
  31. AddCommand("Port", static_cast<CModCommand::ModCmdFunc>(&CLogMySQL::PortCommand), "[0-9]+", "MySQL port.");
  32. AddCommand("Username", static_cast<CModCommand::ModCmdFunc>(&CLogMySQL::UsernameCommand), "[A-Za-z0-9]+", "MySQL username.");
  33. AddCommand("Password", static_cast<CModCommand::ModCmdFunc>(&CLogMySQL::PasswordCommand), "[A-Za-z0-9]+", "MySQL password.");
  34. AddCommand("DatabaseName", static_cast<CModCommand::ModCmdFunc>(&CLogMySQL::DatabaseNameCommand), "[a-z0-9]+", "MySQL database name.");
  35. AddCommand("Connect", static_cast<CModCommand::ModCmdFunc>(&CLogMySQL::ConnectCommand), "", "Reconnect to the MySQL database.");
  36. AddCommand("Replay", static_cast<CModCommand::ModCmdFunc>(&CLogMySQL::ReplayCommand), "[0-9]*", "Play back the messages received.");
  37. AddCommand("ReplayAll", static_cast<CModCommand::ModCmdFunc>(&CLogMySQL::ReplayAllCommand), "[1|0]", "Replay all messages stored.");
  38. AddCommand("AutoReplay", static_cast<CModCommand::ModCmdFunc>(&CLogMySQL::AutoReplayCommand), "[1|0]", "Replay on connect.");
  39. AddCommand("LogLimit", static_cast<CModCommand::ModCmdFunc>(&CLogMySQL::LogLimitCommand), "[0-9]+", "Limit the amount of items to store into the log.");
  40. AddCommand("LogLevel", static_cast<CModCommand::ModCmdFunc>(&CLogMySQL::LogLevelCommand), "[0-4]", "Log level.");
  41. AddCommand("AddIgnore", static_cast<CModCommand::ModCmdFunc>(&CLogMySQL::AddIgnoreCommand), "Type[nick|chan] Target", "Add to ignore list.");
  42. AddCommand("RemoveIgnore", static_cast<CModCommand::ModCmdFunc>(&CLogMySQL::RemoveIgnoreCommand), "Type[nick|chan] Target", "Remove from ignore list.");
  43. AddCommand("IgnoreList", static_cast<CModCommand::ModCmdFunc>(&CLogMySQL::IgnoreListCommand), "", "View what is currently ignored.");
  44. AddCommand("DoNotTrack", static_cast<CModCommand::ModCmdFunc>(&CLogMySQL::DoNotTrack), "", "Adds the current session to the do not track disconnect list.");
  45. }
  46. void HostCommand(const CString &sLine) {
  47. CString sArgs = sLine.Token(1, true);
  48. bool help = sArgs.Equals("HELP");
  49. if (help) {
  50. PutModule("The MySQL host. Can be an IP Address or Hostname. 127.0.0.1 or localhost is for the current server.");
  51. } else if (!sArgs.empty()) {
  52. SetNV("host",sArgs);
  53. }
  54. CString now = (sArgs.empty() || help ? "" : "now ");
  55. PutModule("Host is "+now+"set to: "+GetNV("host"));
  56. }
  57. void PortCommand(const CString &sLine) {
  58. CString sArgs = sLine.Token(1, true);
  59. bool help = sArgs.Equals("HELP");
  60. if (help) {
  61. PutModule("The MySQL port. Usually will be 3306.");
  62. } else if (!sArgs.empty()) {
  63. CString result;
  64. unsigned int port = strtoul(sArgs.c_str(), NULL, 10);
  65. char portStr[20];
  66. snprintf(portStr, sizeof(portStr), "%u", port);
  67. result = portStr;
  68. SetNV("port",result);
  69. }
  70. CString now = (sArgs.empty() || help ? "" : "now ");
  71. PutModule("Port is "+now+"set to: "+GetNV("port"));
  72. }
  73. void UsernameCommand(const CString &sLine) {
  74. CString sArgs = sLine.Token(1, true);
  75. bool help = sArgs.Equals("HELP");
  76. if (help) {
  77. PutModule("Your MySQL user username.");
  78. } else if (!sArgs.empty()) {
  79. SetNV("username",sArgs);
  80. }
  81. CString now = (sArgs.empty() || help ? "" : "now ");
  82. PutModule("Username is "+now+"set to: "+GetNV("username"));
  83. }
  84. void PasswordCommand(const CString &sLine) {
  85. CString sArgs = sLine.Token(1, true);
  86. bool help = sArgs.Equals("HELP");
  87. if (help) {
  88. PutModule("Your MySQL user password.");
  89. } else if (!sArgs.empty()) {
  90. SetNV("password",sArgs);
  91. }
  92. CString status = (GetNV("password").empty() ? "blank" : "set");
  93. CString now = (sArgs.empty() || help ? "" : "now ");
  94. PutModule("Password is "+now+status);
  95. }
  96. void DatabaseNameCommand(const CString &sLine) {
  97. CString sArgs = sLine.Token(1, true);
  98. bool help = sArgs.Equals("HELP");
  99. if (help) {
  100. PutModule("The MySQL database name which contains the messages and settings tables.");
  101. } else if (!sArgs.empty()) {
  102. SetNV("databaseName",sArgs);
  103. }
  104. CString now = (sArgs.empty() || help ? "" : "now ");
  105. PutModule("Database Name is "+now+"set to: "+GetNV("databaseName"));
  106. }
  107. void ConnectCommand(const CString &sLine) {
  108. MySQLConnect();
  109. if (databaseConnected)
  110. PutModule("Database is now connected");
  111. else
  112. PutModule("Unable to connect to database. Check configuration.");
  113. }
  114. void ReplayCommand(const CString &sLine) {
  115. CString sArgs = sLine.Token(1, true);
  116. if (!sArgs.empty()) {
  117. Replay(sArgs.ToInt());
  118. } else {
  119. Replay();
  120. }
  121. PutModule("Replayed");
  122. }
  123. void ReplayAllCommand(const CString &sLine) {
  124. CString sArgs = sLine.Token(1, true);
  125. bool help = sArgs.Equals("HELP");
  126. if (help) {
  127. PutModule("On: All logs stored will be replayed.");
  128. PutModule("Off: Only logs since the last time you connected will be replayed.");
  129. } else if (!sArgs.empty()) {
  130. replayAll = sArgs.ToBool();
  131. SetSetting("replayAll", (replayAll ? "1" : "0"));
  132. }
  133. CString status = (replayAll ? "On" : "Off");
  134. CString now = (sArgs.empty() || help ? "" : "now ");
  135. PutModule("ReplayAll is "+now+"set to: "+status);
  136. }
  137. void AutoReplayCommand(const CString &sLine) {
  138. CString sArgs = sLine.Token(1, true);
  139. bool help = sArgs.Equals("HELP");
  140. if (help) {
  141. PutModule("On: Replay on connect.");
  142. PutModule("Off: Require replay command to replay.");
  143. } else if (!sArgs.empty()) {
  144. autoReplay = sArgs.ToBool();
  145. SetSetting("autoReplay", (autoReplay ? "1" : "0"));
  146. }
  147. CString status = (autoReplay ? "On" : "Off");
  148. CString now = (sArgs.empty() || help ? "" : "now ");
  149. PutModule("AutoReplay is "+now+"set to: "+status);
  150. }
  151. void LogLimitCommand(const CString &sLine) {
  152. CString sArgs = sLine.Token(1, true);
  153. bool help = sArgs.Equals("HELP");
  154. CString setting;
  155. if (help) {
  156. PutModule("0: Everything will be kept in database.");
  157. PutModule("1: Everything will be kept in database until replayed.");
  158. PutModule("2+: Limit logs stored in database to limit set.");
  159. } else if (!sArgs.empty()) {
  160. logLimit = strtoul(sArgs.c_str(), NULL, 10);
  161. char limitStr[20];
  162. snprintf(limitStr, sizeof(limitStr), "%lu", logLimit);
  163. setting = limitStr;
  164. SetSetting("logLimit", setting);
  165. }
  166. if (setting.empty()) {
  167. char limitStr[20];
  168. snprintf(limitStr, sizeof(limitStr), "%lu", logLimit);
  169. setting = limitStr;
  170. }
  171. CString now = (sArgs.empty() || help ? "" : "now ");
  172. PutModule("LogLimit is "+now+"set to: "+setting);
  173. }
  174. void LogLevelCommand(const CString &sLine) {
  175. CString sArgs = sLine.Token(1, true);
  176. bool help = sArgs.Equals("HELP");
  177. CString setting;
  178. if (help) {
  179. PutModule("0: Mentions and messages to you.");
  180. PutModule("1: All messages.");
  181. PutModule("2: Actions, Joins/Parts, and Notices.");
  182. PutModule("3: Server wide messages.");
  183. PutModule("4: All messages, actions, joins/parts, and noticies sent by you.");
  184. } else if (!sArgs.empty()) {
  185. logLevel = atoi(sArgs.c_str());
  186. char levelStr[20];
  187. snprintf(levelStr, sizeof(levelStr), "%d", logLevel);
  188. setting = levelStr;
  189. SetSetting("logLevel", setting);
  190. }
  191. if (setting.empty()) {
  192. char levelStr[20];
  193. snprintf(levelStr, sizeof(levelStr), "%d", logLevel);
  194. setting = levelStr;
  195. }
  196. CString now = (sArgs.empty() || help ? "" : "now ");
  197. PutModule("LogLevel is "+now+"set to: "+setting);
  198. }
  199. void AddIgnoreCommand(const CString &sLine) {
  200. CString type = sLine.Token(1);
  201. CString target = sLine.Token(2);
  202. bool help = sLine.Equals("HELP");
  203. if (help) {
  204. PutModule("Inorder to add an ignore, you must choose what type of ignore it is which can ether be a nick or a chan.");
  205. PutModule("Nicks are matched with wildcards against the full mask.");
  206. PutModule("Channels are matched by #channel which can contain wildcards.");
  207. } else if (!type.empty() && !target.empty()) {
  208. if (type.Equals("nick"))
  209. type = "nick";
  210. else if (type.Equals("chan") || type.Equals("channel"))
  211. type = "chan";
  212. else
  213. type = "";
  214. if (type.empty()) {
  215. PutModule("Unknown type. If you need help, type \"AddIgnore help\".");
  216. return;
  217. }
  218. if (AddIgnore(type, target))
  219. PutModule("Successfully added \""+target+"\" to the ignore list.");
  220. else
  221. PutModule("Failed, maybe it already existed?");
  222. } else {
  223. PutModule("If you need help, type \"AddIgnore help\".");
  224. }
  225. }
  226. void RemoveIgnoreCommand(const CString &sLine) {
  227. CString type = sLine.Token(1);
  228. CString target = sLine.Token(2);
  229. bool help = sLine.Equals("HELP");
  230. if (help) {
  231. PutModule("Inorder to remove an ignore, you must specify the type and the exact pattren used to add it. If you need to find what currently exists, type \"IgnoreList\".");
  232. } else if (!type.empty() && !target.empty()) {
  233. if (type.Equals("nick"))
  234. type = "nick";
  235. else if (type.Equals("chan") || type.Equals("channel"))
  236. type = "chan";
  237. else
  238. type = "";
  239. if (type.empty()) {
  240. PutModule("Unknown type. If you need help, type \"RemoveIgnore help\".");
  241. return;
  242. }
  243. if (RemoveIgnore(type, target))
  244. PutModule("Successfully removed \""+target+"\" from the ignore list.");
  245. else
  246. PutModule("Failed, maybe it does not exist?");
  247. } else {
  248. PutModule("If you need help, type \"RemoveIgnore help\".");
  249. }
  250. }
  251. void IgnoreListCommand(const CString &sLine) {
  252. if (nickIgnoreList.size()==0) {
  253. PutModule("The nick ignore list is currently empty.");
  254. } else {
  255. PutModule("Nick ignore list contains:");
  256. for (std::vector<CString>::iterator it=nickIgnoreList.begin(); it<nickIgnoreList.end(); it++) {
  257. PutModule(*it);
  258. }
  259. }
  260. PutModule("---");
  261. if (chanIgnoreList.size()==0) {
  262. PutModule("The channel ignore list is currently empty.");
  263. } else {
  264. PutModule("Channel ignore list contains:");
  265. for (std::vector<CString>::iterator it=chanIgnoreList.begin(); it<chanIgnoreList.end(); it++) {
  266. PutModule(*it);
  267. }
  268. }
  269. }
  270. void DoNotTrack(const CString &sLine) {
  271. bool tracking = true;
  272. for (std::vector<CClient *>::iterator it=doNotTrackClient.begin(); it<doNotTrackClient.end(); it++) {
  273. if (*it==m_pClient) {
  274. tracking = false;
  275. break;
  276. }
  277. }
  278. if (tracking) {
  279. doNotTrackClient.push_back(m_pClient);
  280. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Added session to do not track list.");
  281. } else {
  282. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Already added session to do not track list.");
  283. }
  284. }
  285. virtual bool OnLoad(const CString& sArgs, CString& sMessage) {
  286. connected = true;
  287. databaseConnected = false;
  288. MySQLConnect();
  289. return true;
  290. }
  291. virtual ~CLogMySQL() {
  292. if (databaseConnected) {
  293. databaseConnected = false;
  294. mysql_close(database);
  295. database = NULL;
  296. }
  297. }
  298. void MySQLConnect() {
  299. CString host = GetNV("host");
  300. CString port = GetNV("port");
  301. if (port.empty()) {
  302. port = "3306";
  303. SetNV("port",port);
  304. }
  305. CString username = GetNV("username");
  306. CString password = GetNV("password");
  307. CString databaseName = GetNV("databaseName");
  308. if (!host.empty() && !username.empty() && !databaseName.empty()) {
  309. if (databaseConnected) {
  310. databaseConnected = false;
  311. mysql_close(database);
  312. database = NULL;
  313. }
  314. database = mysql_init(NULL);
  315. if (database!=NULL) {
  316. void *theRet = mysql_real_connect(database, host.c_str(), username.c_str(), password.c_str(), databaseName.c_str(), (unsigned int)strtoul(port.c_str(), NULL, 10), MYSQL_UNIX_ADDR, CLIENT_COMPRESS);
  317. databaseConnected = (theRet==database);
  318. if (databaseConnected) {
  319. std::cout << "LogMySQL: Database connected.\n";
  320. MYSQL_RES *settings = mysql_list_tables(database, "settings");
  321. if (mysql_num_rows(settings)==0) {
  322. MYSQL_STMT *statement = mysql_stmt_init(database);
  323. int status = mysql_stmt_prepare(statement, "CREATE TABLE `settings` (`name` text,`value` text)", 50);
  324. if (status==0) {
  325. mysql_stmt_execute(statement);
  326. mysql_stmt_close(statement);
  327. SetSetting("replayAll","0");
  328. SetSetting("autoReplay","1");
  329. SetSetting("logLimit","1");
  330. SetSetting("logLevel","1");
  331. SetSetting("version","2");
  332. }
  333. }
  334. if (settings!=NULL)
  335. mysql_free_result(settings);
  336. MYSQL_RES *messages = mysql_list_tables(database, "messages");
  337. if (mysql_num_rows(messages)==0) {
  338. MYSQL_STMT *statement = mysql_stmt_init(database);
  339. int status = mysql_stmt_prepare(statement, "CREATE TABLE `messages` (`rowid` int UNSIGNED AUTO_INCREMENT,`target` text,`nick` text,`type` text,`message` longblob,`time` decimal(20,5) UNSIGNED,PRIMARY KEY (`rowid`))", 170);
  340. if (status==0) {
  341. mysql_stmt_execute(statement);
  342. mysql_stmt_close(statement);
  343. }
  344. }
  345. if (messages!=NULL)
  346. mysql_free_result(messages);
  347. MYSQL_RES *ignorelist = mysql_list_tables(database, "ignorelist");
  348. if (mysql_num_rows(ignorelist)==0) {
  349. MYSQL_STMT *statement = mysql_stmt_init(database);
  350. int status = mysql_stmt_prepare(statement, "CREATE TABLE `ignorelist` (`rowid` int UNSIGNED AUTO_INCREMENT,`type` text,`target` text,PRIMARY KEY (`rowid`))", 111);
  351. if (status==0) {
  352. mysql_stmt_execute(statement);
  353. mysql_stmt_close(statement);
  354. }
  355. }
  356. if (ignorelist!=NULL)
  357. mysql_free_result(ignorelist);
  358. unsigned long version = strtoul(GetSetting("version").c_str(), NULL, 10);
  359. if (version==0) {
  360. SetSetting("version","1");
  361. version = 1;
  362. }
  363. if (version==1) {
  364. SetSetting("autoReplay","1");
  365. SetSetting("version","2");
  366. version = 2;
  367. }
  368. replayAll = atoi(GetSetting("replayAll").c_str());
  369. autoReplay = atoi(GetSetting("autoReplay").c_str());
  370. logLimit = strtoul(GetSetting("logLimit").c_str(), NULL, 10);
  371. logLevel = atoi(GetSetting("logLevel").c_str());
  372. } else {
  373. std::cout << "LogMySQL: Database unable to connect.\n";
  374. }
  375. }
  376. }
  377. }
  378. CString GetUNIXTime() {
  379. struct timeval time;
  380. gettimeofday(&time, NULL);
  381. double microtime = (double)(time.tv_sec + (time.tv_usec/1000000.00));
  382. char timeStr[25];
  383. snprintf(timeStr, sizeof(timeStr), "%f", microtime);
  384. return timeStr;
  385. }
  386. void AddMessage(const CString& target, const CString& nick, const CString& type, const CString& message) {
  387. if (!databaseConnected)
  388. return;
  389. if (IsIgnored("nick",nick) || (target.Left(1).Equals("#") && IsIgnored("chan",target)))
  390. return;
  391. MYSQL_STMT *statement = mysql_stmt_init(database);
  392. int status = mysql_stmt_prepare(statement, "INSERT INTO `messages` (`target`, `nick`, `type`, `message`, `time`) VALUES (?,?,?,?,?)", 87);
  393. if (status!=0)
  394. return;
  395. MYSQL_BIND bind[5];
  396. memset(bind, 0, sizeof(bind));
  397. bind[0].buffer_type = MYSQL_TYPE_STRING;
  398. bind[0].buffer = (void*)target.c_str();
  399. bind[0].buffer_length = target.length();
  400. bind[0].is_null = (my_bool *)0;
  401. bind[1].buffer_type = MYSQL_TYPE_STRING;
  402. bind[1].buffer = (void*)nick.c_str();
  403. bind[1].buffer_length = nick.length();
  404. bind[1].is_null = (my_bool *)0;
  405. bind[2].buffer_type = MYSQL_TYPE_STRING;
  406. bind[2].buffer = (void*)type.c_str();
  407. bind[2].buffer_length = type.length();
  408. bind[2].is_null = (my_bool *)0;
  409. bind[3].buffer_type = MYSQL_TYPE_STRING;
  410. bind[3].buffer = (void*)message.c_str();
  411. bind[3].buffer_length = message.length();
  412. bind[3].is_null = (my_bool *)0;
  413. CString time = GetUNIXTime();
  414. bind[4].buffer_type = MYSQL_TYPE_STRING;
  415. bind[4].buffer = (void*)time.c_str();
  416. bind[4].buffer_length = time.length();
  417. bind[4].is_null = (my_bool *)0;
  418. status = mysql_stmt_bind_param(statement, bind);
  419. if (status!=0) {
  420. mysql_stmt_close(statement);
  421. return;
  422. }
  423. mysql_stmt_execute(statement);
  424. mysql_stmt_close(statement);
  425. if (logLimit>=2) {
  426. statement = mysql_stmt_init(database);
  427. status = mysql_stmt_prepare(statement, "SELECT COUNT(*) FROM `messages`", 31);
  428. if (status!=0)
  429. return;
  430. status = mysql_stmt_execute(statement);
  431. if (status!=0) {
  432. mysql_stmt_close(statement);
  433. return;
  434. }
  435. MYSQL_RES *result = mysql_stmt_result_metadata(statement);
  436. unsigned int dataCount = mysql_num_fields(result);
  437. if (dataCount!=1) {
  438. mysql_free_result(result);
  439. mysql_stmt_close(statement);
  440. std::cout << "LogMySQL: We are only suppose to receive 1 field. We received the count of " << dataCount << " fields.\n";
  441. return;
  442. }
  443. MYSQL_FIELD *fields = mysql_fetch_fields(result);
  444. unsigned long length;
  445. char countData[fields[0].length];
  446. MYSQL_BIND results[1];
  447. memset(results, 0, sizeof(results));
  448. results[0].buffer_type = MYSQL_TYPE_STRING;
  449. results[0].buffer = (void *)countData;
  450. results[0].buffer_length = fields[0].length;
  451. results[0].length = &length;
  452. status = mysql_stmt_bind_result(statement, results);
  453. if (status!=0) {
  454. mysql_free_result(result);
  455. mysql_stmt_close(statement);
  456. return;
  457. }
  458. status = mysql_stmt_fetch(statement);
  459. if (status!=0) {
  460. mysql_free_result(result);
  461. mysql_stmt_close(statement);
  462. return;
  463. }
  464. CString countString = CString(countData, length);
  465. unsigned long count = strtoul(countString.c_str(), NULL, 10);
  466. if (count<=logLimit)
  467. count = 0;
  468. else
  469. count = count-logLimit;
  470. mysql_free_result(result);
  471. mysql_stmt_close(statement);
  472. if (count!=0) {
  473. statement = mysql_stmt_init(database);
  474. char *queryStr = (char *)malloc(73);
  475. snprintf(queryStr, 73, "SELECT `rowid` FROM `messages` ORDER BY `time` LIMIT %lu", count);
  476. status = mysql_stmt_prepare(statement, queryStr, strlen(queryStr));
  477. free(queryStr);
  478. if (status!=0)
  479. return;
  480. status = mysql_stmt_execute(statement);
  481. if (status!=0) {
  482. mysql_stmt_close(statement);
  483. return;
  484. }
  485. result = mysql_stmt_result_metadata(statement);
  486. dataCount = mysql_num_fields(result);
  487. if (dataCount!=1) {
  488. mysql_free_result(result);
  489. mysql_stmt_close(statement);
  490. std::cout << "LogMySQL: We are only suppose to receive 1 field. We received the count of " << dataCount << " fields.\n";
  491. return;
  492. }
  493. fields = mysql_fetch_fields(result);
  494. char rowidData[fields[0].length];
  495. memset(results, 0, sizeof(results));
  496. results[0].buffer_type = MYSQL_TYPE_STRING;
  497. results[0].buffer = (void *)rowidData;
  498. results[0].buffer_length = fields[0].length;
  499. results[0].length = &length;
  500. status = mysql_stmt_bind_result(statement, results);
  501. if (status!=0) {
  502. mysql_free_result(result);
  503. mysql_stmt_close(statement);
  504. return;
  505. }
  506. while (true) {
  507. status = mysql_stmt_fetch(statement);
  508. if (status!=0)
  509. break;
  510. CString rowid = CString(rowidData, length);
  511. MYSQL_STMT *statement2 = mysql_stmt_init(database);
  512. status = mysql_stmt_prepare(statement2, "DELETE FROM `messages` WHERE `rowid`=?", 38);
  513. if (status!=0)
  514. continue;
  515. MYSQL_BIND bind2[1];
  516. memset(bind, 0, sizeof(bind));
  517. bind2[0].buffer_type = MYSQL_TYPE_STRING;
  518. bind2[0].buffer = (void*)rowid.c_str();
  519. bind2[0].buffer_length = rowid.length();
  520. bind2[0].is_null = (my_bool *)0;
  521. status = mysql_stmt_bind_param(statement2, bind2);
  522. if (status!=0) {
  523. mysql_stmt_close(statement2);
  524. continue;
  525. }
  526. mysql_stmt_execute(statement2);
  527. mysql_stmt_close(statement2);
  528. }
  529. mysql_free_result(result);
  530. mysql_stmt_close(statement);
  531. }
  532. }
  533. }
  534. void SetSetting(const CString& name, const CString& value) {
  535. if (!databaseConnected)
  536. return;
  537. bool exists = false;
  538. MYSQL_STMT *statement = mysql_stmt_init(database);
  539. int status = mysql_stmt_prepare(statement, "SELECT `value` FROM `settings` WHERE `name`=?", 45);
  540. if (status==0) {
  541. MYSQL_BIND bind[1];
  542. memset(bind, 0, sizeof(bind));
  543. bind[0].buffer_type = MYSQL_TYPE_STRING;
  544. bind[0].buffer = (void*)name.c_str();
  545. bind[0].buffer_length = name.length();
  546. bind[0].is_null = (my_bool *)0;
  547. status = mysql_stmt_bind_param(statement, bind);
  548. if (status==0) {
  549. mysql_stmt_execute(statement);
  550. status = mysql_stmt_fetch(statement);
  551. if (status==0) {
  552. exists = true;
  553. }
  554. }
  555. mysql_stmt_close(statement);
  556. }
  557. if (exists) {
  558. statement = mysql_stmt_init(database);
  559. status = mysql_stmt_prepare(statement, "UPDATE `settings` SET `value`=? WHERE `name`=?", 46);
  560. if (status!=0)
  561. return;
  562. MYSQL_BIND bind[2];
  563. memset(bind, 0, sizeof(bind));
  564. bind[0].buffer_type = MYSQL_TYPE_STRING;
  565. bind[0].buffer = (void*)value.c_str();
  566. bind[0].buffer_length = value.length();
  567. bind[0].is_null = (my_bool *)0;
  568. bind[1].buffer_type = MYSQL_TYPE_STRING;
  569. bind[1].buffer = (void*)name.c_str();
  570. bind[1].buffer_length = name.length();
  571. bind[1].is_null = (my_bool *)0;
  572. status = mysql_stmt_bind_param(statement, bind);
  573. if (status!=0) {
  574. mysql_stmt_close(statement);
  575. return;
  576. }
  577. mysql_stmt_execute(statement);
  578. mysql_stmt_close(statement);
  579. } else {
  580. statement = mysql_stmt_init(database);
  581. status = mysql_stmt_prepare(statement, "INSERT INTO `settings` (`name`,`value`) VALUES (?,?)", 52);
  582. if (status!=0)
  583. return;
  584. MYSQL_BIND bind[2];
  585. memset(bind, 0, sizeof(bind));
  586. bind[0].buffer_type = MYSQL_TYPE_STRING;
  587. bind[0].buffer = (void*)name.c_str();
  588. bind[0].buffer_length = name.length();
  589. bind[0].is_null = (my_bool *)0;
  590. bind[1].buffer_type = MYSQL_TYPE_STRING;
  591. bind[1].buffer = (void*)value.c_str();
  592. bind[1].buffer_length = value.length();
  593. bind[1].is_null = (my_bool *)0;
  594. status = mysql_stmt_bind_param(statement, bind);
  595. if (status!=0) {
  596. mysql_stmt_close(statement);
  597. return;
  598. }
  599. mysql_stmt_execute(statement);
  600. mysql_stmt_close(statement);
  601. }
  602. }
  603. CString GetSetting(const CString& name) {
  604. CString stringValue;
  605. if (!databaseConnected)
  606. return stringValue;
  607. MYSQL_STMT *statement = mysql_stmt_init(database);
  608. int status = mysql_stmt_prepare(statement, "SELECT `value` FROM `settings` WHERE `name`=?", 45);
  609. if (status!=0)
  610. return stringValue;
  611. MYSQL_BIND bind[1];
  612. memset(bind, 0, sizeof(bind));
  613. bind[0].buffer_type = MYSQL_TYPE_STRING;
  614. bind[0].buffer = (void*)name.c_str();
  615. bind[0].buffer_length = name.length();
  616. bind[0].is_null = (my_bool *)0;
  617. status = mysql_stmt_bind_param(statement, bind);
  618. if (status!=0) {
  619. mysql_stmt_close(statement);
  620. return stringValue;
  621. }
  622. status = mysql_stmt_execute(statement);
  623. if (status!=0) {
  624. mysql_stmt_close(statement);
  625. return stringValue;
  626. }
  627. MYSQL_RES *result = mysql_stmt_result_metadata(statement);
  628. unsigned int dataCount = mysql_num_fields(result);
  629. if (dataCount!=1) {
  630. mysql_free_result(result);
  631. mysql_stmt_close(statement);
  632. std::cout << "LogMySQL: Settings are only suppose to return 1 as the field count. We received the count of " << dataCount << " fields.\n";
  633. return stringValue;
  634. }
  635. MYSQL_FIELD *fields = mysql_fetch_fields(result);
  636. unsigned long length;
  637. char valueData[fields[0].length];
  638. MYSQL_BIND results[1];
  639. memset(results, 0, sizeof(results));
  640. results[0].buffer_type = MYSQL_TYPE_STRING;
  641. results[0].buffer = (void *)valueData;
  642. results[0].buffer_length = fields[0].length;
  643. results[0].length = &length;
  644. status = mysql_stmt_bind_result(statement, results);
  645. if (status!=0) {
  646. mysql_free_result(result);
  647. mysql_stmt_close(statement);
  648. return stringValue;
  649. }
  650. while (true) {
  651. status = mysql_stmt_fetch(statement);
  652. if (status!=0)
  653. break;
  654. stringValue = CString(valueData, length);
  655. break;
  656. }
  657. mysql_free_result(result);
  658. mysql_stmt_close(statement);
  659. return stringValue;
  660. }
  661. void UpdateIgnoreLists() {
  662. if (!databaseConnected)
  663. return;
  664. nickIgnoreList.clear();
  665. MYSQL_STMT *statement = mysql_stmt_init(database);
  666. int status = mysql_stmt_prepare(statement, "SELECT `target` FROM `ignorelist` WHERE `type`='nick'", 53);
  667. if (status==0) {
  668. status = mysql_stmt_execute(statement);
  669. if (status==0) {
  670. MYSQL_RES *result = mysql_stmt_result_metadata(statement);
  671. unsigned int dataCount = mysql_num_fields(result);
  672. if (dataCount==1) {
  673. MYSQL_FIELD *fields = mysql_fetch_fields(result);
  674. unsigned long length;
  675. char targetData[fields[0].length];
  676. MYSQL_BIND results[1];
  677. memset(results, 0, sizeof(results));
  678. results[0].buffer_type = MYSQL_TYPE_STRING;
  679. results[0].buffer = (void *)targetData;
  680. results[0].buffer_length = fields[0].length;
  681. results[0].length = &length;
  682. status = mysql_stmt_bind_result(statement, results);
  683. if (status==0) {
  684. while (true) {
  685. status = mysql_stmt_fetch(statement);
  686. if (status!=0)
  687. break;
  688. CString ignore = CString(targetData, length);
  689. nickIgnoreList.push_back(ignore);
  690. break;
  691. }
  692. }
  693. }
  694. mysql_free_result(result);
  695. }
  696. mysql_stmt_close(statement);
  697. }
  698. chanIgnoreList.clear();
  699. statement = mysql_stmt_init(database);
  700. status = mysql_stmt_prepare(statement, "SELECT `target` FROM `ignorelist` WHERE `type`='chan'", 53);
  701. if (status==0) {
  702. status = mysql_stmt_execute(statement);
  703. if (status==0) {
  704. MYSQL_RES *result = mysql_stmt_result_metadata(statement);
  705. unsigned int dataCount = mysql_num_fields(result);
  706. if (dataCount==1) {
  707. MYSQL_FIELD *fields = mysql_fetch_fields(result);
  708. unsigned long length;
  709. char targetData[fields[0].length];
  710. MYSQL_BIND results[1];
  711. memset(results, 0, sizeof(results));
  712. results[0].buffer_type = MYSQL_TYPE_STRING;
  713. results[0].buffer = (void *)targetData;
  714. results[0].buffer_length = fields[0].length;
  715. results[0].length = &length;
  716. status = mysql_stmt_bind_result(statement, results);
  717. if (status==0) {
  718. while (true) {
  719. status = mysql_stmt_fetch(statement);
  720. if (status!=0)
  721. break;
  722. CString ignore = CString(targetData, length);
  723. chanIgnoreList.push_back(ignore);
  724. break;
  725. }
  726. }
  727. }
  728. mysql_free_result(result);
  729. }
  730. mysql_stmt_close(statement);
  731. }
  732. }
  733. bool AddIgnore(const CString& type, const CString& target) {
  734. if (!IgnoreExists(type, target)) {
  735. MYSQL_STMT *statement = mysql_stmt_init(database);
  736. int status = mysql_stmt_prepare(statement, "INSERT INTO `ignorelist` (`type`, `target`) VALUES (?,?)", 56);
  737. if (status!=0)
  738. return false;
  739. MYSQL_BIND bind[2];
  740. memset(bind, 0, sizeof(bind));
  741. bind[0].buffer_type = MYSQL_TYPE_STRING;
  742. bind[0].buffer = (void*)type.c_str();
  743. bind[0].buffer_length = type.length();
  744. bind[0].is_null = (my_bool *)0;
  745. bind[1].buffer_type = MYSQL_TYPE_STRING;
  746. bind[1].buffer = (void*)target.c_str();
  747. bind[1].buffer_length = target.length();
  748. bind[1].is_null = (my_bool *)0;
  749. status = mysql_stmt_bind_param(statement, bind);
  750. if (status!=0) {
  751. mysql_stmt_close(statement);
  752. return false;
  753. }
  754. mysql_stmt_execute(statement);
  755. mysql_stmt_close(statement);
  756. if (type.Equals("nick"))
  757. nickIgnoreList.push_back(target);
  758. else if (type.Equals("chan"))
  759. chanIgnoreList.push_back(target);
  760. return true;
  761. }
  762. return false;
  763. }
  764. bool RemoveIgnore(const CString& type, const CString& target) {
  765. if (IgnoreExists(type, target)) {
  766. MYSQL_STMT *statement = mysql_stmt_init(database);
  767. int status = mysql_stmt_prepare(statement, "DELETE FROM `ignorelist` WHERE `type`=? AND `target`=?", 54);
  768. if (status!=0)
  769. return false;
  770. MYSQL_BIND bind[2];
  771. memset(bind, 0, sizeof(bind));
  772. bind[0].buffer_type = MYSQL_TYPE_STRING;
  773. bind[0].buffer = (void*)type.c_str();
  774. bind[0].buffer_length = type.length();
  775. bind[0].is_null = (my_bool *)0;
  776. bind[1].buffer_type = MYSQL_TYPE_STRING;
  777. bind[1].buffer = (void*)target.c_str();
  778. bind[1].buffer_length = target.length();
  779. bind[1].is_null = (my_bool *)0;
  780. status = mysql_stmt_bind_param(statement, bind);
  781. if (status!=0) {
  782. mysql_stmt_close(statement);
  783. return false;
  784. }
  785. mysql_stmt_execute(statement);
  786. mysql_stmt_close(statement);
  787. UpdateIgnoreLists();
  788. return true;
  789. }
  790. return false;
  791. }
  792. bool IgnoreExists(const CString& type, const CString& target) {
  793. if (type.Equals("nick")) {
  794. for (std::vector<CString>::iterator it=nickIgnoreList.begin(); it<nickIgnoreList.end(); it++) {
  795. if (target.Equals(*it))
  796. return true;
  797. }
  798. } else if (type.Equals("chan")) {
  799. for (std::vector<CString>::iterator it=chanIgnoreList.begin(); it<chanIgnoreList.end(); it++) {
  800. if (target.Equals(*it))
  801. return true;
  802. }
  803. }
  804. return false;
  805. }
  806. bool IsIgnored(const CString& type, const CString& target) {
  807. if (type.Equals("nick")) {
  808. for (std::vector<CString>::iterator it=nickIgnoreList.begin(); it<nickIgnoreList.end(); it++) {
  809. if (target.WildCmp(*it))
  810. return true;
  811. }
  812. } else if (type.Equals("chan")) {
  813. for (std::vector<CString>::iterator it=chanIgnoreList.begin(); it<chanIgnoreList.end(); it++) {
  814. if (target.WildCmp(*it))
  815. return true;
  816. }
  817. }
  818. return false;
  819. }
  820. //Server stuff
  821. virtual void OnIRCDisconnected() {
  822. if (connected) {
  823. connected = false;
  824. if (logLevel>=3) {
  825. AddMessage("","","DISCONNECT","");
  826. }
  827. }
  828. }
  829. virtual void OnIRCConnected() {
  830. if (!connected) {
  831. connected = true;
  832. if (logLevel>=3) {
  833. AddMessage("","","CONNECT","");
  834. }
  835. }
  836. }
  837. //User stuff
  838. virtual EModRet OnUserAction(CString& sTarget, CString& sMessage) {
  839. if (logLevel>=4) {
  840. AddMessage(sTarget,m_pNetwork->GetIRCNick().GetNickMask(),"ACTION",sMessage);
  841. }
  842. return CONTINUE;
  843. }
  844. virtual EModRet OnUserMsg(CString& sTarget, CString& sMessage) {
  845. if (logLevel>=4) {
  846. AddMessage(sTarget,m_pNetwork->GetIRCNick().GetNickMask(),"PRIVMSG",sMessage);
  847. }
  848. return CONTINUE;
  849. }
  850. virtual EModRet OnUserNotice(CString& sTarget, CString& sMessage) {
  851. if (logLevel>=4) {
  852. AddMessage(sTarget,m_pNetwork->GetIRCNick().GetNickMask(),"NOTICE",sMessage);
  853. }
  854. return CONTINUE;
  855. }
  856. virtual EModRet OnUserJoin(CString& sChannel, CString& sKey) {
  857. if (logLevel>=4) {
  858. AddMessage(sChannel,m_pNetwork->GetIRCNick().GetNickMask(),"JOIN","");
  859. }
  860. return CONTINUE;
  861. }
  862. virtual EModRet OnUserPart(CString& sChannel, CString& sMessage) {
  863. if (logLevel>=4) {
  864. AddMessage(sChannel,m_pNetwork->GetIRCNick().GetNickMask(),"PART",sMessage);
  865. }
  866. return CONTINUE;
  867. }
  868. virtual EModRet OnUserTopic(CString& sChannel, CString& sTopic) {
  869. if (logLevel>=4) {
  870. AddMessage(sChannel,m_pNetwork->GetIRCNick().GetNickMask(),"TOPIC",sTopic);
  871. }
  872. return CONTINUE;
  873. }
  874. //Other stuff
  875. virtual void OnRawMode(const CNick& OpNick, CChan& Channel, const CString& sModes, const CString& sArgs) {
  876. if (logLevel>=3) {
  877. AddMessage(Channel.GetName(),OpNick.GetNickMask(),"MODE",sModes+" "+sArgs);
  878. }
  879. }
  880. virtual void OnKick(const CNick& OpNick, const CString& sKickedNick, CChan& Channel, const CString& sMessage) {
  881. if (logLevel>=2) {
  882. AddMessage(Channel.GetName(),OpNick.GetNickMask(),"KICK",sKickedNick+" "+sMessage);
  883. }
  884. }
  885. virtual void OnQuit(const CNick& Nick, const CString& sMessage, const std::vector<CChan*>& vChans) {
  886. if (logLevel>=2) {
  887. std::vector<CChan*>::const_iterator it;
  888. for (it=vChans.begin(); it!=vChans.end(); it++) {
  889. CChan& channel = **it;
  890. AddMessage(channel.GetName(),Nick.GetNickMask(),"QUIT",sMessage);
  891. }
  892. }
  893. }
  894. virtual void OnJoin(const CNick& Nick, CChan& Channel) {
  895. if (logLevel>=2) {
  896. AddMessage(Channel.GetName(),Nick.GetNickMask(),"JOIN","");
  897. }
  898. }
  899. virtual void OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage) {
  900. if (logLevel>=2) {
  901. AddMessage(Channel.GetName(),Nick.GetNickMask(),"PART",sMessage);
  902. }
  903. }
  904. virtual void OnNick(const CNick& OldNick, const CString& sNewNick, const std::vector<CChan*>& vChans) {
  905. if (logLevel>=2) {
  906. std::vector<CChan*>::const_iterator it;
  907. for (it=vChans.begin(); it!=vChans.end(); it++) {
  908. CChan& channel = **it;
  909. AddMessage(channel.GetName(),OldNick.GetNickMask(),"NICK",sNewNick);
  910. }
  911. }
  912. }
  913. virtual EModRet OnPrivAction(CNick& Nick, CString& sMessage) {
  914. if (logLevel>=0) {
  915. AddMessage(m_pNetwork->GetCurNick(),Nick.GetNickMask(),"ACTION",sMessage);
  916. }
  917. return CONTINUE;
  918. }
  919. virtual EModRet OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) {
  920. if (logLevel==0) {
  921. if (strcasestr(sMessage.c_str(),m_pNetwork->GetCurNick().c_str()))
  922. AddMessage(Channel.GetName(),Nick.GetNickMask(),"ACTION",sMessage);
  923. } else if (logLevel>=1) {
  924. AddMessage(Channel.GetName(),Nick.GetNickMask(),"ACTION",sMessage);
  925. }
  926. return CONTINUE;
  927. }
  928. virtual EModRet OnPrivMsg(CNick& Nick, CString& sMessage) {
  929. if (logLevel>=0) {
  930. AddMessage(m_pNetwork->GetCurNick(),Nick.GetNickMask(),"PRIVMSG",sMessage);
  931. }
  932. return CONTINUE;
  933. }
  934. virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) {
  935. if (logLevel==0) {
  936. if (strcasestr(sMessage.c_str(),m_pNetwork->GetCurNick().c_str()))
  937. AddMessage(Channel.GetName(),Nick.GetNickMask(),"PRIVMSG",sMessage);
  938. } else if (logLevel>=1) {
  939. AddMessage(Channel.GetName(),Nick.GetNickMask(),"PRIVMSG",sMessage);
  940. }
  941. return CONTINUE;
  942. }
  943. virtual EModRet OnPrivNotice(CNick& Nick, CString& sMessage) {
  944. if (logLevel>=2) {
  945. AddMessage(m_pNetwork->GetCurNick(),Nick.GetNickMask(),"NOTICE",sMessage);
  946. }
  947. return CONTINUE;
  948. }
  949. virtual EModRet OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage) {
  950. if (logLevel>=2) {
  951. AddMessage(Channel.GetName(),Nick.GetNickMask(),"NOTICE",sMessage);
  952. }
  953. return CONTINUE;
  954. }
  955. virtual EModRet OnTopic(CNick& Nick, CChan& Channel, CString& sTopic) {
  956. if (logLevel>=2) {
  957. AddMessage(Channel.GetName(),Nick.GetNickMask(),"TOPIC",sTopic);
  958. }
  959. return CONTINUE;
  960. }
  961. virtual EModRet OnRaw(CString& sLine) {
  962. if (logLevel>=3) {
  963. CString sCmd = sLine.Token(1);
  964. if ((sCmd.length() == 3) && (isdigit(sCmd[0])) && (isdigit(sCmd[1])) && (isdigit(sCmd[2]))) {
  965. unsigned int uRaw = sCmd.ToUInt();
  966. if (uRaw!=10 && uRaw!=305 && uRaw!=306 && uRaw!=324 && uRaw!=329 && uRaw<331 && uRaw>333 && uRaw!=352 && uRaw!=353 && uRaw!=366 && uRaw!=432 && uRaw!=433 && uRaw!=437 && uRaw!=451 && uRaw!=670) {
  967. AddMessage("",sLine.Token(0),sCmd,sLine.Token(2, true));
  968. }
  969. }
  970. }
  971. return CONTINUE;
  972. }
  973. //Client Connection
  974. virtual void OnClientLogin() {
  975. SetSetting("clientConnected",GetUNIXTime());
  976. if (autoReplay)
  977. Replay();
  978. }
  979. virtual void OnClientDisconnect() {
  980. bool track = true;
  981. for (std::vector<CClient *>::iterator it=doNotTrackClient.begin(); it<doNotTrackClient.end(); it++) {
  982. if (*it==m_pClient) {
  983. doNotTrackClient.erase(it);
  984. track = false;
  985. break;
  986. }
  987. }
  988. if (track)
  989. SetSetting("clientDisconnected",GetUNIXTime());
  990. }
  991. void Replay() {
  992. Replay(0);
  993. }
  994. void Replay(int replayCount) {
  995. if (!databaseConnected)
  996. return;
  997. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Buffer Playback...");
  998. CString lastOnline = GetSetting("clientDisconnected");
  999. MYSQL_STMT *statement = mysql_stmt_init(database);
  1000. int status = 0;
  1001. if (replayCount!=0) {
  1002. status = mysql_stmt_prepare(statement, "SELECT * FROM (SELECT * FROM `messages` ORDER BY `time` DESC LIMIT ?) ORDER BY `time`", 85);
  1003. if (status!=0) {
  1004. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  1005. return;
  1006. }
  1007. MYSQL_BIND bind[1];
  1008. memset(bind, 0, sizeof(bind));
  1009. bind[0].buffer_type = MYSQL_TYPE_LONG;
  1010. bind[0].buffer = (void*)&replayCount;
  1011. bind[0].buffer_length = 0;
  1012. bind[0].is_null = (my_bool *)0;
  1013. status = mysql_stmt_bind_param(statement, bind);
  1014. if (status!=0) {
  1015. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  1016. mysql_stmt_close(statement);
  1017. return;
  1018. }
  1019. } else if (!replayAll && !lastOnline.empty()) {
  1020. status = mysql_stmt_prepare(statement, "SELECT * FROM `messages` WHERE `time`>? ORDER BY `time`", 55);
  1021. if (status!=0) {
  1022. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  1023. return;
  1024. }
  1025. MYSQL_BIND bind[1];
  1026. memset(bind, 0, sizeof(bind));
  1027. bind[0].buffer_type = MYSQL_TYPE_STRING;
  1028. bind[0].buffer = (void*)lastOnline.c_str();
  1029. bind[0].buffer_length = lastOnline.length();
  1030. bind[0].is_null = (my_bool *)0;
  1031. status = mysql_stmt_bind_param(statement, bind);
  1032. if (status!=0) {
  1033. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  1034. mysql_stmt_close(statement);
  1035. return;
  1036. }
  1037. } else {
  1038. status = mysql_stmt_prepare(statement, "SELECT * FROM `messages` ORDER BY `time`", 40);
  1039. if (status!=0) {
  1040. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  1041. return;
  1042. }
  1043. }
  1044. status = mysql_stmt_execute(statement);
  1045. if (status!=0) {
  1046. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  1047. mysql_stmt_close(statement);
  1048. return;
  1049. }
  1050. MYSQL_RES *result = mysql_stmt_result_metadata(statement);
  1051. unsigned int columnCount = mysql_num_fields(result);
  1052. MYSQL_FIELD *fields = mysql_fetch_fields(result);
  1053. MYSQL_BIND results[columnCount];
  1054. memset(results, 0, sizeof(results));
  1055. std::map<unsigned int,char *> columnData;
  1056. std::map<unsigned int,unsigned long> columnLength;
  1057. std::map<unsigned int,bool> columnNull;
  1058. std::map<unsigned int,CString> columns;
  1059. for (unsigned int i=0; i<columnCount; i++) {
  1060. columns[i] = CString(fields[i].name);
  1061. if (columns[i].empty()) {
  1062. char count[20];
  1063. snprintf(count, sizeof(count), "%u", i);
  1064. columns[i] = count;
  1065. }
  1066. columnData[i] = (char *)malloc(fields[i].length);
  1067. results[i].buffer_type = MYSQL_TYPE_STRING;
  1068. results[i].buffer = (void *)columnData[i];
  1069. results[i].buffer_length = fields[i].length;
  1070. results[i].length = &columnLength[i];
  1071. results[i].is_null = (my_bool *)&columnNull[i];
  1072. }
  1073. status = mysql_stmt_bind_result(statement, results);
  1074. if (status!=0) {
  1075. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  1076. mysql_free_result(result);
  1077. mysql_stmt_close(statement);
  1078. return;
  1079. }
  1080. time_t now = time(NULL);
  1081. while (true) {
  1082. status = mysql_stmt_fetch(statement);
  1083. if (status!=0)
  1084. break;
  1085. std::map<CString,CString> data;
  1086. for (unsigned int i=0; i<columnCount; i++) {
  1087. if (columnNull[i]) {
  1088. data[columns[i]] = "";
  1089. } else {
  1090. data[columns[i]] = CString(columnData[i], columnLength[i]);
  1091. }
  1092. }
  1093. time_t unixTime = (time_t)strtol(data["time"].c_str(),NULL,10);
  1094. CString timeString;
  1095. CString prefixString;
  1096. if (m_pClient->HasServerTime()) {
  1097. char timeStr[20];
  1098. snprintf(timeStr, sizeof(timeStr), "%lu", unixTime);
  1099. prefixString = "@t=";
  1100. prefixString += timeStr;
  1101. prefixString += " ";
  1102. } else {
  1103. struct tm *timeinfo = localtime(&unixTime);
  1104. char timeStr[20];
  1105. if (((long)now-86000)<(long)time)
  1106. strftime(timeStr, sizeof(timeStr), "%I:%M:%S %p", timeinfo);
  1107. else
  1108. strftime(timeStr, sizeof(timeStr), "%m/%d/%y %I:%M:%S %p", timeinfo);
  1109. timeString = "[";
  1110. timeString += timeStr;
  1111. timeString += "] ";
  1112. }
  1113. if (data["type"].Equals("DISCONNECT")) {
  1114. PutUser(prefixString+":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :"+timeString+"Server Disconnected");
  1115. } else if (data["type"].Equals("CONNECT")) {
  1116. PutUser(prefixString+":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :"+timeString+"Server Connected");
  1117. } else if (data["type"].Equals("JOIN")) {
  1118. PutUser(prefixString+":"+data["nick"]+" NOTICE "+data["target"]+" :"+timeString+"joined");
  1119. } else if (data["type"].Equals("PART")) {
  1120. if (data["message"].Equals(""))
  1121. PutUser(prefixString+":"+data["nick"]+" NOTICE "+data["target"]+" :"+timeString+"parted");
  1122. else
  1123. PutUser(prefixString+":"+data["nick"]+" NOTICE "+data["target"]+" :"+timeString+"parted: "+data["message"]);
  1124. } else if (data["type"].Equals("TOPIC")) {
  1125. PutUser(prefixString+":"+data["nick"]+" NOTICE "+data["target"]+" :"+timeString+"changed topic: "+data["message"]);
  1126. } else if (data["type"].Equals("QUIT")) {
  1127. if (data["message"].Equals(""))
  1128. PutUser(prefixString+":"+data["nick"]+" NOTICE "+data["target"]+" :"+timeString+"quit");
  1129. else
  1130. PutUser(prefixString+":"+data["nick"]+" NOTICE "+data["target"]+" :"+timeString+"quit: "+data["message"]);
  1131. } else if (data["type"].Equals("MODE")) {
  1132. PutUser(prefixString+":"+data["nick"]+" NOTICE "+data["target"]+" :"+timeString+"changed mode: "+data["message"]);
  1133. } else if (data["type"].Equals("NICK")) {
  1134. PutUser(prefixString+":"+data["nick"]+" NOTICE "+data["target"]+" :"+timeString+"changed nick: "+data["message"]);
  1135. } else if (data["type"].Equals("ACTION")) {
  1136. PutUser(prefixString+":"+data["nick"]+" PRIVMSG "+data["target"]+" :\001ACTION "+timeString+data["message"]+"\001");
  1137. } else {
  1138. PutUser(prefixString+":"+data["nick"]+" "+data["type"]+" "+data["target"]+" :"+timeString+data["message"]);
  1139. }
  1140. }
  1141. for (unsigned int i=0; i<columnCount; i++) {
  1142. free(columnData[i]);
  1143. }
  1144. mysql_free_result(result);
  1145. mysql_stmt_close(statement);
  1146. if (logLimit==1) {
  1147. statement = mysql_stmt_init(database);
  1148. status = mysql_stmt_prepare(statement, "DELETE FROM `messages`", 22);
  1149. if (status==0) {
  1150. mysql_stmt_execute(statement);
  1151. mysql_stmt_close(statement);
  1152. }
  1153. }
  1154. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback Complete.");
  1155. }
  1156. private:
  1157. MYSQL *database;
  1158. bool databaseConnected;
  1159. bool connected;
  1160. bool replayAll;
  1161. bool autoReplay;
  1162. unsigned long logLimit;
  1163. int logLevel;
  1164. std::vector<CString> nickIgnoreList;
  1165. std::vector<CString> chanIgnoreList;
  1166. std::vector<CClient *> doNotTrackClient;
  1167. };
  1168. template<> void TModInfo<CLogMySQL>(CModInfo& Info) {
  1169. Info.SetWikiPage("logmysql");
  1170. }
  1171. NETWORKMODULEDEFS(CLogMySQL, "Add logging to MySQL")