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