1 /*
2  * jabberd - Jabber Open Source Server
3  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
4  *                    Ryan Eatmon, Robert Norris
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
19  */
20 
21 #include "sm.h"
22 
23 /** @file sm/mod_iq_last.c
24   * @brief last activity
25   * @author Robert Norris
26   * $Date: 2005/08/17 07:48:28 $
27   * $Revision: 1.18 $
28   */
29 
30 #define uri_LAST    "jabber:iq:last"
31 static int ns_LAST = 0;
32 
_iq_last_pkt_sm(mod_instance_t mi,pkt_t pkt)33 static mod_ret_t _iq_last_pkt_sm(mod_instance_t mi, pkt_t pkt) {
34     module_t mod = mi->mod;
35     char uptime[10];
36 
37     /* we only want to play with iq:last gets */
38     if(pkt->type != pkt_IQ || pkt->ns != ns_LAST)
39         return mod_PASS;
40 
41     snprintf(uptime, 10, "%d", (int) (time(NULL) - (time_t) mod->private));
42     nad_set_attr(pkt->nad, 2, -1, "seconds", uptime, 0);
43 
44     /* tell them */
45     nad_set_attr(pkt->nad, 1, -1, "type", "result", 6);
46     pkt_router(pkt_tofrom(pkt));
47 
48     return mod_HANDLED;
49 }
50 
_iq_last_pkt_user(mod_instance_t mi,user_t user,pkt_t pkt)51 static mod_ret_t _iq_last_pkt_user(mod_instance_t mi, user_t user, pkt_t pkt) {
52     char lasttime[10];
53     time_t t;
54     os_t os;
55     os_object_t o;
56     st_ret_t ret;
57 
58     /* we only want to play with iq:last gets */
59     if(pkt->type != pkt_IQ || pkt->ns != ns_LAST)
60         return mod_PASS;
61 
62     /* make sure they're allowed */
63     if(!pres_trust(user, pkt->from))
64         return -stanza_err_FORBIDDEN;
65 
66     /* If the IQ was sent to a JID with a resource, then XMPP-IM 11.1.1
67      * requires we deliver it if that resource is available
68      */
69     if (*pkt->to->resource != '\0')
70 	return mod_PASS;
71 
72     /* If they have an available resource, we should return a query element with a
73      * seconds value of 0
74      */
75     if(user->top != NULL)
76     {
77 	nad_set_attr(pkt->nad, 2, -1, "seconds", "0", 0);
78 	nad_set_attr(pkt->nad, 1, -1, "type", "result", 6);
79 	pkt_router(pkt_tofrom(pkt));
80 
81         return mod_HANDLED;
82     }
83 
84     ret = storage_get(user->sm->st, "logout", jid_user(user->jid), NULL, &os);
85     switch(ret) {
86         case st_SUCCESS:
87             t = 0;
88 
89             if(os_iter_first(os)) {
90                 o = os_iter_object(os);
91 
92                 os_object_get_time(os, o, "time", &t);
93             }
94 
95             os_free(os);
96 
97             snprintf(lasttime, 10, "%d", (int) (time(NULL) - t));
98             nad_set_attr(pkt->nad, 2, -1, "seconds", lasttime, 0);
99 
100             nad_set_attr(pkt->nad, 1, -1, "type", "result", 6);
101             pkt_router(pkt_tofrom(pkt));
102 
103             return mod_HANDLED;
104 
105         case st_FAILED:
106             return -stanza_err_INTERNAL_SERVER_ERROR;
107 
108         case st_NOTFOUND:
109             return -stanza_err_SERVICE_UNAVAILABLE;
110 
111         case st_NOTIMPL:
112             return -stanza_err_FEATURE_NOT_IMPLEMENTED;
113     }
114 
115     /* we never get here */
116     return -stanza_err_INTERNAL_SERVER_ERROR;
117 }
118 
_iq_last_sess_end(mod_instance_t mi,sess_t sess)119 static void _iq_last_sess_end(mod_instance_t mi, sess_t sess) {
120     time_t t;
121     os_t os;
122     os_object_t o;
123 
124     /* store their logout time */
125     t = time(NULL);
126 
127     os = os_new();
128     o = os_object_new(os);
129 
130     os_object_put_time(o, "time", &t);
131 
132     storage_replace(sess->user->sm->st, "logout", jid_user(sess->jid), NULL, os);
133 
134     os_free(os);
135 }
136 
_iq_last_user_delete(mod_instance_t mi,jid_t jid)137 static void _iq_last_user_delete(mod_instance_t mi, jid_t jid) {
138     log_debug(ZONE, "deleting logout time for %s", jid_user(jid));
139 
140     storage_delete(mi->sm->st, "logout", jid_user(jid), NULL);
141 }
142 
_iq_last_free(module_t mod)143 static void _iq_last_free(module_t mod) {
144     sm_unregister_ns(mod->mm->sm, uri_LAST);
145     feature_unregister(mod->mm->sm, uri_LAST);
146 }
147 
module_init(mod_instance_t mi,const char * arg)148 DLLEXPORT int module_init(mod_instance_t mi, const char *arg) {
149     module_t mod = mi->mod;
150 
151     if(mod->init) return 0;
152 
153     mod->sess_end = _iq_last_sess_end;
154     mod->pkt_user = _iq_last_pkt_user;
155     mod->pkt_sm = _iq_last_pkt_sm;
156     mod->user_delete = _iq_last_user_delete;
157     mod->free = _iq_last_free;
158 
159     /* startup time */
160     mod->private = (void *) time(NULL);
161 
162     ns_LAST = sm_register_ns(mod->mm->sm, uri_LAST);
163     feature_register(mod->mm->sm, uri_LAST);
164 
165     return 0;
166 }
167