1 /*
2 * jabberd mod_status - Jabber Open Source Server
3 * Copyright (c) 2004 Lucas Nussbaum <lucas@lucas-nussbaum.net>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
18 */
19
20 /** @file sm/mod_status.c
21 * @brief status info management
22 * @author Lucas Nussbaum
23 * $Date: 2004/09/01 $
24 * $Revision: 1.3 $
25 */
26
27 /* for strndup */
28 #define _GNU_SOURCE
29 #include <string.h>
30 #include "sm.h"
31
32 typedef struct _status_st {
33 sm_t sm;
34 const char *resource;
35 } *status_t;
36
_status_os_replace(storage_t st,const char * jid,char * status,char * show,time_t * lastlogin,time_t * lastlogout,nad_t nad)37 static void _status_os_replace(storage_t st, const char *jid, char *status, char *show, time_t *lastlogin, time_t *lastlogout, nad_t nad) {
38 os_t os = os_new();
39 os_object_t o = os_object_new(os);
40 os_object_put(o, "status", status, os_type_STRING);
41 os_object_put(o, "show", show, os_type_STRING);
42 os_object_put(o, "last-login", (void **) lastlogin, os_type_INTEGER);
43 os_object_put(o, "last-logout", (void **) lastlogout, os_type_INTEGER);
44 if(nad != NULL) os_object_put(o, "xml", nad, os_type_NAD);
45 storage_replace(st, "status", jid, NULL, os);
46 os_free(os);
47 }
48
_status_store(storage_t st,const char * jid,pkt_t pkt,time_t * lastlogin,time_t * lastlogout)49 static void _status_store(storage_t st, const char *jid, pkt_t pkt, time_t *lastlogin, time_t *lastlogout) {
50 char *show;
51 int show_free = 0;
52
53 switch(pkt->type)
54 {
55 int elem;
56 case pkt_PRESENCE_UN:
57 show = "unavailable";
58 break;
59 default:
60 elem = nad_find_elem(pkt->nad, 1, NAD_ENS(pkt->nad, 1), "show", 1);
61 if (elem < 0)
62 {
63 show = "";
64 }
65 else
66 {
67 if (NAD_CDATA_L(pkt->nad, elem) <= 0 || NAD_CDATA_L(pkt->nad, elem) > 19)
68 show = "";
69 else
70 {
71 show = strndup(NAD_CDATA(pkt->nad, elem), NAD_CDATA_L(pkt->nad, elem));
72 show_free = 1;
73 }
74 }
75 }
76
77 _status_os_replace(st, jid, "online", show, lastlogin, lastlogout, pkt->nad);
78 if(show_free) free(show);
79 }
80
_status_sess_start(mod_instance_t mi,sess_t sess)81 static int _status_sess_start(mod_instance_t mi, sess_t sess) {
82 time_t t, lastlogout;
83 os_t os;
84 os_object_t o;
85 st_ret_t ret;
86 nad_t nad = NULL;
87
88 /* not interested if there is other top session */
89 if(sess->user->top != NULL && sess != sess->user->top)
90 return mod_PASS;
91
92 ret = storage_get(sess->user->sm->st, "status", jid_user(sess->jid), NULL, &os);
93 if (ret == st_SUCCESS)
94 {
95 if (os_iter_first(os))
96 {
97 o = os_iter_object(os);
98 os_object_get_time(os, o, "last-logout", &lastlogout);
99 os_object_get_nad(os, o, "xml", &nad);
100 nad = nad_copy(nad);
101 }
102 os_free(os);
103 }
104 else
105 {
106 lastlogout = (time_t) 0;
107 }
108
109 t = time(NULL);
110 _status_os_replace(sess->user->sm->st, jid_user(sess->jid), "online", "", &t, &lastlogout, nad);
111
112 if(nad != NULL) nad_free(nad);
113
114 return mod_PASS;
115 }
116
_status_sess_end(mod_instance_t mi,sess_t sess)117 static void _status_sess_end(mod_instance_t mi, sess_t sess) {
118 time_t t, lastlogin;
119 os_t os;
120 os_object_t o;
121 st_ret_t ret;
122 nad_t nad = NULL;
123
124 /* not interested if there is other top session */
125 if(sess->user->top != NULL && sess != sess->user->top)
126 return;
127
128 ret = storage_get(sess->user->sm->st, "status", jid_user(sess->jid), NULL, &os);
129 if (ret == st_SUCCESS)
130 {
131 if (os_iter_first(os))
132 {
133 o = os_iter_object(os);
134 os_object_get_time(os, o, "last-login", &lastlogin);
135 os_object_get_nad(os, o, "xml", &nad);
136 nad = nad_copy(nad);
137 }
138 os_free(os);
139 }
140 else
141 {
142 lastlogin = (time_t) 0;
143 }
144
145 t = time(NULL);
146 _status_os_replace(sess->user->sm->st, jid_user(sess->jid), "offline", "", &lastlogin, &t, nad);
147
148 if(nad != NULL) nad_free(nad);
149 }
150
_status_in_sess(mod_instance_t mi,sess_t sess,pkt_t pkt)151 static mod_ret_t _status_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) {
152 time_t lastlogin, lastlogout;
153 os_t os;
154 os_object_t o;
155 st_ret_t ret;
156
157 /* only handle presence */
158 if(!(pkt->type == pkt_PRESENCE))
159 return mod_PASS;
160
161 ret = storage_get(sess->user->sm->st, "status", jid_user(sess->jid), NULL, &os);
162 if (ret == st_SUCCESS)
163 {
164 if (os_iter_first(os))
165 {
166 o = os_iter_object(os);
167 os_object_get_time(os, o, "last-login", &lastlogin);
168 os_object_get_time(os, o, "last-logout", &lastlogout);
169 }
170 os_free(os);
171 }
172 else
173 {
174 lastlogin = (time_t) 0;
175 lastlogout = (time_t) 0;
176 }
177
178 /* Store only presence broadcasts. If the presence is for a specific user, ignore it. */
179 if (pkt->to == NULL)
180 _status_store(sess->user->sm->st, jid_user(sess->jid), pkt, &lastlogin, &lastlogout);
181
182 return mod_PASS;
183 }
184
185 /* presence packets incoming from other servers */
_status_pkt_sm(mod_instance_t mi,pkt_t pkt)186 static mod_ret_t _status_pkt_sm(mod_instance_t mi, pkt_t pkt) {
187 time_t t;
188 jid_t jid;
189 module_t mod = mi->mod;
190 status_t st = (status_t) mod->private;
191
192 /* store presence information */
193 if(pkt->type == pkt_PRESENCE || pkt->type == pkt_PRESENCE_UN) {
194 log_debug(ZONE, "storing presence from %s", jid_full(pkt->from));
195
196 t = (time_t) 0;
197
198 _status_store(mod->mm->sm->st, jid_user(pkt->from), pkt, &t, &t);
199 }
200
201 /* answer to probes and subscription requests*/
202 if(st->resource && (pkt->type == pkt_PRESENCE_PROBE || pkt->type == pkt_S10N)) {
203 log_debug(ZONE, "answering presence probe/sub from %s with /%s resource", jid_full(pkt->from), st->resource);
204
205 /* send presence */
206 jid = jid_new(pkt->to->domain, -1);
207 jid = jid_reset_components(jid, jid->node, jid->domain, st->resource);
208 pkt_router(pkt_create(st->sm, "presence", NULL, jid_user(pkt->from), jid_full(jid)));
209 jid_free(jid);
210 }
211
212 /* and handle over */
213 return mod_PASS;
214
215 }
216
_status_user_delete(mod_instance_t mi,jid_t jid)217 static void _status_user_delete(mod_instance_t mi, jid_t jid) {
218 log_debug(ZONE, "deleting status information of %s", jid_user(jid));
219
220 storage_delete(mi->sm->st, "status", jid_user(jid), NULL);
221 }
222
_status_free(module_t mod)223 static void _status_free(module_t mod) {
224 free(mod->private);
225 }
226
module_init(mod_instance_t mi,const char * arg)227 DLLEXPORT int module_init(mod_instance_t mi, const char *arg) {
228 module_t mod = mi->mod;
229
230 status_t tr;
231
232 if (mod->init) return 0;
233
234 tr = (status_t) calloc(1, sizeof(struct _status_st));
235
236 tr->sm = mod->mm->sm;
237 tr->resource = config_get_one(mod->mm->sm->config, "status.resource", 0);
238
239 mod->private = tr;
240
241 mod->sess_start = _status_sess_start;
242 mod->sess_end = _status_sess_end;
243 mod->in_sess = _status_in_sess;
244 mod->pkt_sm = _status_pkt_sm;
245 mod->user_delete = _status_user_delete;
246 mod->free = _status_free;
247
248 return 0;
249 }
250