jabberd2  2.2.16
sm/mod_amp.c
Go to the documentation of this file.
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 }