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_private.c
24 * @brief private xml storage
25 * @author Robert Norris
26 * $Date: 2005/08/17 07:48:28 $
27 * $Revision: 1.24 $
28 */
29
30 #define uri_PRIVATE "jabber:iq:private"
31 static int ns_PRIVATE = 0;
32
_iq_private_in_sess(mod_instance_t mi,sess_t sess,pkt_t pkt)33 static mod_ret_t _iq_private_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) {
34 module_t mod = mi->mod;
35 int ns, elem, target, targetns;
36 st_ret_t ret;
37 char filter[4096];
38 os_t os;
39 os_object_t o;
40 nad_t nad;
41 pkt_t result;
42 sess_t sscan;
43
44 /* only handle private sets and gets */
45 if((pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET) || pkt->ns != ns_PRIVATE)
46 return mod_PASS;
47
48 /* we're only interested in no to, to our host, or to us */
49 if(pkt->to != NULL && jid_compare_user(sess->jid, pkt->to) != 0 && strcmp(sess->jid->domain, jid_user(pkt->to)) != 0)
50 return mod_PASS;
51
52 ns = nad_find_scoped_namespace(pkt->nad, uri_PRIVATE, NULL);
53 elem = nad_find_elem(pkt->nad, 1, ns, "query", 1);
54
55 /* find the first child */
56 target = elem + 1;
57 while(target < pkt->nad->ecur)
58 {
59 if(pkt->nad->elems[target].depth > pkt->nad->elems[elem].depth)
60 break;
61
62 target++;
63 }
64
65 /* not found, so we're done */
66 if(target == pkt->nad->ecur)
67 return -stanza_err_BAD_REQUEST;
68
69 /* find the target namespace */
70 targetns = NAD_ENS(pkt->nad, target);
71
72 /* gotta have a namespace */
73 if(targetns < 0)
74 {
75 log_debug(ZONE, "no namespace specified");
76 return -stanza_err_BAD_REQUEST;
77 }
78
79 log_debug(ZONE, "processing private request for %.*s", NAD_NURI_L(pkt->nad, targetns), NAD_NURI(pkt->nad, targetns));
80
81 /* get */
82 if(pkt->type == pkt_IQ) {
83 /* remember that this resource requested the namespace */
84 if(sess->module_data[mod->index] == NULL) {
85 /* create new hash if necesary */
86 sess->module_data[mod->index] = xhash_new(101);
87 pool_cleanup(sess->p, (void (*))(void *) xhash_free, sess->module_data[mod->index]);
88 }
89 xhash_put(sess->module_data[mod->index], pstrdupx(sess->p, NAD_NURI(pkt->nad, targetns), NAD_NURI_L(pkt->nad, targetns)), (void *) 1);
90 snprintf(filter, 4096, "(ns=%i:%.*s)", NAD_NURI_L(pkt->nad, targetns), NAD_NURI_L(pkt->nad, targetns), NAD_NURI(pkt->nad, targetns));
91 ret = storage_get(sess->user->sm->st, "private", jid_user(sess->jid), filter, &os);
92 switch(ret) {
93 case st_SUCCESS:
94 if(os_iter_first(os)) {
95 o = os_iter_object(os);
96 if(os_object_get_nad(os, o, "xml", &nad)) {
97 result = pkt_new(sess->user->sm, nad_copy(nad));
98 if(result != NULL) {
99 nad_set_attr(result->nad, 1, -1, "type", "result", 6);
100
101 pkt_id(pkt, result);
102
103 pkt_sess(result, sess);
104
105 pkt_free(pkt);
106
107 os_free(os);
108
109 return mod_HANDLED;
110 }
111 }
112 }
113
114 os_free(os);
115
116 /* drop through */
117 log_debug(ZONE, "storage_get succeeded, but couldn't make packet, faking st_NOTFOUND");
118
119 case st_NOTFOUND:
120
121 log_debug(ZONE, "namespace not found, returning");
122
123 /*
124 * !!! really, we should just return a 404. 1.4 just slaps a
125 * result on the packet and sends it back. hurrah for
126 * legacy namespaces.
127 */
128 nad_set_attr(pkt->nad, 1, -1, "type", "result", 6);
129
130 pkt_sess(pkt_tofrom(pkt), sess);
131
132 return mod_HANDLED;
133
134 case st_FAILED:
135 return -stanza_err_INTERNAL_SERVER_ERROR;
136
137 case st_NOTIMPL:
138 return -stanza_err_FEATURE_NOT_IMPLEMENTED;
139 }
140 }
141
142 os = os_new();
143 o = os_object_new(os);
144
145 snprintf(filter, 4096, "%.*s", NAD_NURI_L(pkt->nad, targetns), NAD_NURI(pkt->nad, targetns));
146 os_object_put(o, "ns", filter, os_type_STRING);
147 os_object_put(o, "xml", pkt->nad, os_type_NAD);
148
149 snprintf(filter, 4096, "(ns=%i:%.*s)", NAD_NURI_L(pkt->nad, targetns), NAD_NURI_L(pkt->nad, targetns), NAD_NURI(pkt->nad, targetns));
150
151 ret = storage_replace(sess->user->sm->st, "private", jid_user(sess->jid), filter, os);
152 os_free(os);
153
154 switch(ret) {
155 case st_FAILED:
156 return -stanza_err_INTERNAL_SERVER_ERROR;
157
158 case st_NOTIMPL:
159 return -stanza_err_FEATURE_NOT_IMPLEMENTED;
160
161 default:
162 /* create result packet */
163 result = pkt_create(sess->user->sm, "iq", "result", NULL, NULL);
164 pkt_id(pkt, result);
165 /* and flush it to the session */
166 pkt_sess(result, sess);
167 /* push it to all resources that read this xmlns item */
168 snprintf(filter, 4096, "%.*s", NAD_NURI_L(pkt->nad, targetns), NAD_NURI(pkt->nad, targetns));
169 for(sscan = sess->user->sessions; sscan != NULL; sscan = sscan->next) {
170 /* skip our resource and those that didn't read any private-storage */
171 if(sscan == sess || sscan->module_data[mod->index] == NULL)
172 continue;
173
174 /* check whether namespace was read */
175 if(xhash_get(sscan->module_data[mod->index], filter)) {
176 result = pkt_dup(pkt, jid_full(sscan->jid), NULL);
177 if(result->from != NULL) {
178 jid_free(result->from);
179 result->from = NULL;
180 nad_set_attr(result->nad, 1, -1, "from", NULL, 0);
181 }
182 pkt_id_new(result);
183 pkt_sess(result, sscan);
184 }
185 }
186 /* finally free the packet */
187 pkt_free(pkt);
188 return mod_HANDLED;
189 }
190
191 /* we never get here */
192 return 0;
193 }
194
_iq_private_user_delete(mod_instance_t mi,jid_t jid)195 static void _iq_private_user_delete(mod_instance_t mi, jid_t jid) {
196 log_debug(ZONE, "deleting private xml storage for %s", jid_user(jid));
197
198 storage_delete(mi->sm->st, "private", jid_user(jid), NULL);
199 }
200
_iq_private_free(module_t mod)201 static void _iq_private_free(module_t mod) {
202 sm_unregister_ns(mod->mm->sm, uri_PRIVATE);
203 feature_unregister(mod->mm->sm, uri_PRIVATE);
204 }
205
module_init(mod_instance_t mi,const char * arg)206 DLLEXPORT int module_init(mod_instance_t mi, const char *arg) {
207 module_t mod = mi->mod;
208
209 if (mod->init) return 0;
210
211 mod->in_sess = _iq_private_in_sess;
212 mod->user_delete = _iq_private_user_delete;
213 mod->free = _iq_private_free;
214
215 ns_PRIVATE = sm_register_ns(mod->mm->sm, uri_PRIVATE);
216 feature_register(mod->mm->sm, uri_PRIVATE);
217
218 return 0;
219 }
220