Software /
code /
prosody-modules
Comparison
mod_log_messages_sql/mod_log_messages_sql.lua @ 953:2c38d7d8b332
mod_log_messages_sql: Fork of mod_mam_sql without the protocol bits
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Wed, 03 Apr 2013 20:29:48 +0200 |
child | 1343:7dbde05b48a9 |
comparison
equal
deleted
inserted
replaced
952:ef2253a7858d | 953:2c38d7d8b332 |
---|---|
1 -- Based on mod_mam_sql | |
2 -- Copyright (C) 2011-2012 Kim Alvefur | |
3 -- | |
4 -- This file is MIT/X11 licensed. | |
5 | |
6 local st = require "util.stanza"; | |
7 local jid_bare = require "util.jid".bare; | |
8 local jid_split = require "util.jid".split; | |
9 | |
10 local serialize = require"util.json".encode, require"util.json".decode; | |
11 local tostring = tostring; | |
12 local time_now = os.time; | |
13 | |
14 local sql, setsql, getsql = {}; | |
15 do -- SQL stuff | |
16 local connection; | |
17 local resolve_relative_path = require "core.configmanager".resolve_relative_path; | |
18 local params = module:get_option("message_log_sql", module:get_option("sql")); | |
19 | |
20 local function test_connection() | |
21 if not connection then return nil; end | |
22 if connection:ping() then | |
23 return true; | |
24 else | |
25 module:log("debug", "Database connection closed"); | |
26 connection = nil; | |
27 end | |
28 end | |
29 local function connect() | |
30 if not test_connection() then | |
31 prosody.unlock_globals(); | |
32 local dbh, err = DBI.Connect( | |
33 params.driver, params.database, | |
34 params.username, params.password, | |
35 params.host, params.port | |
36 ); | |
37 prosody.lock_globals(); | |
38 if not dbh then | |
39 module:log("debug", "Database connection failed: %s", tostring(err)); | |
40 return nil, err; | |
41 end | |
42 module:log("debug", "Successfully connected to database"); | |
43 dbh:autocommit(false); -- don't commit automatically | |
44 connection = dbh; | |
45 | |
46 end | |
47 return connection; | |
48 end | |
49 | |
50 do -- process options to get a db connection | |
51 local ok; | |
52 prosody.unlock_globals(); | |
53 ok, DBI = pcall(require, "DBI"); | |
54 if not ok then | |
55 package.loaded["DBI"] = {}; | |
56 module:log("error", "Failed to load the LuaDBI library for accessing SQL databases: %s", DBI); | |
57 module:log("error", "More information on installing LuaDBI can be found at http://prosody.im/doc/depends#luadbi"); | |
58 end | |
59 prosody.lock_globals(); | |
60 if not ok or not DBI.Connect then | |
61 return; -- Halt loading of this module | |
62 end | |
63 | |
64 params = params or { driver = "SQLite3" }; | |
65 | |
66 if params.driver == "SQLite3" then | |
67 params.database = resolve_relative_path(prosody.paths.data or ".", params.database or "prosody.sqlite"); | |
68 end | |
69 | |
70 assert(params.driver and params.database, "Both the SQL driver and the database need to be specified"); | |
71 | |
72 assert(connect()); | |
73 | |
74 end | |
75 | |
76 function getsql(sql, ...) | |
77 if params.driver == "PostgreSQL" then | |
78 sql = sql:gsub("`", "\""); | |
79 end | |
80 -- do prepared statement stuff | |
81 local stmt, err = connection:prepare(sql); | |
82 if not stmt and not test_connection() then error("connection failed"); end | |
83 if not stmt then module:log("error", "QUERY FAILED: %s %s", err, debug.traceback()); return nil, err; end | |
84 -- run query | |
85 local ok, err = stmt:execute(...); | |
86 if not ok and not test_connection() then error("connection failed"); end | |
87 if not ok then return nil, err; end | |
88 | |
89 return stmt; | |
90 end | |
91 function setsql(sql, ...) | |
92 local stmt, err = getsql(sql, ...); | |
93 if not stmt then return stmt, err; end | |
94 return stmt:affected(); | |
95 end | |
96 function sql.rollback(...) | |
97 if connection then connection:rollback(); end -- FIXME check for rollback error? | |
98 return ...; | |
99 end | |
100 function sql.commit(...) | |
101 if not connection:commit() then return nil, "SQL commit failed"; end | |
102 return ...; | |
103 end | |
104 | |
105 end | |
106 | |
107 -- Handle messages | |
108 local function message_handler(event, c2s) | |
109 local origin, stanza = event.origin, event.stanza; | |
110 local orig_type = stanza.attr.type or "normal"; | |
111 local orig_to = stanza.attr.to; | |
112 local orig_from = stanza.attr.from; | |
113 | |
114 if not orig_from and c2s then | |
115 orig_from = origin.full_jid; | |
116 end | |
117 orig_to = orig_to or orig_from; -- Weird corner cases | |
118 | |
119 -- Don't store messages of these types | |
120 if orig_type == "error" | |
121 or orig_type == "headline" | |
122 or orig_type == "groupchat" | |
123 or not stanza:get_child("body") then | |
124 return; | |
125 -- TODO Maybe headlines should be configurable? | |
126 end | |
127 | |
128 local store_user, store_host = jid_split(c2s and orig_from or orig_to); | |
129 local target_jid = c2s and orig_to or orig_from; | |
130 local target_bare = jid_bare(target_jid); | |
131 local _, _, target_resource = jid_split(target_jid); | |
132 | |
133 --local id = uuid(); | |
134 local when = time_now(); | |
135 -- And stash it | |
136 local ok, err = setsql([[ | |
137 INSERT INTO `prosodyarchive` | |
138 (`host`, `user`, `store`, `when`, `with`, `resource`, `stanza`) | |
139 VALUES (?, ?, ?, ?, ?, ?, ?); | |
140 ]], store_host, store_user, "message_log", when, target_bare, target_resource, serialize(st.preserialize(stanza))) | |
141 if ok then | |
142 sql.commit(); | |
143 else | |
144 module:log("error", "SQL error: %s", err); | |
145 sql.rollback(); | |
146 end | |
147 end | |
148 | |
149 local function c2s_message_handler(event) | |
150 return message_handler(event, true); | |
151 end | |
152 | |
153 -- Stanzas sent by local clients | |
154 module:hook("pre-message/bare", c2s_message_handler, 2); | |
155 module:hook("pre-message/full", c2s_message_handler, 2); | |
156 -- Stanszas to local clients | |
157 module:hook("message/bare", message_handler, 2); | |
158 module:hook("message/full", message_handler, 2); | |
159 | |
160 -- In the telnet console, run: | |
161 -- >hosts["this host"].modules.mam_sql.environment.create_sql() | |
162 function create_sql() | |
163 local stm = getsql[[ | |
164 CREATE TABLE `prosodyarchive` ( | |
165 `host` TEXT, | |
166 `user` TEXT, | |
167 `store` TEXT, | |
168 `id` INTEGER PRIMARY KEY AUTOINCREMENT, | |
169 `when` INTEGER, | |
170 `with` TEXT, | |
171 `resource` TEXT, | |
172 `stanza` TEXT | |
173 ); | |
174 CREATE INDEX `hus` ON `prosodyarchive` (`host`, `user`, `store`); | |
175 CREATE INDEX `with` ON `prosodyarchive` (`with`); | |
176 CREATE INDEX `thetime` ON `prosodyarchive` (`when`); | |
177 ]]; | |
178 stm:execute(); | |
179 sql.commit(); | |
180 end |