#include #include #include #include #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////// // TRecipient /////////////////////////////////////////////////////////// class TRecipient : public TObject { TString _address; TString _group; TString _expr; TString _files; public: const TString& address() const { return _address; } const TString& group() const { return _group; } const TString& files() const { return _files; } void add_expr(char op, const TString& expr); bool can_receive(const TRectype& rec) const; void add_files(const TString& files) { _files = files; } virtual bool ok() const { return _address.not_empty() && _expr.not_empty(); } TRecipient(const TToken_string& str); virtual ~TRecipient() { } }; TRecipient::TRecipient(const TToken_string& str) { str.get(0, _address); str.get(1, _group); } void TRecipient::add_expr(char op, const TString& expr) { if (_expr.not_empty()) _expr << (op == 'A' ? "&&" : "||"); if (expr.blank()) _expr << 1; else _expr << '(' << expr << ')'; } bool TRecipient::can_receive(const TRectype& rec) const { TExpression e(_expr, _strexpr, TRUE); TString val; for (int v = 0; v < e.numvar(); v++) { const TFixed_string name(e.varname(v)); val.cut(0); if (rec.exist(name)) val = rec.get(name); else { const TFieldref f(name, 0); const int logicnum = table2logic(f.id()); if (logicnum > 0 && logicnum != rec.num()) { TToken_string & rel = prefix().get_relation(rec.num(), logicnum); if (rel.full()) { TToken_string key; FOR_EACH_TOKEN(rel, tok) key.add(rec.get(tok)); const TRectype & joined_rec = cache().get(logicnum, key); val = f.read(joined_rec); } } else val = f.read(rec); } e.setvar(name, val); } bool yes = e.as_bool(); return yes; } /////////////////////////////////////////////////////////// // TPostman /////////////////////////////////////////////////////////// class TPostman : public TObject { long _firm; int _recipients_lognum; TArray _recipient; TAssoc_array _expr; protected: void test_firm(); TRecipient& recipient(int r) const { return (TRecipient&)_recipient[r]; } void add_expr(const TString& addr, char op, const TString& expr); void add_files(const TString& addr, const TString& files); void load_filters(); public: bool can_dispatch_transaction(const TRectype& rec); bool dispatch_transaction(const TRectype& rec, const TFilename& name); TExpression* get_filter_expr(const char* flt); const char* get_filter(const char* flt); bool user_can(const char* flt, const TRelation* rel); TPostman(); virtual ~TPostman() { } }; void TPostman::test_firm() { const long firm = prefix().get_codditta(); if (firm != _firm) { _firm = firm; _recipients_lognum = 0; _recipient.destroy(); } } void TPostman::add_expr(const TString& addr, char op, const TString& expr) { for (int r = _recipient.last(); r >= 0; r--) { TRecipient& rec = recipient(r); if (rec.address() == addr) { rec.add_expr(op, expr); break; } else { if (rec.group() == addr) rec.add_expr(op, expr); } } } void TPostman::add_files(const TString& addr, const TString& files) { for (int r = _recipient.last(); r >= 0; r--) { TRecipient& rec = recipient(r); if (rec.address() == addr) { rec.add_files(files); break; } else { if (rec.group() == addr) rec.add_files(files); } } } bool TPostman::can_dispatch_transaction(const TRectype& rec) { test_firm(); const int lognum = rec.num(); if (_recipients_lognum != lognum) { _recipients_lognum = lognum; _recipient.destroy(); TConfig cfg(CONFIG_DITTA, "MailTransactions"); TToken_string str("", '¦'); TString addr, opr, expr, files; // Costruisce la lista dei destinatari for (int r = 0; cfg.exist("Recipient", r); r++) { str = cfg.get("Recipient", NULL, r); expand_sys_vars(str); TRecipient* rcp = new TRecipient(str); _recipient.add(rcp); } // Costruisce i filtri per i destinatari for (int f = 0; cfg.exist("Filter", f); f++) { str = cfg.get("Filter", NULL, f); expand_sys_vars(str); str.get(0, addr); const int num = str.get_int(1); if (num != lognum) continue; str.get(2, opr); str.get(3, expr); str.get(4, files); add_expr(addr, opr[0], expr); add_files(addr, files); } // Elimina destinatari inutili for (int d = _recipient.last(); d >= 0; d--) { if (!recipient(d).ok()) _recipient.destroy(d, TRUE); } } return !_recipient.empty(); } static int write_xml(TConfig& cfg, void* jolly) { TAssoc_array &vars = cfg.list_variables(); TXmlItem &item = *(TXmlItem *) jolly; TToken_string tag(cfg.get_paragraph(), ','); const int logicnum = tag.get_int(); const char * attr = logicnum > 0 ? "Field" : "Attr"; int rownum = tag.get_int(); if (logicnum > 0) tag = "Record"; TXmlItem & child =item.AddChild(tag); if (logicnum > 0) { child.SetAttr("LogicNumber", logicnum); if (logicnum > LF_TAB) child.SetAttr("TableName", logic2table(logicnum)); else { TString table; FOR_EACH_ASSOC_STRING(vars, hobj, key, val) if (logicnum <= LF_TAB && strcmp(key, "COD") == 0) { table = val; break; } child.SetAttr("TableName", table); } if (rownum > 0) child.SetAttr("RowNumber", rownum); } TString s; FOR_EACH_ASSOC_STRING(vars, hobj, key, val) if (val && *val) { s = val; if (s[0] == '"' && s.ends_with("\"")) { s.rtrim(1); s.ltrim(1); } s.trim(); if (TDate::isdate(s)) { TDate date(s); child.AddSoapInt(attr, date.date2ansi()).SetAttr("Name", key); } else { if (real::is_natural(s)) child.AddSoapInt(attr, atoi(s)).SetAttr("Name", key); else child.AddSoapString(attr, s).SetAttr("Name", key); } } return 0; } typedef enum { db_add, db_update, db_remove } db_op; const TString & table_name(int logicnum) { TString & name = get_tmp_string(); const int firm = prefix().get_codditta(); TDir d(logicnum); if (d.is_firm()) name << format("%05lda", firm) << '.'; else name << "COM."; name << logic2table(logicnum); return name; } HIDDEN bool create_table(TODBC_recordset & recset, int logicnum, const char * table_name, TTrec & recdef) { TString sql = "CREATE TABLE ["; int nfields = recdef.fields(); sql << table_name << "](\n"; for (int i = 0; i < nfields; i++) { TToken_string def = recdef.fielddef(i); TString field(def.get(0)); TFieldtypes t = (TFieldtypes)def.get_int(1); if (i > 0) sql << ", "; sql << "[" << field << "] "; switch (t) { case _charfld: case _alfafld: sql << "VARCHAR(" << def.get_int(2) << ")"; break; case _memofld: sql << "VARCHAR(MAX)"; break; case _datefld: sql << "DATE"; break; case _boolfld: sql << "BIT DEFAULT 0"; break; case _realfld: case _wordfld: case _intfld: case _longfld: case _intzerofld: case _longzerofld: default: sql << "NUMERIC(" << def.get_int(2) << "," << def.get_int(3) << ")"; break; } } sql << ");\n"; int err = recset.exec(sql); if (err < 0) return false; for (int k = 0; k < recdef.keys(); k++) { TToken_string key(recdef.keydef(k), '+'); bool dupkeys = false; int pos = key.find("|"); int el = 0; if (pos > 0) { dupkeys = key.mid(pos + 1).full(); key = key.left(pos); } sql = "CREATE "; sql << (dupkeys ? "INDEX KEY" : "UNIQUE INDEX KEY") << k + 1 << " ON [" << table_name << "] ("; FOR_EACH_STR_TOKEN(key, field) { if (el++ > 0) sql << ','; if (field.starts_with("UPPER")) { field = field.mid(6, field.len() - 7); sql << field << "_UPPER"; TString alter = "ALTER TABLE ["; alter << table_name << "] ADD " << field << "_UPPER AS UPPER(" << field << ");"; err = recset.exec(alter); } else { int pos = field.find("["); if (pos > 0) { TString fld = field.left(pos); sql << fld << "_MID"; const int start = atoi(field.mid(pos + 1)); pos = field.find(",", pos + 1); const int end = pos > 0 ? atoi(field.mid(pos + 1)) : 0; TString alter = "ALTER TABLE ["; alter << table_name << "] ADD " << fld << "_MID AS " << (recset.driver() == ODBC_mssql ? "SUBSTRING(" : "MID(") << fld << ',' << start; if (end > 0) alter << ',' << end - start + 1; alter << ");"; err = recset.exec(alter); } else sql << field; } } sql << ");\n"; int err = recset.exec(sql); if (err < 0) return false; } return err >= 0; } HIDDEN const TString & build_statement(const db_op op, int logicnum, const char * table_name, const TString_array & keys, const TString_array & values) { TString & statement = get_tmp_string(); if (op == db_add) { statement << "INSERT INTO [" << table_name << "] ("; FOR_EACH_ARRAY_ROW(values, r, row) { if (r > 0) statement << ", "; statement << row->get(0); } statement << ") VALUES ("; FOR_EACH_ARRAY_ROW(values, rk, rowk) { if (rk > 0) statement << ", "; statement << rowk->get(1); } statement << ')'; } else if (op == db_update) { statement << "UPDATE [" << table_name << "] SET "; FOR_EACH_ARRAY_ROW(values, r, row) { if (r > 0) statement << ", "; statement << *row; } statement << " WHERE"; FOR_EACH_ARRAY_ROW(keys, rk, rowk) { if (rk > 0) statement << " AND"; statement << ' ' << *rowk; } } else if (op == db_remove) { statement << "DELETE FROM [" << table_name << "] WHERE "; FOR_EACH_ARRAY_ROW(keys, rk, rowk) { if (rk > 0) statement << " AND"; statement << ' ' << *rowk; } } statement << ';'; return statement; } HIDDEN const char * make_val(const char * fld, TFieldtypes t, TString &src) { TString & val = get_tmp_string(); int pos = src.find("'"); val << fld << '='; while (pos > 0) { src.insert("'", pos); pos = src.find("'", pos + 2); } switch (t) { case _alfafld: case _charfld: case _datefld: case _memofld: val << '\'' << src << '\''; break; case _intfld: case _longfld: case _realfld: case _wordfld: case _intzerofld: case _longzerofld: val << (src.blank() ? "0" : src); break; case _boolfld: val << (src.blank() ? "'FALSE'" : "'TRUE'"); break; default: val << src; break; } return val; } HIDDEN bool popolate_table(TODBC_recordset & recset, int logicnum, const char * table_name, TTrec & recdef) { bool ok = false; int nfields = recdef.fields(); TCursor c(new TRelation(logicnum)); const long items = c.items(); TProgress_monitor pi(items, format(TR("caricamento tabella %s"), table_name)); for (c.pos() = 0; pi.addstatus() && c.pos() < items; ++c) { TString_array values; TString_array keys; for (int i = 0; i < nfields; i++) { TToken_string def = recdef.fielddef(i); TString field(def.get(0)); TFieldtypes t = (TFieldtypes)def.get_int(1); TString str = c.curr().get(field); TToken_string row(make_val(field, t, str), '='); values.add(row); } TToken_string key(recdef.keydef(), '+'); int pos = key.find("|"); if (pos > 0) key = key.left(pos); FOR_EACH_STR_TOKEN(key, field) { TString fld = field; int pos = fld.find("["); if (pos > 0) fld = fld.left(pos); TToken_string def = recdef.fielddef(recdef.field(fld)); TFieldtypes t = (TFieldtypes)def.get_int(1); TString str = c.curr().get(fld); if (pos > 0) { int start = atoi(field.mid(pos)); pos = field.find(',', pos); str = str.smid(start, pos > 0 ? pos : 0); } TToken_string row(make_val(field, t, str), '='); keys.add(row); } const TString & statement = build_statement(db_add, logicnum, table_name, keys, values); int err = recset.exec(statement); if (err <= 0) { const TString & statement = build_statement( db_update, logicnum, table_name, keys, values); err = recset.exec(statement); } if (err <= 0) error_box(FR("Errore n. %d invio file %d"), -err, logicnum); } return ok; } HIDDEN void odbc_send(const TString & dsn, int num, TAuto_token_string & elab_files, const TFilename & name) { TODBC_recordset recset; TToken_string odbc_str(dsn.mid(5), ','); TString_array files; TString_array keys; TString_array values; TConfig ini(name); ini.set_paragraph("Transaction"); TString tr_type = ini.get("Action"); tr_type.upper(); TString main_file; main_file << num; db_op op = db_add; odbc_str.rtrim(1); if (tr_type == TRANSACTION_INSERT) op = db_add; else if (tr_type == TRANSACTION_MODIFY) op = db_update; else if (tr_type == TRANSACTION_DELETE) op = db_remove; if (!recset.connect(odbc_str.get(0), odbc_str.get(1), odbc_str.get(2))) { error_box(FR("Non posso connettermi a (%s)"), (const char *)odbc_str); return; } ini.list_paragraphs(files); FOR_EACH_ARRAY_ROW(files, i, row) { TString curr_file = *row; const int pos = curr_file.find(","); TToken_string rowkey("", ','); if (pos > 0) { rowkey = curr_file.mid(pos + 1); curr_file = curr_file.left(pos); } int curr_file_num = atoi(curr_file); const bool to_elab = (curr_file_num > 0) && (elab_files.blank() || elab_files.find(curr_file) >= 0); if (to_elab) { keys.destroy(); values.destroy(); ini.set_paragraph(*row); TTrec wrk(curr_file_num); int nfields = wrk.fields(); const TString name = table_name(curr_file_num); /*TString check_statement = "SELECT TOP 1 * FROM ["; check_statement << name << "];";*/ TString check_statement = "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = '"; check_statement << name << "';"; int err = recset.exec(check_statement); if (err <= 0) { create_table(recset, curr_file_num, name, wrk); popolate_table(recset, curr_file_num, name, wrk); recset.exec("COMMIT;"); } else { for (int i = 0; i < nfields; i++) { TToken_string def = wrk.fielddef(i); TString field(def.get(0)); TFieldtypes t = (TFieldtypes)def.get_int(1); TString str = ini.get(field); str = str.strip("\""); TToken_string row(make_val(field, t, str), '='); values.add(row); } TToken_string key(wrk.keydef(), '+'); int pos = key.find("|"); if (pos > 0) key = key.left(pos); pos = 0; FOR_EACH_STR_TOKEN(key, field) { TString fld = field; if (fld.starts_with("UPPER")) { fld = fld.smid(7); fld.rtrim(1); fld << "_UPPER"; } else { pos = fld.find("["); if (pos > 0) fld = fld.left(pos); } TString str = ini.get(field); if (str.blank()) { ini.set_paragraph(main_file); str = ini.get(fld); if (str.blank()) str << rowkey.get(); ini.set_paragraph(*row); } str = str.strip("\""); TToken_string def = wrk.fielddef(wrk.field(fld)); TFieldtypes t = (TFieldtypes)def.get_int(1); if (pos > 0) { int start = atoi(field.mid(pos + 1)); pos = field.find(',', pos + 1); str = str.smid(start, pos > 0 ? atoi(field.mid(pos + 1)) : 0); fld << "_MID"; } TToken_string row(make_val(fld, t, str), '='); keys.add(row); } const TString & statement = build_statement(op, curr_file_num, name, keys, values); int err = recset.exec(statement); if (op < db_remove && err <= 0) { const TString & statement = build_statement(op == db_add ? db_update : db_add, curr_file_num, name, keys, values); err = recset.exec(statement); } if (err <= 0) error_box(FR("Errore n. %d invio file %d"), -err, curr_file_num); else recset.exec("COMMIT;"); } } } } bool TPostman::dispatch_transaction(const TRectype& rec, const TFilename& name) { bool ok = can_dispatch_transaction(rec); if (ok) { TToken_string dest; TToken_string file_dest; TToken_string soap_dest; TToken_string odbc_dest; TToken_string odbc_files; TString last_error; for (int r = 0; r < _recipient.items(); r++) { const TRecipient& a = recipient(r); if (a.can_receive(rec)) { TString addr = a.address(); if (addr.starts_with("http")) // Indirizzo http soap_dest.add(addr); else if (addr.find('@') > 0) // Indirizzo posta dest.add(addr); else { if (fexist(addr)) file_dest.add(addr); else if (addr.starts_with("ODBC")) { if (addr.mid(4).blank()) addr << '(' << ini_get_string(CONFIG_DITTA, "MailTransactions", "ODBCDSN") << ',' << ini_get_string(CONFIG_DITTA, "MailTransactions", "ODBCUser") << ',' << decode(ini_get_string(CONFIG_DITTA, "MailTransactions", "ODBCPassword")) << ')'; odbc_dest.add(addr); odbc_files.add(a.files()); } else { if (addr != last_error) { ok = error_box(FR("Non esiste la cartella di destinazione %s"), (const char*)addr); last_error = addr; } } } } } if (dest.items() > 0) { TMail_message msg(dest.get(0)); for (const char* r = dest.get(1); r; r = dest.get()) msg.add_copy_recipient(r); TString16 subject; switch (rec.num()) { case LF_TAB: case LF_TABCOM: case LF_TABGEN: subject << rec.get("COD"); break; case LF_TABMOD: subject << '&' << rec.get("COD"); break; default: subject << rec.num(); break; } msg.set_subject(subject); TScanner trans(name); while (trans.good()) { TString& line = trans.line(); msg.add_line(line); } ok = msg.send(TRUE); } if (file_dest.items() > 0) { TString16 basename; const struct tm* tl = xvt_time_now(); basename.format("%02d%02d%02d_%02d%02d%02d_0", tl->tm_year%100, tl->tm_mon+1, tl->tm_mday, tl->tm_hour, tl->tm_min, tl->tm_sec); FOR_EACH_TOKEN(file_dest, r) { TFilename output; int retry = 0; for (retry = 0; retry < 10; retry++) // Per ora tento solo 10 volte { output = r; output.add(basename); output << retry << ".ini"; if (!output.exist()) // Ho generato un nome buono break; } if (retry >= 10) ok = false; else ok = fcopy(name, output); } } if (soap_dest.items() > 0) { TConfig trans(name); TXmlItem item; TSocketClient socket; char * buf = new char[1024 * 256]; #ifdef WIN32 ostrstream stream(buf, 1024 * 256); #else ostringstream stream(buf); #endif bool ok = true; item.SetTag("m:CampoTransaction"); trans.for_each_paragraph(write_xml, &item); item.Write(stream, 2); stream << '\0'; #ifdef DBG TFilename name; char hostname[256]; int len = strlen(buf); len += 79; xvt_sys_get_host_name(hostname, sizeof(hostname)); name.temp(); ofstream f(name); f << "POST / HTTP/1.1\n" << "User-Agent: Campo\n" << "Host: " << hostname << "\n" << "Content-Type: text/xml; charset=utf-8\n" << "Content-length: " << len << "\n" << "SOAPAction: \"/\"\r\n\r\n" << "\n\r\n"; item.Write(f, 2); f << "\n\n\r\n\r\n"; #endif FOR_EACH_TOKEN(soap_dest, r) { CONNID id = socket.QueryConnection("", r); socket.HttpSoap(id, buf); } } if (odbc_dest.items() > 0) { odbc_files.restart(); FOR_EACH_TOKEN(odbc_dest, r) { const TString & dsn = r; TAuto_token_string files = odbc_files.get(); ::odbc_send(dsn, rec.num(), files, name); } remove_file(name); } } return ok; } void TPostman::load_filters() { TToken_string perm(4096, '\n'); TAuto_token_string row(80); // Costruisce il nome dell'applicazione principale const TApplication& ma = main_app(); TFilename app(ma.argv(0)); app = app.name_only(); for (int a = 1; a < ma.argc(); a++) { row = ma.argv(a); row.upper(); if (row[0] != '/' && row[1] != 'I') app << ' ' << row; else break; } // Stringhe delle espressioni i filtro TAssoc_array expr; // Scandisce l'albero degli utenti/gruppi const TString_array& uag = user_and_groups(); FOR_EACH_ARRAY_ROW(uag, r, rec) { perm = rec->after(rec->separator()); // Permessi del nodo corrente if (!perm.blank()) { FOR_EACH_TOKEN(perm, tok) { row = tok; const TString80 appmod = row.get(0); const bool is_mod = appmod.len() == 2; // Il programma oppure il modulo corrispondono if ((is_mod && app.compare(appmod, 2, TRUE) == 0) || app.compare(appmod, -1, TRUE) == 0) { TString80 key = row.get(2); key.trim(); // Tipo di filtro row = row.get(); row.trim(); // Espressione di filtro if (key.not_empty() && row.not_empty() && row != "1") { key.upper(); TString* str = (TString*)expr.objptr(key); if (str == NULL) { str = new TString(row); // Crea una nuova stringa expr.add(key, str); } else { // Somma alla stringa precendente if (*str != "0") // Se sono FALSE lasciami stare { if (row != "0") // Se aggiungo qualcosa di non ovvio { str->insert("(", 0); *str << ")AND("; *str << row << ')'; } else *str = "0"; } } } } } } } // Trasforma le stringhe in espressioni FOR_EACH_ASSOC_STRING(expr, hash, key, str) { TExpression* e = new TExpression(str, _strexpr, TRUE); _expr.add(key, e); } // Inserisce un elemento fasullo per segnalare l'avvenuta lettura if (_expr.items() == 0) _expr.add("", new TObject); } TExpression* TPostman::get_filter_expr(const char* flt) { if (_expr.items() == 0) load_filters(); TString80 f(flt); f.upper(); TExpression* e = (TExpression*)_expr.objptr(f); return e; } const char* TPostman::get_filter(const char* flt) { TExpression* e = get_filter_expr(flt); return e ? e->string() : NULL; } bool TPostman::user_can(const char* flt, const TRelation* rel) { bool yes_he_can = TRUE; TExpression* e = get_filter_expr(flt); if (e != NULL) { if (rel != NULL) { for (int i = e->numvar()-1; i >= 0; i--) { const TString& name = e->varname(i); const TFieldref ref(name, 0); const TString& val = ref.read(*rel); e->setvar(name, val); } yes_he_can = e->as_bool(); } else { if (e->numvar() == 0) yes_he_can = e->as_bool(); } } return yes_he_can; } TPostman::TPostman() : _firm(-1), _recipients_lognum(0) { } TPostman& postman() { static TPostman* _postman = NULL; if (_postman == NULL) _postman = new TPostman; return *_postman; } /////////////////////////////////////////////////////////// // Public interface /////////////////////////////////////////////////////////// bool can_dispatch_transaction(const TRectype& rec) { return postman().can_dispatch_transaction(rec); } bool dispatch_transaction(const TRectype& rec, const TFilename& name) { return postman().dispatch_transaction(rec, name); } const char* get_user_filter(const char* flt) { return postman().get_filter(flt); } const char* get_user_read_filter() { return get_user_filter("Lettura"); } const char* get_user_write_filter() { return get_user_filter("Scrittura"); } bool user_can_read(const TRelation* rel) { return postman().user_can("Lettura", rel); } bool user_can_write(const TRelation* rel) { return user_can_read(rel) && postman().user_can("Scrittura", rel); } bool user_can_delete(const TRelation* rel) { return user_can_write(rel) && postman().user_can("Eliminazione", rel); } bool user_can_do(const char* azione, const TRelation* rel) { return postman().user_can(azione, rel); }