/*************************************************************************** begin : Thu Oct 04 2018 copyright : (C) 2018 by Martin Preuss email : martin@libchipcard.de *************************************************************************** * This file is part of the project "AqBanking". * * Please see toplevel file COPYING of that project for license details. * ***************************************************************************/ /* This file is included by banking.c */ int AB_Banking_CheckTransactionAgainstLimits_Purpose(const AB_TRANSACTION *t, const AB_TRANSACTION_LIMITS *lim) { int maxn; int maxs; const char *purpose; /* check purpose */ if (lim) { maxn=AB_TransactionLimits_GetMaxLinesPurpose(lim); maxs=AB_TransactionLimits_GetMaxLenPurpose(lim); } else { DBG_INFO(AQBANKING_LOGDOMAIN, "No transaction limits"); maxn=0; maxs=0; } purpose=AB_Transaction_GetPurpose(t); if (purpose && *purpose) { GWEN_STRINGLIST *sl; sl=GWEN_StringList_fromString(purpose, "\n", 0); if (sl && GWEN_StringList_Count(sl)) { int n; GWEN_STRINGLISTENTRY *se; const char *p; n=0; se=GWEN_StringList_FirstEntry(sl); while (se) { p=GWEN_StringListEntry_Data(se); if (p && *p) { n++; if (maxn && n>maxn) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Too many purpose lines (%d>%d)", n, maxn); GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Error, I18N("Too many purpose lines (%d>%d)"), n, maxn); GWEN_StringList_free(sl); return GWEN_ERROR_INVALID; } else if (maxs>0) { int l; GWEN_BUFFER *tbuf; tbuf=GWEN_Buffer_new(0, maxs, 0, 1); AB_ImExporter_Utf8ToDta(p, -1, tbuf); GWEN_Text_CondenseBuffer(tbuf); l=GWEN_Buffer_GetUsedBytes(tbuf); if (maxs && l>maxs) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Too many chars in purpose line %d (%d>%d)", n, l, maxs); GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Error, I18N("Too many chars in purpose line %d (%d>%d)"), n, l, maxs); GWEN_Buffer_free(tbuf); GWEN_StringList_free(sl); return GWEN_ERROR_INVALID; } GWEN_Buffer_free(tbuf); } } se=GWEN_StringListEntry_Next(se); } /* while */ if (!n) { DBG_ERROR(AQBANKING_LOGDOMAIN, "No purpose lines"); GWEN_StringList_free(sl); return GWEN_ERROR_INVALID; } } GWEN_StringList_free(sl); } return 0; } int AB_Banking_CheckTransactionAgainstLimits_Names(const AB_TRANSACTION *t, const AB_TRANSACTION_LIMITS *lim) { int maxs; const char *s; /* check remote name */ if (lim) maxs=AB_TransactionLimits_GetMaxLenRemoteName(lim); else maxs=0; s=AB_Transaction_GetRemoteName(t); if (s && *s) { int l; GWEN_BUFFER *tbuf; tbuf=GWEN_Buffer_new(0, 256, 0, 1); AB_ImExporter_Utf8ToDta(s, -1, tbuf); GWEN_Text_CondenseBuffer(tbuf); l=GWEN_Buffer_GetUsedBytes(tbuf); if (maxs>0 && l>maxs) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Too many chars in remote name (%d>%d)", l, maxs); GWEN_Buffer_free(tbuf); return GWEN_ERROR_INVALID; } GWEN_Buffer_free(tbuf); } else { DBG_ERROR(AQBANKING_LOGDOMAIN, "Missing remote name"); return GWEN_ERROR_INVALID; } /* check local name */ if (lim) maxs=AB_TransactionLimits_GetMaxLenLocalName(lim); else maxs=0; s=AB_Transaction_GetLocalName(t); if (s && *s) { int l; GWEN_BUFFER *tbuf; tbuf=GWEN_Buffer_new(0, 256, 0, 1); AB_ImExporter_Utf8ToDta(s, -1, tbuf); GWEN_Text_CondenseBuffer(tbuf); l=GWEN_Buffer_GetUsedBytes(tbuf); if (maxs>0 && l>maxs) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Too many chars in local name (%d>%d)", l, maxs); GWEN_Buffer_free(tbuf); return GWEN_ERROR_INVALID; } GWEN_Buffer_free(tbuf); } else { DBG_ERROR(AQBANKING_LOGDOMAIN, "Missing local name"); return GWEN_ERROR_INVALID; } return 0; } int AB_Banking_CheckTransactionAgainstLimits_Recurrence(const AB_TRANSACTION *t, const AB_TRANSACTION_LIMITS *lim) { if (lim) { /* check period */ if (AB_Transaction_GetPeriod(t)==AB_Transaction_PeriodMonthly) { int n; n=AB_Transaction_GetCycle(t); if (n==0) { DBG_ERROR(AQBANKING_LOGDOMAIN, "No cycle given"); return GWEN_ERROR_INVALID; } if (AB_TransactionLimits_GetValuesCycleMonthUsed(lim) && !AB_TransactionLimits_ValuesCycleMonthHas(lim, n) && !AB_TransactionLimits_ValuesCycleMonthHas(lim, 0)) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Month day \"%d\" not supported by bank", n); GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Error, I18N("Month day \"%d\" not supported by bank"), n); return GWEN_ERROR_INVALID; } /* check execution day */ n=AB_Transaction_GetExecutionDay(t); if (n==0) { DBG_ERROR(AQBANKING_LOGDOMAIN, "No execution day given"); return GWEN_ERROR_INVALID; } if (AB_TransactionLimits_GetValuesExecutionDayMonthUsed(lim) && !AB_TransactionLimits_ValuesExecutionDayMonthHas(lim, n) && !AB_TransactionLimits_ValuesExecutionDayMonthHas(lim, 0)) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Execution month day \"%d\" not supported by bank", n); GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Error, I18N("Execution month day \"%d\" not supported by bank"), n); return GWEN_ERROR_INVALID; } } /* if (AB_Transaction_GetPeriod(t)==AB_Transaction_PeriodMonthly) */ else if (AB_Transaction_GetPeriod(t)==AB_Transaction_PeriodWeekly) { int n; n=AB_Transaction_GetCycle(t); if (n==0) { DBG_ERROR(AQBANKING_LOGDOMAIN, "No cycle given"); return GWEN_ERROR_INVALID; } if (AB_TransactionLimits_GetValuesCycleWeekUsed(lim) && !AB_TransactionLimits_ValuesCycleWeekHas(lim, n) && !AB_TransactionLimits_ValuesCycleWeekHas(lim, 0)) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Week day \"%d\" not supported by bank", n); GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Error, I18N("Week day \"%d\" not supported by bank"), n); return GWEN_ERROR_INVALID; } /* check execution day */ n=AB_Transaction_GetExecutionDay(t); if (n==0) { DBG_ERROR(AQBANKING_LOGDOMAIN, "No execution day given"); return GWEN_ERROR_INVALID; } if (AB_TransactionLimits_GetValuesExecutionDayWeekUsed(lim) && !AB_TransactionLimits_ValuesExecutionDayWeekHas(lim, n) && !AB_TransactionLimits_ValuesExecutionDayWeekHas(lim, 0)) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Execution week day \"%d\" not supported by bank", n); GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Error, I18N("Execution week day \"%d\" not supported by bank"), n); return GWEN_ERROR_INVALID; } } /* if (AB_Transaction_GetPeriod(t)==AB_Transaction_PeriodWeekly) */ else { DBG_ERROR(AQBANKING_LOGDOMAIN, "Unsupported period %d", AB_Transaction_GetPeriod(t)); return GWEN_ERROR_INVALID; } } /* if limits */ return 0; } int AB_Banking_CheckTransactionAgainstLimits_ExecutionDate(const AB_TRANSACTION *t, const AB_TRANSACTION_LIMITS *lim) { if (lim) { const GWEN_DATE *dt; /* check setup times */ dt=AB_Transaction_GetFirstDate(t); if (dt) { GWEN_DATE *currDate; int diff; int n; currDate=GWEN_Date_CurrentDate(); assert(currDate); diff=GWEN_Date_Diff(dt, currDate); GWEN_Date_free(currDate); /* check minimum setup time */ n=AB_TransactionLimits_GetMinValueSetupTime(lim); if (n && diffn) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Maximum setup time violated (given %d but allowed max=%d)", diff, n); GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Error, I18N("Maximum setup time violated. " "Dated transactions need to be at most %d days away"), n); return GWEN_ERROR_INVALID; } } } return 0; } int AB_Banking_CheckTransactionAgainstLimits_Date(const AB_TRANSACTION *t, const AB_TRANSACTION_LIMITS *lim) { if (lim) { const GWEN_DATE *dt; dt=AB_Transaction_GetDate(t); if (dt) { GWEN_DATE *currDate; int diff; int n; currDate=GWEN_Date_CurrentDate(); assert(currDate); diff=GWEN_Date_Diff(dt, currDate); GWEN_Date_free(currDate); /* check minimum setup time */ n=AB_TransactionLimits_GetMinValueSetupTime(lim); if (n && diffn) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Maximum setup time violated (given %d but allowed max=%d)", diff, n); GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Error, I18N("Maximum setup time violated. " "Dated transactions need to be at most %d days away"), n); return GWEN_ERROR_INVALID; } } } return 0; } int AB_Banking_CheckTransactionAgainstLimits_Sequence(const AB_TRANSACTION *t, const AB_TRANSACTION_LIMITS *lim) { if (lim) { const GWEN_DATE *dt; dt=AB_Transaction_GetDate(t); if (dt) { GWEN_DATE *currDate; int diff; int minTime=0; int maxTime=0; currDate=GWEN_Date_CurrentDate(); assert(currDate); diff=GWEN_Date_Diff(dt, currDate); GWEN_Date_free(currDate); switch (AB_Transaction_GetSequence(t)) { case AB_Transaction_SequenceOnce: minTime=AB_TransactionLimits_GetMinValueSetupTimeOnce(lim); maxTime=AB_TransactionLimits_GetMaxValueSetupTimeOnce(lim); break; case AB_Transaction_SequenceFirst: minTime=AB_TransactionLimits_GetMinValueSetupTimeFirst(lim); maxTime=AB_TransactionLimits_GetMaxValueSetupTimeFirst(lim); break; case AB_Transaction_SequenceFollowing: minTime=AB_TransactionLimits_GetMinValueSetupTimeRecurring(lim); maxTime=AB_TransactionLimits_GetMaxValueSetupTimeRecurring(lim); break; case AB_Transaction_SequenceFinal: minTime=AB_TransactionLimits_GetMinValueSetupTimeFinal(lim); maxTime=AB_TransactionLimits_GetMaxValueSetupTimeFinal(lim); break; case AB_Transaction_SequenceUnknown: break; } if (minTime==0) minTime=AB_TransactionLimits_GetMinValueSetupTime(lim); if (maxTime==0) maxTime=AB_TransactionLimits_GetMaxValueSetupTime(lim); /* check minimum setup time */ if (minTime && diffmaxTime) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Maximum setup time violated (given %d but allowed max=%d for sequence type=%s)", diff, maxTime, AB_Transaction_Sequence_toString(AB_Transaction_GetSequence(t))); GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Error, I18N("Maximum setup time violated. " "Dated transactions need to be at most %d days away but %d days are requested"), maxTime, dt); return GWEN_ERROR_INVALID; } } } return 0; } static int _checkStringForSepaCharset(const char *s, int restricted) { char ascii_chars[]="'&*$%:?,-(+.)/ "; /* last is a blank! */ const char *ascii; #define RESTRICTED_CHARS_OFFSET 3 assert(s); ascii=ascii_chars; if (restricted) ascii+=RESTRICTED_CHARS_OFFSET; while (*s) { unsigned char c=*s++; if (!((c>='A' && c<='Z') || (c>='a' && c<='z') || (c>='0' && c<='9') || strchr(ascii, c)!=NULL)) { char errchr[7]; int i = 0; if (c == 0xC3 && !restricted) { c = *s++; switch (c) { case 0x84: /* AE */ case 0xA4: /* ae */ case 0x96: /* OE */ case 0xB6: /* oe */ case 0x9C: /* UE */ case 0xBC: /* ue */ case 0x9F: /* ss */ if ((*s & 0xC0) != 0x80) break; /* these are no umlauts, after all, so fall through */ default: errchr[i++]=0xC3; if ((c & 0xC0) == 0x80) errchr[i++]=c; else /* UTF-8 sequence ended prematurely */ s--; break; } } else errchr[i++] = c; if (i) { while ((*s & 0xC0) == 0x80) if (i<6) errchr[i++]=*s++; else { i++; s++; } if (i<7 && (i>1 || !(c & 0x80))) { errchr[i] = '\0'; DBG_ERROR(AQBANKING_LOGDOMAIN, "Invalid character in string: '%s'", errchr); } else { DBG_ERROR(AQBANKING_LOGDOMAIN, "String not properly UTF-8 encoded"); } return GWEN_ERROR_BAD_DATA; } } } return 0; } /* This function does not check full UTF8, it only checks whether the given string contains characters * other than "A"-"Z", "a"-"z" and "0"-"9". * We don't use isalnum here because I'm not sure how that function handles UTF-8 chars with umlauts... */ static int _checkStringForAlNum(const char *s, int lcase) { assert(s); while (*s) { unsigned char c=*s; if (!((c>='0' && c<='9') || (c>='A' && c<='Z') || (lcase && c>='a' && c<='z'))) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Invalid character in string: '%c'", c); return GWEN_ERROR_BAD_DATA; } s++; } return 0; } int AB_Banking_CheckTransactionForSepaConformity(const AB_TRANSACTION *t, int restricted) { if (t) { const char *s; int rv; s=AB_Transaction_GetLocalIban(t); if (!(s && *s)) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Missing or empty local IBAN in transaction"); return GWEN_ERROR_BAD_DATA; } rv=_checkStringForAlNum(s, 1); if (rv<0) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Invalid character in local IBAN"); return rv; } #if 0 s=AB_Transaction_GetLocalBic(t); if (!(s && *s)) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Missing or empty local BIC in transaction"); return GWEN_ERROR_BAD_DATA; } rv=_checkStringForAlNum(s, 0); if (rv<0) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Invalid character in local BIC"); return rv; } #else s=AB_Transaction_GetLocalBic(t); if (s && *s) { /* BIC not requeired, but if it exists it must be valid */ rv=_checkStringForAlNum(s, 0); if (rv<0) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Invalid character in local BIC"); return rv; } } #endif s=AB_Transaction_GetRemoteIban(t); if (!(s && *s)) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Missing or empty remote IBAN in transaction"); return GWEN_ERROR_BAD_DATA; } rv=_checkStringForAlNum(s, 1); if (rv<0) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Invalid character in remote IBAN"); return rv; } #if 0 s=AB_Transaction_GetRemoteBic(t); if (!(s && *s)) { if (strncmp(AB_Transaction_GetLocalIban(t), AB_Transaction_GetRemoteIban(t), 2)) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Missing or empty remote BIC in transaction"); return GWEN_ERROR_BAD_DATA; } } else { rv=_checkStringForAlNum(s, 0); if (rv<0) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Invalid character in remote BIC"); return rv; } } #else s=AB_Transaction_GetRemoteBic(t); if (s && *s) { /* BIC not requeired, but if it exists it must be valid */ rv=_checkStringForAlNum(s, 0); if (rv<0) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Invalid character in remote BIC"); return rv; } } #endif s=AB_Transaction_GetLocalName(t); if (!(s && *s)) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Missing or empty local name in transaction"); return GWEN_ERROR_BAD_DATA; } rv=_checkStringForSepaCharset(s, restricted); if (rv<0) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Invalid character in local name"); return rv; } s=AB_Transaction_GetRemoteName(t); if (!(s && *s)) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Missing or empty remote name in transaction"); return GWEN_ERROR_BAD_DATA; } rv=_checkStringForSepaCharset(s, restricted); if (rv<0) { DBG_ERROR(AQBANKING_LOGDOMAIN, "Invalid character in remote name"); return rv; } } else { DBG_ERROR(AQBANKING_LOGDOMAIN, "Missing transaction"); return GWEN_ERROR_BAD_DATA; } DBG_INFO(AQBANKING_LOGDOMAIN, "Transaction conforms to restricted SEPA charset"); return 0; } void AB_Banking_FillTransactionFromAccountSpec(AB_TRANSACTION *t, const AB_ACCOUNT_SPEC *as) { const char *s; assert(t); assert(as); /* unique account id */ AB_Transaction_SetUniqueAccountId(t, AB_AccountSpec_GetUniqueId(as)); /* local account */ s=AB_AccountSpec_GetCountry(as); if (!s || !*s) s="de"; AB_Transaction_SetLocalCountry(t, s); AB_Transaction_SetRemoteCountry(t, s); s=AB_AccountSpec_GetBankCode(as); if (s && *s) AB_Transaction_SetLocalBankCode(t, s); s=AB_AccountSpec_GetAccountNumber(as); if (s && *s) AB_Transaction_SetLocalAccountNumber(t, s); s=AB_AccountSpec_GetOwnerName(as); if (s && *s) AB_Transaction_SetLocalName(t, s); s=AB_AccountSpec_GetBic(as); if (s && *s) AB_Transaction_SetLocalBic(t, s); s=AB_AccountSpec_GetIban(as); if (s && *s) AB_Transaction_SetLocalIban(t, s); }