/* * Mbuni - Open Source MMS Gateway * * MMSBox CFG: MMBox configuration and misc. functions * * Copyright (C) 2003 - 2008, Digital Solutions Ltd. - http://www.dsmagic.com * * Paul Bagyenda * * This program is free software, distributed under the terms of * the GNU General Public License, with a few exceptions granted (see LICENSE) */ #include #include #include #include #include #include #include #include #include #include "mmsbox_cfg.h" #include "mms_queue.h" #include "mmsbox_resolve_shell.h" List *sendmms_users = NULL; /* list of SendMmsUser structs */ List *mms_services = NULL; /* list of MMS Services */ List *mmscs = NULL; Octstr *incoming_qdir, *outgoing_qdir, *dlr_dir; long mmsbox_maxsendattempts, mmsbox_send_back_off, default_msgexpiry; long maxthreads = 0; double queue_interval = -1; Octstr *unified_prefix = NULL; List *strip_prefixes = NULL; int mt_multipart = 0; MmsQueueHandlerFuncs *qfs; /* queue functions. */ MmsBoxResolverFuncStruct *rfs; /* resolver functions. */ void *rfs_data; Octstr *rfs_settings; struct SendMmsPortInfo sendmms_port; struct MmsBoxMTfilter *mt_filter = NULL; int mms_load_mmsbox_settings(mCfg *cfg, gwthread_func_t *mmsc_handler_func) { mCfgGrp *grp = mms_cfg_get_single(cfg, octstr_imm("mbuni")); mCfgGrp *cgrp = mms_cfg_get_single(cfg, octstr_imm("core")); Octstr *gdir = NULL, *s, *tmp; int send_port_ssl = 0; List *l; int i, n, xx; void *catchall = NULL; if (grp == NULL) panic(0,"Missing required group `mbuni' in config file!"); mms_load_core_settings(cgrp); sendmms_users = gwlist_create(); mms_services = gwlist_create(); mmscs = gwlist_create(); gdir = mms_cfg_get(grp, octstr_imm("storage-directory")); if (gdir == NULL) gdir = octstr_imm("."); if (mkdir(octstr_get_cstr(gdir), S_IRWXU|S_IRWXG) < 0 && errno != EEXIST) panic(0, "Failed to create MMSBox storage directory: %s - %s!", octstr_get_cstr(gdir), strerror(errno)); if ((qfs = _mms_load_module(grp, "queue-manager-module", "qfuncs", NULL)) == NULL) { qfs = &default_qfuncs; /* default queue handler. */ qfs->mms_init_queue_module(gdir); } else { Octstr *s = _mms_cfg_getx(grp, octstr_imm("queue-module-init-data")); if (qfs->mms_init_queue_module(s) != 0) panic(0, "failed to initialise queue module, with data: %s", octstr_get_cstr(s)); octstr_destroy(s); } if ((incoming_qdir = qfs->mms_init_queue_dir("mmsbox_incoming", &xx)) == NULL || xx != 0) panic(0, "Failed to initialise incoming mmsbox queue directory: %s - %s!", octstr_get_cstr(incoming_qdir), strerror(errno)); if ((outgoing_qdir = qfs->mms_init_queue_dir("mmsbox_outgoing", &xx)) == NULL || xx != 0) panic(0, "Failed to initialise outgoing mmsbox queue directory: %s - %s!", octstr_get_cstr(outgoing_qdir), strerror(errno)); /* XXX still uses old-style file storage. */ if (qfs != &default_qfuncs) default_qfuncs.mms_init_queue_module(gdir); if ((dlr_dir = default_qfuncs.mms_init_queue_dir("mmsbox_dlr", &xx)) == NULL || xx != 0) panic(0, "Failed to initialise dlr storage directory: %s - %s!", octstr_get_cstr(dlr_dir), strerror(errno)); if (mms_cfg_get_int(grp, octstr_imm("maximum-send-attempts"), &mmsbox_maxsendattempts) < 0) mmsbox_maxsendattempts = MAXQTRIES; if (mms_cfg_get_int(grp, octstr_imm("send-attempt-back-off"), &mmsbox_send_back_off) == -1) mmsbox_send_back_off = BACKOFF_FACTOR; if (mms_cfg_get_int(grp, octstr_imm("default-message-expiry"), &default_msgexpiry) == -1) default_msgexpiry = DEFAULT_EXPIRE; if (mms_cfg_get_int(grp, octstr_imm("max-send-threads"), &maxthreads) == -1) maxthreads = 10; s = mms_cfg_get(grp, octstr_imm("queue-run-interval")); if (s) { queue_interval = atof(octstr_get_cstr(s)); octstr_destroy(s); } if (queue_interval <= 0) queue_interval = QUEUERUN_INTERVAL; unified_prefix = _mms_cfg_getx(grp, octstr_imm("unified-prefix")); if ((s = mms_cfg_get(grp, octstr_imm("strip-prefixes"))) != NULL) { strip_prefixes = octstr_split(s, octstr_imm(";")); octstr_destroy(s); } else strip_prefixes = NULL; mms_cfg_get_int(grp, octstr_imm("sendmms-port"), &sendmms_port.port); #ifdef HAVE_LIBSSL mms_cfg_get_bool(grp, octstr_imm("sendmms-port-ssl"), &send_port_ssl); #endif if (http_open_port(sendmms_port.port, send_port_ssl) < 0) error(0, "MMSBox: Failed to start sendmms HTTP server on %ld: %s!", sendmms_port.port, strerror(errno)); sendmms_port.allow_ip = mms_cfg_get(grp, octstr_imm("allow-ip")); sendmms_port.deny_ip = mms_cfg_get(grp, octstr_imm("deny-ip")); /* load the filter if any. */ if ((mt_filter = _mms_load_module(grp, "mmsbox-mt-filter-library", "mmsbox_mt_filter", NULL)) != NULL) info(0, "MMSBox: Loaded MT Filter [%s]", mt_filter->name); mms_cfg_get_bool(grp, octstr_imm("mmsbox-mt-always-multipart"), &mt_multipart); /* load the resolver module. */ if ((rfs = _mms_load_module(grp, "resolver-library", "mmsbox_resolvefuncs", &mmsbox_resolvefuncs_shell)) == NULL) rfs = &mmsbox_resolvefuncs; rfs_settings = _mms_cfg_getx(grp, octstr_imm("resolver-module-parameters")); rfs_data = rfs->mmsbox_resolvermodule_init(rfs_settings ? octstr_get_cstr(rfs_settings) : NULL); /* Now get sendmms users. */ l = mms_cfg_get_multi(cfg, octstr_imm("send-mms-user")); for (i = 0, n = gwlist_len(l); i < n; i++) { mCfgGrp *x = gwlist_get(l, i); SendMmsUser *u = gw_malloc(sizeof *u); memset(u, 0, sizeof *u); u->user = _mms_cfg_getx(x, octstr_imm("username")); u->pass = _mms_cfg_getx(x, octstr_imm("password")); u->faked_sender = mms_cfg_get(x, octstr_imm("faked-sender")); u->dlr_url = _mms_cfg_getx(x, octstr_imm("delivery-report-url")); u->rr_url = _mms_cfg_getx(x, octstr_imm("read-report-url")); gwlist_append(sendmms_users, u); } gwlist_destroy(l, NULL); /* Get mmsc list. */ l = mms_cfg_get_multi(cfg, octstr_imm("mmsc")); for (i = 0, n = gwlist_len(l); i < n; i++) { mCfgGrp *x = gwlist_get(l, i); MmscGrp *m = gw_malloc(sizeof *m); int ssl = 0; Octstr *type; Octstr *xver; Octstr *s; memset(m, 0, sizeof *m); m->id = _mms_cfg_getx(x, octstr_imm("id")); if (octstr_len(m->id) < 1) panic(0,"Missing required value `id' in config file!"); m->group_id = mms_cfg_get(x, octstr_imm("group-id")); if (m->group_id == NULL) m->group_id = octstr_duplicate(m->id); m->mmsc_url = _mms_cfg_getx(x, octstr_imm("mmsc-url")); m->allowed_prefix = mms_cfg_get(x, octstr_imm("allowed-prefix")); m->denied_prefix = mms_cfg_get(x, octstr_imm("denied-prefix")); m->allowed_sender_prefix = mms_cfg_get(x, octstr_imm("allowed-sender-prefix")); m->denied_sender_prefix = mms_cfg_get(x, octstr_imm("denied-sender-prefix")); m->incoming.allow_ip = mms_cfg_get(x, octstr_imm("allow-ip")); m->incoming.deny_ip = mms_cfg_get(x, octstr_imm("deny-ip")); info(0, "MMSC[%s], allow=[%s], deny=[%s] group_id=[%s]", octstr_get_cstr(m->id), octstr_get_cstr(m->incoming.allow_ip), octstr_get_cstr(m->incoming.deny_ip), octstr_get_cstr(m->group_id)); m->incoming.user = _mms_cfg_getx(x, octstr_imm("incoming-username")); m->incoming.pass = _mms_cfg_getx(x, octstr_imm("incoming-password")); mms_cfg_get_int(x, octstr_imm("incoming-port"), &m->incoming.port); #ifdef HAVE_LIBSSL mms_cfg_get_bool(x, octstr_imm("incoming-port-ssl"), &ssl); #endif if ((tmp = mms_cfg_get(x, octstr_imm("max-throughput"))) != NULL) { if (octstr_parse_double(&m->throughput, tmp, 0) == -1) m->throughput = 0; info(0, "Set throughput to %.3f for mmsc id <%s>", m->throughput, octstr_get_cstr(m->id)); octstr_destroy(tmp); } type = _mms_cfg_getx(x, octstr_imm("type")); if (octstr_case_compare(type, octstr_imm("eaif")) == 0) m->type = EAIF_MMSC; else if (octstr_case_compare(type, octstr_imm("soap")) == 0) m->type = SOAP_MMSC; else if (octstr_case_compare(type, octstr_imm("custom")) == 0) { m->type = CUSTOM_MMSC; m->settings = _mms_cfg_getx(x, octstr_imm("custom-settings")); /* also load the libary. */ if ((m->fns = _mms_load_module(x, "mmsc-library", "mmsc_funcs", NULL)) == NULL) panic(0, "failed to load MMSC libary functions from module!"); } else warning(0, "MMSBox: Unknown MMSC type [%s]!", octstr_get_cstr(type)); if ((xver = _mms_cfg_getx(x, octstr_imm("mm7-version"))) != NULL && octstr_len(xver) > 0) sscanf(octstr_get_cstr(xver), "%d.%d.%d", &m->ver.major, &m->ver.minor1, &m->ver.minor2); else { /* Put in some defaults. */ if (m->type == SOAP_MMSC) { m->ver.major = MAJOR_VERSION(DEFAULT_MM7_VERSION); m->ver.minor1 = MINOR1_VERSION(DEFAULT_MM7_VERSION); m->ver.minor2 = MINOR2_VERSION(DEFAULT_MM7_VERSION); } else if (m->type == EAIF_MMSC) { m->ver.major = 3; m->ver.minor1 = 0; } } if ((s = mms_cfg_get(x, octstr_imm("mm7-soap-xmlns"))) != NULL) { strncpy(m->ver.xmlns, octstr_get_cstr(s), sizeof m->ver.xmlns); m->ver.xmlns[-1 + sizeof m->ver.xmlns] = 0; /* NULL terminate, just in case. */ octstr_destroy(s); } else m->ver.xmlns[0] = 0; m->ver.use_mm7_namespace = 1; mms_cfg_get_bool(x, octstr_imm("use-mm7-soap-namespace-prefix"), &m->ver.use_mm7_namespace); octstr_destroy(xver); octstr_destroy(type); /* Init for filter. */ if ((s = mms_cfg_get(x, octstr_imm("mm7-mt-filter-params"))) != NULL) { if (mt_filter) m->use_mt_filter = (mt_filter->init(m->mmsc_url, m->id, s) == 1); else panic(0, "MMSBox: mt-filter-params set for MMSC[%s] but no MT-filter lib " "specified!", octstr_get_cstr(m->id)); if (!m->use_mt_filter) warning(0, "MMSBox: MT MMS filter turned off for MMSC[%s]. Init failed", octstr_get_cstr(m->id)); octstr_destroy(s); } else m->use_mt_filter = 0; mms_cfg_get_bool(x, octstr_imm("reroute"), &m->reroute); mms_cfg_get_bool(x, octstr_imm("reroute-add-sender-to-subject"), &m->reroute_mod_subject); m->reroute_mmsc_id = mms_cfg_get(x, octstr_imm("reroute-mmsc-id")); if (m->reroute_mmsc_id != NULL && m->reroute == 0) warning(0, "MMSBox: reroute-mmsc-id parameter set but reroute=false!"); mms_cfg_get_bool(x, octstr_imm("no-sender-address"), &m->no_senderaddress); m->mutex = mutex_create(); /* finally start the thingie. */ if (m->type == CUSTOM_MMSC) { if (m->fns->start_conn(m, qfs, unified_prefix, strip_prefixes, &m->data) != 0) { warning(0, "MMSBox: Failed to start custom MMSC [%s]", octstr_get_cstr(m->id)); m->custom_started = 0; } else m->custom_started = 1; } else { if (m->incoming.port > 0 && http_open_port(m->incoming.port, ssl) < 0) { warning(0, "MMSBox: Failed to start HTTP server on receive port for " " MMSC %s, port %ld: %s!", octstr_get_cstr(m->id), m->incoming.port, strerror(errno)); m->incoming.port = 0; /* so we don't listen on it. */ } if (mmsc_handler_func && m->incoming.port > 0) { /* Only start threads if func passed and ... */ if ((m->threadid = gwthread_create(mmsc_handler_func, m)) < 0) error(0, "MMSBox: Failed to start MMSC handler thread for MMSC[%s]: %s!", octstr_get_cstr(m->id), strerror(errno)); } else m->threadid = -1; } gwlist_append(mmscs, m); } gwlist_destroy(l, NULL); l = mms_cfg_get_multi(cfg, octstr_imm("mms-service")); for (i = 0, n = gwlist_len(l); i < n; i++) { mCfgGrp *x = gwlist_get(l, i); MmsService *m = gw_malloc(sizeof *m); Octstr *s; m->name = _mms_cfg_getx(x, octstr_imm("name")); if ((m->url = mms_cfg_get(x, octstr_imm("get-url"))) != NULL) m->type = TRANS_TYPE_GET_URL; else if ((m->url = mms_cfg_get(x, octstr_imm("post-url"))) != NULL) m->type = TRANS_TYPE_POST_URL; else if ((m->url = mms_cfg_get(x, octstr_imm("file"))) != NULL) m->type = TRANS_TYPE_FILE; else if ((m->url = mms_cfg_get(x, octstr_imm("exec"))) != NULL) m->type = TRANS_TYPE_EXEC; else if ((m->url = mms_cfg_get(x, octstr_imm("text"))) != NULL) m->type = TRANS_TYPE_TEXT; else panic(0, "MMSBox: Service [%s] has no url!", octstr_get_cstr(m->name)); m->faked_sender = mms_cfg_get(x, octstr_imm("faked-sender")); m->isdefault = 0; mms_cfg_get_bool(x, octstr_imm("catch-all"), &m->isdefault); if (m->isdefault) { if (catchall) warning(0, "MMSBox: Multiple default mms services defined!"); catchall = m; } if (mms_cfg_get_bool(x, octstr_imm("omit-empty"), &m->omitempty) < 0) m->omitempty = 0; if (mms_cfg_get_bool(x, octstr_imm("suppress-reply"), &m->noreply) < 0) m->noreply = 0; mms_cfg_get_bool(x, octstr_imm("accept-x-mbuni-headers"), &m->accept_x_headers); if ((s = mms_cfg_get(x, octstr_imm("pass-thro-headers"))) != NULL) { m->passthro_headers = octstr_split(s, octstr_imm(",")); octstr_destroy(s); } else m->passthro_headers = NULL; mms_cfg_get_bool(x, octstr_imm("assume-plain-text"), &m->assume_plain_text); if ((s = mms_cfg_get(x, octstr_imm("accepted-mmscs"))) != NULL) { m->allowed_mmscs = octstr_split(s, octstr_imm(";")); octstr_destroy(s); } else m->allowed_mmscs = NULL; /* means allow all. */ if ((s = mms_cfg_get(x, octstr_imm("denied-mmscs"))) != NULL) { m->denied_mmscs = octstr_split(s, octstr_imm(";")); octstr_destroy(s); } else m->denied_mmscs = NULL; /* means allow all. */ m->allowed_receiver_prefix = mms_cfg_get(x, octstr_imm("allowed-receiver-prefix")); m->denied_receiver_prefix = mms_cfg_get(x, octstr_imm("denied-receiver-prefix")); /* Get key words. Start with aliases to make life easier. */ if ((s = mms_cfg_get(x, octstr_imm("aliases"))) != NULL) { m->keywords = octstr_split(s, octstr_imm(";")); octstr_destroy(s); } else m->keywords = gwlist_create(); s = mms_cfg_get(x, octstr_imm("keyword")); if (!s) panic(0, "MMSBox: Service [%s] has no keyword!", octstr_get_cstr(m->name)); else gwlist_append(m->keywords, s); if ((s = mms_cfg_get(x, octstr_imm("http-post-parameters"))) != NULL) { List *r = octstr_split(s, octstr_imm("&")); int i, n; m->params = gwlist_create(); if (m->type != TRANS_TYPE_POST_URL) warning(0, "MMSBox: Service [%s] specifies HTTP Post parameters " "without specifying post-url type/url!", octstr_get_cstr(m->name)); for (i = 0, n = gwlist_len(r); i < n; i++) { Octstr *y = gwlist_get(r, i); int ii = octstr_search_char(y, '=', 0); if (ii < 0) ii = octstr_len(y); if (ii > 0) { MmsServiceUrlParam *p = gw_malloc(sizeof *p); int ch; p->name = octstr_copy(y, 0, ii); p->value = NULL; if (octstr_get_char(y, ii+1) == '%') { switch(ch = octstr_get_char(y, ii+2)) { case 'a': p->type = AUDIO_PART; break; case 'b': p->type = WHOLE_BINARY; break; case 'i': p->type = IMAGE_PART; break; case 'v': p->type = VIDEO_PART; break; case 't': p->type = TEXT_PART; break; case 's': p->type = SMIL_PART; break; case 'o': p->type = OTHER_PART; break; case 'z': p->type = ANY_PART; break; case 'k': p->type = KEYWORD_PART; break; case '%': p->type = NO_PART; break; default: warning(0, "MMSBox: Unknown conversion character %c " "in http-post-parameters. Service [%s]!", ch, octstr_get_cstr(m->name)); p->type = NO_PART; break; } p->value = octstr_copy(y, ii+3, octstr_len(y)); } else { /* No conversion spec. */ p->type = NO_PART; p->value = octstr_copy(y, ii+1, octstr_len(y)); } gwlist_append(m->params, p); } else warning(0, "MMSBox: Missing http-post-parameter name? Service [%s]!", octstr_get_cstr(m->name)); } gwlist_destroy(r, (gwlist_item_destructor_t *)octstr_destroy); octstr_destroy(s); } else m->params = NULL; m->service_code = mms_cfg_get(x, octstr_imm("service-code")); gwlist_append(mms_services, m); } gwlist_destroy(l, NULL); octstr_destroy(gdir); return 0; } /* Get the MMC that should handler this recipient. */ MmscGrp *get_handler_mmc(Octstr *id, Octstr *to, Octstr *from) { MmscGrp *mmc = NULL, *res = NULL; int i, n; Octstr *phonenum = NULL, *xfrom = NULL; if (id) /* If ID is set, use it. */ for (i = 0, n = gwlist_len(mmscs); i < n; i++) if ((mmc = gwlist_get(mmscs, i)) != NULL && mmc->id && octstr_compare(mmc->id, id) == 0) return mmc; if (octstr_search_char(to, '@', 0) > 0 || octstr_case_search(to, octstr_imm("/TYPE=IPv"), 0) > 0) /* For emails, or ip take first mmsc. */ return gwlist_get(mmscs, 0); /* now try allow/deny stuff. */ phonenum = extract_phonenum(to, unified_prefix); xfrom = extract_phonenum(from, NULL); for (i = 0, n = gwlist_len(mmscs); i < n; i++) { if ((mmc = gwlist_get(mmscs, i)) == NULL) continue; if (mmc->allowed_prefix && does_prefix_match(mmc->allowed_prefix, phonenum) == 0) continue; /* does not match. */ if (mmc->denied_prefix && does_prefix_match(mmc->denied_prefix, phonenum) != 0) continue; /* matches. */ if (mmc->allowed_sender_prefix && does_prefix_match(mmc->allowed_sender_prefix, xfrom) == 0) continue; /* does not match. */ if (mmc->denied_sender_prefix && does_prefix_match(mmc->denied_sender_prefix, xfrom) != 0) continue; /* matches. */ res = mmc; /* otherwise it matches, so go away. */ break; } octstr_destroy(phonenum); octstr_destroy(xfrom); return res; } /* handle message routing. */ Octstr *get_mmsbox_queue_dir(Octstr *from, List *to, MmscGrp *m, Octstr **mmc_id) { if (m->reroute) { *mmc_id = m->reroute_mmsc_id ? octstr_duplicate(m->reroute_mmsc_id) : NULL; return outgoing_qdir; } else { Octstr *_mcid, *qdir = NULL; Octstr *fto; if (gwlist_len(to) > 0 && (fto = gwlist_extract_first(to)) != NULL) { /* we route based on first recipient XXX */ Octstr *xto = octstr_duplicate(fto); Octstr *xfrom = octstr_duplicate(from); if (unified_prefix) _mms_fixup_address(&xfrom, octstr_get_cstr(unified_prefix), strip_prefixes, 0); if (unified_prefix) _mms_fixup_address(&fto, octstr_get_cstr(unified_prefix), strip_prefixes, 0); _mcid = rfs->mmsbox_resolve(xfrom,fto,octstr_get_cstr(m->id), rfs_data, rfs_settings); /* modify what was sent to us. */ if (octstr_len(_mcid) == 0) { /* put recipient back, unmodified if incoming only. */ gwlist_insert(to, 0, xto); octstr_destroy(fto); } else { if (unified_prefix) _mms_fixup_address(&fto, octstr_get_cstr(unified_prefix), strip_prefixes, 1); gwlist_insert(to, 0, fto); octstr_destroy(xto); } if (xfrom) { octstr_delete(from, 0, octstr_len(from)); octstr_append(from, xfrom); } octstr_destroy(xfrom); } else _mcid = NULL; if (octstr_len(_mcid) == 0) { *mmc_id = NULL; qdir = incoming_qdir; } else { *mmc_id = octstr_duplicate(_mcid); qdir = outgoing_qdir; } octstr_destroy(_mcid); return qdir; } return 0; }