Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

aqbanking / src / libs / plugins / parsers / swift / swift940.c @ 12efe1ea

History | View | Annotate | Download (39.4 KB)

1
/***************************************************************************
2
 begin       : Fri Apr 02 2004
3
 copyright   : (C) 2019 by Martin Preuss
4
 email       : martin@libchipcard.de
5

6
 ***************************************************************************
7
 *          Please see toplevel file COPYING for license details           *
8
 ***************************************************************************/
9

    
10
#ifdef HAVE_CONFIG_H
11
# include <config.h>
12
#endif
13

    
14
#include "swift940_p.h"
15
#include "aqbanking/i18n_l.h"
16

    
17
/* #include <aqhbci/aqhbci.h> */
18
#include <aqbanking/error.h>
19
#include <aqbanking/backendsupport/imexporter_be.h>
20

    
21
#include <gwenhywfar/text.h>
22
#include <gwenhywfar/debug.h>
23
#include <gwenhywfar/gwentime.h>
24
#include <gwenhywfar/gui.h>
25

    
26
#include <ctype.h>
27

    
28

    
29

    
30
/*#define ENABLE_FULL_SEPA_LOG*/
31

    
32

    
33
#define CENTURY_CUTOFF_YEAR 79
34

    
35

    
36

    
37
/* ------------------------------------------------------------------------------------------------
38
 * forward declarations
39
 * ------------------------------------------------------------------------------------------------
40
 */
41

    
42
static void _extractAndHandleSepaTags(GWEN_DB_NODE *dbData, uint32_t flags);
43
static void _transformPurposeIntoOneString(GWEN_DB_NODE *dbData, uint32_t flags);
44
static void _readSubTagsIntoDb(AHB_SWIFT_SUBTAG_LIST *stlist, GWEN_DB_NODE *dbData, uint32_t flags);
45
static int _readSepaTags(const char *sPurpose, GWEN_DB_NODE *dbSepaTags);
46
static int _storeSepaTag(const char *sTagStart, int tagLen, GWEN_DB_NODE *dbSepaTags);
47
static void _transformSepaTags(GWEN_DB_NODE *dbData, GWEN_DB_NODE *dbSepaTags, uint32_t flags);
48
static void _parseTransactionData(const char *p, GWEN_DB_NODE *dbData, uint32_t flags);
49

    
50

    
51

    
52
/* ------------------------------------------------------------------------------------------------
53
 * implementations
54
 * ------------------------------------------------------------------------------------------------
55
 */
56

    
57

    
58
static char *my_strndup(const char *src, size_t n)
59
{
60
  int len;
61

    
62
  len=strlen(src);
63
  if (len<n)
64
    return strdup(src);
65
  else {
66
    char *cpy;
67

    
68
    cpy=(char *) malloc(n+1);
69
    assert(cpy);
70
    memmove(cpy, src, n);
71
    cpy[n]=0;
72
    return cpy;
73
  }
74
}
75

    
76

    
77

    
78
static void _iso8859_1ToUtf8(const char *p, int size, GWEN_BUFFER *buf)
79
{
80
  while (*p) {
81
    unsigned int c;
82

    
83
    if (!size)
84
      break;
85

    
86
    c=(unsigned char)(*(p++));
87
    if (c<32 || c==127)
88
      c=32;
89
    if (c & 0x80) {
90
      GWEN_Buffer_AppendByte(buf, 0xc0 | c>>6);
91
      c &= ~0x40;
92
    }
93
    GWEN_Buffer_AppendByte(buf, c);
94
    if (size!=-1)
95
      size--;
96
  } /* while */
97
}
98

    
99

    
100

    
101

    
102
int AHB_SWIFT__SetCharValue(GWEN_DB_NODE *db,
103
                            uint32_t flags,
104
                            const char *name,
105
                            const char *s)
106
{
107
  GWEN_BUFFER *vbuf;
108
  int rv;
109

    
110
  vbuf=GWEN_Buffer_new(0, strlen(s)+32, 0, 1);
111
  _iso8859_1ToUtf8(s, -1, vbuf);
112
  rv=GWEN_DB_SetCharValue(db, flags, name, GWEN_Buffer_GetStart(vbuf));
113
  GWEN_Buffer_free(vbuf);
114
  return rv;
115
}
116

    
117

    
118

    
119
int AHB_SWIFT940_Parse_25(const AHB_SWIFT_TAG *tg,
120
                          uint32_t flags,
121
                          GWEN_DB_NODE *data,
122
                          GWEN_DB_NODE *cfg)
123
{
124
  const char *p;
125
  const char *p2;
126

    
127
  p=AHB_SWIFT_Tag_GetData(tg);
128
  assert(p);
129

    
130
  while (*p && *p==32)
131
    p++;
132
  if (*p==0) {
133
    DBG_WARN(AQBANKING_LOGDOMAIN, "Tag 25 is empty");
134
    return 0;
135
  }
136

    
137
  p2=strchr(p, '/');
138
  if (p2) {
139
    char *s;
140

    
141
    /* "BLZ/Konto" */
142
    s=(char *)GWEN_Memory_malloc(p2-p+1);
143
    memmove(s, p, p2-p+1);
144
    s[p2-p]=0;
145
    AHB_SWIFT__SetCharValue(data,
146
                            GWEN_DB_FLAGS_OVERWRITE_VARS,
147
                            "localBankCode", s);
148
    GWEN_Memory_dealloc(s);
149
    p=p2+1;
150
  }
151

    
152
  /* Skip leading whitespaces */
153
  while (*p && *p==32)
154
    p++;
155

    
156
  if (*p) {
157
    char *s;
158
    int ll;
159

    
160
    /* Reaching this point, the remainder is at least 1 byte long. */
161
    p2 = p + strlen(p) - 1;
162

    
163
    /* Remove trailing whitespaces. */
164
    while ((*p2 == 32) && (p2>p))
165
      p2--;
166

    
167
    /* p2 now points to the last non-space character (or the beginning of the string),
168
     * so the total size without the trailing zero is (p2-p)+1
169
     */
170
    ll=(p2-p)+1;
171
    s=(char *)GWEN_Memory_malloc(ll+1); /* account for trailing zero */
172
    memmove(s, p, ll);                 /* copy string without trailing zero */
173
    s[ll]=0;                           /* ensure terminating zero */
174
    AHB_SWIFT__SetCharValue(data,
175
                            GWEN_DB_FLAGS_OVERWRITE_VARS,
176
                            "localAccountNumber", s);
177
    GWEN_Memory_dealloc(s);
178
  }
179
  else {
180
    DBG_INFO(AQBANKING_LOGDOMAIN,
181
             "LocalAccountNumber is empty (%s)", p);
182
    AHB_SWIFT__SetCharValue(data,
183
                            GWEN_DB_FLAGS_OVERWRITE_VARS,
184
                            "localAccountNumber", p);
185
  }
186

    
187
  return 0;
188
}
189

    
190

    
191

    
192
int AHB_SWIFT940_Parse_86(const AHB_SWIFT_TAG *tg,
193
                          uint32_t flags,
194
                          GWEN_DB_NODE *dbData,
195
                          GWEN_DB_NODE *cfg)
196
{
197
  const char *p;
198
  int isStructured;
199
  int code;
200
  int keepMultipleBlanks;
201

    
202
  keepMultipleBlanks=GWEN_DB_GetIntValue(cfg, "keepMultipleBlanks", 0, 1);
203
  p=AHB_SWIFT_Tag_GetData(tg);
204
  assert(p);
205
  isStructured=0;
206
  code=999;
207
  if (strlen(p)>3) {
208
    if (isdigit(p[0]) && isdigit(p[1]) && isdigit(p[2])) {
209
      /* starts with a three digit number */
210
      code=(((p[0]-'0')*100) + ((p[1]-'0')*10) + (p[2]-'0'));
211
      if (p[3]=='?')
212
        /* it is structured, get the code */
213
        isStructured=1;
214
      p+=3;
215
    }
216
  }
217

    
218
  if (isStructured) {
219
    AHB_SWIFT_SUBTAG_LIST *stlist;
220
    int rv;
221

    
222
    /* store code */
223
    GWEN_DB_SetIntValue(dbData, flags, "transactioncode", code);
224

    
225
    stlist=AHB_SWIFT_SubTag_List_new();
226
    rv=AHB_SWIFT_ParseSubTags(p, stlist, keepMultipleBlanks);
227
    if (rv<0) {
228
      DBG_WARN(AQBANKING_LOGDOMAIN, "Handling tag :86: as unstructured (%d)", rv);
229
      isStructured=0;
230
    }
231
    else {
232
      if (code<900) {
233
        /* sepa */
234
        DBG_INFO(AQBANKING_LOGDOMAIN, "Reading as SEPA tag (%d)", code);
235
        _readSubTagsIntoDb(stlist, dbData, flags);
236
        _extractAndHandleSepaTags(dbData, flags);
237
        _transformPurposeIntoOneString(dbData, flags);
238
      }
239
      else {
240
        /* non-sepa */
241
        DBG_INFO(AQBANKING_LOGDOMAIN, "Reading as non-SEPA tag (%d)", code);
242
        _readSubTagsIntoDb(stlist, dbData, flags);
243
        _transformPurposeIntoOneString(dbData, flags);
244
      }
245
    } /* if really structured */
246
    AHB_SWIFT_SubTag_List_free(stlist);
247
  } /* if isStructured */
248
  else {
249
    /* unstructured :86:, simply store as mutliple purpose lines */
250
    _parseTransactionData(p, dbData, flags);
251
  }
252

    
253
  return 0;
254
}
255

    
256

    
257

    
258
int AHB_SWIFT940_Parse_61(const AHB_SWIFT_TAG *tg,
259
                          uint32_t flags,
260
                          GWEN_DB_NODE *data,
261
                          GWEN_DB_NODE *cfg)
262
{
263
  const char *p;
264
  const char *p2;
265
  char *s;
266
  char buffer[32];
267
  unsigned int bleft;
268
  int d1a, d2a, d3a;
269
  int d1b, d2b, d3b;
270
  int neg;
271
  GWEN_DATE *dt;
272
  //char curr3=0;
273

    
274
  p=AHB_SWIFT_Tag_GetData(tg);
275
  assert(p);
276
  bleft=strlen(p);
277

    
278
  /* valuata date (M) */
279
  if (bleft<6) {
280
    DBG_ERROR(AQBANKING_LOGDOMAIN, "Missing valuta date (%s)", p);
281
    GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Error,
282
                         "SWIFT: Missing valuta date");
283
    return -1;
284
  }
285
  d1a=((p[0]-'0')*10) + (p[1]-'0');
286
  if (d1a>CENTURY_CUTOFF_YEAR)
287
    d1a+=1900;
288
  else
289
    d1a+=2000;
290
  d2a=((p[2]-'0')*10) + (p[3]-'0');
291
  d3a=((p[4]-'0')*10) + (p[5]-'0');
292

    
293
  if (d3a==30 && d2a==2) {
294
    int ju;
295

    
296
    /* date is Feb 30, this date is invalid. However, some banks use this
297
     * to indicate the last day of February, so we move along */
298
    d3a=1;
299
    d2a=3;
300
    dt=GWEN_Date_fromGregorian(d1a, d2a, d3a);
301
    assert(dt);
302
    ju=GWEN_Date_GetJulian(dt);
303
    /* subtract a day to get the last day in FEB */
304
    ju--;
305
    GWEN_Date_free(dt);
306
    dt=GWEN_Date_fromJulian(ju);
307
  }
308
  else {
309
    dt=GWEN_Date_fromGregorian(d1a, d2a, d3a);
310
    assert(dt);
311
  }
312
  GWEN_DB_SetCharValue(data, GWEN_DB_FLAGS_DEFAULT, "valutaDate", GWEN_Date_GetString(dt));
313
  GWEN_Date_free(dt);
314
  p+=6;
315
  bleft-=6;
316

    
317
  /* booking date (K) */
318
  if (*p && isdigit(*p)) {
319
    if (bleft<4) {
320
      DBG_ERROR(AQBANKING_LOGDOMAIN, "Bad booking date (%s)", p);
321
      GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Info,
322
                           "SWIFT: Bad booking date");
323
      return -1;
324
    }
325
    d2b=((p[0]-'0')*10) + (p[1]-'0');
326
    d3b=((p[2]-'0')*10) + (p[3]-'0');
327
    /* use year from valutaDate.
328
     * However: if valuta date and booking date are in different years
329
     * the booking year might be too high.
330
     * We detect this case by comparing the months: If the booking month
331
     * is and the valuta month differ by more than 10 months then the year
332
     * of the booking date will be adjusted.
333
     */
334
    if (d2b-d2a>7) {
335
      /* booked before actually withdrawn */
336
      d1b=d1a-1;
337
    }
338
    else if (d2a-d2b>7) {
339
      /* withdrawn and booked later */
340
      d1b=d1a+1;
341
    }
342
    else
343
      d1b=d1a;
344

    
345
    dt=GWEN_Date_fromGregorian(d1b, d2b, d3b);
346
    assert(dt);
347
    GWEN_DB_SetCharValue(data, GWEN_DB_FLAGS_DEFAULT, "date", GWEN_Date_GetString(dt));
348
    GWEN_Date_free(dt);
349
    p+=4;
350
    bleft-=4;
351
  }
352

    
353
  /* credit/debit mark (M) */
354
  if (bleft<2) {
355
    DBG_ERROR(AQBANKING_LOGDOMAIN, "Bad value string (%s)", p);
356
    GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Error,
357
                         "SWIFT: Bad value string");
358
    return -1;
359
  }
360
  neg=0;
361
  if (*p=='R') {
362
    if (p[1]=='C' || p[1]=='c')
363
      neg=1;
364
    p+=2;
365
    bleft-=2;
366
  }
367
  else {
368
    if (*p=='D' || *p=='d')
369
      neg=1;
370
    p++;
371
    bleft--;
372
  }
373

    
374
  /* third character of currency (K) */
375
  if (bleft<1) {
376
    DBG_ERROR(AQBANKING_LOGDOMAIN, "Bad data (%s)", p);
377
    GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Error,
378
                         "SWIFT: Bad currency");
379
    return -1;
380
  }
381
  if (!isdigit(*p)) {
382
    /* found third character, skip it */
383
    //curr3=*p;
384
    p++;
385
    bleft--;
386
  }
387

    
388
  /* value (M) */
389
  p2=p;
390
  while (*p2 && (isdigit(*p2) || *p2==','))
391
    p2++;
392
  if (p2==p) {
393
    DBG_ERROR(AQBANKING_LOGDOMAIN, "No value (%s)", p);
394
    GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Error,
395
                         "SWIFT: Bad value");
396
    return -1;
397
  }
398
  s=(char *)GWEN_Memory_malloc(p2-p+1+(neg?1:0));
399
  if (neg) {
400
    s[0]='-';
401
    memmove(s+1, p, p2-p+1);
402
    s[p2-p+1]=0;
403
  }
404
  else {
405
    memmove(s, p, p2-p+1);
406
    s[p2-p]=0;
407
  }
408
  if (1) {
409
    GWEN_BUFFER *tbuf;
410
    const char *cu;
411

    
412
    tbuf=GWEN_Buffer_new(0, 64, 0, 1);
413
    GWEN_Buffer_AppendString(tbuf, s);
414
    cu=GWEN_DB_GetCharValue(cfg, "currency", 0, 0);
415
    if (cu) {
416
      GWEN_Buffer_AppendString(tbuf, ":");
417
      GWEN_Buffer_AppendString(tbuf, cu);
418
    }
419
    AHB_SWIFT__SetCharValue(data, flags, "value", GWEN_Buffer_GetStart(tbuf));
420
    GWEN_Buffer_free(tbuf);
421
  }
422
  GWEN_Memory_dealloc(s);
423
  bleft-=p2-p;
424
  p=p2;
425

    
426
  /* skip 'N' */
427
  p++;
428
  bleft--;
429

    
430
  /* key (M) */
431
  if (bleft<3) {
432
    DBG_ERROR(AQBANKING_LOGDOMAIN, "Missing booking key (%s)", p);
433
    GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Error,
434
                         "SWIFT: Missing booking key");
435
    return -1;
436
  }
437
  memmove(buffer, p, 3);
438
  buffer[3]=0;
439
  AHB_SWIFT__SetCharValue(data, flags, "transactionKey", buffer);
440
  p+=3;
441
  bleft-=3;
442

    
443
  /* customer reference (M) */
444
  if (bleft>0) {
445
    if (bleft>1) {
446
      if (*p=='/' && p[1]!='/') {
447
        p++;
448
        bleft--;
449
      }
450
    }
451

    
452
    p2=p;
453
    while (*p2 && *p2!='/' && *p2!=10)
454
      p2++;
455

    
456
    if (p2==p) {
457
      DBG_WARN(AQBANKING_LOGDOMAIN, "Missing customer reference (%s)", p);
458
      GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Error,
459
                           "SWIFT: Missing customer reference");
460
    }
461
    else {
462
      s=(char *)GWEN_Memory_malloc(p2-p+1);
463
      memmove(s, p, p2-p);
464
      s[p2-p]=0;
465
      if (strcasecmp(s, "NONREF")!=0)
466
        AHB_SWIFT__SetCharValue(data, flags, "customerReference", s);
467
      GWEN_Memory_dealloc(s);
468
    }
469
    bleft-=p2-p;
470
    p=p2;
471
    assert(bleft>=0);
472
  }
473

    
474
  /* bank reference (K) */
475
  if (bleft>1) {
476
    if (*p=='/' && p[1]=='/') {
477
      /* found bank reference */
478
      p+=2;
479
      bleft-=2;
480

    
481
      p2=p;
482
      while (*p2 && *p2!='/' && *p2!=10)
483
        p2++;
484
      if (p2==p) {
485
        DBG_WARN(AQBANKING_LOGDOMAIN, "Missing bank reference (%s) - ignored", p);
486
        GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Warning,
487
                             "SWIFT: Non-Standard MT940 file: Missing bank reference field in :61: line - ignored.");
488
        return 0;
489
      }
490
      s=(char *)GWEN_Memory_malloc(p2-p+1);
491
      memmove(s, p, p2-p+1);
492
      s[p2-p]=0;
493
      AHB_SWIFT__SetCharValue(data, flags, "bankReference", s);
494
      GWEN_Memory_dealloc(s);
495
      bleft-=p2-p;
496
      p=p2;
497
      assert(bleft>=0);
498
    }
499
  }
500

    
501
  /* more information ? */
502
  if (*p==10) {
503
    /* yes... */
504
    p++;
505
    bleft--;
506

    
507
    while (*p) {
508
      /* read extra information */
509
      if (*p=='/') {
510
        if (p[1]==0)
511
          return 0;
512

    
513
        if (bleft<6) {
514
          DBG_WARN(AQBANKING_LOGDOMAIN,
515
                   "Unknown extra data, ignoring (%s)", p);
516
          return 0;
517
        }
518
        if (strncasecmp(p, "/OCMT/", 6)==0) {
519
          /* original value */
520
          p+=6;
521
          bleft-=6;
522
          /* get currency */
523
          memmove(buffer, p, 3);
524
          buffer[3]=0;
525
          AHB_SWIFT__SetCharValue(data, flags, "origvalue/currency", buffer);
526
          p+=3;
527
          bleft-=3;
528
          if (*p=='/') { /* Deutsche Bank seems to be sending */
529
            p++;         /* a "/" between currency and amount */
530
            bleft--;
531
          }
532
          /* get value */
533
          p2=p;
534
          while (*p2 && *p2!='/')
535
            p2++;
536
          if (p2==p) {
537
            DBG_ERROR(AQBANKING_LOGDOMAIN, "Bad original value (%s)", p);
538
            GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Error,
539
                                 "SWIFT: Bad original value");
540
            return -1;
541
          }
542
          s=(char *)GWEN_Memory_malloc(p2-p+1);
543
          memmove(s, p, p2-p+1);
544
          s[p2-p]=0;
545
          AHB_SWIFT__SetCharValue(data, flags, "origvalue", s);
546
          GWEN_Memory_dealloc(s);
547
          bleft-=p2-p;
548
          p=p2;
549
        }
550
        else if (strncasecmp(p, "/CHGS/", 6)==0) {
551
          /* charges */
552
          p+=6;
553
          bleft-=6;
554
          /* get currency */
555
          memmove(buffer, p, 3);
556
          buffer[3]=0;
557
          AHB_SWIFT__SetCharValue(data, flags, "charges/currency", buffer);
558
          p+=3;
559
          bleft-=3;
560
          if (*p=='/') { /* Deutsche Bank seems to be sending */
561
            p++;         /* a "/" between currency and amount */
562
            bleft--;
563
          }
564
          /* get value */
565
          p2=p;
566
          while (*p2 && *p2!='/')
567
            p2++;
568
          if (p2==p) {
569
            DBG_ERROR(AQBANKING_LOGDOMAIN, "Bad charges value (%s)", p);
570
            GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Error,
571
                                 "SWIFT: Bad charges value");
572
            return -1;
573
          }
574
          s=(char *)GWEN_Memory_malloc(p2-p+1);
575
          memmove(s, p, p2-p+1);
576
          s[p2-p]=0;
577
          AHB_SWIFT__SetCharValue(data, flags, "charges", s);
578
          GWEN_Memory_dealloc(s);
579
          bleft-=p2-p;
580
          p=p2;
581
        }
582
        else {
583
          DBG_WARN(AQBANKING_LOGDOMAIN,
584
                   "Unknown extra data, ignoring (%s)", p);
585
          return 0;
586
        }
587
      }
588
      else {
589
        DBG_WARN(AQBANKING_LOGDOMAIN, "Bad extra data, ignoring (%s)", p);
590
        return 0;
591
      }
592
    } /* while */
593
  } /* if there is extra data */
594

    
595
  return 0;
596
}
597

    
598

    
599

    
600
int AHB_SWIFT940_Parse_6_0_2(const AHB_SWIFT_TAG *tg,
601
                             uint32_t flags,
602
                             GWEN_DB_NODE *data,
603
                             GWEN_DB_NODE *cfg)
604
{
605
  const char *p;
606
  const char *p2;
607
  char *s;
608
  char buffer[32];
609
  unsigned int bleft;
610
  int d1, d2, d3;
611
  int neg;
612
  GWEN_DATE *dt;
613

    
614
  p=AHB_SWIFT_Tag_GetData(tg);
615
  assert(p);
616
  bleft=strlen(p);
617

    
618
  /* credit/debit mark (M) */
619
  if (bleft<2) {
620
    DBG_ERROR(AQBANKING_LOGDOMAIN, "Bad value string (%s)", p);
621
    GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Error,
622
                         "SWIFT: Bad value string");
623
    return -1;
624
  }
625
  neg=0;
626
  if (*p=='D' || *p=='d')
627
    neg=1;
628
  p++;
629
  bleft--;
630

    
631
  /* date (M) */
632
  if (bleft<6) {
633
    DBG_ERROR(AQBANKING_LOGDOMAIN, "Missing date (%s)", p);
634
    GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Error,
635
                         "SWIFT: Missing date");
636
    return -1;
637
  }
638
  d1=((p[0]-'0')*10) + (p[1]-'0');
639
  if (d1>CENTURY_CUTOFF_YEAR)
640
    d1+=1900;
641
  else
642
    d1+=2000;
643
  d2=((p[2]-'0')*10) + (p[3]-'0');
644
  d3=((p[4]-'0')*10) + (p[5]-'0');
645

    
646
  dt=GWEN_Date_fromGregorian(d1, d2, d3);
647
  assert(dt);
648
  GWEN_DB_SetCharValue(data, GWEN_DB_FLAGS_OVERWRITE_VARS, "date", GWEN_Date_GetString(dt));
649
  GWEN_Date_free(dt);
650

    
651
  p+=6;
652
  bleft-=6;
653

    
654
  /* currency (M) */
655
  if (!isdigit(*p)) {
656
    /* only read currency if this is not part of the value (like in some
657
     * swiss MT940) */
658
    if (bleft<3) {
659
      DBG_ERROR(AQBANKING_LOGDOMAIN, "Missing currency (%s)", p);
660
      GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Error,
661
                           "SWIFT: Missing currency");
662
      return -1;
663
    }
664
    memmove(buffer, p, 3);
665
    buffer[3]=0;
666
    AHB_SWIFT__SetCharValue(data, GWEN_DB_FLAGS_OVERWRITE_VARS, "value/currency", buffer);
667
    p+=3;
668
    bleft-=3;
669
  }
670

    
671
  /* value (M) */
672
  if (bleft<1) {
673
    DBG_ERROR(AQBANKING_LOGDOMAIN, "Missing value (%s)", p);
674
    GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Error,
675
                         "SWIFT: Missing value");
676
    return -1;
677
  }
678

    
679
  p2=p;
680
  while (*p2 && (isdigit(*p2) || *p2==','))
681
    p2++;
682
  if (p2==p) {
683
    DBG_ERROR(AQBANKING_LOGDOMAIN, "Bad value (%s)", p);
684
    GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Error,
685
                         "SWIFT: Bad value");
686
    return -1;
687
  }
688
  s=(char *)GWEN_Memory_malloc(p2-p+1+(neg?1:0));
689
  if (neg) {
690
    s[0]='-';
691
    memmove(s+1, p, p2-p+1);
692
    s[p2-p+1]=0;
693
  }
694
  else {
695
    memmove(s, p, p2-p+1);
696
    s[p2-p]=0;
697
  }
698
  AHB_SWIFT__SetCharValue(data, GWEN_DB_FLAGS_OVERWRITE_VARS, "value/value", s);
699
  GWEN_Memory_dealloc(s);
700
  /*bleft-=p2-p;*/
701
  /*p=p2;*/
702

    
703
  return 0;
704
}
705

    
706

    
707

    
708
int AHB_SWIFT940_Parse_NS(const AHB_SWIFT_TAG *tg,
709
                          uint32_t flags,
710
                          GWEN_DB_NODE *data,
711
                          GWEN_DB_NODE *cfg)
712
{
713
  const char *p;
714
  const char *p2;
715

    
716
  /* TODO: Use AHB_SWIFT_ParseSubTags */
717
  p=AHB_SWIFT_Tag_GetData(tg);
718
  assert(p);
719

    
720
  while (*p) {
721
    int code;
722

    
723
    code=0;
724
    /* read code */
725
    if (strlen(p)>2) {
726
      if (isdigit(p[0]) && isdigit(p[1])) {
727
        /* starts with a two digit number */
728
        code=(((p[0]-'0')*10) + (p[1]-'0'));
729
        p+=2;
730
      }
731
    }
732

    
733
    /* search for end of line */
734
    p2=p;
735
    while (*p2 && *p2!=10 && *p2!=13)
736
      p2++;
737

    
738
    if (code==0) {
739
      DBG_WARN(AQBANKING_LOGDOMAIN, "No code in line");
740
      p=p2;
741
    }
742
    else {
743
      int len;
744

    
745
      len=p2-p;
746
      if (len<1 || (len==1 && *p=='/')) {
747
        DBG_DEBUG(AQBANKING_LOGDOMAIN, "Empty field %02d", code);
748
      }
749
      else {
750
        char *s;
751

    
752
        s=(char *)GWEN_Memory_malloc(len+1);
753
        memmove(s, p, len);
754
        s[len]=0;
755
        DBG_DEBUG(AQBANKING_LOGDOMAIN, "Got his field: %02d: %s", code, s);
756

    
757
        switch (code) {
758
        case 1:
759
        case 2:
760
        case 3:
761
        case 4:
762
        case 5:
763
        case 6:
764
        case 7:
765
        case 8:
766
        case 9:
767
        case 10:
768
        case 11:
769
        case 12:
770
        case 13:
771
        case 14:
772
          AHB_SWIFT__SetCharValue(data, flags, "purpose", s);
773
          break;
774

    
775
        case 15: /* Auftraggeber1 */
776
        case 16: /* Auftraggeber2 */
777
          AHB_SWIFT__SetCharValue(data, flags, "localName", s);
778
          break;
779

    
780
        case 17: /* Buchungstext */
781
          AHB_SWIFT__SetCharValue(data, flags, "transactionText", s);
782
          break;
783

    
784
        case 18: /* Primanota */
785
          AHB_SWIFT__SetCharValue(data, flags, "primanota", s);
786
          break;
787

    
788
        case 19: /* Uhrzeit der Buchung */
789
        case 20: /* Anzahl der Sammlerposten */
790
        case 33: /* BLZ Auftraggeber */
791
        case 34: /* Konto Auftraggeber */
792
          break;
793

    
794
        default: /* ignore all other fields (if any) */
795
          DBG_WARN(AQBANKING_LOGDOMAIN,
796
                   "Unknown :NS: field \"%02d\" (%s) (%s)",
797
                   code, s,
798
                   AHB_SWIFT_Tag_GetData(tg));
799
          break;
800
        }
801
        GWEN_Memory_dealloc(s);
802
      }
803
      p=p2;
804
    }
805

    
806
    if (*p==10)
807
      p++;
808
    if (*p==13)
809
      p++;
810
    if (*p==10)
811
      p++;
812
  } /* while */
813

    
814
  return 0;
815
}
816

    
817

    
818
/* Import SWIFT MT940 data.
819
   @param tl input: list of tags. Tags are lines in a SWIFT data block (block 4). A tag has an
820
          id and content. See the AHB_SWIFT_Tag_new function for more information.
821
 */
822
int AHB_SWIFT940_Import(AHB_SWIFT_TAG_LIST *tl,
823
                        GWEN_DB_NODE *data,
824
                        GWEN_DB_NODE *cfg,
825
                        uint32_t flags)
826
{
827
  AHB_SWIFT_TAG *tg;
828
  GWEN_DB_NODE *dbDay=NULL;
829
  GWEN_DB_NODE *dbTemplate=NULL;
830
  GWEN_DB_NODE *dbTransaction=NULL;
831
  GWEN_DB_NODE *dbDate=NULL;
832
  uint32_t progressId;
833
  const char *acceptTag20="*";
834
  const char *rejectTag20=NULL;
835
  int ignoreCurrentReport=0;
836

    
837
  acceptTag20=GWEN_DB_GetCharValue(cfg, "acceptTag20", 0, NULL);
838
  if (acceptTag20 && *acceptTag20==0)
839
    acceptTag20=NULL;
840
  rejectTag20=GWEN_DB_GetCharValue(cfg, "rejectTag20", 0, NULL);
841
  if (rejectTag20 && *rejectTag20==0)
842
    rejectTag20=NULL;
843

    
844
  dbTemplate=GWEN_DB_Group_new("template");
845

    
846
  progressId=GWEN_Gui_ProgressStart(GWEN_GUI_PROGRESS_DELAY |
847
                                    GWEN_GUI_PROGRESS_ALLOW_EMBED |
848
                                    GWEN_GUI_PROGRESS_SHOW_PROGRESS |
849
                                    GWEN_GUI_PROGRESS_SHOW_ABORT,
850
                                    I18N("Importing SWIFT tags..."),
851
                                    NULL,
852
                                    AHB_SWIFT_Tag_List_GetCount(tl),
853
                                    0);
854

    
855
  tg=AHB_SWIFT_Tag_List_First(tl);
856
  while (tg) {
857
    const char *id;
858

    
859
    id=AHB_SWIFT_Tag_GetId(tg);
860
    assert(id);
861

    
862
    if (strcasecmp(id, "20")==0) {
863
      if (acceptTag20 || rejectTag20) {
864
        const char *p;
865

    
866
        p=AHB_SWIFT_Tag_GetData(tg);
867
        assert(p);
868
        if (rejectTag20) {
869
          if (-1!=GWEN_Text_ComparePattern(p, rejectTag20, 0)) {
870
            DBG_INFO(AQBANKING_LOGDOMAIN, "Ignoring report [%s]", p);
871
            ignoreCurrentReport=1;
872
          }
873
          else {
874
            ignoreCurrentReport=0;
875
          }
876
        }
877
        else if (acceptTag20) {
878
          if (-1==GWEN_Text_ComparePattern(p, acceptTag20, 0)) {
879
            DBG_INFO(AQBANKING_LOGDOMAIN,
880
                     "Ignoring report [%s] (not matching [%s])",
881
                     p, acceptTag20);
882
            ignoreCurrentReport=1;
883
          }
884
          else {
885
            ignoreCurrentReport=0;
886
          }
887
        }
888

    
889
      }
890
    }
891
    else {
892
      if (!ignoreCurrentReport) {
893
        if (strcasecmp(id, "25")==0) { /* LocalAccount */
894
          if (AHB_SWIFT940_Parse_25(tg, flags, dbTemplate, cfg)) {
895
            DBG_INFO(AQBANKING_LOGDOMAIN, "Error in tag");
896
            GWEN_DB_Group_free(dbTemplate);
897
            GWEN_Gui_ProgressEnd(progressId);
898
            return -1;
899
          }
900
        }
901
        else if (strcasecmp(id, "28C")==0) {
902
          /* Sequence/Statement Number - currently ignored */
903
          /* PostFinance statements don't have a correctly incrementing count... */
904
        }
905
        else if (strcasecmp(id, "60M")==0 || /* Interim StartSaldo */
906
                 strcasecmp(id, "60F")==0) { /* StartSaldo */
907
          GWEN_DB_NODE *dbSaldo;
908
          const char *curr;
909

    
910
          /* start a new day */
911
          dbDay=GWEN_DB_GetGroup(data, GWEN_PATH_FLAGS_CREATE_GROUP, "day");
912

    
913
          dbTransaction=0;
914
          DBG_INFO(AQBANKING_LOGDOMAIN, "Starting new day");
915
          if (strcasecmp(id, "60F")==0)
916
            dbSaldo=GWEN_DB_GetGroup(dbDay, GWEN_PATH_FLAGS_CREATE_GROUP, "StartSaldo");
917
          else
918
            dbSaldo=GWEN_DB_GetGroup(dbDay, GWEN_PATH_FLAGS_CREATE_GROUP, "InterimStartSaldo");
919
          GWEN_DB_AddGroupChildren(dbSaldo, dbTemplate);
920
          if (AHB_SWIFT940_Parse_6_0_2(tg, flags, dbSaldo, cfg)) {
921
            DBG_INFO(AQBANKING_LOGDOMAIN, "Error in tag");
922
            GWEN_DB_Group_free(dbTemplate);
923
            GWEN_Gui_ProgressEnd(progressId);
924
            return -1;
925
          }
926
          else {
927
            dbDate=GWEN_DB_GetGroup(dbSaldo, GWEN_PATH_FLAGS_NAMEMUSTEXIST,
928
                                    "date");
929
          }
930

    
931
          curr=GWEN_DB_GetCharValue(dbSaldo, "value/currency", 0, 0);
932
          if (curr) {
933
            AHB_SWIFT__SetCharValue(dbTemplate, flags,
934
                                    "value/currency", curr);
935
          }
936
          if (strcasecmp(id, "60F")==0)
937
            GWEN_DB_SetCharValue(dbSaldo, GWEN_DB_FLAGS_OVERWRITE_VARS, "type", "final");
938
          else
939
            GWEN_DB_SetCharValue(dbSaldo, GWEN_DB_FLAGS_OVERWRITE_VARS, "type", "interim");
940

    
941
        }
942
        else if (strcasecmp(id, "62M")==0 || /* Interim EndSaldo */
943
                 strcasecmp(id, "62F")==0) { /* EndSaldo */
944
          GWEN_DB_NODE *dbSaldo;
945

    
946
          /* end current day */
947
          dbTransaction=0;
948
          if (!dbDay) {
949
            DBG_WARN(AQBANKING_LOGDOMAIN, "Your bank does not send an opening saldo");
950
            dbDay=GWEN_DB_GetGroup(data, GWEN_PATH_FLAGS_CREATE_GROUP, "day");
951
          }
952
          dbSaldo=GWEN_DB_GetGroup(dbDay, GWEN_PATH_FLAGS_CREATE_GROUP, "EndSaldo");
953
          GWEN_DB_AddGroupChildren(dbSaldo, dbTemplate);
954
          if (AHB_SWIFT940_Parse_6_0_2(tg, flags, dbSaldo, cfg)) {
955
            DBG_INFO(AQBANKING_LOGDOMAIN, "Error in tag");
956
            GWEN_DB_Group_free(dbTemplate);
957
            GWEN_Gui_ProgressEnd(progressId);
958
            return -1;
959
          }
960
          if (strcasecmp(id, "62F")==0)
961
            GWEN_DB_SetCharValue(dbSaldo, GWEN_DB_FLAGS_OVERWRITE_VARS, "type", "final");
962
          else
963
            GWEN_DB_SetCharValue(dbSaldo, GWEN_DB_FLAGS_OVERWRITE_VARS, "type", "interim");
964
          dbDay=0;
965

    
966
        }
967
        else if (strcasecmp(id, "61")==0) {
968
          if (!dbDay) {
969
            DBG_WARN(AQBANKING_LOGDOMAIN,
970
                     "Your bank does not send an opening saldo");
971
            dbDay=GWEN_DB_GetGroup(data, GWEN_PATH_FLAGS_CREATE_GROUP, "day");
972
          }
973

    
974
          DBG_INFO(AQBANKING_LOGDOMAIN, "Creating new transaction");
975
          dbTransaction=GWEN_DB_GetGroup(dbDay, GWEN_PATH_FLAGS_CREATE_GROUP,
976
                                         "transaction");
977
          GWEN_DB_AddGroupChildren(dbTransaction, dbTemplate);
978
          if (dbDate) {
979
            GWEN_DB_NODE *dbT;
980

    
981
            /* dbDate is set upon parsing of tag 60F, use it as a default
982
             * if possible */
983
            dbT=GWEN_DB_GetGroup(dbTransaction, GWEN_DB_FLAGS_OVERWRITE_GROUPS,
984
                                 "date");
985
            assert(dbT);
986
            GWEN_DB_AddGroupChildren(dbT, dbDate);
987
          }
988
          if (AHB_SWIFT940_Parse_61(tg, flags, dbTransaction, cfg)) {
989
            DBG_INFO(AQBANKING_LOGDOMAIN, "Error in tag");
990
            GWEN_DB_Group_free(dbTemplate);
991
            GWEN_Gui_ProgressEnd(progressId);
992
            return -1;
993
          }
994
        }
995
        else if (strcasecmp(id, "86")==0) {
996
          if (!dbTransaction) {
997
            DBG_WARN(AQBANKING_LOGDOMAIN,
998
                     "Bad sequence of tags (86 before 61), ignoring");
999
          }
1000
          else {
1001
            if (AHB_SWIFT940_Parse_86(tg, flags, dbTransaction, cfg)) {
1002
              DBG_INFO(AQBANKING_LOGDOMAIN, "Error in tag");
1003
              GWEN_DB_Group_free(dbTemplate);
1004
              GWEN_Gui_ProgressEnd(progressId);
1005
              return -1;
1006
            }
1007
          }
1008
        }
1009
        else if (strcasecmp(id, "NS")==0) {
1010
          if (!dbTransaction) {
1011
            DBG_DEBUG(AQBANKING_LOGDOMAIN,
1012
                      "Ignoring NS tags outside transactions");
1013
          }
1014
          else {
1015
            if (AHB_SWIFT940_Parse_NS(tg, flags, dbTransaction, cfg)) {
1016
              DBG_INFO(AQBANKING_LOGDOMAIN, "Error in tag");
1017
              GWEN_DB_Group_free(dbTemplate);
1018
              GWEN_Gui_ProgressEnd(progressId);
1019
              return -1;
1020
            }
1021
          }
1022
        }
1023
        else if (strcmp(id, "21")==0) {
1024
          const char *p;
1025

    
1026
          p=AHB_SWIFT_Tag_GetData(tg);
1027
          assert(p);
1028
          if (0==strcmp(p, "NONREF")) {
1029
            DBG_INFO(AQBANKING_LOGDOMAIN, "Ignoring related reference '%s' in document tag 21.", p);
1030
          }
1031
          else {
1032
            DBG_WARN(AQBANKING_LOGDOMAIN, "Unexpected related reference '%s' in document tag 21 encountered.", p);
1033
          }
1034
        }
1035
        else if (strcmp(id, "13")==0 ||  /* "Erstellungszeitpunkt */
1036
                 strcmp(id, "34F")==0 || /* "Mindestbetrag" (sometimes contains some strange values) */
1037
                 strcmp(id, "90D")==0 || /* "Anzahl und Summe Soll-Buchungen" (examples I've seen are invalid anyway) */
1038
                 strcmp(id, "90C")==0) { /* "Anzahl und Summe Haben-Buchungen" (examples I've seen are invalid anyway) */
1039
          /* ignore some well known tags */
1040
          DBG_INFO(AQBANKING_LOGDOMAIN, "Ignoring well known tag \"%s\"", id);
1041
        }
1042
        else {
1043
          DBG_WARN(AQBANKING_LOGDOMAIN,
1044
                   "Unhandled tag '%s' found. "
1045
                   "This only means the file contains info we currently don't read, "
1046
                   "in most cases this is unimportant data.",
1047
                   id);
1048
          DBG_WARN(AQBANKING_LOGDOMAIN,
1049
                   "To debug set environment variable AQBANKING_LOGLEVEL=info and rerun,"
1050
                   "otherwise just ignore this message.");
1051
        }
1052

    
1053
      }
1054
    }
1055

    
1056
    if (GWEN_Gui_ProgressAdvance(progressId, GWEN_GUI_PROGRESS_ONE)==
1057
        GWEN_ERROR_USER_ABORTED) {
1058
      GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Error,
1059
                           I18N("Aborted by user"));
1060
      GWEN_Gui_ProgressEnd(progressId);
1061
      GWEN_DB_Group_free(dbTemplate);
1062
      return GWEN_ERROR_USER_ABORTED;
1063
    }
1064

    
1065
    tg=AHB_SWIFT_Tag_List_Next(tg);
1066
  } /* while */
1067

    
1068
  GWEN_DB_Group_free(dbTemplate);
1069
  GWEN_Gui_ProgressEnd(progressId);
1070

    
1071
  return 0;
1072
}
1073

    
1074

    
1075

    
1076
void _readSubTagsIntoDb(AHB_SWIFT_SUBTAG_LIST *stlist, GWEN_DB_NODE *dbData, uint32_t flags)
1077
{
1078
  AHB_SWIFT_SUBTAG *stg;
1079

    
1080
  stg=AHB_SWIFT_SubTag_List_First(stlist);
1081
  while (stg) {
1082
    const char *s;
1083
    int id;
1084
    int intVal;
1085

    
1086
    id=AHB_SWIFT_SubTag_GetId(stg);
1087
    s=AHB_SWIFT_SubTag_GetData(stg);
1088
    switch (id) {
1089
    case 0: /* Buchungstext */
1090
      AHB_SWIFT__SetCharValue(dbData, flags, "transactionText", s);
1091
      break;
1092
    case 10: /* Primanota */
1093
      AHB_SWIFT__SetCharValue(dbData, flags, "primanota", s);
1094
      break;
1095

    
1096
    case 20:
1097
    case 21:
1098
    case 22:
1099
    case 23:
1100
    case 24:
1101
    case 25:
1102
    case 26:
1103
    case 27:
1104
    case 28:
1105
    case 29:
1106
    case 60:
1107
    case 61:
1108
    case 62:
1109
    case 63: /* Verwendungszweck */
1110
      GWEN_DB_SetCharValue(dbData, GWEN_DB_FLAGS_DEFAULT, "purpose", s);
1111
      break;
1112

    
1113
    case 30: /* BLZ Gegenseite */
1114
      AHB_SWIFT__SetCharValue(dbData, flags, "remoteBankCode", s);
1115
      break;
1116

    
1117
    case 31: /* Kontonummer Gegenseite */
1118
      AHB_SWIFT__SetCharValue(dbData, flags, "remoteAccountNumber", s);
1119
      break;
1120

    
1121
    case 32:
1122
    case 33: /* Name Auftraggeber */
1123
      //DBG_ERROR(AQBANKING_LOGDOMAIN, "Setting remote name: [%s]", s);
1124
      AHB_SWIFT__SetCharValue(dbData, flags, "remoteName", s);
1125
      break;
1126

    
1127
    case 34: /* Textschluesselergaenzung */
1128
      if (1==sscanf(s, "%d", &intVal)) {
1129
        GWEN_DB_SetIntValue(dbData, flags, "textkeyExt", intVal);
1130
      }
1131
      else {
1132
        DBG_WARN(AQBANKING_LOGDOMAIN, "Value [%s] is not a number (textkeyext)", s);
1133
      }
1134
      break;
1135

    
1136
    case 38: /* IBAN */
1137
      AHB_SWIFT__SetCharValue(dbData, flags, "remoteIban", s);
1138
      break;
1139

    
1140
    default: /* ignore all other fields (if any) */
1141
      DBG_WARN(AQBANKING_LOGDOMAIN, "Unknown :86: field \"%02d\" (%s)", id, s);
1142
      break;
1143
    } /* switch */
1144
    stg=AHB_SWIFT_SubTag_List_Next(stg);
1145
  } /* while */
1146
}
1147

    
1148

    
1149

    
1150
void _extractAndHandleSepaTags(GWEN_DB_NODE *dbData, uint32_t flags)
1151
{
1152
  GWEN_BUFFER *tbuf;
1153
  int i;
1154
  
1155
  tbuf=GWEN_Buffer_new(0, 256, 0, 1);
1156
  for (i=0; i<99; i++) {
1157
    const char *s;
1158
  
1159
    s=GWEN_DB_GetCharValue(dbData, "purpose", i, 0);
1160
    if (s && *s)
1161
      GWEN_Buffer_AppendString(tbuf, s);
1162
  }
1163

    
1164
  if (GWEN_Buffer_GetUsedBytes(tbuf)) {
1165
    GWEN_DB_NODE *dbSepaTags;
1166
    int realSepaTagCount;
1167

    
1168
    dbSepaTags=GWEN_DB_Group_new("sepa-tags");
1169
    realSepaTagCount=_readSepaTags(GWEN_Buffer_GetStart(tbuf), dbSepaTags);
1170

    
1171
    if (realSepaTagCount>0 && GWEN_DB_Variables_Count(dbSepaTags))
1172
      _transformSepaTags(dbData, dbSepaTags, flags);
1173

    
1174
    GWEN_DB_Group_free(dbSepaTags);
1175
  }
1176

    
1177
  /* buffer no longer needed */
1178
  GWEN_Buffer_free(tbuf);
1179
}
1180

    
1181

    
1182

    
1183
void _transformPurposeIntoOneString(GWEN_DB_NODE *dbData, uint32_t flags)
1184
{
1185
  GWEN_BUFFER *tbuf;
1186
  int i;
1187
  
1188
  tbuf=GWEN_Buffer_new(0, 256, 0, 1);
1189
  for (i=0; i<99; i++) {
1190
    const char *s;
1191
  
1192
    s=GWEN_DB_GetCharValue(dbData, "purpose", i, 0);
1193
    if (s && *s) {
1194
      if (GWEN_Buffer_GetUsedBytes(tbuf))
1195
        GWEN_Buffer_AppendString(tbuf, "\n");
1196
      GWEN_Buffer_AppendString(tbuf, s);
1197
    }
1198
  }
1199

    
1200
  if (GWEN_Buffer_GetUsedBytes(tbuf)) {
1201
    GWEN_DB_DeleteVar(dbData, "purpose");
1202
    GWEN_DB_SetCharValue(dbData, GWEN_DB_FLAGS_DEFAULT, "purpose", GWEN_Buffer_GetStart(tbuf));
1203
  }
1204
  GWEN_Buffer_free(tbuf);
1205
}
1206

    
1207

    
1208

    
1209
int _readSepaTags(const char *sPurpose, GWEN_DB_NODE *dbSepaTags)
1210
{
1211
  const char *s;
1212
  const char *sLastTagStart;
1213
  int realSepaTagCount=0;
1214

    
1215
#ifdef ENABLE_FULL_SEPA_LOG
1216
  DBG_ERROR(AQHBCI_LOGDOMAIN, "FullPurposeBuffer");
1217
  GWEN_Buffer_Dump(bufFullPurpose, 2);
1218
#endif
1219

    
1220
  s=sPurpose;
1221
  sLastTagStart=s;
1222

    
1223
  /* sample all SEPA fields from concatenated string of purpose lines */
1224
  while (*s) {
1225
    /* look for begin of next tag */
1226
    while (*s) {
1227
      if ((*s && isalpha(*s)) &&
1228
          (s[1] && isalpha(s[1])) &&
1229
          (s[2] && isalpha(s[2])) &&
1230
          (s[3] && isalpha(s[3])) &&
1231
          s[4]=='+') {
1232
        if (strncasecmp(s, "EREF+", 5)==0 ||
1233
            strncasecmp(s, "KREF+", 5)==0 ||
1234
            strncasecmp(s, "MREF+", 5)==0 ||
1235
            strncasecmp(s, "CRED+", 5)==0 ||
1236
            strncasecmp(s, "DEBT+", 5)==0 ||
1237
            strncasecmp(s, "SVWZ+", 5)==0 ||
1238
            strncasecmp(s, "ABWA+", 5)==0 ||
1239
            strncasecmp(s, "ABWE+", 5)==0)
1240
          break;
1241
      }
1242
      /* not the beginning of a SEPA field, just skip */
1243
      s++;
1244
    }
1245

    
1246
    /* found begin of the next SEPA field or end of buffer */
1247
    if (s > sLastTagStart) {
1248
      int tagLen;
1249

    
1250
      /* we currently have a field, close that first */
1251
      tagLen=s-sLastTagStart;
1252

    
1253
      if (_storeSepaTag(sLastTagStart, tagLen, dbSepaTags)>0)
1254
        realSepaTagCount++;
1255
    }
1256

    
1257
    if (*s) {
1258
      /* save start of next tag */
1259
      sLastTagStart=s;
1260
      /* skip XXX+ at the beginning, otherwise we would immediately stop in next loop
1261
       * we know that the next 5 bytes are valid, so it is safe to skip them */
1262
      s+=5;
1263
    }
1264
  } /* while */
1265

    
1266
  return realSepaTagCount;
1267
}
1268

    
1269

    
1270

    
1271
int _storeSepaTag(const char *sTagStart, int tagLen, GWEN_DB_NODE *dbSepaTags)
1272
{
1273
  int isRealSepaTag=0;
1274

    
1275
#ifdef ENABLE_FULL_SEPA_LOG
1276
  DBG_ERROR(0, "Current tag:");
1277
  GWEN_Text_LogString(sTagStart, tagLen, 0, GWEN_LoggerLevel_Error);
1278
#endif
1279

    
1280
  /* check tag length (must be long enough for 'XXX+', i.e. at least 5 bytes) */
1281
  if (tagLen>5 && sTagStart[4]=='+') {
1282
    char sIdentifier[6];
1283
    const char *sPayload;
1284

    
1285
    /* ok, 5 bytes or more, 4 alphas and a plus sign, should be the begin of a SEPA tag */
1286
    strncpy(sIdentifier, sTagStart, 5);
1287
    sIdentifier[5]=0;
1288

    
1289
    /* remove leading blanks */
1290
    sPayload=sTagStart+5;
1291
    tagLen-=5;
1292
    while (tagLen>0 && *sPayload && isblank(*sPayload)) {
1293
      sPayload++;
1294
      tagLen--;
1295
    }
1296

    
1297
    /* remove trailing blanks */
1298
    if (tagLen>0) {
1299
      while (tagLen>0) {
1300
        if (!isblank(sPayload[tagLen-1]))
1301
          break;
1302
        tagLen--;
1303
      }
1304
    }
1305

    
1306
    /* store tag, if still data left */
1307
    if (tagLen>0) {
1308
      char *sCopyPayload;
1309

    
1310
      sCopyPayload=my_strndup(sPayload, tagLen);
1311
      GWEN_DB_SetCharValue(dbSepaTags, GWEN_DB_FLAGS_DEFAULT, sIdentifier, sCopyPayload);
1312
      free(sCopyPayload);
1313
      isRealSepaTag=1;
1314
    }
1315
    else {
1316
      DBG_WARN(GWEN_LOGDOMAIN, "Ignoring empty SEPA field \"%s\"", sIdentifier);
1317
    }
1318
  }
1319
  else {
1320
    /* tag is shorter than 5 bytes or pos 4 doesn't contain a plus, treat as normal purpose */
1321
    if (tagLen>0) {
1322
      char *sCopyPayload;
1323

    
1324
      sCopyPayload=my_strndup(sTagStart, tagLen);
1325
      GWEN_DB_SetCharValue(dbSepaTags, GWEN_DB_FLAGS_DEFAULT, "_purpose", sCopyPayload);
1326
      free(sCopyPayload);
1327
    }
1328
  }
1329

    
1330
  return isRealSepaTag?1:0;
1331
}
1332

    
1333

    
1334

    
1335
void _transformSepaTags(GWEN_DB_NODE *dbData, GWEN_DB_NODE *dbSepaTags, uint32_t flags)
1336
{
1337
  GWEN_DB_NODE *dbVar;
1338

    
1339
#ifdef ENABLE_FULL_SEPA_LOG
1340
  DBG_ERROR(0, "Got these SEPA tags:");
1341
  GWEN_DB_Dump(dbSepaTags, 2);
1342
#endif
1343

    
1344
  /* clear purpose variable, since we are about to add it back from SEPA tags */
1345
  GWEN_DB_DeleteVar(dbData, "purpose");
1346

    
1347
  dbVar=GWEN_DB_GetFirstVar(dbSepaTags);
1348
  while (dbVar) {
1349
    const char *sVarName;
1350

    
1351
    sVarName=GWEN_DB_VariableName(dbVar);
1352
    if (sVarName && *sVarName) {
1353
      GWEN_BUFFER *tbuf;
1354
      GWEN_DB_NODE *dbValue;
1355

    
1356
      /* sample all values into a buffer and concatenate */
1357
      tbuf=GWEN_Buffer_new(0, 128, 0, 1);
1358
      dbValue=GWEN_DB_GetFirstValue(dbVar);
1359
      while (dbValue) {
1360
        const char *s;
1361

    
1362
        s=GWEN_DB_GetCharValueFromNode(dbValue);
1363
        if (s && *s)
1364
          GWEN_Buffer_AppendString(tbuf, s);
1365

    
1366
        dbValue=GWEN_DB_GetNextValue(dbValue);
1367
      }
1368

    
1369
      if (strcasecmp(sVarName, "EREF+")==0) {
1370
        AHB_SWIFT__SetCharValue(dbData, flags, "endToEndReference", GWEN_Buffer_GetStart(tbuf));
1371
      }
1372
      else if (strcasecmp(sVarName, "KREF+")==0) {
1373
        AHB_SWIFT__SetCharValue(dbData, flags, "customerReference", GWEN_Buffer_GetStart(tbuf));
1374
      }
1375
      else if (strcasecmp(sVarName, "MREF+")==0) {
1376
        AHB_SWIFT__SetCharValue(dbData, flags, "mandateId", GWEN_Buffer_GetStart(tbuf));
1377
      }
1378
      else if (strcasecmp(sVarName, "CRED+")==0) {
1379
        AHB_SWIFT__SetCharValue(dbData, flags, "creditorSchemeId", GWEN_Buffer_GetStart(tbuf));
1380
      }
1381
      else if (strcasecmp(sVarName, "DEBT+")==0) {
1382
        AHB_SWIFT__SetCharValue(dbData, flags, "originatorId", GWEN_Buffer_GetStart(tbuf));
1383
      }
1384
      else if (strcasecmp(sVarName, "SVWZ+")==0) {
1385
        AHB_SWIFT__SetCharValue(dbData, flags | GWEN_DB_FLAGS_OVERWRITE_VARS, "purpose", GWEN_Buffer_GetStart(tbuf));
1386
      }
1387
      else if (strcasecmp(sVarName, "ABWA+")==0) {
1388
        /* "abweichender Auftraggeber" */
1389
        AHB_SWIFT__SetCharValue(dbData, flags, "sepa/ABWA", GWEN_Buffer_GetStart(tbuf));
1390
      }
1391
      else if (strcasecmp(sVarName, "ABWE+")==0) {
1392
        /* "abweichender Empfaenger" */
1393
        AHB_SWIFT__SetCharValue(dbData, flags, "sepa/ABWE", GWEN_Buffer_GetStart(tbuf));
1394
      }
1395
      else if (strcasecmp(sVarName, "_purpose")==0) {
1396
        /* manually added tag (i.e. data outside a tag)
1397
        * will be replaced if there was a real purpose field (i.e. "SVWZ+") */
1398
        AHB_SWIFT__SetCharValue(dbData, flags, "purpose", GWEN_Buffer_GetStart(tbuf));
1399
      }
1400

    
1401
      GWEN_Buffer_free(tbuf);
1402
    }
1403

    
1404
    dbVar=GWEN_DB_GetNextVar(dbVar);
1405
  }
1406
}
1407

    
1408

    
1409

    
1410
void _parseTransactionData(const char *p, GWEN_DB_NODE *dbData, uint32_t flags)
1411
{
1412
  char *pcopy=strdup(p);
1413
  char *p1;
1414

    
1415
  /* unstructured :86:, simply store as mutliple purpose lines */
1416
  p1=pcopy;
1417
  while (p1 && *p1) {
1418
    char *p2;
1419

    
1420
    p2=strchr(p1, 10);
1421
    if (p2) {
1422
      *p2=0;
1423
      p2++;
1424
    }
1425

    
1426
    /* look for pattern "KTO/BLZ", if found try to extract remote account info
1427
     * from unstructured purpose string */
1428
    if (-1!=GWEN_Text_ComparePattern(p1, "*KTO/BLZ */*", 0)) {
1429
      char *p3;
1430
      char *kto;
1431

    
1432
      p3=p1;
1433
      while (*p3) {
1434
        *p3=toupper(*p3);
1435
        p3++;
1436
      }
1437
      kto=strstr(p1, "KTO/BLZ ");
1438
      if (kto) {
1439
        char *blz;
1440

    
1441
        kto+=8;
1442
        blz=strchr(kto, '/');
1443
        if (blz) {
1444
          *blz=0;
1445
          blz++;
1446

    
1447
          p3=blz;
1448
          while (*p3 && isdigit(*p3))
1449
            p3++;
1450
          *p3=0;
1451

    
1452
          AHB_SWIFT__SetCharValue(dbData, flags, "remoteBankCode", blz);
1453
          AHB_SWIFT__SetCharValue(dbData, flags, "remoteAccountNumber", kto);
1454
        }
1455
      }
1456
      else {
1457
        AHB_SWIFT__SetCharValue(dbData, flags, "purpose", p1);
1458
      }
1459
    }
1460
    else
1461
      AHB_SWIFT__SetCharValue(dbData, flags, "purpose", p1);
1462
    p1=p2;
1463
  }
1464
  free(pcopy);
1465
}
1466

    
1467

    
1468