summaryrefslogtreecommitdiffstats
path: root/db_structure.xml
Commit message (Expand)AuthorAgeFilesLines
* Drop unused table file_mapMorris Jobke2015-11-051-61/+0
* Use integer for availability instead of boolRobin McCorkell2015-09-151-2/+2
* Merge pull request #17662 from owncloud/locking-dbThomas Müller2015-08-261-0/+73
|\
| * rename path field to keyRobin Appelman2015-08-031-4/+5
| * Fix db schemaRobin Appelman2015-08-031-1/+34
| * Add database backend for high level lockingRobin Appelman2015-08-031-0/+39
* | Store storage availability in databaseRobin McCorkell2015-07-201-0/+12
|/
* Remove index on file_target of share tableMorris Jobke2015-07-141-7/+0
* Add indexes to speedup DB queriesMorris Jobke2015-07-081-0/+15
* we no longer support webdav locks - no need to keep the columnThomas Müller2015-03-121-83/+0
* set max argument length to 4000Robin Appelman2015-02-251-1/+1
* expand cron argument column to 2048Robin Appelman2015-02-251-1/+1
* Revert "Improve: Index for file_map"Joas Schilling2015-02-131-18/+0
* Revert "Incorporate review changes"Joas Schilling2015-02-131-0/+2
* Incorporate review changesLukas Reschke2015-02-121-2/+0
* Improve: Index for file_mapwindaishi2015-02-121-0/+18
* Revert "add share index"Thomas Müller2014-07-171-12/+0
* shorten userid columns to 64 chars, ref #9186Jörn Friedrich Dreyer2014-06-271-2/+2
* - drop permissions table and related codeThomas Müller2014-06-051-53/+10
* Merge pull request #8806 from owncloud/add_share_indexMorris Jobke2014-06-021-0/+12
|\
| * add share indexFrank Karlitschek2014-05-311-0/+12
* | add another index to the filecache to reduce the number of queries without a ...Frank Karlitschek2014-05-311-0/+15
|/
* # This is a combination of 2 commits.Thomas Müller2014-05-281-1/+1
* Add index on oc_appconfig for appidRobin Appelman2013-12-181-0/+7
* Fix spacing.Andreas Fischer2013-11-261-0/+16
* db-doc: privatedata descriptionAndreas Fischer2013-11-261-1/+6
* db-doc: vcategory* descriptionsAndreas Fischer2013-11-261-2/+8
* db-doc: users descriptionAndreas Fischer2013-11-261-1/+3
* db-doc: jobs descriptionAndreas Fischer2013-11-261-1/+4
* db-doc: share descriptionAndreas Fischer2013-11-261-1/+14
* db-doc: properties descriptionAndreas Fischer2013-11-261-1/+4
* db-doc: preferences descriptionAndreas Fischer2013-11-261-1/+6
* db-doc: locks descriptionAndreas Fischer2013-11-261-1/+4
* db-doc: groups descriptionAndreas Fischer2013-11-261-1/+3
* db-doc: group_admin reduction noteAndreas Fischer2013-11-261-0/+3
* db-doc: group_admin descriptionAndreas Fischer2013-11-261-1/+7
* db-doc: group_user descriptionAndreas Fischer2013-11-261-1/+7
* Remove the dbprefix from comments. It has no information.Andreas Fischer2013-11-261-6/+6
* db-doc: permissions descriptionAndreas Fischer2013-11-261-1/+6
* Prettier ASCII lists. ^^Andreas Fischer2013-11-261-9/+9
* db-doc: filecache descriptionAndreas Fischer2013-11-261-1/+14
* db-doc: mimetypes descriptionAndreas Fischer2013-11-261-1/+6
* db-doc: storages descriptionAndreas Fischer2013-11-261-1/+7
* db-doc: appconfig descriptionAndreas Fischer2013-11-261-0/+5
* Add some more indexes to tables to improve db queriesBart Visscher2013-11-111-0/+30
* fixing Specified key was too long; max key length is 767 bytesThomas Mueller2013-11-021-1/+1
* fix the privatedata key value storeFrank Karlitschek2013-10-301-0/+63
* Merge branch 'master' into sharing_mail_notification_masterBjoern Schiessle2013-09-231-1/+1
|\
| * setting a default on filecache column unencrypted_sizeThomas Müller2013-09-201-1/+1
* | Merge branch 'master' into sharing_mail_notification_masterBjoern Schiessle2013-09-021-3/+1
|\|
ass="p">; if ((name = g_hash_table_lookup (attrs, "name")) == NULL) { msg_err ("worker param tag must have \"name\" attribute"); return FALSE; } g_hash_table_insert (wrk->params, name, memory_pool_strdup (cfg->cfg_pool, data)); return TRUE; } gboolean worker_handle_type (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { struct worker_conf *wrk = ctx->section_pointer; if (g_ascii_strcasecmp (data, "normal") == 0) { wrk->type = TYPE_WORKER; wrk->has_socket = TRUE; } else if (g_ascii_strcasecmp (data, "controller") == 0) { wrk->type = TYPE_CONTROLLER; wrk->has_socket = TRUE; } else if (g_ascii_strcasecmp (data, "lmtp") == 0) { wrk->type = TYPE_LMTP; wrk->has_socket = TRUE; } else if (g_ascii_strcasecmp (data, "smtp") == 0) { wrk->type = TYPE_SMTP; wrk->has_socket = TRUE; } else if (g_ascii_strcasecmp (data, "fuzzy") == 0) { wrk->type = TYPE_FUZZY; wrk->has_socket = FALSE; } else { msg_err ("unknown worker type: %s", data); return FALSE; } return TRUE; } gboolean worker_handle_bind (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { struct worker_conf *wrk = ctx->section_pointer; if (!parse_bind_line (cfg, wrk, data)) { msg_err ("cannot parse bind_socket: %s", data); return FALSE; } return TRUE; } /* Factors section */ gboolean handle_factor (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { char *name, *err; double *value; if ((name = g_hash_table_lookup (attrs, "name")) == NULL) { msg_err ("factor tag must have \"name\" attribute"); return FALSE; } value = memory_pool_alloc (cfg->cfg_pool, sizeof (double)); errno = 0; *value = strtod (data, &err); if (errno != 0 || (err != NULL && *err != 0)) { msg_err ("invalid number: %s, %s", data, strerror (errno)); return FALSE; } g_hash_table_insert (cfg->factors, name, value); return TRUE; } /* Modules section */ gboolean handle_module_opt (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { char *name, *val; GList *cur_opt; struct module_opt *cur; gboolean is_lua = FALSE; if ((name = g_hash_table_lookup (attrs, "name")) == NULL) { msg_err ("param tag must have \"name\" attribute"); return FALSE; } /* Check for lua */ if ((val = g_hash_table_lookup (attrs, "lua")) != NULL) { if (g_ascii_strcasecmp (val, "yes") == 0) { is_lua = TRUE; } } cur_opt = ctx->section_pointer; /* Insert option */ cur = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct module_opt)); cur->param = name; cur->value = data; cur->is_lua = is_lua; ctx->section_pointer = g_list_prepend (ctx->section_pointer, cur); return TRUE; } /* Handle lua tag */ gboolean handle_lua (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { gchar *val, *cur_dir, *lua_dir, *lua_file, *tmp1, *tmp2; lua_State *L = cfg->lua_state; /* First check for global variable 'config' */ lua_getglobal (L, "config"); if (lua_isnil (L, -1)) { /* Assign global table to set up attributes */ lua_newtable (L); lua_setglobal (L, "config"); /* Now config table can be used for configuring rspamd */ } /* First check "src" attribute */ if ((val = g_hash_table_lookup (attrs, "src")) != NULL) { /* Chdir */ tmp1 = g_strdup (val); tmp2 = g_strdup (val); lua_dir = dirname (tmp1); lua_file = basename (tmp2); if (lua_dir && lua_file) { cur_dir = g_malloc (PATH_MAX); if (getcwd (cur_dir, PATH_MAX) != NULL && chdir (lua_dir) != -1) { if (luaL_dofile (L, lua_file) != 0) { msg_err ("cannot load lua file %s: %s", val, lua_tostring (L, -1)); if (chdir (cur_dir) == -1) { msg_err ("cannot chdir to %s: %s", cur_dir, strerror (errno));; } g_free (cur_dir); g_free (tmp1); g_free (tmp2); return FALSE; } } else { msg_err ("cannot chdir to %s: %s", lua_dir, strerror (errno));; if (chdir (cur_dir) == -1) { msg_err ("cannot chdir to %s: %s", cur_dir, strerror (errno));; } g_free (cur_dir); g_free (tmp1); g_free (tmp2); return FALSE; } if (chdir (cur_dir) == -1) { msg_err ("cannot chdir to %s: %s", cur_dir, strerror (errno));; } g_free (cur_dir); g_free (tmp1); g_free (tmp2); } else { msg_err ("directory for file %s does not exists", val); } } else if (data != NULL && *data != '\0') { /* Try to load a string */ if (luaL_dostring (L, data) != 0) { msg_err ("cannot load lua chunk: %s", lua_tostring (L, -1)); return FALSE; } } return TRUE; } /* Modules section */ gboolean handle_module_path (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { struct stat st; struct script_module *cur; glob_t globbuf; char *pattern; size_t len; int i; if (stat (data, &st) == -1) { msg_err ("cannot stat path %s, %s", data, strerror (errno)); return FALSE; } /* Handle directory */ if (S_ISDIR (st.st_mode)) { globbuf.gl_offs = 0; len = strlen (data) + sizeof ("*.lua"); pattern = g_malloc (len); snprintf (pattern, len, "%s%s", data, "*.lua"); if (glob (pattern, GLOB_DOOFFS, NULL, &globbuf) == 0) { for (i = 0; i < globbuf.gl_pathc; i ++) { cur = memory_pool_alloc (cfg->cfg_pool, sizeof (struct script_module)); cur->path = memory_pool_strdup (cfg->cfg_pool, globbuf.gl_pathv[i]); cfg->script_modules = g_list_prepend (cfg->script_modules, cur); } globfree (&globbuf); g_free (pattern); } else { msg_err ("glob failed: %s", strerror (errno)); g_free (pattern); return FALSE; } } else { /* Handle single file */ cur = memory_pool_alloc (cfg->cfg_pool, sizeof (struct script_module)); cur->path = memory_pool_strdup (cfg->cfg_pool, data); cfg->script_modules = g_list_prepend (cfg->script_modules, cur); } return TRUE; } /* Variables and composites */ gboolean handle_variable (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { gchar *val; if ((val = g_hash_table_lookup (attrs, "name")) == NULL) { msg_err ("'name' attribute is required for tag 'variable'"); return FALSE; } g_hash_table_insert (cfg->variables, val, memory_pool_strdup (cfg->cfg_pool, data)); return TRUE; } gboolean handle_composite (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { gchar *val; struct expression *expr; if ((val = g_hash_table_lookup (attrs, "name")) == NULL) { msg_err ("'name' attribute is required for tag 'composite'"); return FALSE; } if ((expr = parse_expression (cfg->cfg_pool, data)) == NULL) { msg_err ("cannot parse composite expression: %s", data); return FALSE; } g_hash_table_insert (cfg->composite_symbols, val, expr); return TRUE; } /* View section */ gboolean handle_view_ip (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { struct rspamd_view *view = ctx->section_pointer; if (!add_view_ip (view, data)) { msg_err ("invalid ip line in view definition: ip = '%s'", data); return FALSE; } return TRUE; } gboolean handle_view_client_ip (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { struct rspamd_view *view = ctx->section_pointer; if (!add_view_client_ip (view, data)) { msg_err ("invalid ip line in view definition: ip = '%s'", data); return FALSE; } return TRUE; } gboolean handle_view_from (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { struct rspamd_view *view = ctx->section_pointer; if (!add_view_from (view, data)) { msg_err ("invalid from line in view definition: ip = '%s'", data); return FALSE; } return TRUE; } gboolean handle_view_symbols (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { struct rspamd_view *view = ctx->section_pointer; if (!add_view_symbols (view, data)) { msg_err ("invalid symbols line in view definition: ip = '%s'", data); return FALSE; } cfg->domain_settings_str = memory_pool_strdup (cfg->cfg_pool, data); return TRUE; } /* Settings */ gboolean handle_user_settings (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { if (!read_settings (data, cfg, cfg->user_settings)) { msg_err ("cannot read settings %s", data); return FALSE; } cfg->user_settings_str = memory_pool_strdup (cfg->cfg_pool, data); return TRUE; } gboolean handle_domain_settings (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { if (!read_settings (data, cfg, cfg->domain_settings)) { msg_err ("cannot read settings %s", data); return FALSE; } return TRUE; } /* Classifier */ gboolean handle_classifier_tokenizer (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { struct classifier_config *ccf = ctx->section_pointer; if ((ccf->tokenizer = get_tokenizer (data)) == NULL) { msg_err ("unknown tokenizer %s", data); return FALSE; } return TRUE; } gboolean handle_classifier_opt (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { struct classifier_config *ccf = ctx->section_pointer; gchar *val; if ((val = g_hash_table_lookup (attrs, "name")) == NULL) { msg_err ("'name' attribute is required for tag 'option'"); return FALSE; } g_hash_table_insert (ccf->opts, val, memory_pool_strdup (cfg->cfg_pool, data)); return TRUE; } /* Statfile */ gboolean handle_statfile_normalizer (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { struct statfile *st = ctx->section_pointer; if (!parse_normalizer (cfg, st, data)) { msg_err ("cannot parse normalizer string: %s", data); return FALSE; } return TRUE; } gboolean handle_statfile_binlog (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { struct statfile *st = ctx->section_pointer; if (st->binlog == NULL) { st->binlog = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct statfile_binlog_params)); } if (g_ascii_strcasecmp (data, "master") == 0) { st->binlog->affinity = AFFINITY_MASTER; } else if (g_ascii_strcasecmp (data, "slave") == 0) { st->binlog->affinity = AFFINITY_SLAVE; } else { st->binlog->affinity = AFFINITY_NONE; } return TRUE; } gboolean handle_statfile_binlog_rotate (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { struct statfile *st = ctx->section_pointer; if (st->binlog == NULL) { st->binlog = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct statfile_binlog_params)); } st->binlog->rotate_time = parse_seconds (data); return TRUE; } gboolean handle_statfile_binlog_master (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { struct statfile *st = ctx->section_pointer; if (st->binlog == NULL) { st->binlog = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct statfile_binlog_params)); } if (!parse_host_port (data, &st->binlog->master_addr, &st->binlog->master_port)) { msg_err ("cannot parse master address: %s", data); return FALSE; } return TRUE; } /* Common handlers */ gboolean xml_handle_string (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { /* Simply assign pointer to pointer */ gchar **dest; dest = (char **)G_STRUCT_MEMBER_P (dest_struct, offset); *dest = memory_pool_strdup (cfg->cfg_pool, data); return TRUE; } gboolean xml_handle_size (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { gsize *dest; dest = (gsize *)G_STRUCT_MEMBER_P (dest_struct, offset); *dest = parse_limit (data); return TRUE; } gboolean xml_handle_seconds (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { time_t *dest; dest = (time_t *)G_STRUCT_MEMBER_P (dest_struct, offset); *dest = parse_seconds (data); return TRUE; } gboolean xml_handle_boolean (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { gboolean *dest; dest = (gboolean *)G_STRUCT_MEMBER_P (dest_struct, offset); *dest = parse_flag (data); /* gchar -> gboolean */ if (*dest == -1) { msg_err ("bad boolean: %s", data); return FALSE; } else if (*dest == 1) { *dest = TRUE; } return TRUE; } gboolean xml_handle_double (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { double *dest; char *err = NULL; dest = (double *)G_STRUCT_MEMBER_P (dest_struct, offset); errno = 0; *dest = strtod (data, &err); if (errno != 0 || (err != NULL && *err != 0)) { msg_err ("invalid number: %s, %s", data, strerror (errno)); return FALSE; } return TRUE; } gboolean xml_handle_int (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { int *dest; char *err = NULL; dest = (int *)G_STRUCT_MEMBER_P (dest_struct, offset); errno = 0; *dest = strtol (data, &err, 10); if (errno != 0 || (err != NULL && *err != 0)) { msg_err ("invalid number: %s, %s", data, strerror (errno)); return FALSE; } return TRUE; } gboolean xml_handle_uint32 (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { uint32_t *dest; char *err = NULL; dest = (uint32_t *)G_STRUCT_MEMBER_P (dest_struct, offset); errno = 0; *dest = strtoul (data, &err, 10); if (errno != 0 || (err != NULL && *err != 0)) { msg_err ("invalid number: %s, %s", data, strerror (errno)); return FALSE; } return TRUE; } gboolean xml_handle_uint16 (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { uint16_t *dest; char *err = NULL; dest = (uint16_t *)G_STRUCT_MEMBER_P (dest_struct, offset); errno = 0; *dest = strtoul (data, &err, 10); if (errno != 0 || (err != NULL && *err != 0)) { msg_err ("invalid number: %s, %s", data, strerror (errno)); return FALSE; } return TRUE; } /* XML callbacks */ void rspamd_xml_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error) { struct rspamd_xml_userdata *ud = user_data; struct classifier_config *ccf; gchar *res; switch (ud->state) { case XML_READ_START: if (g_ascii_strcasecmp (element_name, "rspamd") != 0) { /* Invalid XML, it must contains root element <rspamd></rspamd> */ *error = g_error_new (xml_error_quark (), XML_START_MISSING, "start element is missing"); ud->state = XML_ERROR; } else { ud->state = XML_READ_PARAM; } break; case XML_READ_PARAM: /* Read parameter name and try to find among list of known parameters */ if (g_ascii_strcasecmp (element_name, "module") == 0) { /* Read module data */ if (extract_attr ("name", attribute_names, attribute_values, &res)) { ud->parent_pointer = memory_pool_strdup (ud->cfg->cfg_pool, res); /* Empty list */ ud->section_pointer = NULL; ud->state = XML_READ_MODULE; } else { *error = g_error_new (xml_error_quark (), XML_PARAM_MISSING, "param 'name' is required for tag 'module'"); ud->state = XML_ERROR; } } else if (g_ascii_strcasecmp (element_name, "factors") == 0) { ud->state = XML_READ_FACTORS; } else if (g_ascii_strcasecmp (element_name, "modules") == 0) { ud->state = XML_READ_MODULES; } else if (g_ascii_strcasecmp (element_name, "logging") == 0) { ud->state = XML_READ_LOGGING; } else if (g_ascii_strcasecmp (element_name, "metric") == 0) { if (extract_attr ("name", attribute_names, attribute_values, &res)) { g_strlcpy (ud->section_name, res, sizeof (ud->section_name)); ud->state = XML_READ_METRIC; /* Create object */ ud->section_pointer = memory_pool_alloc0 (ud->cfg->cfg_pool, sizeof (struct metric)); } else { *error = g_error_new (xml_error_quark (), XML_PARAM_MISSING, "param 'name' is required for tag 'metric'"); ud->state = XML_ERROR; } } else if (g_ascii_strcasecmp (element_name, "classifier") == 0) { if (extract_attr ("type", attribute_names, attribute_values, &res)) { ud->state = XML_READ_CLASSIFIER; /* Create object */ ccf = check_classifier_cfg (ud->cfg, NULL); if ((ccf->classifier = get_classifier (res)) == NULL) { *error = g_error_new (xml_error_quark (), XML_INVALID_ATTR, "invalid classifier type: %s", res); ud->state = XML_ERROR; } else { ud->section_pointer = ccf; } } else { *error = g_error_new (xml_error_quark (), XML_PARAM_MISSING, "param 'type' is required for tag 'classifier'"); ud->state = XML_ERROR; } } else if (g_ascii_strcasecmp (element_name, "worker") == 0) { ud->state = XML_READ_WORKER; /* Create object */ ud->section_pointer = check_worker_conf (ud->cfg, NULL); } else if (g_ascii_strcasecmp (element_name, "view") == 0) { ud->state = XML_READ_VIEW; /* Create object */ ud->section_pointer = init_view (ud->cfg->cfg_pool); } else { /* Extract other tags */ g_strlcpy (ud->section_name, element_name, sizeof (ud->section_name)); ud->cur_attrs = process_attrs (ud->cfg, attribute_names, attribute_values); ud->state = XML_READ_VALUE; } break; case XML_READ_CLASSIFIER: if (g_ascii_strcasecmp (element_name, "statfile") == 0) { ud->state = XML_READ_STATFILE; /* Now section pointer is statfile and parent pointer is classifier */ ud->parent_pointer = ud->section_pointer; ud->section_pointer = memory_pool_alloc0 (ud->cfg->cfg_pool, sizeof (struct statfile)); } else { g_strlcpy (ud->section_name, element_name, sizeof (ud->section_name)); /* Save attributes */ ud->cur_attrs = process_attrs (ud->cfg, attribute_names, attribute_values); } break; case XML_READ_MODULE: case XML_READ_METRIC: case XML_READ_MODULES: case XML_READ_FACTORS: case XML_READ_STATFILE: case XML_READ_WORKER: case XML_READ_LOGGING: case XML_READ_VIEW: g_strlcpy (ud->section_name, element_name, sizeof (ud->section_name)); /* Save attributes */ ud->cur_attrs = process_attrs (ud->cfg, attribute_names, attribute_values); break; default: *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "element %s is unexpected in this state", element_name); break; } } #define CHECK_TAG(x, required) \ do { \ if (g_ascii_strcasecmp (element_name, (x)) == 0) { \ ud->state = XML_READ_PARAM; \ res = TRUE; \ } \ else { \ res = FALSE; \ if ((required) == TRUE) { \ *error = g_error_new (xml_error_quark (), XML_UNMATCHED_TAG, "element %s is unexpected in this state, expected %s", element_name, (x)); \ ud->state = XML_ERROR; \ } \ } \ } while (0) void rspamd_xml_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { struct rspamd_xml_userdata *ud = user_data; struct metric *m; struct classifier_config *ccf; struct statfile *st; gboolean res; switch (ud->state) { case XML_READ_MODULE: CHECK_TAG ("module", FALSE); if (res) { if (ud->section_pointer != NULL) { g_hash_table_insert (ud->cfg->modules_opts, ud->parent_pointer, ud->section_pointer); ud->parent_pointer = NULL; ud->section_pointer = NULL; } } break; case XML_READ_CLASSIFIER: CHECK_TAG ("classifier", FALSE); if (res) { ccf = ud->section_pointer; if (ccf->statfiles == NULL) { *error = g_error_new (xml_error_quark (), XML_PARAM_MISSING, "classifier cannot contains no statfiles"); ud->state = XML_ERROR; return; } ud->cfg->classifiers = g_list_prepend (ud->cfg->classifiers, ccf); } break; case XML_READ_STATFILE: CHECK_TAG ("statfile", FALSE); if (res) { ccf = ud->parent_pointer; st = ud->section_pointer; /* Check statfile and insert it into classifier */ if (st->path == NULL || st->size == 0 || st->symbol == NULL) { *error = g_error_new (xml_error_quark (), XML_PARAM_MISSING, "not enough arguments in statfile definition"); ud->state = XML_ERROR; return; } ccf->statfiles = g_list_prepend (ccf->statfiles, st); ud->cfg->statfiles = g_list_prepend (ud->cfg->statfiles, st); g_hash_table_insert (ud->cfg->classifiers_symbols, st->symbol, ccf); ud->section_pointer = ccf; ud->parent_pointer = NULL; ud->state = XML_READ_CLASSIFIER; } break; case XML_READ_FACTORS: CHECK_TAG ("factors", FALSE); break; case XML_READ_MODULES: CHECK_TAG ("modules", FALSE); break; case XML_READ_METRIC: CHECK_TAG ("metric", FALSE); if (res) { m = ud->section_pointer; if (m->name == NULL) { *error = g_error_new (xml_error_quark (), XML_PARAM_MISSING, "metric attribute \"name\" is required but missing"); ud->state = XML_ERROR; return; } if (m->classifier == NULL) { m->classifier = get_classifier ("winnow"); } g_hash_table_insert (ud->cfg->metrics, m->name, m); ud->cfg->metrics_list = g_list_prepend (ud->cfg->metrics_list, m); } break; case XML_READ_WORKER: CHECK_TAG ("worker", FALSE); if (res) { /* Insert object to list */ ud->cfg->workers = g_list_prepend (ud->cfg->workers, ud->section_pointer); } break; case XML_READ_VIEW: CHECK_TAG ("view", FALSE); if (res) { /* Insert object to list */ ud->cfg->views = g_list_prepend (ud->cfg->views, ud->section_pointer); } break; case XML_READ_VALUE: /* Check tags parity */ CHECK_TAG (ud->section_name, TRUE); break; case XML_READ_LOGGING: CHECK_TAG ("logging", FALSE); break; case XML_READ_PARAM: if (g_ascii_strcasecmp (element_name, "rspamd") == 0) { /* End of document */ ud->state = XML_END; } else { *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "element %s is umatched", element_name); ud->state = XML_ERROR; } break; default: ud->state = XML_ERROR; break; } } #undef CHECK_TAG void rspamd_xml_text (GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error) { struct rspamd_xml_userdata *ud = user_data; char *val; struct config_file *cfg = ud->cfg; if (*text == '\n') { return; } val = xml_asciiz_string (cfg->cfg_pool, text, text_len); switch (ud->state) { case XML_READ_MODULE: if (!call_param_handler (ud, ud->section_name, val, ud->section_pointer, XML_SECTION_MODULE)) { *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag '%s' data: %s", ud->section_name, val); ud->state = XML_ERROR; } break; case XML_READ_MODULES: if (!call_param_handler (ud, ud->section_name, val, cfg, XML_SECTION_MODULES)) { *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag '%s' data: %s", ud->section_name, val); ud->state = XML_ERROR; } break; case XML_READ_CLASSIFIER: if (!call_param_handler (ud, ud->section_name, val, ud->section_pointer, XML_SECTION_CLASSIFIER)) { *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag '%s' data: %s", ud->section_name, val); ud->state = XML_ERROR; } break; case XML_READ_STATFILE: if (!call_param_handler (ud, ud->section_name, val, ud->section_pointer, XML_SECTION_STATFILE)) { *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag '%s' data: %s", ud->section_name, val); ud->state = XML_ERROR; } break; case XML_READ_FACTORS: if (!call_param_handler (ud, ud->section_name, val, cfg, XML_SECTION_FACTORS)) { *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag '%s' data: %s", ud->section_name, val); ud->state = XML_ERROR; } break; case XML_READ_METRIC: if (!call_param_handler (ud, ud->section_name, val, ud->section_pointer, XML_SECTION_METRIC)) { *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag '%s' data: %s", ud->section_name, val); ud->state = XML_ERROR; } break; case XML_READ_WORKER: if (!call_param_handler (ud, ud->section_name, val, ud->section_pointer, XML_SECTION_WORKER)) { *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag '%s' data: %s", ud->section_name, val); ud->state = XML_ERROR; } break; case XML_READ_VIEW: if (!call_param_handler (ud, ud->section_name, val, ud->section_pointer, XML_SECTION_VIEW)) { *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag '%s' data: %s", ud->section_name, val); ud->state = XML_ERROR; } break; case XML_READ_VALUE: if (!call_param_handler (ud, ud->section_name, val, cfg, XML_SECTION_MAIN)) { *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag '%s' data: %s", ud->section_name, val); ud->state = XML_ERROR; } break; case XML_READ_LOGGING: if (!call_param_handler (ud, ud->section_name, val, cfg, XML_SECTION_LOGGING)) { *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag '%s' data: %s", ud->section_name, val); ud->state = XML_ERROR; } break; default: ud->state = XML_ERROR; break; } } static inline const gchar * xml_state_to_string (struct rspamd_xml_userdata *ud) { switch (ud->state) { case XML_READ_START: return "read start tag"; case XML_READ_PARAM: return "read param"; case XML_READ_MODULE: return "read module section"; case XML_READ_MODULES: return "read modules section"; case XML_READ_CLASSIFIER: return "read classifier section"; case XML_READ_STATFILE: return "read statfile section"; case XML_READ_FACTORS: return "read factors section"; case XML_READ_METRIC: return "read metric section"; case XML_READ_WORKER: return "read worker section"; case XML_READ_VIEW: return "read view section"; case XML_READ_LOGGING: return "read logging section"; case XML_READ_VALUE: return "read value"; case XML_ERROR: return "error occured"; case XML_END: return "read final tag"; } /* Unreached */ return "unknown state"; } void rspamd_xml_error (GMarkupParseContext *context, GError *error, gpointer user_data) { struct rspamd_xml_userdata *ud = user_data; msg_err ("xml parser error: %s, at state \"%s\"", error->message, xml_state_to_string (ud)); } /* Dumper part */ /* Dump specific sections */ /* Dump main section variables */ static gboolean xml_dump_main (struct config_file *cfg, FILE *f) { char *escaped_str; /* Print header comment */ fprintf (f, "<!-- Main section -->" EOL); escaped_str = g_markup_escape_text (cfg->temp_dir, -1); fprintf (f, "<tempdir>%s</tempdir>" EOL, escaped_str); g_free (escaped_str); escaped_str = g_markup_escape_text (cfg->pid_file, -1); fprintf (f, "<pidfile>%s</pidfile>" EOL, escaped_str); g_free (escaped_str); escaped_str = g_markup_escape_text (cfg->filters_str, -1); fprintf (f, "<filters>%s</filters>" EOL, escaped_str); g_free (escaped_str); if (cfg->user_settings_str) { escaped_str = g_markup_escape_text (cfg->user_settings_str, -1); fprintf (f, "<user_settings>%s</user_settings>" EOL, escaped_str); g_free (escaped_str); } if (cfg->domain_settings_str) { escaped_str = g_markup_escape_text (cfg->domain_settings_str, -1); fprintf (f, "<domain_settings>%s</domain_settings>" EOL, escaped_str); g_free (escaped_str); } fprintf (f, "<statfile_pool_size>%llu</statfile_pool_size>" EOL, (long long unsigned)cfg->max_statfile_size); if (cfg->checksum) { escaped_str = g_markup_escape_text (cfg->checksum, -1); fprintf (f, "<checksum>%s</checksum>" EOL, escaped_str); g_free (escaped_str); } fprintf (f, "<raw_mode>%s</raw_mode>" EOL, cfg->raw_mode ? "yes" : "no"); /* Print footer comment */ fprintf (f, "<!-- End of main section -->" EOL EOL); return TRUE; } /* Dump variables section */ static void xml_variable_callback (gpointer key, gpointer value, gpointer user_data) { FILE *f = user_data; char *escaped_key, *escaped_value; escaped_key = g_markup_escape_text (key, -1); escaped_value = g_markup_escape_text (value, -1); fprintf (f, "<variable name=\"%s\">%s</variable>" EOL, escaped_key, escaped_value); g_free (escaped_key); g_free (escaped_value); } static gboolean xml_dump_variables (struct config_file *cfg, FILE *f) { /* Print header comment */ fprintf (f, "<!-- Variables section -->" EOL); /* Iterate through variables */ g_hash_table_foreach (cfg->variables, xml_variable_callback, (gpointer)f); /* Print footer comment */ fprintf (f, "<!-- End of variables section -->" EOL EOL); return TRUE; } /* Dump factors section */ static void xml_factors_callback (gpointer key, gpointer value, gpointer user_data) { FILE *f = user_data; char *escaped_key; escaped_key = g_markup_escape_text (key, -1); fprintf (f, " <factor name=\"%s\">%.2f</factor>" EOL, escaped_key, *(double *)value); g_free (escaped_key); } static gboolean xml_dump_factors (struct config_file *cfg, FILE *f) { /* Print header comment */ fprintf (f, "<!-- Factors section -->" EOL "<factors>" EOL ); /* Iterate through variables */ g_hash_table_foreach (cfg->factors, xml_factors_callback, (gpointer)f); /* Grow factor */ fprintf (f, " <grow_factor>%.2f</grow_factor>" EOL, cfg->grow_factor); /* Print footer comment */ fprintf (f, "</factors>" EOL "<!-- End of factors section -->" EOL EOL); return TRUE; } /* Composites section */ static void xml_composite_callback (gpointer key, gpointer value, gpointer user_data) { FILE *f = user_data; struct expression *expr; char *escaped_key, *escaped_value; expr = value; escaped_key = g_markup_escape_text (key, -1); escaped_value = g_markup_escape_text (expr->orig, -1); fprintf (f, "<composite name=\"%s\">%s</composite>" EOL, escaped_key, escaped_value); g_free (escaped_key); g_free (escaped_value); } static gboolean xml_dump_composites (struct config_file *cfg, FILE *f) { /* Print header comment */ fprintf (f, "<!-- Composites section -->" EOL); /* Iterate through variables */ g_hash_table_foreach (cfg->composite_symbols, xml_composite_callback, (gpointer)f); /* Print footer comment */ fprintf (f, "<!-- End of composites section -->" EOL EOL); return TRUE; } /* Workers */ static void xml_worker_param_callback (gpointer key, gpointer value, gpointer user_data) { FILE *f = user_data; char *escaped_key, *escaped_value; escaped_key = g_markup_escape_text (key, -1); escaped_value = g_markup_escape_text (value, -1); fprintf (f, " <param name=\"%s\">%s</param>" EOL, escaped_key, escaped_value); g_free (escaped_key); g_free (escaped_value); } static gboolean xml_dump_workers (struct config_file *cfg, FILE *f) { GList *cur; struct worker_conf *wrk; char *escaped_str; /* Print header comment */ fprintf (f, "<!-- Workers section -->" EOL); /* Iterate through list */ cur = g_list_first (cfg->workers); while (cur) { wrk = cur->data; fprintf (f, "<worker>" EOL); switch (wrk->type) { case TYPE_WORKER: fprintf (f, " <type>normal</type>" EOL); break; case TYPE_CONTROLLER: fprintf (f, " <type>controller</type>" EOL); break; case TYPE_FUZZY: fprintf (f, " <type>fuzzy</type>" EOL); break; case TYPE_LMTP: fprintf (f, " <type>lmtp</type>" EOL); break; case TYPE_SMTP: fprintf (f, " <type>smtp</type>" EOL); break; } escaped_str = g_markup_escape_text (wrk->bind_host, -1); fprintf (f, " <bind_socket>%s</bind_socket>" EOL, escaped_str); g_free (escaped_str); fprintf (f, " <count>%u</count>" EOL, wrk->count); fprintf (f, " <maxfiles>%u</maxfiles>" EOL, wrk->rlimit_nofile); fprintf (f, " <maxcore>%u</maxcore>" EOL, wrk->rlimit_maxcore); /* Now dump other attrs */ fprintf (f, "<!-- Other params -->" EOL); g_hash_table_foreach (wrk->params, xml_worker_param_callback, f); fprintf (f, "</worker>" EOL); cur = g_list_next (cur); } /* Print footer comment */ fprintf (f, "<!-- End of workers section -->" EOL EOL); return TRUE; } /* Modules dump */ static void xml_module_callback (gpointer key, gpointer value, gpointer user_data) { FILE *f = user_data; char *escaped_key, *escaped_value; GList *cur; struct module_opt *opt; escaped_key = g_markup_escape_text (key, -1); fprintf (f, "<!-- %s -->" EOL, escaped_key); fprintf (f, "<module name=\"%s\">" EOL, escaped_key); g_free (escaped_key); cur = g_list_first (value); while (cur) { opt = cur->data; escaped_key = g_markup_escape_text (opt->param, -1); escaped_value = g_markup_escape_text (opt->value, -1); fprintf (f, " <option name=\"%s\">%s</option>" EOL, escaped_key, escaped_value); g_free (escaped_key); g_free (escaped_value); cur = g_list_next (cur); } fprintf (f, "</module>" EOL EOL); } static gboolean xml_dump_modules (struct config_file *cfg, FILE *f) { /* Print header comment */ fprintf (f, "<!-- Modules section -->" EOL); /* Iterate through variables */ g_hash_table_foreach (cfg->modules_opts, xml_module_callback, (gpointer)f); /* Print footer comment */ fprintf (f, "<!-- End of modules section -->" EOL EOL); return TRUE; } /* Classifiers dump */ static void xml_classifier_callback (gpointer key, gpointer value, gpointer user_data) { FILE *f = user_data; char *escaped_key, *escaped_value; escaped_key = g_markup_escape_text (key, -1); escaped_value = g_markup_escape_text (value, -1); fprintf (f, " <option name=\"%s\">%s</option>" EOL, escaped_key, escaped_value); g_free (escaped_key); g_free (escaped_value); } static gboolean xml_dump_classifiers (struct config_file *cfg, FILE *f) { GList *cur, *cur_st; struct classifier_config *ccf; struct statfile *st; /* Print header comment */ fprintf (f, "<!-- Classifiers section -->" EOL); /* Iterate through classifiers */ cur = g_list_first (cfg->classifiers); while (cur) { ccf = cur->data; fprintf (f, "<classifier type=\"%s\">" EOL, ccf->classifier->name); fprintf (f, " <tokenizer>%s</tokenizer>" EOL, ccf->tokenizer->name); fprintf (f, " <metric>%s</metric>" EOL, ccf->metric); g_hash_table_foreach (ccf->opts, xml_classifier_callback, f); /* Statfiles */ cur_st = g_list_first (ccf->statfiles); while (cur_st) { st = cur_st->data; fprintf (f, " <statfile>" EOL); fprintf (f, " <symbol>%s</symbol>" EOL " <size>%lu</size>" EOL " <path>%s</path>" EOL, st->symbol, (long unsigned)st->size, st->path); fprintf (f, " <normalizer>%s</normalizer>" EOL, st->normalizer_str); /* Binlog */ if (st->binlog) { if (st->binlog->affinity == AFFINITY_MASTER) { fprintf (f, " <binlog>master</binlog>" EOL); } else if (st->binlog->affinity == AFFINITY_SLAVE) { fprintf (f, " <binlog>slave</binlog>" EOL); fprintf (f, " <binlog_master>%s:%d</binlog_master>" EOL, inet_ntoa (st->binlog->master_addr), ntohs (st->binlog->master_port)); } fprintf (f, " <binlog_rotate>%lu</binlog_rotate>" EOL, (long unsigned)st->binlog->rotate_time); } fprintf (f, " </statfile>" EOL); cur_st = g_list_next (cur_st); } fprintf (f, "</classifier>" EOL); cur = g_list_next (cur); } /* Print footer comment */ fprintf (f, "<!-- End of classifiers section -->" EOL EOL); return TRUE; } /* Logging section */ static gboolean xml_dump_logging (struct config_file *cfg, FILE *f) { gchar *escaped_value; /* Print header comment */ fprintf (f, "<!-- Logging section -->" EOL); fprintf (f, "<logging>" EOL); /* Level */ if (cfg->log_level < G_LOG_LEVEL_WARNING) { fprintf (f, " <level>error</level>" EOL); } else if (cfg->log_level < G_LOG_LEVEL_MESSAGE) { fprintf (f, " <level>warning</level>" EOL); } else if (cfg->log_level < G_LOG_LEVEL_DEBUG) { fprintf (f, " <level>info</level>" EOL); } else { fprintf (f, " <level>debug</level>" EOL); } /* Other options */ fprintf (f, " <log_urls>%s</log_urls>" EOL, cfg->log_urls ? "yes" : "no"); if (cfg->log_buf_size != 0) { fprintf (f, " <log_buffer>%u</log_buffer>" EOL, (unsigned)cfg->log_buf_size); } if (cfg->debug_ip_map != NULL) { escaped_value = g_markup_escape_text (cfg->debug_ip_map, -1); fprintf (f, " <debug_ip>%s</debug_ip>" EOL, escaped_value); g_free (escaped_value); } /* Handle type */ if (cfg->log_type == RSPAMD_LOG_FILE) { escaped_value = g_markup_escape_text (cfg->log_file, -1); fprintf (f, " <type filename=\"%s\">file</type>" EOL, escaped_value); g_free (escaped_value); } else if (cfg->log_type == RSPAMD_LOG_CONSOLE) { fprintf (f, " <type>console</type>" EOL); } else if (cfg->log_type == RSPAMD_LOG_SYSLOG) { escaped_value = NULL; switch (cfg->log_facility) { case LOG_AUTH: escaped_value = "LOG_AUTH"; break; case LOG_CRON: escaped_value = "LOG_CRON"; break; case LOG_DAEMON: escaped_value = "LOG_DAEMON"; break; case LOG_MAIL: escaped_value = "LOG_MAIL"; break; case LOG_USER: escaped_value = "LOG_USER"; break; case LOG_LOCAL0: escaped_value = "LOG_LOCAL0"; break; case LOG_LOCAL1: escaped_value = "LOG_LOCAL1"; break; case LOG_LOCAL2: escaped_value = "LOG_LOCAL2"; break; case LOG_LOCAL3: escaped_value = "LOG_LOCAL3"; break; case LOG_LOCAL4: escaped_value = "LOG_LOCAL4"; break; case LOG_LOCAL5: escaped_value = "LOG_LOCAL5"; break; case LOG_LOCAL6: escaped_value = "LOG_LOCAL6"; break; case LOG_LOCAL7: escaped_value = "LOG_LOCAL7"; break; } fprintf (f, " <type facility=\"%s\">syslog</type>" EOL, escaped_value); } fprintf (f, "</logging>" EOL); /* Print footer comment */ fprintf (f, "<!-- End of logging section -->" EOL EOL); return TRUE; } /* Modules */ static gboolean xml_dump_modules_paths (struct config_file *cfg, FILE *f) { GList *cur; gchar *escaped_value; struct script_module *module; /* Print header comment */ fprintf (f, "<!-- Modules section -->" EOL); fprintf (f, "<modules>" EOL); cur = cfg->script_modules; while (cur) { module = cur->data; escaped_value = g_markup_escape_text (module->path, -1); fprintf (f, " <path>%s</path>" EOL, escaped_value); g_free (escaped_value); cur = g_list_next (cur); } fprintf (f, "</modules>" EOL); /* Print footer comment */ fprintf (f, "<!-- End of modules section -->" EOL EOL); return TRUE; } #define CHECK_RES do { if (!res) { fclose (f); return FALSE; } } while (0) gboolean xml_dump_config (struct config_file *cfg, const char *filename) { FILE *f; gboolean res = FALSE; f = fopen (filename, "w"); if (f == NULL) { msg_err ("cannot open file '%s': %s", filename, strerror (errno)); return FALSE; } /* Header */ fprintf (f, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" EOL "<rspamd>" EOL); /* Now dump all parts of config */ res = xml_dump_main (cfg, f); CHECK_RES; res = xml_dump_logging (cfg, f); CHECK_RES; res = xml_dump_variables (cfg, f); CHECK_RES; res = xml_dump_factors (cfg, f); CHECK_RES; res = xml_dump_composites (cfg, f); CHECK_RES; res = xml_dump_workers (cfg, f); CHECK_RES; res = xml_dump_modules (cfg, f); CHECK_RES; res = xml_dump_classifiers (cfg, f); CHECK_RES; res = xml_dump_modules_paths (cfg, f); CHECK_RES; /* Footer */ fprintf (f, "</rspamd>" EOL); fclose (f); return TRUE; }