1 /*
2  *
3  *  Copyright (C) 1993-2020, OFFIS e.V.
4  *  All rights reserved.  See COPYRIGHT file for details.
5  *
6  *  This software and supporting documentation were developed by
7  *
8  *    OFFIS e.V.
9  *    R&D Division Health
10  *    Escherweg 2
11  *    D-26121 Oldenburg, Germany
12  *
13  *
14  *  Module:  dcmqrdb
15  *
16  *  Author:  Marco Eichelberg / Ralph Meyer
17  *
18  *  Purpose: class DcmQueryRetrieveConfig
19  *
20  */
21 
22 
23 #include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
24 #include "dcmtk/dcmqrdb/dcmqrcnf.h"
25 
26 /* includes */
27 #define INCLUDE_CSTDIO
28 #define INCLUDE_CCTYPE
29 #define INCLUDE_CSTDARG
30 #define INCLUDE_CSTRING
31 #define INCLUDE_CLIMITS
32 #include "dcmtk/ofstd/ofstdinc.h"
33 #include "dcmtk/ofstd/ofcmdln.h"
34 #include "dcmtk/ofstd/ofmap.h"
35 #include "dcmtk/ofstd/ofchrenc.h"
36 
37 OFLogger DCM_dcmqrdbLogger = OFLog::getLogger("dcmtk.dcmqrdb");
38 
freePeer(OFMap<const void *,OFBool> & pointersToFree,struct DcmQueryRetrieveConfigPeer * entry)39 static void freePeer(OFMap<const void *, OFBool> &pointersToFree, struct DcmQueryRetrieveConfigPeer *entry)
40 {
41     // Hack to make sure we don't double-free
42     pointersToFree[entry->ApplicationTitle] = OFTrue;
43     pointersToFree[entry->HostName] = OFTrue;
44 }
45 
freeConfigAEEntry(OFMap<const void *,OFBool> & pointersToFree,struct DcmQueryRetrieveConfigAEEntry * entry)46 static void freeConfigAEEntry(OFMap<const void *, OFBool> &pointersToFree, struct DcmQueryRetrieveConfigAEEntry *entry)
47 {
48     for (int i = 0; i < entry->noOfPeers; i++) {
49         freePeer(pointersToFree, &entry->Peers[i]);
50     }
51     free(OFconst_cast(char *, entry->ApplicationTitle));
52     free(OFconst_cast(char *, entry->StorageArea));
53     free(OFconst_cast(char *, entry->Access));
54     free(entry->StorageQuota);
55     free(entry->Peers);
56 }
57 
freeConfigHostEntry(OFMap<const void *,OFBool> & pointersToFree,struct DcmQueryRetrieveConfigHostEntry * entry)58 static void freeConfigHostEntry(OFMap<const void *, OFBool> &pointersToFree, struct DcmQueryRetrieveConfigHostEntry *entry)
59 {
60     for (int i = 0; i< entry->noOfPeers; i++) {
61         freePeer(pointersToFree, &entry->Peers[i]);
62     }
63     free(OFconst_cast(char *, entry->SymbolicName));
64     free(entry->Peers);
65 }
66 
DcmQueryRetrieveCharacterSetOptions()67 DcmQueryRetrieveCharacterSetOptions::DcmQueryRetrieveCharacterSetOptions()
68 : characterSet()
69 , flags(0)
70 , conversionFlags(0)
71 {
72 
73 }
74 
parseOptions(const char * mnemonic,char * valueptr)75 OFBool DcmQueryRetrieveCharacterSetOptions::parseOptions(const char* mnemonic, char* valueptr)
76 {
77     struct RAIIFree
78     {
79         RAIIFree(char* p) : ptr(p) {}
80         ~RAIIFree() {free(ptr);}
81         char* ptr;
82     };
83     if (strcmp(mnemonic,"SpecificCharacterSet") != 0)
84         return OFFalse;
85     characterSet.clear();
86     flags = Configured;
87     conversionFlags = 0;
88     for (char* c = DcmQueryRetrieveConfig::parsevalues(&valueptr); c;
89          c = DcmQueryRetrieveConfig::parsevalues(&valueptr)) {
90         // ensure free is called when this scope is left
91         RAIIFree cleanup(c);
92         if (!strcmp(c, "override")) {
93             flags |= Override;
94         } else if(!strcmp(c, "fallback")) {
95             flags |= Fallback;
96         } else if(!strcmp(c, "abort")) {
97             conversionFlags |= OFCharacterEncoding::AbortTranscodingOnIllegalSequence;
98         } else if(!strcmp(c, "discard")) {
99             conversionFlags |= OFCharacterEncoding::DiscardIllegalSequences;
100         } else if(!strcmp(c, "transliterate")) {
101             conversionFlags |= OFCharacterEncoding::TransliterateIllegalSequences;
102         } else {
103             characterSet = c;
104         }
105     }
106     return OFTrue;
107 }
108 
~DcmQueryRetrieveConfig()109 DcmQueryRetrieveConfig::~DcmQueryRetrieveConfig()
110 {
111     // There can be more than one DcmQueryRetrieveConfigPeer which points to the
112     // same strings. To make sure that we don't free them more than once, we use
113     // a std::set<void*> which contains the pointers which we have to free.
114     // This happens in DcmQueryRetrieveConfig::readPeerList() while handling
115     // symbolic names (DcmQueryRetrieveConfigPeer gets copied via memcpy()).
116     //
117     // TODO: Since OFSet and std::set have nothing in common, we have to fake a
118     // set via a map.
119     OFMap<const void *, OFBool> pointersToFree;
120     OFMap<const void *, OFBool>::const_iterator it;
121     int i;
122 
123     for (i = 0; i < CNF_Config.noOfAEEntries; i++) {
124         freeConfigAEEntry(pointersToFree, &CNF_Config.AEEntries[i]);
125     }
126     free(CNF_Config.AEEntries);
127 
128     for (i = 0; i < CNF_HETable.noOfHostEntries; i++) {
129         freeConfigHostEntry(pointersToFree, &CNF_HETable.HostEntries[i]);
130     }
131     free(CNF_HETable.HostEntries);
132 
133     for (i = 0; i < CNF_VendorTable.noOfHostEntries; i++) {
134         freeConfigHostEntry(pointersToFree, &CNF_VendorTable.HostEntries[i]);
135     }
136     free(CNF_VendorTable.HostEntries);
137 
138     for (it = pointersToFree.begin(); it != pointersToFree.end(); ++it) {
139         free(OFconst_cast(void *, it->first));
140     }
141 }
142 
aeTitlesForPeer(const char * hostName,const char *** aeTitleList) const143 int DcmQueryRetrieveConfig::aeTitlesForPeer(const char *hostName, const char *** aeTitleList) const
144 {
145     int n = 0;
146     int i, j, k;
147     const int chunkSize = 1;
148     int maxAlloc = 0;
149     const char *hname;
150     const char *aetitle;
151     int found;
152 
153     *aeTitleList = (const char**)malloc(chunkSize*sizeof(const char*));
154     maxAlloc = chunkSize;
155 
156     /* collect up titles for peer, search in host table */
157     for (i=0; i<CNF_HETable.noOfHostEntries; i++) {
158         for (j=0; j<CNF_HETable.HostEntries[i].noOfPeers; j++) {
159             hname = CNF_HETable.HostEntries[i].Peers[j].HostName;
160             aetitle = CNF_HETable.HostEntries[i].Peers[j].ApplicationTitle;
161 #ifdef HAVE_PROTOTYPE_STRCASECMP
162             if (strcasecmp(hname, hostName) == 0) {  /* DNS is not case-sensitive */
163 #elif defined(HAVE_PROTOTYPE__STRICMP)
164             if (_stricmp(hname, hostName) == 0) {
165 #else
166             if (strcmp(hname, hostName) == 0) {  /* fallback if case insensitive compare is unavailable */
167 #endif
168                 /* found an entry for peer host */
169                 /* make sure its not already in list */
170                 found = 0;
171                 for (k=0; !found && k<n; k++) {
172                     found = (strcmp((*aeTitleList)[k], aetitle) == 0);
173                 }
174                 if (!found) {
175                     if (n >= maxAlloc) {
176                         *aeTitleList = (const char**)realloc(*aeTitleList,
177                                       (maxAlloc + chunkSize)*sizeof(const char*));
178                         maxAlloc += chunkSize;
179                     }
180                     (*aeTitleList)[n] = aetitle;
181 
182                     n++;
183                 }
184             }
185         }
186     }
187     /* collect up titles for peer, search in AE table */
188     for (i=0; i<CNF_Config.noOfAEEntries; i++) {
189         for (j=0; j<CNF_Config.AEEntries[i].noOfPeers; j++) {
190             hname = CNF_Config.AEEntries[i].Peers[j].HostName;
191             aetitle = CNF_Config.AEEntries[i].Peers[j].ApplicationTitle;
192 
193 #ifdef HAVE_PROTOTYPE_STRCASECMP
194             if (strcasecmp(hname, hostName) == 0) {  /* DNS is not case-sensitive */
195 #elif defined(HAVE_PROTOTYPE__STRICMP)
196             if (_stricmp(hname, hostName) == 0) {
197 #else
198             if (strcmp(hname, hostName) == 0) {  /* fallback if case insensitive compare is unavailable */
199 #endif
200                 /* found an entry for peer host */
201                 /* make sure its not already in list */
202                 found = 0;
203                 for (k=0; !found && k<n; k++) {
204                     found = (strcmp((*aeTitleList)[k], aetitle) == 0);
205                 }
206                 if (!found) {
207                     if (n >= maxAlloc) {
208                         *aeTitleList = (const char**)realloc(*aeTitleList,
209                                         (maxAlloc + chunkSize)*sizeof(const char*));
210                         maxAlloc += chunkSize;
211                     }
212                     (*aeTitleList)[n] = aetitle;
213 
214                     n++;
215                 }
216             }
217         }
218     }
219 
220     if (n == 0) {
221         free(*aeTitleList);
222         *aeTitleList = NULL;
223     }
224     return n;
225 }
226 
227 
228 int DcmQueryRetrieveConfig::ctnTitles(const char *** ctnTitleList) const
229 {
230     int i;
231     int n = 0;
232 
233     n = CNF_Config.noOfAEEntries;
234     *ctnTitleList = (const char**)malloc(n * sizeof(const char*));
235 
236     for (i=0; i<n; i++) {
237         (*ctnTitleList)[i] = CNF_Config.AEEntries[i].ApplicationTitle;
238     }
239     return n;
240 }
241 
242 
243 int DcmQueryRetrieveConfig::aeTitlesForSymbolicName(const char *symbolicName, const char *** aeTitleList) const
244 {
245     int i = 0;
246     int j = 0;
247     int n = 0;
248 
249     for (i=0; i<CNF_HETable.noOfHostEntries; i++) {
250         if (strcmp(symbolicName, CNF_HETable.HostEntries[i].SymbolicName)==0) {
251             n = CNF_HETable.HostEntries[i].noOfPeers;
252             *aeTitleList = (const char**)malloc(n * sizeof(const char*));
253             for (j=0; j<n; j++) {
254                 (*aeTitleList)[j] =
255                     CNF_HETable.HostEntries[i].Peers[j].ApplicationTitle;
256             }
257             return n;
258         }
259     }
260     return 0;
261 
262 }
263 
264 const char *DcmQueryRetrieveConfig::vendorForPeerAETitle(const char *peerAETitle) const
265 {
266     int i = 0;
267     int j = 0;
268 
269     for (i=0; i<CNF_VendorTable.noOfHostEntries; i++) {
270         for (j=0; j<CNF_VendorTable.HostEntries[i].noOfPeers; j++) {
271             if (strcmp(peerAETitle,
272                 CNF_VendorTable.HostEntries[i].Peers[j].ApplicationTitle)==0) {
273                 return CNF_VendorTable.HostEntries[i].SymbolicName;
274             }
275         }
276     }
277     return NULL;
278 }
279 
280 int DcmQueryRetrieveConfig::countCtnTitles() const
281 {
282     return CNF_Config.noOfAEEntries;
283 }
284 
285 
286 void DcmQueryRetrieveConfig::initConfigStruct()
287 {
288    UserName_ = "";
289    GroupName_ = "";
290    networkTCPPort_ = 104;
291    maxPDUSize_ = 16384;
292    maxAssociations_ = 16;
293    CNF_Config.noOfAEEntries = 0;
294    CNF_HETable.noOfHostEntries = 0;
295    CNF_VendorTable.noOfHostEntries = 0;
296 }
297 
298 
299 void DcmQueryRetrieveConfig::panic(const char *fmt, ...)
300 {
301    va_list  ap;
302    va_start(ap, fmt);
303 
304 #if defined(HAVE_VSNPRINTF) && defined(HAVE_PROTOTYPE_VSNPRINTF)
305    char buf[4096];
306 
307 #ifdef HAVE_PROTOTYPE_STD__VSNPRINTF
308    std::vsnprintf(buf, sizeof(buf), fmt, ap);
309 #else
310    vsnprintf(buf, sizeof(buf), fmt, ap);
311 #endif
312 
313    // Since we can't do anything about a too small buffer for vsnprintf(), we
314    // ignore it. But we do make sure the buffer is null-terminated!
315    buf[4095] = '\0';
316 
317    DCMQRDB_ERROR("CONFIG Error: " << buf << "!");
318 #else
319    fprintf(stderr, "CONFIG Error: ");
320    vfprintf(stderr, fmt, ap);
321    fprintf(stderr, "!\n");
322 #endif
323    va_end(ap);
324 }
325 
326 
327 int DcmQueryRetrieveConfig::readConfigLines(FILE *cnffp)
328 {
329    int  lineno = 0,       /* line counter */
330         error = 0;        /* error flag */
331    char rcline[512],      /* line in configuration file */
332         mnemonic[512],    /* mnemonic in line */
333         value[512],       /* parameter value */
334         *valueptr;        /* pointer to value list */
335    char *c;
336 
337    // read all lines from configuration file
338    while (fgets(rcline, sizeof(rcline), cnffp)) {
339       lineno++;
340       if (rcline[0] == '#' || rcline[0] == 10 || rcline[0] == 13)
341          continue;        /* comment or blank line */
342 
343       if (sscanf(rcline, "%s", mnemonic) != 1)
344         continue;  /* ignore lines containing only whitespace */
345 
346       valueptr = skipmnemonic(rcline);
347 
348       if (!strcmp("ApplicationTitle", mnemonic)) {
349         // ignore this entry which was used (really?) in previous versions
350       }
351       else if (!strcmp("ApplicationContext", mnemonic)) {
352         // ignore this entry which was used (really?) in previous versions
353       }
354       else if (!strcmp("ImplementationClass", mnemonic)) {
355         // ignore this entry which was used (really?) in previous versions
356       }
357       else if (!strcmp("ImplementationVersion", mnemonic)) {
358         // ignore this entry which was used (really?) in previous versions
359       }
360       else if (!strcmp("NetworkType", mnemonic)) {
361         // ignore this entry which was used (really?) in previous versions
362       }
363       else if (!strcmp("UserName", mnemonic)) {
364          c = parsevalues(&valueptr);
365          UserName_ = c;
366          free(c);
367       }
368       else if (!strcmp("GroupName", mnemonic)) {
369          c = parsevalues(&valueptr);
370          GroupName_ = c;
371          free(c);
372       }
373       else if (!strcmp("NetworkTCPPort", mnemonic)) {
374          sscanf(valueptr, "%d", &networkTCPPort_);
375       }
376       else if (!strcmp("MaxPDUSize", mnemonic)) {
377          unsigned long ul = 0;
378          sscanf(valueptr, "%lu", &ul);
379          maxPDUSize_ = OFstatic_cast(Uint32, ul);
380       }
381       else if (!strcmp("MaxAssociations", mnemonic)) {
382          sscanf(valueptr, "%d", &maxAssociations_);
383       }
384       else if (!strcmp("Display", mnemonic))
385       {
386         // ignore this entry which was needed for ctndisp
387       }
388       else if (!strcmp("DisplayPort", mnemonic))
389       {
390         // ignore this entry which was needed for ctndisp
391       }
392       else if (characterSetOptions_.parseOptions(mnemonic, valueptr))
393       {
394         // already handled by parseOptions(), nothing else to do
395       }
396       else if (!strcmp("HostTable", mnemonic)) {
397          sscanf(valueptr, "%s", value);
398          if (!strcmp("BEGIN", value)) {
399             if (!readHostTable(cnffp, &lineno))
400                error = 1;
401          }
402          else if (!strcmp("END", value)) {
403             panic("No \"HostTable BEGIN\" before END in configuration file, line %d", lineno);
404             error = 1;
405          }
406          else {
407             panic("Unknown HostTable status \"%s\" in configuration file, line %d", value, lineno);
408             error = 1;
409          }
410       }
411       else if (!strcmp("VendorTable", mnemonic)) {
412          sscanf(valueptr, "%s", value);
413          if (!strcmp("BEGIN", value)) {
414             if (!readVendorTable(cnffp, &lineno))
415                error = 1;
416          }
417          else if (!strcmp("END", value)) {
418             panic("No \"VendorTable BEGIN\" before END in configuration file, line %d", lineno);
419             error = 1;
420          }
421          else {
422             panic("Unknown VendorTable status \"%s\" in configuration file, line %d", value, lineno);
423             error = 1;
424          }
425       }
426       else if (!strcmp("AETable", mnemonic)) {
427          sscanf(valueptr, "%s", value);
428          if (!strcmp("BEGIN", value)) {
429             if (!readAETable(cnffp, &lineno))
430                error = 1;
431          }
432          else if (!strcmp("END", value)) {
433             panic("No \"AETable BEGIN\" before END in configuration file, line %d", lineno);
434             error = 1;
435          }
436          else {
437             panic("Unknown AETable status \"%s\" in configuration file, line %d", value, lineno);
438             error = 1;
439          }
440       }
441       else {
442          panic("Unknown mnemonic \"%s\" in configuration file, line %d", mnemonic, lineno);
443          error = 1;
444       }
445    }
446 
447    return(error ? 0 : 1);
448 }
449 
450 
451 int DcmQueryRetrieveConfig::readHostTable(FILE *cnffp, int *lineno)
452 {
453    int  error = 0,        /* error flag */
454         end = 0,          /* end flag */
455         noOfPeers;        /* number of peers for entry */
456    char rcline[512],      /* line in configuration file */
457         mnemonic[512],    /* mnemonic in line */
458         value[512],       /* parameter value */
459         *lineptr;         /* pointer to line */
460    DcmQueryRetrieveConfigHostEntry *helpentry;
461 
462    // read certain lines from configuration file
463    while (fgets(rcline, sizeof(rcline), cnffp)) {
464       (*lineno)++;
465       if (rcline[0] == '#' || rcline[0] == 10 || rcline[0] == 13)
466          continue;        /* comment or blank line */
467 
468       sscanf(rcline, "%s %s", mnemonic, value);
469       if (!strcmp("HostTable", mnemonic)) {
470          if (!strcmp("END", value)) {
471             end = 1;
472             break;
473          }
474          else {
475             panic("Illegal HostTable status \"%s\" in configuration file, line %d", value, *lineno);
476             error = 1;
477             break;
478          }
479       }
480 
481       lineptr = rcline;
482       CNF_HETable.noOfHostEntries++;
483       if ((helpentry = (DcmQueryRetrieveConfigHostEntry *)malloc(CNF_HETable.noOfHostEntries * sizeof(DcmQueryRetrieveConfigHostEntry))) == NULL)
484          panic("Memory allocation 1 (%d)", CNF_HETable.noOfHostEntries);
485       if (CNF_HETable.noOfHostEntries - 1) {
486          memcpy((char*)helpentry, (char*)CNF_HETable.HostEntries, (CNF_HETable.noOfHostEntries - 1) *sizeof(DcmQueryRetrieveConfigHostEntry));
487          free(CNF_HETable.HostEntries);
488       }
489       CNF_HETable.HostEntries = helpentry;
490 
491       CNF_HETable.HostEntries[CNF_HETable.noOfHostEntries - 1].SymbolicName = parsevalues(&lineptr);
492       CNF_HETable.HostEntries[CNF_HETable.noOfHostEntries - 1].Peers = readPeerList(&lineptr, &noOfPeers);
493       CNF_HETable.HostEntries[CNF_HETable.noOfHostEntries - 1].noOfPeers = noOfPeers;
494       if (!noOfPeers)
495          error = 1;
496    }
497 
498    if (!end) {
499       error = 1;
500       panic("No \"HostTable END\" in configuration file, line %d", *lineno);
501     }
502    return(error ? 0 : 1);
503 }
504 
505 
506 int DcmQueryRetrieveConfig::readVendorTable(FILE *cnffp, int *lineno)
507 {
508    int  error = 0,        /* error flag */
509         end = 0,          /* end flag */
510         noOfPeers;        /* number of peers for entry */
511    char rcline[512],      /* line in configuration file */
512         mnemonic[512],     /* mnemonic in line */
513         value[512],       /* parameter value */
514         *lineptr;         /* pointer to line */
515    DcmQueryRetrieveConfigHostEntry *helpentry;
516 
517    // read certain lines from configuration file
518    while (fgets(rcline, sizeof(rcline), cnffp)) {
519       (*lineno)++;
520       if (rcline[0] == '#' || rcline[0] == 10 || rcline[0] == 13)
521          continue;        /* comment or blank line */
522 
523       sscanf(rcline, "%s %s", mnemonic, value);
524       if (!strcmp("VendorTable", mnemonic)) {
525          if (!strcmp("END", value)) {
526             end = 1;
527             break;
528          }
529          else {
530             panic("Illegal VendorTable status \"%s\" in configuration file, line %d", value, *lineno);
531             error = 1;
532             break;
533          }
534       }
535 
536       lineptr = rcline;
537       CNF_VendorTable.noOfHostEntries++;
538       if ((helpentry = (DcmQueryRetrieveConfigHostEntry *)malloc(CNF_VendorTable.noOfHostEntries * sizeof(DcmQueryRetrieveConfigHostEntry))) == NULL)
539          panic("Memory allocation 2 (%d)", CNF_VendorTable.noOfHostEntries);
540       if (CNF_VendorTable.noOfHostEntries - 1) {
541          memcpy((char*)helpentry, (char*)CNF_VendorTable.HostEntries, (CNF_VendorTable.noOfHostEntries - 1) *sizeof(DcmQueryRetrieveConfigHostEntry));
542          free(CNF_VendorTable.HostEntries);
543       }
544       CNF_VendorTable.HostEntries = helpentry;
545 
546       CNF_VendorTable.HostEntries[CNF_VendorTable.noOfHostEntries - 1].SymbolicName = parsevalues(&lineptr);
547       CNF_VendorTable.HostEntries[CNF_VendorTable.noOfHostEntries - 1].Peers = readPeerList(&lineptr, &noOfPeers);
548       CNF_VendorTable.HostEntries[CNF_VendorTable.noOfHostEntries - 1].noOfPeers = noOfPeers;
549       if (!noOfPeers)
550          error = 1;
551    }
552 
553    if (!end) {
554       error = 1;
555       panic("No \"VendorTable END\" in configuration file, line %d", *lineno);
556     }
557    return(error ? 0 : 1);
558 }
559 
560 
561 int DcmQueryRetrieveConfig::readAETable(FILE *cnffp, int *lineno)
562 {
563    int  error = 0,          /* error flag */
564         end = 0,            /* end flag */
565         noOfAEEntries = 0;  /* number of AE entries */
566    char rcline[512],        /* line in configuration file */
567         mnemonic[512],      /* mnemonic in line */
568         value[512],         /* parameter value */
569         *lineptr;           /* pointer to line */
570    DcmQueryRetrieveConfigAEEntry *helpentry;
571 
572    // read certain lines from configuration file
573    while (fgets(rcline, sizeof(rcline), cnffp)) {
574       (*lineno)++;
575       if (rcline[0] == '#' || rcline[0] == 10 || rcline[0] == 13)
576          continue;        /* comment or blank line */
577 
578       sscanf(rcline, "%s %s", mnemonic, value);
579       if (!strcmp("AETable", mnemonic)) {
580          if (!strcmp("END", value)) {
581             end = 1;
582             break;
583          }
584          else {
585             panic("Illegal AETable status \"%s\" in configuration file, line %d", value, *lineno);
586             error = 1;
587             break;
588          }
589       }
590 
591       lineptr = rcline;
592       noOfAEEntries++;
593       if ((helpentry = (DcmQueryRetrieveConfigAEEntry *)malloc(noOfAEEntries * sizeof(DcmQueryRetrieveConfigAEEntry))) == NULL)
594          panic("Memory allocation 3 (%d)", noOfAEEntries);
595       if (noOfAEEntries - 1) {
596          memcpy((char*)helpentry, (char*)CNF_Config.AEEntries, (noOfAEEntries - 1) *sizeof(DcmQueryRetrieveConfigAEEntry));
597          free(CNF_Config.AEEntries);
598       }
599       CNF_Config.AEEntries = helpentry;
600 
601       CNF_Config.AEEntries[noOfAEEntries - 1].ApplicationTitle = parsevalues(&lineptr);
602       CNF_Config.AEEntries[noOfAEEntries - 1].StorageArea = parsevalues(&lineptr);
603       CNF_Config.AEEntries[noOfAEEntries - 1].Access = parsevalues(&lineptr);
604       CNF_Config.AEEntries[noOfAEEntries - 1].StorageQuota = parseQuota(&lineptr);
605       CNF_Config.AEEntries[noOfAEEntries - 1].Peers = parsePeers(&lineptr, &CNF_Config.AEEntries[noOfAEEntries - 1].noOfPeers);
606 
607       // check the validity of the storage quota and peers values before continuing
608       if (CNF_Config.AEEntries[noOfAEEntries - 1].StorageQuota->maxStudies == 0 ||
609          CNF_Config.AEEntries[noOfAEEntries - 1].StorageQuota->maxBytesPerStudy == 0 ||
610          CNF_Config.AEEntries[noOfAEEntries - 1].noOfPeers == 0) {
611           error = 1;
612       }
613    }
614 
615    if (!end) {
616       error = 1;
617       panic("No \"AETable END\" in configuration file, line %d", *lineno);
618     }
619    CNF_Config.noOfAEEntries = noOfAEEntries;
620    return(error ? 0 : 1);
621 }
622 
623 
624 DcmQueryRetrieveConfigQuota *DcmQueryRetrieveConfig::parseQuota(char **valuehandle)
625 {
626    int  studies;
627    char *helpvalue,
628         helpval[512];
629    DcmQueryRetrieveConfigQuota *helpquota;
630 
631    if ((helpquota = (DcmQueryRetrieveConfigQuota *)malloc(sizeof(DcmQueryRetrieveConfigQuota))) == NULL)
632       panic("Memory allocation 4");
633    helpvalue = parsevalues(valuehandle);
634    if (helpvalue)
635    {
636      sscanf(helpvalue, "%d , %s", &studies, helpval);
637      helpquota->maxStudies = studies;
638      helpquota->maxBytesPerStudy = quota(helpval);
639    } else {
640      helpquota->maxStudies = 0;
641      helpquota->maxBytesPerStudy = 0;
642    }
643    free(helpvalue);
644 
645    return(helpquota);
646 }
647 
648 
649 DcmQueryRetrieveConfigPeer *DcmQueryRetrieveConfig::parsePeers(char **valuehandle, int *peers)
650 {
651    char *helpvalue;
652    char *valueptr = *valuehandle;
653 
654    helpvalue = parsevalues(valuehandle);
655 
656    if (!helpvalue) {
657       *peers = 0; // indicates error to caller
658       return NULL;
659    }
660 
661    if (!strcmp("ANY", helpvalue)) {     /* keyword ANY used */
662       free(helpvalue);
663       *peers = -1;
664       return((DcmQueryRetrieveConfigPeer *) 0);
665    }
666 
667    free(helpvalue);         /* regular peer list */
668    return(readPeerList(&valueptr, peers));
669 }
670 
671 
672 DcmQueryRetrieveConfigPeer *DcmQueryRetrieveConfig::readPeerList(char **valuehandle, int *peers)
673 {
674    int  i,
675    found,
676    noOfPeers = 0;
677    char *helpvalue;
678    DcmQueryRetrieveConfigPeer *helppeer,
679    *peerlist = NULL;
680 
681    while((helpvalue = parsevalues(valuehandle)) != NULL) {
682       found = 0;
683       if (strchr(helpvalue, ',') == NULL) {   /* symbolic name */
684          if (!CNF_HETable.noOfHostEntries) {
685             panic("No symbolic names defined");
686             *peers = 0;
687             free(helpvalue);
688             return((DcmQueryRetrieveConfigPeer *) 0);
689          }
690          for(i = 0; i < CNF_HETable.noOfHostEntries; i++) {
691             if (!strcmp(CNF_HETable.HostEntries[i].SymbolicName, helpvalue)) {
692                found = 1;
693                break;
694             }
695          }
696          if (!found) {
697             panic("Symbolic name \"%s\" not defined", helpvalue);
698             *peers = 0;
699             free(helpvalue);
700             return((DcmQueryRetrieveConfigPeer *) 0);
701          }
702 
703          noOfPeers += CNF_HETable.HostEntries[i].noOfPeers;
704         if ((helppeer = (DcmQueryRetrieveConfigPeer *)malloc(noOfPeers * sizeof(DcmQueryRetrieveConfigPeer))) == NULL)
705             panic("Memory allocation 5 (%d)", noOfPeers);
706         if (noOfPeers - CNF_HETable.HostEntries[i].noOfPeers) {
707             memcpy((char*)helppeer, (char*)peerlist, (noOfPeers - CNF_HETable.HostEntries[i].noOfPeers) * sizeof(DcmQueryRetrieveConfigPeer));
708             free(peerlist);
709          }
710          peerlist = helppeer;
711          memcpy((char*)(peerlist + (noOfPeers - CNF_HETable.HostEntries[i].noOfPeers)), (char*)CNF_HETable.HostEntries[i].Peers, CNF_HETable.HostEntries[i].noOfPeers * sizeof(DcmQueryRetrieveConfigPeer));
712       }
713 
714       else {            /* peer */
715          noOfPeers++;
716          if ((helppeer = (DcmQueryRetrieveConfigPeer *)malloc(noOfPeers * sizeof(DcmQueryRetrieveConfigPeer))) == NULL)
717             panic("Memory allocation 6 (%d)", noOfPeers);
718          if (noOfPeers - 1) {
719             memcpy((char*)helppeer, (char*)peerlist, (noOfPeers - 1) *sizeof(DcmQueryRetrieveConfigPeer));
720             free(peerlist);
721          }
722          peerlist = helppeer;
723 
724          char *tempvalue = helpvalue;
725          peerlist[noOfPeers - 1].ApplicationTitle = parsevalues(&helpvalue);
726          peerlist[noOfPeers - 1].HostName = parsevalues(&helpvalue);
727          peerlist[noOfPeers - 1].PortNumber = atoi(helpvalue);
728          helpvalue = tempvalue;
729       }
730       free(helpvalue);
731    }
732    *peers = noOfPeers;
733    return(peerlist);
734 }
735 
736 
737 char *DcmQueryRetrieveConfig::skipmnemonic (char *rcline)
738 {
739    char *help = rcline;
740 
741    while(*help != '\0') {                       /* leading spaces */
742       if (isgap(*help)) help++;
743       else break;
744     }
745    while(*help != '\0') {
746       if (!isspace(OFstatic_cast(unsigned char, *help))) help++;    /* Mnemonic */
747       else break;
748    }
749    while(*help != '\0') {
750       if (isgap(*help)) help++;     /* Gap */
751       else break;
752    }
753    return(help);
754 }
755 
756 
757 int DcmQueryRetrieveConfig::isgap (char gap)
758 {
759    if (isspace(OFstatic_cast(unsigned char, gap)))
760       return(1);
761    if (gap == '=' || gap == ',' || gap == 10 || gap == 13)
762       return(1);
763    else
764       return(0);
765 }
766 
767 
768 int DcmQueryRetrieveConfig::isquote (char quote)
769 {
770    if (quote == '"' || quote == '\'' || quote == '(' || quote == ')')
771       return(1);
772    else
773       return(0);
774 }
775 
776 
777 char *DcmQueryRetrieveConfig::parsevalues (char **valuehandle)
778 {
779    int i,
780        inquotes = 0,
781        count = 0;
782    char *value = NULL;
783    const char *help,
784    *valueptr = *valuehandle;
785 
786    if (isquote(*valueptr)) {
787       inquotes = 1;
788       valueptr++;
789    }
790 
791    help = valueptr;
792 
793    while(*help != '\0') {
794       if (inquotes) {
795          if (isquote(*help)) {
796             if ((value = (char*)malloc(count * sizeof(char) + 1)) == NULL)
797                panic("Memory allocation 7 (%d)", count);
798             for(i = 0; i < count; i++)
799                value[i] = valueptr[i];
800             value[count] = '\0';
801             count++;
802             help++;
803             while (*help != '\0') {
804                if (isgap(*help)) {
805                   count++;
806                   help++;
807                }
808                else
809                   break;
810             }
811             *valuehandle += (count + 1);
812             break;
813          }
814          else {
815             count++;
816             help++;
817          }
818       }
819       else {
820          if (isgap(*help)) {
821             if ((value = (char*)malloc(count * sizeof(char) + 1)) == NULL)
822                panic("Memory allocation 8 (%d)", count);
823             for(i = 0; i < count; i++)
824                value[i] = valueptr[i];
825             value[count] = '\0';
826             while (*help != '\0') {
827                if (isgap(*help)) {
828                   count++;
829                   help++;
830                }
831                else
832                   break;
833             }
834             *valuehandle += count;
835             break;
836          }
837          else {
838            count++;
839            help++;
840          }
841       } /* inquotes */
842    } /* while */
843 
844    return(value);
845 }
846 
847 
848 long DcmQueryRetrieveConfig::quota (const char *value)
849 {
850    int  number;
851    long factor;
852    char last = *(value + strlen(value) - 1),  /* last character */
853    mult = *(value + strlen(value) - 2);       /* multiplier */
854 
855    if (last == 'b' || last == 'B') {
856       if (mult == 'k' || mult == 'K') factor = 1024;
857       else if (mult == 'm' || mult == 'M') factor = 1024 * 1024;
858       else if (mult == 'g' || mult == 'G') factor = 1024 * 1024 * 1024;
859       else factor = 1;
860    }
861    else return(-1L);
862 
863    number = atoi(value);
864 
865    // check for overflow
866    if (number > 0 && factor > LONG_MAX / number)
867      return LONG_MAX;
868 
869    return(number * factor);
870 }
871 
872 
873 int DcmQueryRetrieveConfig::init(const char *ConfigurationFile)
874 {
875    int  error = 0;        /* error flag */
876    FILE *cnffp;         /* configuration file pointer */
877 
878    if ((cnffp = fopen(ConfigurationFile, "r")) == NULL) {
879       panic("Unable to open configuration file \"%s\"", ConfigurationFile);
880       return(0);
881    }
882 
883    initConfigStruct();
884 
885    if (!readConfigLines(cnffp)) {
886       panic("Reading configuration file \"%s\" with errors", ConfigurationFile);
887       error = 1;
888    }
889 
890    fclose(cnffp);
891 
892    return(error ? 0 : 1);
893 }
894 
895 
896 void DcmQueryRetrieveConfig::printConfig()
897 {
898    int i,j;
899 
900    DCMQRDB_INFO("\nHostTable: " <<  CNF_HETable.noOfHostEntries);
901    for(i = 0; i < CNF_HETable.noOfHostEntries; i++) {
902       DCMQRDB_INFO(CNF_HETable.HostEntries[i].SymbolicName << " " << CNF_HETable.HostEntries[i].noOfPeers);
903       for(j = 0; j < CNF_HETable.HostEntries[i].noOfPeers; j++) {
904          DCMQRDB_INFO(CNF_HETable.HostEntries[i].Peers[j].ApplicationTitle << " " <<
905             CNF_HETable.HostEntries[i].Peers[j].HostName << " " << CNF_HETable.HostEntries[i].Peers[j].PortNumber);
906       }
907    }
908    DCMQRDB_INFO("\nVendorTable: " << CNF_VendorTable.noOfHostEntries);
909    for(i = 0; i < CNF_VendorTable.noOfHostEntries; i++) {
910       DCMQRDB_INFO(CNF_VendorTable.HostEntries[i].SymbolicName << " " << CNF_VendorTable.HostEntries[i].noOfPeers);
911       for(j = 0; j < CNF_VendorTable.HostEntries[i].noOfPeers; j++) {
912          DCMQRDB_INFO(CNF_VendorTable.HostEntries[i].Peers[j].ApplicationTitle << " " <<
913             CNF_VendorTable.HostEntries[i].Peers[j].HostName << " " << CNF_VendorTable.HostEntries[i].Peers[j].PortNumber);
914       }
915    }
916    DCMQRDB_INFO("\nGlobal Parameters:\n" << networkTCPPort_ << "\n" << OFstatic_cast(unsigned long, maxPDUSize_)
917       << "\n" << maxAssociations_);
918    DCMQRDB_INFO("\nAEEntries: " << CNF_Config.noOfAEEntries);
919    for(i = 0; i < CNF_Config.noOfAEEntries; i++) {
920       DCMQRDB_INFO(CNF_Config.AEEntries[i].ApplicationTitle << "\n" << CNF_Config.AEEntries[i].StorageArea
921          << "\n" << CNF_Config.AEEntries[i].Access << "\n" << CNF_Config.AEEntries[i].StorageQuota->maxStudies
922          << ", " << CNF_Config.AEEntries[i].StorageQuota->maxBytesPerStudy);
923       if (CNF_Config.AEEntries[i].noOfPeers == -1)
924          DCMQRDB_INFO("Peers: ANY");
925       else {
926          DCMQRDB_INFO("Peers: " << CNF_Config.AEEntries[i].noOfPeers);
927          for(j = 0; j < CNF_Config.AEEntries[i].noOfPeers; j++) {
928             DCMQRDB_INFO(CNF_Config.AEEntries[i].Peers[j].ApplicationTitle << " " <<
929                CNF_Config.AEEntries[i].Peers[j].HostName << " " << CNF_Config.AEEntries[i].Peers[j].PortNumber);
930          }
931       }
932       DCMQRDB_INFO("----------------------------------\n");
933    }
934 }
935 
936 
937 int DcmQueryRetrieveConfig::getNetworkTCPPort() const
938 {
939    return(networkTCPPort_);
940 }
941 
942 
943 OFCmdUnsignedInt DcmQueryRetrieveConfig::getMaxPDUSize() const
944 {
945    return(maxPDUSize_);
946 }
947 
948 
949 int DcmQueryRetrieveConfig::getMaxAssociations() const
950 {
951    return(maxAssociations_);
952 }
953 
954 
955 const char *DcmQueryRetrieveConfig::getStorageArea(const char *AETitle) const
956 {
957    int  i;
958 
959    for(i = 0; i < CNF_Config.noOfAEEntries; i++) {
960       if (!strcmp(AETitle, CNF_Config.AEEntries[i].ApplicationTitle))
961          return(CNF_Config.AEEntries[i].StorageArea);
962    }
963    return(NULL);        /* AETitle not found */
964 }
965 
966 
967 const char *DcmQueryRetrieveConfig::getAccess(const char *AETitle) const
968 {
969    int  i;
970 
971    for(i = 0; i < CNF_Config.noOfAEEntries; i++) {
972       if (!strcmp(AETitle, CNF_Config.AEEntries[i].ApplicationTitle))
973          return(CNF_Config.AEEntries[i].Access);
974    }
975    return(NULL);        /* AETitle not found */
976 }
977 
978 
979 int DcmQueryRetrieveConfig::getMaxStudies(const char *AETitle) const
980 {
981    int  i;
982 
983    for(i = 0; i < CNF_Config.noOfAEEntries; i++) {
984       if (!strcmp(AETitle, CNF_Config.AEEntries[i].ApplicationTitle))
985          return(CNF_Config.AEEntries[i].StorageQuota->maxStudies);
986    }
987    return(0);       /* AETitle not found */
988 }
989 
990 long DcmQueryRetrieveConfig::getMaxBytesPerStudy(const char *AETitle) const
991 {
992    int  i;
993 
994    for(i = 0; i < CNF_Config.noOfAEEntries; i++) {
995       if (!strcmp(AETitle, CNF_Config.AEEntries[i].ApplicationTitle))
996          return(CNF_Config.AEEntries[i].StorageQuota->maxBytesPerStudy);
997    }
998    return(0);       /* AETitle not found */
999 }
1000 
1001 
1002 int DcmQueryRetrieveConfig::peerInAETitle(const char *calledAETitle, const char *callingAETitle, const char *HostName) const
1003 {
1004    int  i,
1005     j;
1006 
1007    for(i = 0; i < CNF_Config.noOfAEEntries; i++) {
1008       if (!strcmp(calledAETitle, CNF_Config.AEEntries[i].ApplicationTitle)) {
1009          if (CNF_Config.AEEntries[i].noOfPeers == -1) /* ANY Peer allowed */
1010             return(1);
1011          for(j = 0; j < CNF_Config.AEEntries[i].noOfPeers; j++) {
1012             if (!strcmp(callingAETitle, CNF_Config.AEEntries[i].Peers[j].ApplicationTitle) &&
1013 #ifdef HAVE_PROTOTYPE_STRCASECMP
1014                 /* DNS is not case-sensitive */
1015                 !strcasecmp(HostName, CNF_Config.AEEntries[i].Peers[j].HostName))
1016 #elif defined(HAVE_PROTOTYPE__STRICMP)
1017                 !_stricmp(HostName, CNF_Config.AEEntries[i].Peers[j].HostName))
1018 #else
1019                 /* fallback solution is to do case sensitive comparison on systems
1020                    which do not implement strcasecmp or _stricmp */
1021                 !strcmp(HostName, CNF_Config.AEEntries[i].Peers[j].HostName))
1022 #endif
1023                return(1);       /* Peer found */
1024          }
1025       }
1026    }
1027    return(0);           /* Peer not found */
1028 }
1029 
1030 
1031 int DcmQueryRetrieveConfig::peerForAETitle(const char *AETitle, const char **HostName, int *PortNumber) const
1032 {
1033    int  i,
1034     j;
1035 
1036    for(i = 0; i < CNF_Config.noOfAEEntries; i++) {
1037       for(j = 0; j < CNF_Config.AEEntries[i].noOfPeers; j++) {
1038          if (!strcmp(AETitle, CNF_Config.AEEntries[i].Peers[j].ApplicationTitle)) {
1039             *HostName = CNF_Config.AEEntries[i].Peers[j].HostName;
1040             *PortNumber = CNF_Config.AEEntries[i].Peers[j].PortNumber;
1041             return(1);        /* Peer found in AETable */
1042          }
1043       }
1044    }
1045 
1046    for(i = 0; i < CNF_HETable.noOfHostEntries; i++) {
1047       for(j = 0; j < CNF_HETable.HostEntries[i].noOfPeers; j++) {
1048          if (!strcmp(AETitle, CNF_HETable.HostEntries[i].Peers[j].ApplicationTitle)) {
1049             *HostName = CNF_HETable.HostEntries[i].Peers[j].HostName;
1050             *PortNumber = CNF_HETable.HostEntries[i].Peers[j].PortNumber;
1051             return(2);        /* Peer found in HostTable */
1052          }
1053       }
1054    }
1055 
1056    return(0);         /* Peer not found */
1057 }
1058 
1059 
1060 int DcmQueryRetrieveConfig::checkForSameVendor(const char *AETitle1, const char *AETitle2) const
1061 {
1062    int  i,
1063     j,
1064     k,
1065     found = 0;
1066 
1067    for(i = 0; i < CNF_VendorTable.noOfHostEntries; i++) {
1068       for(j = 0; j < CNF_VendorTable.HostEntries[i].noOfPeers; j++) {
1069          if (!strcmp(AETitle1, CNF_VendorTable.HostEntries[i].Peers[j].ApplicationTitle)) {
1070             for(k = 0; k < CNF_VendorTable.HostEntries[i].noOfPeers; k++) {
1071                if (!strcmp(AETitle2, CNF_VendorTable.HostEntries[i].Peers[k].ApplicationTitle))
1072                   found = 1;
1073             }
1074          }
1075       }
1076    }
1077 
1078    return(found);
1079 }
1080 
1081 
1082 int DcmQueryRetrieveConfig::HostNamesForVendor(const char *Vendor, const char ***HostNameArray) const
1083 {
1084    int  i, j,
1085     found = 0;
1086 
1087    for(i = 0; i < CNF_VendorTable.noOfHostEntries; i++) {
1088       if (!strcmp(CNF_VendorTable.HostEntries[i].SymbolicName, Vendor)) {
1089          found = 1;
1090          break;
1091       }
1092    }
1093 
1094    if (!found)
1095       return(0);
1096 
1097    if ((*HostNameArray = (const char**)malloc(CNF_VendorTable.HostEntries[i].noOfPeers * sizeof(const char *))) == NULL) {
1098       panic("Memory allocation A (%d)", CNF_VendorTable.HostEntries[i].noOfPeers);
1099       return(0);
1100    }
1101    for(j = 0; j < CNF_VendorTable.HostEntries[i].noOfPeers; j++)
1102       (*HostNameArray)[j] = CNF_VendorTable.HostEntries[i].Peers[j].HostName;
1103 
1104    return(CNF_VendorTable.HostEntries[i].noOfPeers);
1105 }
1106 
1107 OFBool DcmQueryRetrieveConfig::writableStorageArea(const char *aeTitle) const
1108 {
1109     const char *axs = getAccess((char*)aeTitle);
1110     if (strcmp(axs, "RW") == 0) return OFTrue;
1111     if (strcmp(axs, "WR") == 0) return OFTrue;
1112     if (strcmp(axs, "W") == 0) return OFTrue;
1113     return OFFalse;
1114 }
1115 
1116 const char *DcmQueryRetrieveConfig::getUserName() const
1117 {
1118    return UserName_.c_str();
1119 }
1120 
1121 const char *DcmQueryRetrieveConfig::getGroupName() const
1122 {
1123    return GroupName_.c_str();
1124 }
1125 
1126 const DcmQueryRetrieveCharacterSetOptions& DcmQueryRetrieveConfig::getCharacterSetOptions() const
1127 {
1128    return characterSetOptions_;
1129 }
1130 
1131 DcmQueryRetrieveCharacterSetOptions& DcmQueryRetrieveConfig::getCharacterSetOptions()
1132 {
1133    return characterSetOptions_;
1134 }
1135