jabberd2  2.5.0
mod_amp.c
Go to the documentation of this file.
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 #define _GNU_SOURCE
22 #include <string.h>
23 #include "sm.h"
24 #include "util/util.h"
25 #include <stringprep.h>
26 
33 typedef struct _mod_amp_config_st {
44 
45 #define AMP_TRIGGERED 1
46 #define AMP_INVALID_RULE 2
47 #define AMP_INVALID_CONDITION 3
48 #define AMP_INVALID_ACTION 4
49 #define AMP_INVALID_VALUE 5
50 #define AMP_NOT_ACCEPTABLE 6
51 
52 typedef struct amp_rule_st {
53  int result;
54  char *condition;
55  char *value;
56  char *action;
57  struct amp_rule_st *next;
58 } *amp_rule_t;
59 
60 
62  amp_rule_t rule_c = rule;
63  amp_rule_t rule_tmp;
64  while (rule_c != NULL) {
65  if (rule_c->condition) free(rule_c->condition);
66  if (rule_c->value) free(rule_c->value);
67  if (rule_c->action) free(rule_c->action);
68  rule_tmp = rule_c->next;
69  free(rule_c);
70  rule_c = rule_tmp;
71  }
72 }
73 
75  if (!pkt || !rule) return NULL;
76 
77  if (rule->result == AMP_TRIGGERED) {
78  int ns;
79  pkt_t res = pkt_create(pkt->sm, "message", NULL, jid_full(pkt->from), jid_full(pkt->to));
80  pkt_id(pkt, res);
81 
82  ns = nad_add_namespace(res->nad, uri_AMP, NULL);
83  nad_append_elem(res->nad, ns, "amp", 2);
84  nad_append_attr(res->nad, -1, "status", rule->action);
85  nad_append_attr(res->nad, -1, "from", jid_full(pkt->from));
86  nad_append_attr(res->nad, -1, "to", jid_full(pkt->to));
87 
88  nad_append_elem(res->nad, ns, "rule", 3);
89  nad_append_attr(res->nad, -1, "condition", rule->condition);
90  nad_append_attr(res->nad, -1, "value", rule->value);
91  nad_append_attr(res->nad, -1, "action", rule->action);
92 
93  return res;
94  }
95 
96  return NULL;
97 }
98 
99 void amp_error_pkt(pkt_t pkt, amp_rule_t rule) {
100  /* TODO: implementation */
101 }
102 
103 
105  /* only handle messages */
106  if (!(pkt->type & pkt_MESSAGE))
107  return mod_PASS;
108 
109  /* we're only interested in no to, to our host, or to us */
110  if (pkt->to != NULL && jid_compare_user(sess->jid, pkt->to) != 0 && strcmp(sess->jid->domain, jid_user(pkt->to)) != 0)
111  return mod_PASS;
112 
113  /* TODO: implementation */
114 
115  return mod_PASS;
116 }
117 
120  int ns, elem, attr;
121  amp_rule_t rule, rule_c;
122  int errormode = 0;
123 
124  /* only handle messages */
125  if (!(pkt->type & pkt_MESSAGE))
126  return mod_PASS;
127 
128  /* does message have at least one rule for us? */
129  ns = nad_find_scoped_namespace(pkt->nad, uri_AMP, NULL);
130  elem = nad_find_elem(pkt->nad, 1, ns, "amp", 1);
131  if (elem < 0
132  || nad_find_attr(pkt->nad, elem, -1, "status", NULL) >= 0
133  || (elem = nad_find_elem(pkt->nad, elem, ns, "rule", 1)) < 0)
134  return mod_PASS;
135 
136  /* loop for rules */
137  rule = calloc(1, sizeof(struct amp_rule_st));
138  rule_c = rule;
139  while (elem >= 0) {
140 
141  /* actions */
142  if (nad_find_attr(pkt->nad, elem, -1, "action", "drop") >= 0
143  && !config->disableActionDrop)
144  rule_c->action = strdup("drop");
145  else if (nad_find_attr(pkt->nad, elem, -1, "action", "alert") >= 0
146  && !config->disableActionAlert)
147  rule_c->action = strdup("alert");
148  else if (nad_find_attr(pkt->nad, elem, -1, "action", "error") >= 0
149  && !config->disableActionError)
150  rule_c->action = strdup("error");
151  else if (nad_find_attr(pkt->nad, elem, -1, "action", "notify") >= 0
152  && !config->disableActionNotify)
153  rule_c->action = strdup("notify");
154 
155  if (!rule_c->action) {
156  if ((attr = nad_find_attr(pkt->nad, elem, -1, "action", NULL)) >= 0)
157  rule_c->action = strndup(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
158  rule_c->result = AMP_INVALID_ACTION;
159  }
160 
161  /* deliver condition */
162  if (nad_find_attr(pkt->nad, elem, -1, "condition", "deliver") >= 0
163  && !config->disableConditionDeliver) {
164  rule_c->condition = strdup("deliver");
165 
166  /* direct */
167  if (nad_find_attr(pkt->nad, elem, -1, "value", "direct") >= 0) {
168  rule_c->value = strdup("direct");
169  if (user->top != NULL) /* active session so it will be direct */
170  rule_c->result = AMP_TRIGGERED;
171  }
172 
173  /* stored */
174  else if (nad_find_attr(pkt->nad, elem, -1, "value", "stored") >= 0) {
175  rule_c->value = strdup("none");
176  if (!config->offlinestorageDisabled
177  && user->top == NULL) /* no active session so it will be stored */
178  rule_c->result = AMP_TRIGGERED;
179  }
180 
181  /* none */
182  else if (nad_find_attr(pkt->nad, elem, -1, "value", "none") >= 0) {
183  rule_c->value = strdup("none");
184  if (config->offlinestorageDisabled
185  && user->top == NULL) /* no active session and no offline storage */
186  rule_c->result = AMP_TRIGGERED;
187  }
188 
189  if (!rule_c->value) {
190  if ((attr = nad_find_attr(pkt->nad, elem, -1, "value", NULL)) >= 0)
191  rule_c->value = strndup(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
192  rule_c->result = AMP_INVALID_VALUE;
193  }
194  }
195 
196  /* match-resource condition */
197  else if (nad_find_attr(pkt->nad, elem, -1, "condition", "match-resource") >= 0
198  && !config->disableConditionMatchResource) {
199  rule_c->condition = strdup("match-resource");
200 
201  /* exact */
202  if (nad_find_attr(pkt->nad, elem, -1, "value", "exact") >= 0) {
203  rule_c->value = strdup("exact");
204  if (sess_match(user, pkt->to->resource)) /* resource found */
205  rule_c->result = AMP_TRIGGERED;
206  }
207 
208  /* any */
209  else if (nad_find_attr(pkt->nad, elem, -1, "value", "any") >= 0) {
210  rule_c->value = strdup("any");
211  if (user->top == NULL) /* no active resource */
212  rule_c->result = AMP_TRIGGERED;
213  }
214 
215  /* other */
216  else if (nad_find_attr(pkt->nad, elem, -1, "value", "other") >= 0) {
217  rule_c->value = strdup("other");
218  if (!sess_match(user, pkt->to->resource)) /* resource not found */
219  rule_c->result = AMP_TRIGGERED;
220  }
221 
222  if (!rule_c->value) {
223  if ((attr = nad_find_attr(pkt->nad, elem, -1, "value", NULL)) >= 0)
224  rule_c->value = strndup(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
225  rule_c->result = AMP_INVALID_VALUE;
226  }
227  }
228 
229  /* expire-at condition */
230  else if (nad_find_attr(pkt->nad, elem, -1, "condition", "expire-at") >= 0
231  && !config->disableConditionExpireAt) {
232  rule_c->condition = strdup("expire-at");
233 
234  if ((attr = nad_find_attr(pkt->nad, elem, -1, "value", NULL)) < 0)
235  rule_c->result = AMP_INVALID_VALUE;
236  else {
237  time_t stamp;
238  rule_c->value = strndup(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
239  stamp = datetime_in(rule_c->value);
240  if (stamp < 0)
241  rule_c->result = AMP_INVALID_VALUE;
242  else if (stamp < time(NULL)) /* expired! */
243  rule_c->result = AMP_TRIGGERED;
244  }
245  }
246 
247  if (!rule_c->condition) {
248  if ((attr = nad_find_attr(pkt->nad, elem, -1, "condition", NULL)) >= 0)
249  rule_c->condition = strndup(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
250  rule_c->result = AMP_INVALID_CONDITION;
251  }
252 
253  /* if an error is triggered, pass in error mode */
254  if (rule_c->result > AMP_TRIGGERED)
255  errormode = 1;
256 
257  /* processing stops at first rule triggerred */
258  if (rule_c->result == AMP_TRIGGERED && !errormode)
259  break;
260 
261  /* jump to next rule (if any) */
262  if ((elem = nad_find_elem(pkt->nad, elem, ns, "rule", 0)) >= 0) {
263  rule_c->next = calloc(1, sizeof(struct amp_rule_st));
264  rule_c = rule_c->next;
265  }
266  }
267 
268  /* build result packet (if any) */
269  if (rule_c->result != AMP_TRIGGERED || errormode)
270  rule_c = rule;
271  while (rule_c != NULL) {
272  if (rule_c->result > 0) {
273 
274  /* drop action */
275  if (!strcmp(rule_c->action, "drop") && !errormode)
276  goto handled;
277 
278  /* alert action */
279  else if (!strcmp(rule_c->action, "alert") && !errormode) {
280  pkt_t res = amp_build_response_pkt(pkt, rule_c);
281  pkt_router(res);
282  goto handled;
283  }
284 
285  /* error action */
286  else if (!strcmp(rule_c->action, "error") && !errormode) {
287  pkt_t res = amp_build_response_pkt(pkt, rule_c);
288  pkt_router(res);
289  goto handled;
290  }
291 
292  /* notify action */
293  else if (!strcmp(rule_c->action, "notify") && !errormode) {
294  pkt_t res = amp_build_response_pkt(pkt, rule_c);
295  pkt_router(res);
296  goto pass; /* ...resume the pkt-user chain happily :) */
297  }
298  }
299 
300  rule_c = rule_c->next;
301  }
302 
303  pass:
304  amp_rule_free(rule);
305  return mod_PASS;
306 
307  handled:
308  amp_rule_free(rule);
309  pkt_free(pkt);
310  return mod_HANDLED;
311 }
312 
315  pkt_t res;
316  int ns, attr;
317 
318  /* we only want to play with iq disco#info gets */
319  if(pkt->type != pkt_IQ || pkt->ns != ns_DISCO_INFO)
320  return mod_PASS;
321 
322  /* is disco#info for us ? */
323  if ((attr = nad_find_attr(pkt->nad, 2, -1, "node", NULL)) < 0
324  || strncmp(NAD_AVAL(pkt->nad, attr), uri_AMP, NAD_AVAL_L(pkt->nad, attr)) != 0)
325  return mod_PASS;
326 
327  res = pkt_create(config->sm, "iq", "result", jid_full(pkt->from), jid_full(pkt->to));
328  pkt_id(pkt, res);
329  pkt_free(pkt);
330 
331  ns = nad_add_namespace(res->nad, uri_DISCO_INFO, NULL);
332  nad_append_elem(res->nad, ns, "query", 2);
333  nad_append_attr(res->nad, -1, "node", uri_AMP);
334 
335  nad_append_elem(res->nad, ns, "identity", 3);
336  nad_append_attr(res->nad, -1, "name", "Advanced Message Processing support");
337  nad_append_attr(res->nad, -1, "category", "im");
338  nad_append_attr(res->nad, -1, "type", "server");
339 
340  nad_append_elem(res->nad, ns, "feature", 3);
341  nad_append_attr(res->nad, -1, "var", uri_AMP);
342  if (!config->disableActionDrop) {
343  nad_append_elem(res->nad, ns, "feature", 3);
344  nad_append_attr(res->nad, -1, "var", uri_AMP_ACTION_DROP);
345  }
346  if (!config->disableActionError) {
347  nad_append_elem(res->nad, ns, "feature", 3);
348  nad_append_attr(res->nad, -1, "var", uri_AMP_ACTION_ERROR);
349  }
350  if (!config->disableActionNotify) {
351  nad_append_elem(res->nad, ns, "feature", 3);
352  nad_append_attr(res->nad, -1, "var", uri_AMP_ACTION_NOTIFY);
353  }
354  if (!config->disableConditionDeliver) {
355  nad_append_elem(res->nad, ns, "feature", 3);
356  nad_append_attr(res->nad, -1, "var", uri_AMP_CONDITION_DELIVER);
357  }
358  if (!config->disableConditionExpireAt) {
359  nad_append_elem(res->nad, ns, "feature", 3);
360  nad_append_attr(res->nad, -1, "var", uri_AMP_CONDITION_EXPIREAT);
361  }
362  if (!config->disableConditionMatchResource) {
363  nad_append_elem(res->nad, ns, "feature", 3);
364  nad_append_attr(res->nad, -1, "var", uri_AMP_CONDITION_MATCHRESOURCE);
365  }
366 
367  /* tell them */
368  pkt_router(res);
369 
370  return mod_HANDLED;
371 }
372 
373 static void _amp_free(module_t mod) {
374  free(mod->private);
375 }
376 
377 DLLEXPORT int module_init(mod_instance_t mi, const char *arg) {
378  module_t mod = mi->mod;
379  mod_amp_config_t config;
380  const char* option;
381 
382  if (mod->init) return 0;
383 
384  config = (mod_amp_config_t) calloc(1, sizeof(struct _mod_amp_config_st));
385 
386  config->sm = mod->mm->sm;
387  option = config_get_one(mod->mm->sm->config, "amp.disableactions.drop", 0);
388  if (option != NULL) {
389  log_debug(ZONE, "action Drop disabled in config.");
390  config->disableActionDrop = 1;
391  }
392  option = config_get_one(mod->mm->sm->config, "amp.disableactions.error", 0);
393  if (option != NULL) {
394  log_debug(ZONE, "action Error disabled in config.");
395  config->disableActionError = 1;
396  }
397  option = config_get_one(mod->mm->sm->config, "amp.disableactions.alert", 0);
398  if (option != NULL) {
399  log_debug(ZONE, "action Alert disabled in config.");
400  config->disableActionAlert = 1;
401  }
402  option = config_get_one(mod->mm->sm->config, "amp.disableactions.notify", 0);
403  if (option != NULL) {
404  log_debug(ZONE, "action Notify disabled in config.");
405  config->disableActionNotify = 1;
406  }
407  option = config_get_one(mod->mm->sm->config, "amp.disableconditions.deliver", 0);
408  if (option != NULL) {
409  log_debug(ZONE, "condition Deliver disabled in config.");
410  config->disableConditionDeliver = 1;
411  }
412  option = config_get_one(mod->mm->sm->config, "amp.disableconditions.expireat", 0);
413  if (option != NULL) {
414  log_debug(ZONE, "condition Expire-At disabled in config.");
415  config->disableConditionExpireAt = 1;
416  }
417  option = config_get_one(mod->mm->sm->config, "amp.disableconditions.matchresource", 0);
418  if (option != NULL) {
419  log_debug(ZONE, "condition Match-Resource disabled in config.");
420  config->disableConditionMatchResource = 1;
421  }
422  option = config_get_one(mod->mm->sm->config, "amp.offlinestoragedisabled", 0);
423  if (option != NULL) {
424  log_debug(ZONE, "offline storage disabled in config.");
425  config->offlinestorageDisabled = 1;
426  }
427  option = config_get_one(mod->mm->sm->config, "offline.dropmessages", 0);
428  if (option != NULL) {
429  log_debug(ZONE, "offline storage disabled in config.");
430  config->offlinestorageDisabled = 1;
431  }
432 
433  mod->private = config;
434 
435  mod->in_sess = _amp_in_sess;
436  mod->pkt_user = _amp_pkt_user;
437  mod->pkt_sm = _amp_pkt_sm;
438  mod->free = _amp_free;
439 
440  feature_register(mod->mm->sm, uri_AMP);
441 
442  return 0;
443 }
pkt_type_t type
packet type
Definition: sm.h:138
jid_t jid
session jid (user@host/res)
Definition: sm.h:258
#define AMP_INVALID_CONDITION
Definition: mod_amp.c:47
int nad_append_attr(nad_t nad, int ns, const char *name, const char *val)
attach new attr to the last elem
Definition: nad.c:745
data structures and prototypes for the session manager
#define ns_DISCO_INFO
Definition: sm.h:81
char * condition
Definition: mod_amp.c:54
int offlinestorageDisabled
Definition: mod_amp.c:42
#define uri_AMP
Definition: uri.h:85
const char * jid_user(jid_t jid)
expand and return the user
Definition: jid.c:338
static mod_ret_t _amp_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt)
Definition: mod_amp.c:104
const char * jid_full(jid_t jid)
expand and return the full
Definition: jid.c:346
int nad_find_attr(nad_t nad, unsigned int elem, int ns, const char *name, const char *val)
get a matching attr on this elem, both name and optional val
Definition: nad.c:237
single instance of a module in a chain
Definition: sm.h:446
config_t config
config context
Definition: sm.h:198
int init
number of times the module intialiser has been called
Definition: sm.h:416
static void _amp_free(module_t mod)
Definition: mod_amp.c:373
struct amp_rule_st * amp_rule_t
void amp_rule_free(amp_rule_t rule)
Definition: mod_amp.c:61
int nad_add_namespace(nad_t nad, const char *uri, const char *prefix)
bring a new namespace into scope
Definition: nad.c:778
struct _mod_amp_config_st * mod_amp_config_t
int result
Definition: mod_amp.c:53
#define uri_AMP_ACTION_DROP
Definition: uri.h:87
sm_t sm
sm context
Definition: sm.h:130
char * resource
Definition: jid.h:46
int nad_append_elem(nad_t nad, int ns, const char *name, int depth)
create a new elem on the list
Definition: nad.c:711
static mod_ret_t _amp_pkt_user(mod_instance_t mi, user_t user, pkt_t pkt)
Definition: mod_amp.c:118
#define AMP_INVALID_ACTION
Definition: mod_amp.c:48
mm_t mm
module manager
Definition: sm.h:404
int disableConditionMatchResource
Definition: mod_amp.c:41
int disableConditionExpireAt
Definition: mod_amp.c:40
struct amp_rule_st * next
Definition: mod_amp.c:57
#define DLLEXPORT
Definition: c2s.h:47
int disableConditionDeliver
Definition: mod_amp.c:39
sess_t top
top priority session
Definition: sm.h:244
int disableActionError
Definition: mod_amp.c:36
sm_t sm
sm context
Definition: sm.h:366
mod_ret_t(* in_sess)(mod_instance_t mi, sess_t sess, pkt_t pkt)
in-sess handler
Definition: sm.h:423
#define uri_AMP_ACTION_NOTIFY
Definition: uri.h:89
module_t mod
module that this is an instance of
Definition: sm.h:449
jid_t from
packet addressing (not used for routing)
Definition: sm.h:140
void * private
module private data
Definition: sm.h:418
packet summary data wrapper
Definition: sm.h:129
nad_t nad
nad of the entire packet
Definition: sm.h:146
session manager global context
Definition: sm.h:167
void amp_error_pkt(pkt_t pkt, amp_rule_t rule)
Definition: mod_amp.c:99
char * domain
Definition: jid.h:45
#define AMP_TRIGGERED
Definition: mod_amp.c:45
DLLEXPORT int module_init(mod_instance_t mi, const char *arg)
Definition: mod_amp.c:377
#define uri_AMP_CONDITION_MATCHRESOURCE
Definition: uri.h:92
#define NAD_AVAL_L(N, A)
Definition: nad.h:190
void pkt_id(pkt_t src, pkt_t dest)
convenience - copy the packet id from src to dest
Definition: pkt.c:353
#define uri_AMP_ACTION_ERROR
Definition: uri.h:88
mod_ret_t(* pkt_user)(mod_instance_t mi, user_t user, pkt_t pkt)
pkt-user handler
Definition: sm.h:430
char * value
Definition: mod_amp.c:55
void pkt_router(pkt_t pkt)
Definition: pkt.c:379
void pkt_free(pkt_t pkt)
Definition: pkt.c:315
#define log_debug(...)
Definition: log.h:65
pkt_t amp_build_response_pkt(pkt_t pkt, amp_rule_t rule)
Definition: mod_amp.c:74
void feature_register(sm_t sm, const char *feature)
register a feature
Definition: feature.c:37
info/query (get)
Definition: sm.h:106
sess_t sess_match(user_t user, const char *resource)
match a session by resource
Definition: sess.c:206
#define NAD_AVAL(N, A)
Definition: nad.h:189
static mod_ret_t _amp_pkt_sm(mod_instance_t mi, pkt_t pkt)
Definition: mod_amp.c:313
packet was unhandled, should be passed to the next module
Definition: sm.h:340
int ns
iq sub-namespace
Definition: sm.h:142
char * action
Definition: mod_amp.c:56
packet was handled (and freed)
Definition: sm.h:339
There is one instance of this struct per user who is logged in to this c2s instance.
Definition: c2s.h:74
int jid_compare_user(jid_t a, jid_t b)
compare the user portion of two jids
Definition: jid.c:354
int nad_find_elem(nad_t nad, unsigned int elem, int ns, const char *name, int depth)
locate the next elem at a given depth with an optional matching name
Definition: nad.c:206
time_t datetime_in(char *date)
Definition: datetime.c:34
mod_ret_t(* pkt_sm)(mod_instance_t mi, pkt_t pkt)
pkt-sm handler
Definition: sm.h:429
message
Definition: sm.h:95
jid_t to
Definition: sm.h:140
#define AMP_INVALID_VALUE
Definition: mod_amp.c:49
int disableActionAlert
Definition: mod_amp.c:37
int disableActionNotify
Definition: mod_amp.c:38
#define ZONE
Definition: mio_impl.h:76
int disableActionDrop
Definition: mod_amp.c:35
const char * config_get_one(config_t c, const char *key, int num)
get config value n for this key
Definition: config.c:278
void(* free)(module_t mod)
called when module is freed
Definition: sm.h:442
data for a single module
Definition: sm.h:403
#define uri_DISCO_INFO
Definition: uri.h:81
pkt_t pkt_create(sm_t sm, const char *elem, const char *type, const char *to, const char *from)
Definition: pkt.c:328
mod_ret_t
module return values
Definition: sm.h:338
#define uri_AMP_CONDITION_DELIVER
Definition: uri.h:90
#define uri_AMP_CONDITION_EXPIREAT
Definition: uri.h:91
data for a single user
Definition: sm.h:234
int nad_find_scoped_namespace(nad_t nad, const char *uri, const char *prefix)
find a namespace in scope
Definition: nad.c:292