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