jabberd2
2.2.16
|
00001 /* 00002 * jabberd - Jabber Open Source Server 00003 * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney, 00004 * Ryan Eatmon, Robert Norris 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA 00019 */ 00020 00021 #define _GNU_SOURCE 00022 #include <string.h> 00023 #include "sm.h" 00024 #include "util/util.h" 00025 #include <stringprep.h> 00026 00033 typedef struct _mod_amp_config_st { 00034 sm_t sm; 00035 int disableActionDrop; 00036 int disableActionError; 00037 int disableActionAlert; 00038 int disableActionNotify; 00039 int disableConditionDeliver; 00040 int disableConditionExpireAt; 00041 int disableConditionMatchResource; 00042 int offlinestorageDisabled; 00043 } *mod_amp_config_t; 00044 00045 #define AMP_TRIGGERED 1 00046 #define AMP_INVALID_RULE 2 00047 #define AMP_INVALID_CONDITION 3 00048 #define AMP_INVALID_ACTION 4 00049 #define AMP_INVALID_VALUE 5 00050 #define AMP_NOT_ACCEPTABLE 6 00051 00052 typedef struct amp_rule_st { 00053 int result; 00054 char *condition; 00055 char *value; 00056 char *action; 00057 struct amp_rule_st *next; 00058 } *amp_rule_t; 00059 00060 00061 void amp_rule_free(amp_rule_t rule) { 00062 amp_rule_t rule_c = rule; 00063 amp_rule_t rule_tmp; 00064 while (rule_c != NULL) { 00065 if (rule_c->condition) free(rule_c->condition); 00066 if (rule_c->value) free(rule_c->value); 00067 if (rule_c->action) free(rule_c->action); 00068 rule_tmp = rule_c->next; 00069 free(rule_c); 00070 rule_c = rule_tmp; 00071 } 00072 } 00073 00074 pkt_t amp_build_response_pkt(pkt_t pkt, amp_rule_t rule) { 00075 if (!pkt || !rule) return NULL; 00076 00077 if (rule->result == AMP_TRIGGERED) { 00078 int ns; 00079 pkt_t res = pkt_create(pkt->sm, "message", NULL, jid_full(pkt->from), jid_full(pkt->to)); 00080 pkt_id(pkt, res); 00081 00082 ns = nad_add_namespace(res->nad, uri_AMP, NULL); 00083 nad_append_elem(res->nad, ns, "amp", 2); 00084 nad_append_attr(res->nad, -1, "status", rule->action); 00085 nad_append_attr(res->nad, -1, "from", jid_full(pkt->from)); 00086 nad_append_attr(res->nad, -1, "to", jid_full(pkt->to)); 00087 00088 nad_append_elem(res->nad, ns, "rule", 3); 00089 nad_append_attr(res->nad, -1, "condition", rule->condition); 00090 nad_append_attr(res->nad, -1, "value", rule->value); 00091 nad_append_attr(res->nad, -1, "action", rule->action); 00092 00093 return res; 00094 } 00095 00096 return NULL; 00097 } 00098 00099 void amp_error_pkt(pkt_t pkt, amp_rule_t rule) { 00100 /* TODO: implementation */ 00101 } 00102 00103 00104 static mod_ret_t _amp_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) { 00105 /* only handle messages */ 00106 if (!(pkt->type & pkt_MESSAGE)) 00107 return mod_PASS; 00108 00109 /* we're only interested in no to, to our host, or to us */ 00110 if (pkt->to != NULL && jid_compare_user(sess->jid, pkt->to) != 0 && strcmp(sess->jid->domain, jid_user(pkt->to)) != 0) 00111 return mod_PASS; 00112 00113 /* TODO: implementation */ 00114 00115 return mod_PASS; 00116 } 00117 00118 static mod_ret_t _amp_pkt_user(mod_instance_t mi, user_t user, pkt_t pkt) { 00119 mod_amp_config_t config = (mod_amp_config_t) mi->mod->private; 00120 int ns, elem, attr; 00121 amp_rule_t rule, rule_c; 00122 int errormode = 0; 00123 00124 /* only handle messages */ 00125 if (!(pkt->type & pkt_MESSAGE)) 00126 return mod_PASS; 00127 00128 /* does message have at least one rule for us? */ 00129 ns = nad_find_scoped_namespace(pkt->nad, uri_AMP, NULL); 00130 elem = nad_find_elem(pkt->nad, 1, ns, "amp", 1); 00131 if (elem < 0 00132 || nad_find_attr(pkt->nad, elem, -1, "status", NULL) >= 0 00133 || (elem = nad_find_elem(pkt->nad, elem, ns, "rule", 1)) < 0) 00134 return mod_PASS; 00135 00136 /* loop for rules */ 00137 rule = calloc(1, sizeof(struct amp_rule_st)); 00138 rule_c = rule; 00139 while (elem >= 0) { 00140 00141 /* actions */ 00142 if (nad_find_attr(pkt->nad, elem, -1, "action", "drop") >= 0 00143 && !config->disableActionDrop) 00144 rule_c->action = strdup("drop"); 00145 else if (nad_find_attr(pkt->nad, elem, -1, "action", "alert") >= 0 00146 && !config->disableActionAlert) 00147 rule_c->action = strdup("alert"); 00148 else if (nad_find_attr(pkt->nad, elem, -1, "action", "error") >= 0 00149 && !config->disableActionError) 00150 rule_c->action = strdup("error"); 00151 else if (nad_find_attr(pkt->nad, elem, -1, "action", "notify") >= 0 00152 && !config->disableActionNotify) 00153 rule_c->action = strdup("notify"); 00154 00155 if (!rule_c->action) { 00156 if ((attr = nad_find_attr(pkt->nad, elem, -1, "action", NULL)) >= 0) 00157 rule_c->action = strndup(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr)); 00158 rule_c->result = AMP_INVALID_ACTION; 00159 } 00160 00161 /* deliver condition */ 00162 if (nad_find_attr(pkt->nad, elem, -1, "condition", "deliver") >= 0 00163 && !config->disableConditionDeliver) { 00164 rule_c->condition = strdup("deliver"); 00165 00166 /* direct */ 00167 if (nad_find_attr(pkt->nad, elem, -1, "value", "direct") >= 0) { 00168 rule_c->value = strdup("direct"); 00169 if (user->top != NULL) /* active session so it will be direct */ 00170 rule_c->result = AMP_TRIGGERED; 00171 } 00172 00173 /* stored */ 00174 else if (nad_find_attr(pkt->nad, elem, -1, "value", "stored") >= 0) { 00175 rule_c->value = strdup("none"); 00176 if (!config->offlinestorageDisabled 00177 && user->top == NULL) /* no active session so it will be stored */ 00178 rule_c->result = AMP_TRIGGERED; 00179 } 00180 00181 /* none */ 00182 else if (nad_find_attr(pkt->nad, elem, -1, "value", "none") >= 0) { 00183 rule_c->value = strdup("none"); 00184 if (config->offlinestorageDisabled 00185 && user->top == NULL) /* no active session and no offline storage */ 00186 rule_c->result = AMP_TRIGGERED; 00187 } 00188 00189 if (!rule_c->value) { 00190 if ((attr = nad_find_attr(pkt->nad, elem, -1, "value", NULL)) >= 0) 00191 rule_c->value = strndup(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr)); 00192 rule_c->result = AMP_INVALID_VALUE; 00193 } 00194 } 00195 00196 /* match-resource condition */ 00197 else if (nad_find_attr(pkt->nad, elem, -1, "condition", "match-resource") >= 0 00198 && !config->disableConditionMatchResource) { 00199 rule_c->condition = strdup("match-resource"); 00200 00201 /* exact */ 00202 if (nad_find_attr(pkt->nad, elem, -1, "value", "exact") >= 0) { 00203 rule_c->value = strdup("exact"); 00204 if (sess_match(user, pkt->to->resource)) /* resource found */ 00205 rule_c->result = AMP_TRIGGERED; 00206 } 00207 00208 /* any */ 00209 else if (nad_find_attr(pkt->nad, elem, -1, "value", "any") >= 0) { 00210 rule_c->value = strdup("any"); 00211 if (user->top == NULL) /* no active resource */ 00212 rule_c->result = AMP_TRIGGERED; 00213 } 00214 00215 /* other */ 00216 else if (nad_find_attr(pkt->nad, elem, -1, "value", "other") >= 0) { 00217 rule_c->value = strdup("other"); 00218 if (!sess_match(user, pkt->to->resource)) /* resource not found */ 00219 rule_c->result = AMP_TRIGGERED; 00220 } 00221 00222 if (!rule_c->value) { 00223 if ((attr = nad_find_attr(pkt->nad, elem, -1, "value", NULL)) >= 0) 00224 rule_c->value = strndup(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr)); 00225 rule_c->result = AMP_INVALID_VALUE; 00226 } 00227 } 00228 00229 /* expire-at condition */ 00230 else if (nad_find_attr(pkt->nad, elem, -1, "condition", "expire-at") >= 0 00231 && !config->disableConditionExpireAt) { 00232 rule_c->condition = strdup("expire-at"); 00233 00234 if ((attr = nad_find_attr(pkt->nad, elem, -1, "value", NULL)) < 0) 00235 rule_c->result = AMP_INVALID_VALUE; 00236 else { 00237 time_t stamp; 00238 rule_c->value = strndup(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr)); 00239 stamp = datetime_in(rule_c->value); 00240 if (stamp < 0) 00241 rule_c->result = AMP_INVALID_VALUE; 00242 else if (stamp < time(NULL)) /* expired! */ 00243 rule_c->result = AMP_TRIGGERED; 00244 } 00245 } 00246 00247 if (!rule_c->condition) { 00248 if ((attr = nad_find_attr(pkt->nad, elem, -1, "condition", NULL)) >= 0) 00249 rule_c->condition = strndup(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr)); 00250 rule_c->result = AMP_INVALID_CONDITION; 00251 } 00252 00253 /* if an error is triggered, pass in error mode */ 00254 if (rule_c->result > AMP_TRIGGERED) 00255 errormode = 1; 00256 00257 /* processing stops at first rule triggerred */ 00258 if (rule_c->result == AMP_TRIGGERED && !errormode) 00259 break; 00260 00261 /* jump to next rule (if any) */ 00262 if ((elem = nad_find_elem(pkt->nad, elem, ns, "rule", 0)) >= 0) { 00263 rule_c->next = calloc(1, sizeof(struct amp_rule_st)); 00264 rule_c = rule_c->next; 00265 } 00266 } 00267 00268 /* build result packet (if any) */ 00269 if (rule_c->result != AMP_TRIGGERED || errormode) 00270 rule_c = rule; 00271 while (rule_c != NULL) { 00272 if (rule_c->result > 0) { 00273 00274 /* drop action */ 00275 if (!strcmp(rule_c->action, "drop") && !errormode) 00276 goto handled; 00277 00278 /* alert action */ 00279 else if (!strcmp(rule_c->action, "alert") && !errormode) { 00280 pkt_t res = amp_build_response_pkt(pkt, rule_c); 00281 pkt_router(res); 00282 goto handled; 00283 } 00284 00285 /* error action */ 00286 else if (!strcmp(rule_c->action, "error") && !errormode) { 00287 pkt_t res = amp_build_response_pkt(pkt, rule_c); 00288 pkt_router(res); 00289 goto handled; 00290 } 00291 00292 /* notify action */ 00293 else if (!strcmp(rule_c->action, "notify") && !errormode) { 00294 pkt_t res = amp_build_response_pkt(pkt, rule_c); 00295 pkt_router(res); 00296 goto pass; /* ...resume the pkt-user chain happily :) */ 00297 } 00298 } 00299 00300 rule_c = rule_c->next; 00301 } 00302 00303 pass: 00304 amp_rule_free(rule); 00305 return mod_PASS; 00306 00307 handled: 00308 amp_rule_free(rule); 00309 pkt_free(pkt); 00310 return mod_HANDLED; 00311 } 00312 00313 static mod_ret_t _amp_pkt_sm(mod_instance_t mi, pkt_t pkt) { 00314 mod_amp_config_t config = (mod_amp_config_t) mi->mod->private; 00315 pkt_t res; 00316 int ns, attr; 00317 00318 /* we only want to play with iq disco#info gets */ 00319 if(pkt->type != pkt_IQ || pkt->ns != ns_DISCO_INFO) 00320 return mod_PASS; 00321 00322 /* is disco#info for us ? */ 00323 if ((attr = nad_find_attr(pkt->nad, 2, -1, "node", NULL)) < 0 00324 || strncmp(NAD_AVAL(pkt->nad, attr), uri_AMP, NAD_AVAL_L(pkt->nad, attr)) != 0) 00325 return mod_PASS; 00326 00327 res = pkt_create(config->sm, "iq", "result", jid_full(pkt->from), jid_full(pkt->to)); 00328 pkt_id(pkt, res); 00329 pkt_free(pkt); 00330 00331 ns = nad_add_namespace(res->nad, uri_DISCO_INFO, NULL); 00332 nad_append_elem(res->nad, ns, "query", 2); 00333 nad_append_attr(res->nad, -1, "node", uri_AMP); 00334 00335 nad_append_elem(res->nad, ns, "identity", 3); 00336 nad_append_attr(res->nad, -1, "name", "Advanced Message Processing support"); 00337 nad_append_attr(res->nad, -1, "category", "im"); 00338 nad_append_attr(res->nad, -1, "type", "server"); 00339 00340 nad_append_elem(res->nad, ns, "feature", 3); 00341 nad_append_attr(res->nad, -1, "var", uri_AMP); 00342 if (!config->disableActionDrop) { 00343 nad_append_elem(res->nad, ns, "feature", 3); 00344 nad_append_attr(res->nad, -1, "var", uri_AMP_ACTION_DROP); 00345 } 00346 if (!config->disableActionError) { 00347 nad_append_elem(res->nad, ns, "feature", 3); 00348 nad_append_attr(res->nad, -1, "var", uri_AMP_ACTION_ERROR); 00349 } 00350 if (!config->disableActionNotify) { 00351 nad_append_elem(res->nad, ns, "feature", 3); 00352 nad_append_attr(res->nad, -1, "var", uri_AMP_ACTION_NOTIFY); 00353 } 00354 if (!config->disableConditionDeliver) { 00355 nad_append_elem(res->nad, ns, "feature", 3); 00356 nad_append_attr(res->nad, -1, "var", uri_AMP_CONDITION_DELIVER); 00357 } 00358 if (!config->disableConditionExpireAt) { 00359 nad_append_elem(res->nad, ns, "feature", 3); 00360 nad_append_attr(res->nad, -1, "var", uri_AMP_CONDITION_EXPIREAT); 00361 } 00362 if (!config->disableConditionMatchResource) { 00363 nad_append_elem(res->nad, ns, "feature", 3); 00364 nad_append_attr(res->nad, -1, "var", uri_AMP_CONDITION_MATCHRESOURCE); 00365 } 00366 00367 /* tell them */ 00368 pkt_router(res); 00369 00370 return mod_HANDLED; 00371 } 00372 00373 static void _amp_free(module_t mod) { 00374 free(mod->private); 00375 } 00376 00377 DLLEXPORT int module_init(mod_instance_t mi, char *arg) { 00378 module_t mod = mi->mod; 00379 mod_amp_config_t config; 00380 char* option; 00381 00382 if (mod->init) return 0; 00383 00384 config = (mod_amp_config_t) calloc(1, sizeof(struct _mod_amp_config_st)); 00385 00386 config->sm = mod->mm->sm; 00387 option = config_get_one(mod->mm->sm->config, "amp.disableactions.drop", 0); 00388 if (option != NULL) { 00389 log_debug(ZONE, "action Drop disabled in config."); 00390 config->disableActionDrop = 1; 00391 } 00392 option = config_get_one(mod->mm->sm->config, "amp.disableactions.error", 0); 00393 if (option != NULL) { 00394 log_debug(ZONE, "action Error disabled in config."); 00395 config->disableActionError = 1; 00396 } 00397 option = config_get_one(mod->mm->sm->config, "amp.disableactions.alert", 0); 00398 if (option != NULL) { 00399 log_debug(ZONE, "action Alert disabled in config."); 00400 config->disableActionAlert = 1; 00401 } 00402 option = config_get_one(mod->mm->sm->config, "amp.disableactions.notify", 0); 00403 if (option != NULL) { 00404 log_debug(ZONE, "action Notify disabled in config."); 00405 config->disableActionNotify = 1; 00406 } 00407 option = config_get_one(mod->mm->sm->config, "amp.disableconditions.deliver", 0); 00408 if (option != NULL) { 00409 log_debug(ZONE, "condition Deliver disabled in config."); 00410 config->disableConditionDeliver = 1; 00411 } 00412 option = config_get_one(mod->mm->sm->config, "amp.disableconditions.expireat", 0); 00413 if (option != NULL) { 00414 log_debug(ZONE, "condition Expire-At disabled in config."); 00415 config->disableConditionExpireAt = 1; 00416 } 00417 option = config_get_one(mod->mm->sm->config, "amp.disableconditions.matchresource", 0); 00418 if (option != NULL) { 00419 log_debug(ZONE, "condition Match-Resource disabled in config."); 00420 config->disableConditionMatchResource = 1; 00421 } 00422 option = config_get_one(mod->mm->sm->config, "amp.offlinestoragedisabled", 0); 00423 if (option != NULL) { 00424 log_debug(ZONE, "offline storage disabled in config."); 00425 config->offlinestorageDisabled = 1; 00426 } 00427 option = config_get_one(mod->mm->sm->config, "offline.dropmessages", 0); 00428 if (option != NULL) { 00429 log_debug(ZONE, "offline storage disabled in config."); 00430 config->offlinestorageDisabled = 1; 00431 } 00432 00433 mod->private = config; 00434 00435 mod->in_sess = _amp_in_sess; 00436 mod->pkt_user = _amp_pkt_user; 00437 mod->pkt_sm = _amp_pkt_sm; 00438 mod->free = _amp_free; 00439 00440 feature_register(mod->mm->sm, uri_AMP); 00441 00442 return 0; 00443 }