/* * Mbuni - Open Source MMS Gateway * * Misc. functions * * Copyright (C) 2003 - 2005, 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 #include "mms_util.h" #include "mms_uaprof.h" #define MAXQTRIES 100 #define BACKOFF_FACTOR 5*60 /* In seconds */ #define QUEUERUN_INTERVAL 15*60 /* 15 minutes. */ #define DEFAULT_EXPIRE 3600*24*7 /* One week */ #define MMS_PORT 8191 /* Default content fetch port. */ static Octstr *cfg_getx(CfgGroup *grp, Octstr *item) { Octstr *v = cfg_get(grp, item); return v ? v : octstr_create(""); } static void *load_module(CfgGroup *grp, char *config_key, char *symbolname) { Octstr *s; void *retval = NULL; s = cfg_get(grp, octstr_imm(config_key)); if (s) { void *x = dlopen(octstr_get_cstr(s), RTLD_LAZY); void *y = NULL; if (x == NULL || (y = dlsym(x, symbolname)) == NULL) panic(0, "Error, unable to load dynamic libary (%s): " "libhandle is %s, funcs is %s, err=%s", octstr_get_cstr(s), x ? "OK" : "Not OK", y ? "OK" : "Not OK", dlerror()); else retval = y; octstr_destroy(s); } return retval; } MmsBoxSettings *mms_load_mmsbox_settings(Cfg *cfg) { Octstr *s; List *l; CfgGroup *grp = cfg_get_single_group(cfg, octstr_imm("mmsbox")); MmsBoxSettings *m = gw_malloc(sizeof *m); long port = -1; Octstr *user, *pass, *from; memset(m, 0, sizeof *m); s = cfg_getx(grp, octstr_imm("local-mmsc-domains")); if (s) { l = octstr_split(s, octstr_imm(",")); octstr_destroy(s); } else l = list_create(); m->local_domains = l; if (cfg_get_integer(&m->maxthreads, grp, octstr_imm("max-send-threads")) == -1) m->maxthreads = 10; m->unified_prefix = cfg_getx(grp, octstr_imm("unified-prefix")); m->local_prefix = cfg_getx(grp, octstr_imm("local-prefixes")); m->hostname = cfg_getx(grp, octstr_imm("hostname")); if (m->hostname == NULL || octstr_len(m->hostname) == 0) m->hostname = octstr_create("localhost"); m->name = cfg_getx(grp, octstr_imm("name")); m->host_alias = cfg_getx(grp, octstr_imm("host-alias")); m->sendmail = cfg_getx(grp, octstr_imm("send-mail-prog")); m->global_queuedir = cfg_getx(grp, octstr_imm("send-queue-directory")); m->mm1_queuedir = cfg_getx(grp, octstr_imm("mm1-queue-directory")); m->mm4_queuedir = cfg_getx(grp, octstr_imm("mm4-queue-directory")); m->mmbox_rootdir = cfg_getx(grp, octstr_imm("mmbox-root-directory")); if (cfg_get_integer(&m->maxsendattempts, grp, octstr_imm("maximum-send-attempts")) == -1) m->maxsendattempts = MAXQTRIES; if (cfg_get_integer(&m->default_msgexpiry, grp, octstr_imm("default-message-expiry")) == -1) m->default_msgexpiry = DEFAULT_EXPIRE; s = cfg_getx(grp, octstr_imm("queue-run-interval")); if (!s || (m->queue_interval = atof(octstr_get_cstr(s))) <= 0) m->queue_interval = QUEUERUN_INTERVAL; if (cfg_get_integer(&m->send_back_off, grp, octstr_imm("send-attempt-back-off")) == -1) m->send_back_off = BACKOFF_FACTOR; /* Make send sms url. */ m->sendsms_url = cfg_getx(grp, octstr_imm("sendsms-url")); user = cfg_getx(grp, octstr_imm("sendsms-username")); pass = cfg_getx(grp, octstr_imm("sendsms-password")); from = cfg_get(grp, octstr_imm("sendsms-global-sender")); if (user && octstr_len(user) > 1) octstr_url_encode(user); if (pass && octstr_len(pass) > 1) octstr_url_encode(pass); if (from && octstr_len(from) > 1) octstr_url_encode(from); octstr_format_append(m->sendsms_url, (from && octstr_len(from) > 1) ? "?username=%S&password=%S&from=%S" : "?username=%S&password=%S", user, pass,from); m->system_user = octstr_format("system-user@%S", m->hostname); octstr_destroy(user); octstr_destroy(pass); if (from) octstr_destroy(from); cfg_get_integer(&port, grp, octstr_imm("mms-port")); m->port = (port > 0) ? port : MMS_PORT; m->allow_ip = cfg_getx(grp, octstr_imm("allow-ip")); m->deny_ip = cfg_getx(grp, octstr_imm("deny-ip")); m->email2mmsrelay_prefixes = cfg_getx(grp, octstr_imm("email2mms-relay-prefixes")); m->ua_profile_cache_dir = cfg_getx(grp, octstr_imm("ua-profile-cache-directory")); m->prov_notify = cfg_getx(grp,octstr_imm("prov-server-notify-script")); m->prov_notify_arg = cfg_getx(grp,octstr_imm("prov-server-notify-script-arg")); m->prov_getstatus = cfg_getx(grp,octstr_imm("prov-server-sub-status-script")); m->mms_notify_txt = cfg_getx(grp, octstr_imm("mms-notify-text")); m->mms_notify_unprov_txt = cfg_getx(grp, octstr_imm("mms-notify-unprovisioned-text")); m->mmbox_host = cfg_getx(grp, octstr_imm("mm-box-host")); m->mms_email_txt = cfg_getx(grp, octstr_imm("mms-to-email-txt")); m->mms_email_html = cfg_getx(grp, octstr_imm("mms-to-email-html")); m->mms_toolarge = cfg_getx(grp, octstr_imm("mms-message-too-large-txt")); m->wap_gw_msisdn_header = cfg_get(grp, octstr_imm("mms-client-msisdn-header")); if (!m->wap_gw_msisdn_header) m->wap_gw_msisdn_header = octstr_imm(XMSISDN_HEADER); m->wap_gw_ip_header = cfg_get(grp, octstr_imm("mms-client-ip-header")); if (!m->wap_gw_ip_header) m->wap_gw_ip_header = octstr_imm(XIP_HEADER); cfg_get_bool(&m->notify_unprovisioned, grp, octstr_imm("notify-unprovisioned")); m->billing_params = cfg_getx(grp, octstr_imm("billing-module-parameters")); /* Get and load the billing lib if any. */ if ((m->mms_billfuncs = load_module(grp, "billing-library", "mms_billfuncs"))) { if (m->mms_billfuncs->mms_billingmodule_init == NULL || m->mms_billfuncs->mms_billmsg == NULL || m->mms_billfuncs->mms_billingmodule_fini == NULL || m->mms_billfuncs->mms_logcdr == NULL) panic(0, "Missing or NULL functions in billing module!"); } else m->mms_billfuncs = &mms_billfuncs; /* The default one. */ m->mms_bill_module_data = m->mms_billfuncs->mms_billingmodule_init(octstr_get_cstr(m->billing_params)); m->resolver_params = cfg_getx(grp, octstr_imm("resolver-module-parameters")); /* Get and load the resolver lib if any. */ if ((m->mms_resolvefuncs = load_module(grp, "resolver-library", "mms_resolvefuncs"))) { if (m->mms_resolvefuncs->mms_resolvermodule_init == NULL || m->mms_resolvefuncs->mms_resolve == NULL || m->mms_resolvefuncs->mms_resolvermodule_fini == NULL) panic(0, "Missing or NULL functions in resolver module!"); } else m->mms_resolvefuncs = &mms_resolvefuncs; /* The default one. */ m->mms_resolver_module_data = m->mms_resolvefuncs->mms_resolvermodule_init(octstr_get_cstr(m->resolver_params)); m->detokenizer_params = cfg_getx(grp, octstr_imm("detokenizer-module-parameters")); /* Get and load the detokenizer lib if any. */ if ((m->mms_detokenizefuncs = load_module(grp, "detokenizer-library", "mms_detokenizefuncs"))) { if (m->mms_detokenizefuncs->mms_detokenizer_init == NULL || m->mms_detokenizefuncs->mms_detokenize == NULL || m->mms_detokenizefuncs->mms_detokenizer_fini == NULL) panic(0, "Missing or NULL functions in detokenizer module!"); if (m->mms_detokenizefuncs->mms_detokenizer_init(octstr_get_cstr(m->detokenizer_params))) panic(0, "Detokenizer module failed to initialize"); } else m->mms_detokenizefuncs = NULL; cfg_get_bool(&m->allow_ip_type, grp, octstr_imm("allow-ip-type")); cfg_get_bool(&m->optimize_notification_size, grp, octstr_imm("optimize-notification-size")); if (mmbox_root_init(octstr_get_cstr(m->mmbox_rootdir)) != 0) warning(0, "Failed to initialise mmbox root directory, error: %s!", strerror(errno)); return m; } List *mms_proxy_relays(Cfg *cfg) { List *gl = cfg_get_multi_group(cfg, octstr_imm("mmsproxy")); int i, n; List *l = list_create(); for (i = 0, n = list_len(gl); i < n; i++) { CfgGroup *grp = list_get(gl, i); MmsProxyRelay *m = gw_malloc(sizeof *m); m->host = cfg_getx(grp, octstr_imm("host")); m->name = cfg_getx(grp, octstr_imm("name")); m->allowed_prefix = cfg_getx(grp, octstr_imm("allowed-prefix")); m->denied_prefix = cfg_getx(grp, octstr_imm("denied-prefix")); list_append(l, m); } list_destroy(gl, NULL); return l; } Octstr *mms_makefetchurl(char *qf, Octstr *token, int loc, MmsBoxSettings *settings) { Octstr *url = octstr_create(""); Octstr *host_alias = settings->host_alias; Octstr *hstr; if (host_alias && octstr_len(host_alias) > 0) hstr = octstr_duplicate(host_alias); else hstr = octstr_format("%S:%d", settings->hostname, settings->port); octstr_format_append(url, "http://%S/%s@%d/%S", hstr, qf, loc, token ? token : octstr_imm("x")); octstr_destroy(hstr); return url; } Octstr *mms_find_sender_msisdn(Octstr **send_url, List *request_hdrs, Octstr *msisdn_header, MmsDetokenizerFuncStruct* detokenizerfuncs) { /* Either we have a WAP gateway header as defined, or we look for * last part of url as our number. */ Octstr *xsend_url = *send_url; Octstr *phonenum = http_header_value(request_hdrs, msisdn_header); if (!phonenum || octstr_len(phonenum) == 0) { List *l = octstr_split(xsend_url, octstr_imm("/")); if (l && list_len(l) > 1) { int i, n = list_len(l); Octstr *s; if (detokenizerfuncs) { phonenum = detokenizerfuncs->mms_detokenize(list_get(l, list_len(l) - 1)); } else { phonenum = octstr_duplicate(list_get(l, list_len(l) - 1)); } /* After getting it, remove it from the end... */ for (i = 0, s = octstr_create(""); i < n-1; i++) { Octstr *p = list_get(l, i); if (octstr_len(p) > 0) octstr_format_append(s, "/%S", p); } octstr_destroy(xsend_url); *send_url = s; } if (l) list_destroy(l, (list_item_destructor_t *)octstr_destroy); } return phonenum; } Octstr *mms_find_sender_ip(List *request_hdrs, Octstr *ip_header, Octstr *ip, int *isv6) { Octstr *xip; /* Look in the headers, if none is defined, return actual IP */ Octstr *client_ip = http_header_value(request_hdrs, ip_header); char *s; xip = client_ip ? client_ip : ip; s = octstr_get_cstr(xip); /* Crude test for ipv6 */ *isv6 = (index(s, ':') >= 0); return xip; } int mms_decodefetchurl(Octstr *fetch_url, Octstr **qf, Octstr **token, int *loc) { Octstr *xfurl = octstr_duplicate(fetch_url); int i, j, n; char *s, *p; for (i = 0, n = 0, s = octstr_get_cstr(xfurl); i < octstr_len(xfurl); i++) if (s[i] == '/') n++; if (n < 2) /* We need at least two slashes. */ octstr_append_char(xfurl, '/'); i = 0; n = octstr_len(xfurl); s = octstr_get_cstr(xfurl); p = strrchr(s, '/'); /* Find last slash. */ if (p) i = (p - s) - 1; else i = n-1; if (i < 0) i = 0; while (i>0 && s[i] != '/') i--; /* Go back, find first slash */ if (i>=0 && s[i] == '/') i++; /* Now we have qf, find its end. */ j = i; while (j= 0) { long l; int j = octstr_parse_long(&l, *qf, i+1, 10); if (j > 0) *loc = l; octstr_delete(*qf, i, octstr_len(*qf)); } return 0; } Octstr *mms_maketransid(char *qf, Octstr *mmscname) { Octstr *res; Octstr *x; static int ct; if (!qf) x = octstr_format("msg.%ld.x%d.%d.%d", time(NULL), ++ct, getpid(), random()%10000); else x = octstr_create(qf); res = octstr_format("%S@%S", x, mmscname); octstr_destroy(x); return res; } extern Octstr *mms_getqf_fromtransid(Octstr *transid) { int i = octstr_search_char(transid, '@', 0); return octstr_copy(transid, 0, i); } Octstr *mms_isodate(time_t t) { Octstr *current_time; struct tm now; now = gw_gmtime(t); current_time = octstr_format("%04d-%02d-%02dT%02d:%02d:%02dZ", now.tm_year + 1900, now.tm_mon + 1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec); return current_time; } void mms_lib_init(void) { gwlib_init(); mms_strings_init(); } static void strip_quotes(Octstr *s) { int l = s ? octstr_len(s) : 0; if (l == 0) return; if (octstr_get_char(s, 0) == '"') { octstr_delete(s, 0, 1); l--; } if (octstr_get_char(s, l-1) == '"') octstr_delete(s, l-1, 1); } List *get_value_parameters(Octstr *params) { int i,n, k = 0; List *h = http_create_empty_headers(); Octstr *xparams = octstr_duplicate(params); octstr_format_append(xparams, ";"); /* So parsing is easier. (aka cheap hack) */ for (i = 0, n = octstr_len(xparams); i < n; i++) { int c = octstr_get_char(xparams, i); if (c == ';') { int j = octstr_search_char(xparams, '=', k); Octstr *name, *value; if (j > 0 && j < i) { name = octstr_copy(xparams, k, j - k); value = octstr_copy(xparams, j+1,i-j-1); octstr_strip_blanks(name); octstr_strip_blanks(value); strip_quotes(value); if (octstr_len(name) > 0) http_header_add(h, octstr_get_cstr(name), octstr_get_cstr(value)); octstr_destroy(name); octstr_destroy(value); } k = i + 1; } else if (c == '"') i += http_header_quoted_string_len(xparams, i) - 1; } octstr_destroy(xparams); return h; } int split_header_value(Octstr *value, Octstr **base_value, Octstr **params) { int i, n; for (i = 0, n = octstr_len(value); i < n; i++) { int c = octstr_get_char(value, i); if (c == ';') break; else if (c == '"') i += http_header_quoted_string_len(value, i) - 1; } *base_value = octstr_duplicate(value); if (i < n) { *params = octstr_copy(value, i+1, octstr_len(value)); octstr_delete(*base_value, i, octstr_len(*base_value)); } else *params = octstr_create(""); return 0; } int get_content_type(List *hdrs, Octstr **type, Octstr **params) { Octstr *v; v = http_header_find_first(hdrs, "Content-Type"); *params =NULL; if (!v) { *type = octstr_create("application/octet-stream"); *params = octstr_create(""); return -1; } split_header_value(v, type, params); octstr_destroy(v); return 0; } static int needs_quotes(Octstr *s) { int i, n; if (!s) return 0; for (i = 0, n = octstr_len(s); iheaders); iheaders, i, &header, &value); if (header == NULL || octstr_str_compare(header, "X-Unknown") == 0 || octstr_search_chars(header, octstr_imm(" \n\t"), 0) >= 0) /* Don't allow space in the name. */ goto loop; if (octstr_case_compare(header, octstr_imm("Cc")) == 0 || octstr_case_compare(header, octstr_imm("To")) == 0 || octstr_case_compare(header, octstr_imm("Bcc")) == 0) skip = 0; else skip = 1; /* XXX This may not be safe. Need to skip over quotes. */ if (!skip && octstr_search_char(value, ',', 0) > 0 && (l = http_header_split_value(value)) != NULL && list_len(l) > 1) for (j = 0, m = list_len(l); jheaders); mm->headers = h; } /* Undo base64 content coding for mime entities that need it. */ void unbase64_mimeparts(MIMEEntity *m) { int i, n; if (m->multiparts && list_len(m->multiparts) > 0) for (i = 0, n = list_len(m->multiparts); imultiparts, i)); else { /* A non-multipart message .*/ Octstr *ctype = http_header_value(m->headers, octstr_imm("Content-Type")); Octstr *te = http_header_value(m->headers, octstr_imm("Content-Transfer-Encoding")); if (ctype && te && octstr_case_compare(te,octstr_imm("base64")) == 0) octstr_base64_to_binary(m->body); http_header_remove_all(m->headers, "Content-Transfer-Encoding"); /* Remove it in all cases (?).*/ /* XXX may be we should deal with other transfer encodings here as well... */ if (ctype) octstr_destroy(ctype); if (te) octstr_destroy(te); } } #if 1 static int gw_isprint(int c) { return isprint(c) || isspace(c); } #endif /* Change content coding for mime entities that need it. */ void base64_mimeparts(MIMEEntity *m) { int i, n; if (m->multiparts && list_len(m->multiparts) > 0) for (i = 0, n = list_len(m->multiparts); imultiparts, i)); else { /* A non-multipart message .*/ Octstr *ctype = http_header_value(m->headers, octstr_imm("Content-Type")); Octstr *te = http_header_value(m->headers, octstr_imm("Content-Transfer-Encoding")); if (ctype && !te #if 1 && octstr_check_range(m->body, 0, octstr_len(m->body), gw_isprint) == 0 #endif ) { octstr_binary_to_base64(m->body); http_header_add(m->headers, "Content-Transfer-Encoding", "base64"); } if (ctype) octstr_destroy(ctype); if (te) octstr_destroy(te); } } void notify_prov_server(char *cmd, char *from, char *event, char *arg) { Octstr *s; if (cmd == NULL || cmd[0] == '\0') return; s = octstr_format("%s '%s' '%s' '%s'", cmd, event, from, arg); if (s) { system(octstr_get_cstr(s)); octstr_destroy(s); } } int mms_ind_send(Octstr *prov_cmd, Octstr *to) { Octstr *s; int res = 1; if (prov_cmd == NULL || octstr_len(prov_cmd) == 0) return 1; s = octstr_format("%S %S", prov_cmd, to); if (s) { int x = system(octstr_get_cstr(s)); int y = WEXITSTATUS(x); if (x < 0) { error(0, "Checking MMS Ind.Send: Failed to run command %s!", octstr_get_cstr(s)); res = 1; } else if (y != 0 && y != 1) res = -1; else res = y; octstr_destroy(s); } else warning(0, "Checking MMS Ind.Send: Failed call to compose command [%s] ", octstr_get_cstr(prov_cmd)); return res; } static void addmmscname(Octstr *s, Octstr *myhostname) { int j; int len = octstr_len(s); if (octstr_search_char(s, '@', 0) >= 0) return; /* Nothing to do. */ j = octstr_case_search(s, octstr_imm("/TYPE=PLMN"), 0); if (j > 0 && j - 1 + sizeof "/TYPE=PLMN" == len) { /* A proper number. */ octstr_delete(s, j, -1 + sizeof "/TYPE=PLMN"); /* XXX We strip off /TYPE=PLMN, should we ? */ octstr_format_append(s, "@%S", myhostname); } } static int send2email(Octstr *to, Octstr *from, Octstr *subject, Octstr *msgid, MIMEEntity *m, int append_hostname, Octstr **error, char *sendmail_cmd, Octstr *myhostname) { Octstr *s; FILE *f; int ret = MMS_SEND_OK, i; Octstr *cmd = octstr_create(""); if (append_hostname) { /* Add our hostname to all phone numbers. */ int i, n; List *l = http_create_empty_headers(); Octstr *xfrom = http_header_value(m->headers, octstr_imm("From")); List *lto = http_header_find_all(m->headers, "To"); List *lcc = http_header_find_all(m->headers, "Cc"); if (xfrom) { addmmscname(xfrom, myhostname); http_header_add(l, "From", octstr_get_cstr(xfrom)); octstr_destroy(xfrom); } http_header_remove_all(m->headers, "From"); for (i = 0, n = list_len(lto); i < n; i++) { Octstr *name, *value; http_header_get(lto, i, &name, &value); if (!value || !name || octstr_case_compare(name, octstr_imm("To")) != 0) goto loop; addmmscname(value, myhostname); http_header_add(l, "To", octstr_get_cstr(value)); loop: if (value) octstr_destroy(value); if (name) octstr_destroy(name); } http_destroy_headers(lto); http_header_remove_all(m->headers, "To"); for (i = 0, n = list_len(lcc); i < n; i++) { Octstr *name, *value; http_header_get(lcc, i, &name, &value); if (!value || !name || octstr_case_compare(name, octstr_imm("Cc")) != 0) goto loop2; addmmscname(value, myhostname); http_header_add(l, "Cc", octstr_get_cstr(value)); loop2: if (value) octstr_destroy(value); if (name) octstr_destroy(name); } http_destroy_headers(lcc); http_header_remove_all(m->headers, "Cc"); http_append_headers(m->headers, l); /* combine old with new. */ http_destroy_headers(l); } /* Pack headers, get string rep of mime entity. */ http_header_pack(m->headers); s = mime_entity_to_octstr(m); /* * Make the command: Transpose % formatting characters: * f - from address * t - recipient * s - subject * m - message id */ i = 0; for (;;) { while (sendmail_cmd[i]) { char c = sendmail_cmd[i]; if (c == '%' && sendmail_cmd[i + 1]) break; octstr_append_char(cmd, c); i++; } if (!sendmail_cmd[i]) break; switch(sendmail_cmd[i+1]) { case 't': octstr_append(cmd, to); break; case 'f': if (append_hostname) { Octstr *xfrom = octstr_duplicate(from); addmmscname(xfrom, myhostname); octstr_append(cmd, xfrom); octstr_destroy(xfrom); } else octstr_append(cmd, from); break; case 's': octstr_append(cmd, subject); break; case 'm': octstr_append(cmd, msgid); break; case '%': octstr_format_append(cmd, "%%"); break; default: octstr_format_append(cmd, "%%%c", sendmail_cmd[i+1]); break; } i += 2; } debug("mms.sendtoemail", 0, "preparing to execute %s to send to email: ", octstr_get_cstr(cmd)); if ((f = popen(octstr_get_cstr(cmd), "w")) == NULL) { *error = octstr_format("popen failed for %S: %d: %s", cmd, errno, strerror(errno)); ret = MMS_SEND_ERROR_TRANSIENT; goto done; } if (octstr_print(f, s) < 0) { *error = octstr_format("send email failed in octstr_print %d: %s", errno, strerror(errno)); pclose(f); ret = MMS_SEND_ERROR_TRANSIENT; goto done; } if ((ret = pclose(f)) != 0) { *error = octstr_format("Send email command returned non-zero %d: errno=%s", ret, strerror(errno)); ret = MMS_SEND_ERROR_TRANSIENT; } else ret = MMS_SEND_OK; done: octstr_destroy(cmd); octstr_destroy(s); return ret; } /* Send this message to email recipient. */ int mms_sendtoemail(Octstr *from, Octstr *to, Octstr *subject, Octstr *msgid, MmsMsg *msg, int dlr, Octstr **error, char *sendmail_cmd, Octstr *myhostname, int trans_msg, int trans_smil, char *txt, char *html, int append_hostname) { MIMEEntity *m = NULL; List *newhdrs = http_create_empty_headers(); int ret; if (!to || octstr_search_char(to, '@', 0) < 0) { *error = octstr_format("Invalid email address %S!", to); return MMS_SEND_ERROR_FATAL; } if (!trans_msg) m = mms_tomime(msg); else if ((ret = mms_format_special(msg, trans_smil, txt, html, &m)) < 0 || m == NULL) { warning(0, "MMS: send2email failed to format message (msg=%s,ret=%d)", m ? "OK" : "Not transformed",ret); return -ret; } base64_mimeparts(m); /* Before we send it, we insert some email friendly headers if they are missing. */ http_header_add(newhdrs, "Subject", subject ? octstr_get_cstr(subject) : "MMS Message"); http_header_add(newhdrs, "From", octstr_get_cstr(from)); http_header_add(newhdrs, "To", octstr_get_cstr(to)); http_header_add(newhdrs, "Message-ID", msgid ? octstr_get_cstr(msgid) : ""); http_header_combine(newhdrs, m->headers); http_destroy_headers(m->headers); m->headers = newhdrs; ret = send2email(to, from, subject, msgid, m, append_hostname, error, sendmail_cmd, myhostname); mime_entity_destroy(m); return ret; } void mms_log2(char *logmsg, Octstr *from, Octstr *to, int msize, Octstr *msgid, Octstr *acct, Octstr *viaproxy, char *interface, Octstr *ua, Octstr *mmboxloc) { List *l; if (to) { l = list_create(); list_append(l, to); } else l = NULL; mms_log(logmsg, from,l,msize,msgid,acct,viaproxy,interface,ua,mmboxloc); if (l) list_destroy(l, NULL); } void mms_log(char *logmsg, Octstr *from, List *to, int msize, Octstr *msgid, Octstr *acct, Octstr *viaproxy, char *interface, Octstr *ua, Octstr *mmboxloc) { Octstr *xto = octstr_create(""); int i, n = to ? list_len(to) : 0; for (i = 0; i < n; i++) octstr_format_append(xto, "%s%S", (i == 0) ? "" : ", ", list_get(to,i)); alog("%s MMS [INT:%s] [ACT:%s] [MMSC:%s] [from:%s] [to:%s] [msgid:%s] [size=%d] [UA:%s] [MMBox:%s]", logmsg, interface, acct ? octstr_get_cstr(acct) : "", viaproxy ? octstr_get_cstr(viaproxy) : "", from ? octstr_get_cstr(from) : "", octstr_get_cstr(xto), msgid ? octstr_get_cstr(msgid) : "", msize, ua ? octstr_get_cstr(ua) : "", mmboxloc ? octstr_get_cstr(mmboxloc) : ""); octstr_destroy(xto); } static int lockfile(int fd, int shouldblock) { int n, stop; unsigned flg = shouldblock ? 0 : LOCK_NB; do { n = flock(fd, LOCK_EX|flg); if (n < 0) { if (errno == EINTR) stop = 0; else stop = 1; } else stop = 1; } while (!stop); return (n == 0) ? 0 : errno; } static int check_lock(int fd, char *fname) { struct stat fs = {0}, ds = {0}; /* You might grab a lock on a file, but the file * might be changed just before you grabbed the lock. Detect that and fail.. */ if (fstat(fd, &ds) < 0 || stat(fname, &fs) < 0 || ds.st_nlink != fs.st_nlink || memcmp(&ds.st_dev,&fs.st_dev, sizeof ds.st_dev) != 0 || memcmp(&ds.st_ino,&fs.st_ino, sizeof ds.st_ino) != 0 || ds.st_uid != fs.st_uid || ds.st_gid != fs.st_gid || ds.st_size != fs.st_size) return -1; else return 0; } int mm_lockfile(int fd, char *fname, int shouldblock) { int ret = lockfile(fd,shouldblock); if (ret != 0 || (ret = check_lock(fd,fname)) != 0) return ret; return 0; }