source: branches/port5/NAMES/names.cxx

Last change on this file was 6084, checked in by westram, 15 years ago
  • name servers with add.field have to be started with '-fFIELD=DEFAULT' instead of '-fFIELD' only.
  • convert existing nameserver-DBs on the fly
  • increased namesDB version to 5
  • added a version check (too late - as always)
  • nameserver sends fatal errors to running ARB (using arb_message)
  • fixed nameserver definitions in arb_tcp_org.dat (arb_tcp.dat needs to be updated manually by users)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.1 KB
Line 
1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include <unistd.h>
5#include <ctype.h>
6
7#include <names_server.h>
8#include <names_client.h>
9#include "names.h"
10#include <arbdb.h>
11#include <names_prototypes.h>
12#include <server.h>
13#include <client.h>
14#include <servercntrl.h>
15#include <struct_man.h>
16
17#include <list>
18#include <string>
19
20using namespace std;
21
22// --------------------------------------------------------------------------------
23
24// overloaded functions to avoid problems with type-punning:
25inline long aisc_find_lib(dll_public *dll, char *key) { return aisc_find_lib(reinterpret_cast<dllpublic_ext*>(dll), key); }
26
27inline void aisc_link(dll_public *dll, AN_shorts *shorts)   { aisc_link(reinterpret_cast<dllpublic_ext*>(dll), reinterpret_cast<dllheader_ext*>(shorts)); }
28inline void aisc_link(dll_public *dll, AN_revers *revers)   { aisc_link(reinterpret_cast<dllpublic_ext*>(dll), reinterpret_cast<dllheader_ext*>(revers)); }
29
30// --------------------------------------------------------------------------------
31
32#if defined(DEBUG)
33// #define DUMP_NAME_CREATION
34#endif // DEBUG
35
36#define UPPERCASE(c) do{ (c) = toupper(c); }while(0)
37
38// --------------------------------------------------------------------------------
39
40struct AN_gl_struct  AN_global;
41AN_main             *aisc_main; /* muss so heissen */
42
43const int SERVER_VERSION = 5;
44
45// --------------------------------------------------------------------------------
46
47inline char *an_strlwr(char *str) {
48    for (int i = 0; str[i]; i++) {
49        str[i] = tolower(str[i]);
50    }
51    return str;
52}
53
54inline int an_strnicmp(const char *s1, const char *s2, int len) {
55    int cmp = 0;
56
57    for (int i = 0; i<len; i++) {
58        cmp = tolower(s1[i])-tolower(s2[i]);
59        if (cmp || !s1[i]) break;      // different or string-end reached in both strings
60    }
61
62    return cmp;
63}
64
65inline int an_stricmp(const char *s1, const char *s2) {
66    int cmp = 0;
67
68    for (int i = 0; ; i++) {
69        cmp = tolower(s1[i])-tolower(s2[i]);
70        if (cmp || !s1[i]) break;      // different or string-end reached in both strings
71    }
72
73    return cmp;
74}
75
76
77static AN_revers *lookup_an_revers(AN_main *main, const char *shortname) {
78    char      *key        = an_strlwr(strdup(shortname));
79    AN_revers *an_reverse = (AN_revers*)aisc_find_lib(&main->prevers, key);
80   
81    free(key);
82
83    return an_reverse;
84}
85
86static AN_shorts *lookup_an_shorts(AN_main *main, const char *identifier) {
87    // 'identifier' is either '*acc*add_id' or 'name1*name2*S' (see get_short() for details)
88    // 'add_id' is the value of an additional DB field and may be empty.
89
90    char      *key       = an_strlwr(strdup(identifier));
91    AN_shorts *an_shorts = (AN_shorts*)aisc_find_lib(&main->pnames, key);
92
93    free(key);
94
95    return an_shorts;
96}
97
98// ----------------------------------------
99// prefix hash
100
101static size_t an_shorts_elems(AN_shorts *sin) {
102    size_t count = 0;
103    while (sin) {
104        sin = sin->next;
105        count++;
106    }
107    return count;
108}
109
110#define PREFIXLEN 3
111
112static GB_HASH *an_get_prefix_hash() {
113    if (!aisc_main->prefix_hash) {
114        AN_shorts *sin       = aisc_main->shorts1;
115        size_t     elems     = an_shorts_elems(sin);
116        if (elems<100) elems = 100;
117       
118        GB_HASH *hash = GBS_create_hash(2*elems, GB_IGNORE_CASE);
119
120        while (sin) {
121            GBS_write_hash_no_strdup(hash, GB_strndup(sin->shrt, PREFIXLEN), (long)sin);
122            sin = sin->next;
123        }
124
125        aisc_main->prefix_hash = (long)hash;
126    }
127    return (GB_HASH*)aisc_main->prefix_hash;
128}
129
130static const char *an_make_prefix(const char *str) {
131    static char buf[] = "xxx";
132
133    buf[0] = str[0];
134    buf[1] = str[1];
135    buf[2] = str[2];
136   
137    return buf;
138}
139
140static AN_shorts *an_find_shrt_prefix(const char *search) {
141    return (AN_shorts*)GBS_read_hash(an_get_prefix_hash(), an_make_prefix(search));
142}
143
144// ----------------------------------------
145
146static void an_add_short(AN_local *locs, const char *new_name,
147                         const char *parsed_name, const char *parsed_sym,
148                         const char *shrt, const char *acc, const char *add_id)
149{
150    AN_shorts *an_shorts;
151    AN_revers *an_revers;
152    char      *full_name;
153    locs = locs;
154
155    if (strlen(parsed_sym)){
156        full_name = (char *)calloc(sizeof(char), strlen(parsed_name) + strlen(" sym")+1);
157        sprintf(full_name,"%s sym",parsed_name);
158    }
159    else {
160        full_name = strdup(parsed_name);
161    }
162
163    an_shorts = create_AN_shorts();
164    an_revers = create_AN_revers();
165
166    an_shorts->mh.ident  = an_strlwr(strdup(new_name));
167    an_shorts->shrt      = strdup(shrt);
168    an_shorts->full_name = strdup(full_name);
169    an_shorts->acc       = strdup(acc);
170    an_shorts->add_id    = strdup(add_id);
171
172    aisc_link(&aisc_main->pnames, an_shorts);
173
174    an_revers->mh.ident  = an_strlwr(strdup(shrt));
175    an_revers->full_name = full_name;
176    an_revers->acc       = strdup(acc);
177    an_revers->add_id    = strdup(add_id);
178
179    aisc_link(&aisc_main->prevers, an_revers);
180
181    GB_HASH *phash = an_get_prefix_hash();
182    GBS_write_hash(phash, an_make_prefix(an_shorts->shrt), (long)an_shorts); // add an_shorts to hash
183    GBS_optimize_hash(phash);
184
185    aisc_main->touched = 1;
186}
187
188static void an_remove_short(AN_shorts *an_shorts) {
189    /* this should only be used to remove illegal entries from name-server.
190       normally removing names does make problems - so use it very rarely */
191
192    GBS_write_hash(an_get_prefix_hash(), an_make_prefix(an_shorts->shrt), 0); // delete an_shorts from hash
193   
194    AN_revers *an_revers = lookup_an_revers(aisc_main, an_shorts->shrt);
195
196    if (an_revers) {
197        aisc_unlink((struct_dllheader_ext*)an_revers);
198
199        free(an_revers->mh.ident);
200        free(an_revers->full_name);
201        free(an_revers->acc);
202        free(an_revers);
203    }
204
205    aisc_unlink((struct_dllheader_ext*)an_shorts);
206
207    free(an_shorts->mh.ident);
208    free(an_shorts->shrt);
209    free(an_shorts->full_name);
210    free(an_shorts->acc);
211    free(an_shorts);
212}
213
214static char *nas_string_2_key(const char *str)
215// converts any string to a valid key
216{
217#if defined(DUMP_NAME_CREATION)
218    const char *org_str = str;
219#endif // DUMP_NAME_CREATION
220   
221    char buf[GB_KEY_LEN_MAX+1];
222    int  i;
223    int  c;
224    for (i=0;i<GB_KEY_LEN_MAX;) {
225        c                        = *(str++);
226        if (!c) break;
227        if (isalpha(c)) buf[i++] = c;
228        // name should not contain _'s (not compatible with FastDNAml)
229        //else if (c==' ' || c=='_') buf[i++] = '_';
230    }
231    for (;i<GB_KEY_LEN_MIN;i++) buf[i] = '0';
232    buf[i] = 0;
233#if defined(DUMP_NAME_CREATION)
234    printf("nas_string_2_key('%s') = '%s'\n", org_str, buf);
235#endif // DUMP_NAME_CREATION
236    return strdup(buf);
237}
238
239static char *nas_remove_small_vocals(const char *str) {
240#if defined(DUMP_NAME_CREATION)
241    const char *org_str = str;
242#endif // DUMP_NAME_CREATION
243    char buf[GB_KEY_LEN_MAX+1];
244    int i;
245    int c;
246
247    for (i=0; i<GB_KEY_LEN_MAX; ) {
248        c = *str++;
249        if (!c) break;
250        if (strchr("aeiouy", c)==0) {
251            buf[i++] = c;
252        }
253    }
254    for (; i<GB_KEY_LEN_MIN; i++) buf[i] = '0';
255    buf[i] = 0;
256#if defined(DUMP_NAME_CREATION)
257    printf("nas_remove_small_vocals('%s') = '%s'\n", org_str, buf);
258#endif // DUMP_NAME_CREATION
259    return strdup(buf);
260}
261
262static void an_complete_shrt(char *shrt, const char *rest_of_full) {
263    int len = strlen(shrt);
264
265    while (len<5) {
266        char c = *rest_of_full++;
267
268        if (!c) break;
269        shrt[len++] = c;
270    }
271
272    while (len<GB_KEY_LEN_MIN) {
273        shrt[len++] = '0';
274    }
275
276    shrt[len] = 0;
277}
278
279static void an_autocaps(char *str) {
280    // automatically capitalizes a string if it is completely up- or downcase
281
282    bool is_single_case = true;
283    {
284        bool seen_upper_case = false;
285        bool seen_lower_case = false;
286        for (int i = 0; str[i] && is_single_case; i++) {
287            char c = str[i];
288            if (isalpha(c)) {
289                if (islower(c)) seen_lower_case = true;
290                else seen_upper_case            = true;
291
292                if (seen_lower_case == seen_upper_case) { // both cases occurred
293                    is_single_case = false;
294                }
295            }
296        }
297    }
298
299    if (is_single_case) {
300        bool next_is_capital = true;
301
302        for (int i = 0; str[i]; i++) {
303            char c = str[i];
304            if (isalnum(c)) {
305                if (next_is_capital) {
306                    str[i]          = toupper(c);
307                    next_is_capital = false;
308                }
309                else {
310                    str[i] = tolower(c);
311                }
312            }
313            else {
314                next_is_capital = true;
315            }
316        }
317    }
318}
319
320static char *an_get_short(AN_shorts *IF_DEBUG(shorts), dll_public *parent, const char *full){
321    AN_shorts *look;
322
323    gb_assert(full);
324    gb_assert(shorts == aisc_main->shorts1); // otherwise prefix_hash does not work!
325
326    if (full[0]==0) {
327        return strdup("Xxx");
328    }
329
330    char *result = 0;
331    char *full1  = strdup(full);
332    an_autocaps(full1);
333
334    char *full2 = nas_string_2_key(full1);
335
336    look = (AN_shorts *)aisc_find_lib((struct_dllpublic_ext*)parent, full2);
337    if (look) {                 /* name is already known */
338        free(full2);
339        free(full1);
340        return strdup(look->shrt);
341    }
342
343    char *full3 = 0;
344    char  shrt[10];
345    int   len2, len3;
346    int   p1, p2, p3;
347
348    // try first three letters:
349
350    strncpy(shrt,full2,3);
351    UPPERCASE(shrt[0]);
352    shrt[3] = 0;
353
354    look = an_find_shrt_prefix(shrt);
355    if (!look) {
356        len2   = strlen(full2);
357        an_complete_shrt(shrt, len2>=3 ? full2+3 : "");
358        goto found_short;
359    }
360
361    // generate names from first char + consonants:
362
363    full3 = nas_remove_small_vocals(full2);
364    len3 = strlen(full3);
365
366    for (p1=1; p1<(len3-1); p1++) {
367        shrt[1] = full3[p1];
368        for (p2=p1+1; p2<len3; p2++) {
369            shrt[2] = full3[p2];
370            look = an_find_shrt_prefix(shrt);
371            if (!look) {
372                an_complete_shrt(shrt, full3+p2+1);
373                goto found_short;
374            }
375        }
376    }
377
378    // generate names from first char + all characters:
379
380    len2 = strlen(full2);
381    for (p1=1; p1<(len2-1); p1++) {
382        shrt[1] = full2[p1];
383        for (p2=p1+1; p2<len2; p2++) {
384            shrt[2] = full2[p2];
385            look = an_find_shrt_prefix(shrt);
386            if (!look) {
387                an_complete_shrt(shrt, full2+p2+1);
388                goto found_short;
389            }
390        }
391    }
392
393    // generate names containing first char + characters from name + one digit:
394
395    for (p1=1; p1<len2; p1++) {
396        shrt[1] = full2[p1];
397        for (p2=0; p2<=9; p2++) {
398            shrt[2] = '0'+p2;
399            look = an_find_shrt_prefix(shrt);
400            if (!look) {
401                an_complete_shrt(shrt, full2+p1+1);
402                goto found_short;
403            }
404        }
405    }
406
407    // generate names containing first char + two digits:
408
409    for (p1=1; p1<=99; p1++) {
410        shrt[1] = '0'+(p1/10);
411        shrt[2] = '0'+(p1%10);
412        look = an_find_shrt_prefix(shrt);
413        if (!look) {
414            an_complete_shrt(shrt, full2+1);
415            goto found_short;
416        }
417    }
418
419    // generate names containing 1 random character + 2 digits:
420
421    for (p1='A'; p1<='Z'; p1++) {
422        shrt[0] = p1;
423        for (p2=0; p2<=99; p2++) {
424            shrt[1] = '0'+(p2/10);
425            shrt[2] = '0'+(p2%10);
426            look = an_find_shrt_prefix(shrt);
427            if (!look) {
428                an_complete_shrt(shrt, full2);
429                goto found_short;
430            }
431        }
432    }
433
434    // generate names containing 2 random characters + 1 digit:
435
436    for (p1='A'; p1<='Z'; p1++) {
437        shrt[0] = p1;
438        for (p2='a'; p2<='z'; p2++) {
439            shrt[1] = p2;
440            for (p3=0; p3<=9; p3++) {
441                shrt[2] = '0'+p3;
442                look = an_find_shrt_prefix(shrt);
443                if (!look) {
444                    an_complete_shrt(shrt, full2);
445                    goto found_short;
446                }
447            }
448        }
449    }
450
451    // generate names containing 3 random characters:
452
453    for (p1='A'; p1<='Z'; p1++) {
454        shrt[0] = p1;
455        for (p2='a'; p2<='z'; p2++) {
456            shrt[1] = p2;
457            for (p3='a'; p3<='z'; p3++) {
458                shrt[2] = p3;
459                look = an_find_shrt_prefix(shrt);
460                if (!look) {
461                    an_complete_shrt(shrt, full2);
462                found_short:
463                    result = shrt;
464                    goto done;
465                }
466            }
467        }
468    }
469
470 done: 
471   
472    if (result) {
473#if defined(DUMP_NAME_CREATION)
474        if (isdigit(result[0]) || isdigit(result[1])) {
475            printf("generated new short-name '%s' for full-name '%s' full2='%s' full3='%s'\n", shrt, full, full2, full3);
476        }
477#endif    // DUMP_NAME_CREATION
478
479        look           = create_AN_shorts();
480        look->mh.ident = strdup(full2);
481        look->shrt     = strdup(result);
482        aisc_link((struct_dllpublic_ext*)parent,(struct_dllheader_ext*)look);
483       
484        aisc_main->touched = 1;
485    }
486    else {
487        printf("ARB_name_server: Failed to make a short-name for '%s'\n", full);
488    }
489
490    free(full3);
491    free(full2);
492    free(full1);
493
494    return strdup(result);
495}
496
497// --------------------------------------------------------------------------------
498
499static const char *default_full_name = "No name";
500
501class NameInformation {
502    const char *full_name;
503
504    char *parsed_name;
505    char *parsed_sym;
506    char *parsed_acc;
507    char *parsed_add_id;
508
509    char *first_name;
510    char *rest_of_name;
511
512    char *id;
513
514public:
515    NameInformation(AN_local *locs);
516    ~NameInformation();
517
518    const char *get_id() const { return id; }
519    const char *get_full_name() const { return full_name; }
520    const char *get_first_name() const { return first_name; }
521    const char *get_rest_of_name() const { return rest_of_name; }
522
523    void add_short(AN_local *locs, const char *shrt) const {
524        if (strlen(parsed_name)>3) {
525            an_add_short(locs, id, parsed_name, parsed_sym, shrt, parsed_acc, parsed_add_id);
526        }
527    }
528};
529
530static bool stralnum(const char *str) {
531    bool nonalnum = false;
532    for (char c = *str++; c; c = *str++) {
533        if (!isalnum(c)) {
534            nonalnum = true;
535            break;
536        }
537    }
538    return !nonalnum;
539}
540
541static char *make_alnum(const char *str) {
542    // returns a heap-copy containing all alphanumeric characters of 'str'
543
544    char *newStr = (char*)malloc(strlen(str)+1);
545    int   n      = 0;
546
547    for (int p = 0; str[p]; ++p) {
548        if (isalnum(str[p])) newStr[n++] = str[p];
549    }
550    newStr[n] = 0;
551    return newStr;
552}
553static char *make_alpha(const char *str) {
554    // returns a heap-copy containing all alpha characters of 'str'
555
556    char *newStr = (char*)malloc(strlen(str)+1);
557    int   n      = 0;
558
559    for (int p = 0; str[p]; ++p) {
560        if (isalpha(str[p])) newStr[n++] = str[p];
561    }
562    newStr[n] = 0;
563    return newStr;
564}
565
566#if defined(DEBUG)
567#define assert_alnum(s) gb_assert(stralnum(s))
568#else
569#define assert_alnum(s)
570#endif // DEBUG
571
572NameInformation::NameInformation(AN_local *locs) {
573    full_name = locs->full_name;
574    if (!full_name || !full_name[0]) full_name = default_full_name;
575
576    parsed_name = GBS_string_eval(full_name,
577                                  "\t= :\"= :'= :" // replace TABs and quotes by space
578                                  "sp.=species:spec.=species:SP.=SPECIES:SPEC.=SPECIES:" // replace common abbreviations of 'species'
579                                  ".= :" // replace dots by spaces
580                                  "  = :" // multiple spaces -> 1 space
581                                  "* * *=*1 *2" // skip all beyond 2nd word
582                                  ,0);
583    an_autocaps(parsed_name);
584
585    parsed_sym = GBS_string_eval(full_name, "\t= :* * *sym*=S",0);
586    if (strlen(parsed_sym)>1) freedup(parsed_sym, "");
587
588    const char *add_id = locs->add_id[0] ? locs->add_id : aisc_main->add_field_default;
589
590    parsed_acc    = make_alnum(locs->acc);
591    parsed_add_id = make_alnum(add_id);
592    first_name    = GBS_string_eval(parsed_name,"* *=*1",0);
593    rest_of_name  = make_alnum(parsed_name+strlen(first_name));
594
595    freeset(first_name, make_alnum(first_name));
596
597    assert_alnum(parsed_acc);
598    assert_alnum(first_name);
599    assert_alnum(rest_of_name);
600
601    UPPERCASE(rest_of_name[0]);
602
603    // build id
604
605    id = (strlen(parsed_acc)+strlen(parsed_add_id))
606        ? GBS_global_string_copy("*%s*%s", parsed_acc, parsed_add_id)
607        : GBS_global_string_copy("%s*%s*%s", first_name, rest_of_name, parsed_sym);
608}
609
610NameInformation::~NameInformation() {
611    free(id);
612   
613    free(rest_of_name);
614    free(first_name);
615
616    free(parsed_add_id);
617    free(parsed_acc);
618    free(parsed_sym);
619    free(parsed_name);
620}
621
622// --------------------------------------------------------------------------------
623// AISC functions
624
625extern "C" int del_short(AN_local *locs)
626/* forget about a short name */
627{
628    NameInformation  info(locs);
629    int              removed   = 0;
630    AN_shorts       *an_shorts = lookup_an_shorts(aisc_main, info.get_id());
631
632    if (an_shorts) {
633        an_remove_short(an_shorts);
634        removed = 1;
635    }
636
637    return removed;
638}
639
640static GB_HASH *nameModHash = 0; // key = default name; value = max. counter tested
641
642extern "C" aisc_string get_short(AN_local *locs)
643/* get the short name from the previously set names */
644{
645    static char *shrt = 0;
646
647    freeset(shrt, 0);
648
649    NameInformation  info(locs);
650    AN_shorts       *an_shorts = lookup_an_shorts(aisc_main, info.get_id());
651
652    if (an_shorts) {            // we already have a short name
653        bool recreate = false;
654
655        if (!stralnum(an_shorts->shrt)) { // contains non-alphanumeric characters
656            recreate = true;
657        }
658        else if (strcmp(an_shorts->full_name, default_full_name) == 0 && // fullname in name server is default_full_name
659                 strcmp(info.get_full_name(), an_shorts->full_name) != 0) // and differs from current
660        {
661            recreate = true;
662        }
663        if (recreate) {
664            an_remove_short(an_shorts);
665            an_shorts = 0;
666        }
667        else {
668            shrt = strdup(an_shorts->shrt);
669        }
670    }
671    if (!shrt) { /* now there is no short name (or an illegal one) */
672        char *first_advice=0,*second_advice=0;
673
674        if (locs->advice[0] && !stralnum(locs->advice)) { // bad advice
675            locs->advice[0] = 0; // delete it
676        }
677
678        if (locs->advice[0]) {
679            char *advice = make_alpha(locs->advice);
680
681            first_advice = strdup(advice);
682            if (strlen(advice) > 3) {
683                second_advice = strdup(advice+3);
684                first_advice[3] = 0;
685            }
686        }
687
688        if (!first_advice) first_advice = strdup("Xxx");
689        if (!second_advice) second_advice = strdup("Yyyyy");
690
691        char *first_short;
692        int   first_len;
693        {
694            const char *first_name = info.get_first_name();
695            first_short      = first_name[0]
696                ? an_get_short(aisc_main->shorts1, &(aisc_main->pshorts1), first_name)
697                : strdup(first_advice);
698
699            gb_assert(first_short);
700            if (first_short[0] == 0) { // empty?
701                freedup(first_short, "Xxx");
702            }
703            first_len = strlen(first_short);
704        }
705
706        char *second_short = (char*)calloc(10, 1);
707        int   second_len;
708        {
709            const char *rest_of_name = info.get_rest_of_name();
710            int         restlen      = strlen(rest_of_name);
711
712            if (!restlen) {
713                rest_of_name = second_advice;
714                restlen      = strlen(rest_of_name);
715                if (!restlen) {
716                    rest_of_name = "Yyyyy";
717                    restlen      = 5;
718                }
719            }
720
721            second_short[0] = 0;
722
723            if (restlen<5 && first_len>3) {
724                strcpy(second_short, first_short+3); // take additional characters from first_short
725                second_short[5-restlen] = 0; // but not too many
726            }
727
728            char *strend = strchr(second_short, 0);
729            strncpy(strend, rest_of_name, 8);
730            second_len   = strlen(second_short);
731        }
732
733        if (first_len>3) {
734            first_short[3] = 0;
735            first_len      = 3;;
736        }
737
738        int both_len = first_len+second_len;
739        if (both_len<8) {
740            freeset(second_short, GBS_global_string_copy("%s00000000", second_short));
741            second_len += 8;
742            both_len   += 8;
743        }
744
745        if (both_len>8) {
746            second_len               = 8-first_len;
747            second_short[second_len] = 0;
748            both_len                 = 8;
749        }
750
751        char test_short[9];
752        sprintf(test_short,"%s%s", first_short, second_short);
753       
754        gb_assert(size_t(both_len) == strlen(test_short));
755        gb_assert(second_len>=5 && second_len <= 8);
756
757        if (lookup_an_revers(aisc_main, test_short)) {
758            if (!nameModHash) nameModHash = GBS_create_hash(100, GB_IGNORE_CASE);
759
760            char *test_short_dup = strdup(test_short);
761            long  count          = GBS_read_hash(nameModHash, test_short);
762            if (count<2) count   = 2; // first name modification uses number 2
763
764            int  printOffset = both_len;
765            bool foundUnused = false;
766
767            // first create alternate name with digits only
768            {
769                int digLimit[6] = { 0, 9, 99, 999, 9999, 99999 };
770                for (int digits = 1; !foundUnused && digits <= 5; ++digits) {
771                    int maxOffset = 8-digits;
772                    int limit     = digLimit[digits];
773
774                    if (printOffset>maxOffset) printOffset = maxOffset;
775               
776                    char *printAt = test_short+printOffset;
777
778                    for (; !foundUnused && count <= limit; ++count) {
779                        IF_DEBUG(int printed =) sprintf(printAt, "%li", count);
780                        gb_assert((printed+printOffset) <= 8);
781                        if (!lookup_an_revers(aisc_main, test_short)) foundUnused = true; // name does not exist
782                    }
783                }
784            }
785
786            // if no unused name found, create one with alpha-chars
787            if (!foundUnused) {
788                strcpy(test_short, test_short_dup);
789
790                long        count2  = count-100000; // 100000 numbers were used above
791                char       *printAt = test_short+3;
792                const char *base36  = "0123456789abcdefghijklmnopqrstuvwxyz";
793
794                printAt[5] = 0;
795
796                for (; !foundUnused && count2<16796160; ++count2) { // 16796160 = 36^4*10
797                    // now print count2 converted to base 36
798
799                    int c = count2;
800                    for (int pos = 0; pos<5; ++pos) {
801                        int nextc = c/36;
802                        int rest  = c-36*nextc;
803
804                        printAt[4-pos] = base36[rest];
805                        c              = nextc;
806
807                        gb_assert(pos != 4 || c == 0);
808                    }
809
810                    if (!lookup_an_revers(aisc_main, test_short)) foundUnused = true; // name does not exist
811                }
812
813                count = count2+100000;
814            }
815
816            gb_assert(foundUnused);
817            GBS_write_hash(nameModHash, test_short_dup, count);
818            GBS_optimize_hash(nameModHash);
819
820            free(test_short_dup);
821        }
822
823        assert_alnum(test_short);
824
825        shrt = strdup(test_short);
826        info.add_short(locs, shrt);
827
828        free(first_short);
829        free(second_short);
830        free(first_advice);
831        free(second_advice);
832    }
833
834    assert_alnum(shrt);
835    return shrt;
836}
837
838extern "C" int server_save(AN_main *main, int dummy)
839{
840    FILE        *file;
841    int error;
842    char        *sec_name;
843    dummy = dummy;
844    if (main->touched) {
845        int server_date = GB_time_of_file(main->server_file);
846        if (server_date>main->server_filedate) {
847            printf("Another nameserver changed '%s' - your changes are lost.\n", main->server_file);
848        }
849        else {
850            sec_name = (char *)calloc(sizeof(char), strlen(main->server_file)+2);
851            sprintf(sec_name,"%s%%", main->server_file);
852            printf("Saving '%s'..\n", main->server_file);
853            file     = fopen(sec_name,"w");
854            if (!file) {
855                fprintf(stderr,"ERROR cannot save file '%s'\n",sec_name);
856            }
857            else { 
858                error = save_AN_main(main,file);
859                fclose(file);
860                if (!error) {
861                    if (GB_rename_file(sec_name, main->server_file)) {
862                        GB_print_error();
863                    }
864                    else {
865                        main->touched = 0;
866                    }
867                }
868            }
869            free(sec_name);
870            main->server_filedate = GB_time_of_file(main->server_file);
871        }       
872    }
873    else {
874        printf("No changes to ARB_name_server data.\n");
875    }
876
877    return 0;
878}
879
880#if defined(DEBUG) && 0
881static void check_list(AN_shorts *start) {
882    int count = 0;
883    while (++count) {
884        start = start->next;
885        if (!start) {
886            fprintf(stderr, "<list has %i elements>\n", count);
887            return;
888        }
889    }
890
891    fprintf(stderr, "<ERROR - list is looped>\n");
892    gb_assert(0);
893}
894#endif // DEBUG
895
896static void check_for_case_error(AN_main *main) {
897    // test for duplicated names or name parts (only differing in case)
898    // such names were created by old name server versions
899
900    bool case_error_occurred = false;
901    int  idents_changed      = 0;
902    // first check name parts
903    for (AN_shorts *shrt = main->shorts1; shrt; ) {
904        AN_shorts *next  = shrt->next;
905        AN_shorts *found = an_find_shrt_prefix(shrt->shrt);
906        if (found != shrt) {
907            fprintf(stderr, "- Correcting error in name-database: '%s' equals '%s'\n",
908                    found->shrt, shrt->shrt);
909            an_remove_short(shrt);
910            case_error_occurred = true;
911        }
912        shrt = next;
913    }
914
915    // then check full short-names
916    for (AN_shorts *shrt = main->names; shrt; ) {
917        AN_shorts *next  = shrt->next;
918        AN_revers *found = lookup_an_revers(main, shrt->shrt);
919
920        if (found && (shrt->acc && found->acc && an_stricmp(shrt->acc, found->acc) != 0)) {
921            fprintf(stderr, "- Correcting error in name-database: '%s' equals '%s' (but acc differs)\n",
922                    found->mh.ident, shrt->shrt);
923
924            an_remove_short(shrt);
925            case_error_occurred = true;
926        }
927        else if (found && (shrt->add_id && found->add_id && an_stricmp(shrt->add_id, found->add_id) != 0)) {
928            fprintf(stderr, "- Correcting error in name-database: '%s' equals '%s' (but add_id differs)\n",
929                    found->mh.ident, shrt->shrt);
930
931            an_remove_short(shrt);
932            case_error_occurred = true;
933        }
934        else {
935            AN_shorts *self_find = lookup_an_shorts(main, shrt->mh.ident);
936            if (!self_find) { // stored with wrong key (not lowercase)
937                aisc_unlink((struct_dllheader_ext*)shrt);
938                an_strlwr(shrt->mh.ident);
939                aisc_link(&main->pnames, shrt);
940                main->touched = 1;
941
942                case_error_occurred = true;
943                idents_changed++;
944            }
945            else if (self_find != shrt) {
946                fprintf(stderr, "- Correcting error in name-database: '%s' equals '%s' (case-difference in full_name or acc)\n",
947                        shrt->mh.ident, self_find->mh.ident);
948                an_remove_short(shrt);
949                case_error_occurred = true;
950            }
951        }
952
953        shrt = next;
954    }
955
956    if (case_error_occurred) {
957        int regen_name_parts = 0;
958        int deleted_names    = 0;
959
960        // now capitalize all name parts
961        {
962            list<string> idents_to_recreate;
963
964            for (AN_shorts *shrt =  main->shorts1; shrt; ) {
965                char      *cap_name = strdup(shrt->shrt);
966                an_autocaps(cap_name);
967
968                if (strcmp(cap_name, shrt->shrt) != 0) {
969                    idents_to_recreate.push_back(shrt->mh.ident);
970                }
971                free(cap_name);
972               
973                AN_shorts *next = shrt->next;
974                an_remove_short(shrt);
975                shrt = next;
976            }
977
978            list<string>::const_iterator end = idents_to_recreate.end();
979            for (list<string>::const_iterator i = idents_to_recreate.begin(); i != end; ++i) {
980                const char *ident = i->c_str();
981                an_get_short(main->shorts1, &(main->pshorts1), ident);
982                regen_name_parts++;
983            }
984        }
985
986        // now capitalize all short names
987        for (AN_shorts *shrt =  main->names; shrt; ) {
988            AN_shorts *next     = shrt->next;
989            char      *cap_name = strdup(shrt->shrt);
990            an_autocaps(cap_name);
991
992            if (strcmp(cap_name, shrt->shrt) != 0) {
993                // fprintf(stderr, "Deleting entry '%s'\n", shrt->shrt);
994                an_remove_short(shrt);
995                deleted_names++;
996            }
997            shrt = next;
998            free(cap_name);
999        }
1000
1001        if (idents_changed)   fprintf(stderr, "* Changed case of %i identifiers.\n", idents_changed);
1002        if (regen_name_parts) fprintf(stderr, "* Regenerated %i prefix names.\n", regen_name_parts);
1003        if (deleted_names)    fprintf(stderr, "* Removed %i names with wrong case.\n"
1004                                      "=> This leads to name changes when generating new names (which is recommended now).\n", deleted_names);
1005    }
1006}
1007
1008static void check_for_illegal_chars(AN_main *main) {
1009    // test for names containing illegal characters
1010    int illegal_names = 0;
1011
1012    // first check name parts
1013    for (AN_shorts *shrt = main->shorts1; shrt; ) {
1014        AN_shorts *next = shrt->next;
1015        if (!stralnum(shrt->shrt)) {
1016            fprintf(stderr, "- Fixing illegal chars in '%s'\n", shrt->shrt);
1017            an_remove_short(shrt);
1018            illegal_names++;
1019        }
1020        shrt = next;
1021    }
1022    // then check full short-names
1023    for (AN_shorts *shrt = main->names; shrt; ) {
1024        AN_shorts *next  = shrt->next;
1025        if (!stralnum(shrt->shrt)) {
1026            fprintf(stderr, "- Fixing illegal chars in '%s'\n", shrt->shrt);
1027            an_remove_short(shrt);
1028            illegal_names++;
1029        }
1030        shrt = next;
1031    }
1032   
1033    if (illegal_names>0) {
1034        fprintf(stderr, "* Removed %i names containing illegal characters.\n"
1035                "=> This leads to name changes when generating new names (which is recommended now).\n", illegal_names);
1036    }
1037}
1038
1039static void set_empty_addids(AN_main *main) {
1040    // fill all empty add.ids with default value
1041
1042    if (main->add_field_default[0]) {
1043        // if we use a non-empty default, old entries need to be changed
1044        // (empty default was old behavior)
1045
1046        long count = 0;
1047        for (AN_shorts *shrt = main->names; shrt; ) {
1048            AN_shorts *next  = shrt->next;
1049            if (!shrt->add_id[0]) {
1050                aisc_unlink((struct_dllheader_ext*)shrt);
1051
1052                freedup(shrt->add_id, main->add_field_default);
1053                gb_assert(strchr(shrt->mh.ident, 0)[-1] == '*');
1054                freeset(shrt->mh.ident, GBS_global_string_copy("%s%s", shrt->mh.ident, main->add_field_default));
1055           
1056                aisc_link(&main->pnames, shrt);
1057
1058                count++;
1059            }
1060            shrt = next;
1061        }
1062        if (count>0) {
1063            printf("  Filled in default value '%s' for %li names\n", main->add_field_default, count);
1064            main->touched = 1;
1065        }
1066    }
1067}
1068
1069static GB_ERROR server_load(AN_main *main)
1070{
1071    FILE      *file;
1072    GB_ERROR   error = 0;
1073    AN_shorts *shrt;
1074    AN_revers *revers;
1075
1076    fprintf(stderr, "Starting ARB_name_server..\n");
1077
1078    file = fopen(main->server_file,"r");
1079    if (!file) {
1080        error = GBS_global_string("No such file '%s'", main->server_file);
1081    }
1082    else {
1083        fprintf(stderr, "* Loading %s\n", main->server_file);
1084        int err_code = load_AN_main(main,file);
1085        if (err_code) {
1086            error = GBS_global_string("Error #%i while loading '%s'", err_code, main->server_file);
1087        }
1088    }
1089
1090    if (!error) {
1091        fprintf(stderr, "* Parsing data\n");
1092        long nameCount =  0;
1093        for (shrt = main->names; shrt; shrt = shrt->next) {
1094            revers            = create_AN_revers();
1095            revers->mh.ident  = an_strlwr(strdup(shrt->shrt));
1096            revers->full_name = strdup(shrt->full_name);
1097            revers->acc       = strdup(shrt->acc);
1098            revers->add_id    = shrt->add_id ? strdup(shrt->add_id) : 0;
1099            aisc_link(&main->prevers, revers);
1100            nameCount++;
1101        }
1102
1103        int namesDBversion = main->dbversion; // version used to save names.dat
1104        fprintf(stderr, "* Loaded NAMEDB v%i (contains %li names)\n", namesDBversion, nameCount);
1105
1106        if (namesDBversion < SERVER_VERSION) {
1107            if (namesDBversion<4) {
1108                fprintf(stderr, "* Checking for case-errors\n");
1109                check_for_case_error(main);
1110
1111                fprintf(stderr, "* Checking for illegal characters\n");
1112                check_for_illegal_chars(main);
1113            }
1114
1115            fprintf(stderr, "* NAMEDB version upgrade %i -> %i\n", namesDBversion, SERVER_VERSION);
1116            main->dbversion = SERVER_VERSION;
1117            main->touched   = 1; // force save
1118        }
1119        if (namesDBversion > SERVER_VERSION) {
1120            error = GBS_global_string("NAMEDB is from version %i, but your nameserver can only handle version %i",
1121                                      namesDBversion, SERVER_VERSION);
1122        }
1123        else {
1124            fprintf(stderr, "ARB_name_server is up.\n");
1125            main->server_filedate = GB_time_of_file(main->server_file);
1126        }
1127    }
1128    else {
1129        main->server_filedate = -1;
1130    }
1131    return error;
1132}
1133
1134void names_server_shutdown(int exitcode) __ATTR__NORETURN;
1135void names_server_shutdown(int exitcode) {
1136    aisc_server_shutdown_and_exit(AN_global.server_communication, exitcode); // never returns
1137}
1138
1139int names_server_save(void) {
1140    server_save(aisc_main, 0);
1141    return 0;
1142}
1143
1144extern "C" int server_shutdown(AN_main *pm, aisc_string passwd){
1145    /** passwdcheck **/
1146    if( strcmp(passwd, "ldfiojkherjkh") ) return 1;
1147    printf("\narb_name_server: I got the shutdown message.\n");
1148
1149    /** shoot clients **/
1150    aisc_broadcast(AN_global.server_communication, 0,
1151                   "SERVER SHUTDOWN BY ADMINISTRATOR!\n");
1152
1153    /** shutdown **/
1154    printf("ARB_name_server: server shutdown by administrator\n");
1155    names_server_shutdown(0);   // never returns!
1156    pm = pm;
1157    return 0;
1158}
1159
1160static void usage(const char *exeName, const char *err) __ATTR__NORETURN;
1161static void usage(const char *exeName, const char *err) {
1162    printf("ARB nameserver v%i\n"
1163           "Usage: %s command server-parameters\n"
1164           "command = -boot\n"
1165           "          -kill\n"
1166           "          -look\n"
1167           , SERVER_VERSION, exeName);
1168    arb_print_server_params();
1169    if (err) printf("Error: %s\n", err);
1170    exit(-1);
1171}
1172
1173int main(int argc,char **argv)
1174{
1175    char              *name;
1176    int                i;
1177    struct Hs_struct  *so;
1178    struct arb_params *params;
1179
1180    params                 = arb_trace_argv(&argc,argv);
1181    const char *executable = argv[0];
1182
1183    if (!params->default_file) usage(executable, "Missing default file");
1184
1185    if (argc==1) { // default command is '-look'
1186        char flag[]="-look";
1187        argv[1] = flag;
1188        argc = 2;
1189    }
1190
1191    if (argc!=2) usage(executable, "Too many parameters");
1192
1193    aisc_main = create_AN_main();
1194    /***** try to open com with any other pb server ******/
1195    if (params->tcp) {
1196        name = params->tcp;
1197    }
1198    else {
1199        const char *cname = GBS_read_arb_tcp("ARB_NAME_SERVER");
1200
1201        if (!cname) {
1202            GB_print_error();
1203            exit(-1);
1204        }
1205        name = strdup(cname);
1206    }
1207
1208    AN_global.cl_link = (aisc_com *)aisc_open(name, (long *)&AN_global.cl_main,AISC_MAGIC_NUMBER);
1209
1210    if (AN_global.cl_link) {
1211        if( !strcmp(argv[1],"-look")) {
1212            printf("ARB_name_server: No client - terminating.\n");
1213            aisc_close(AN_global.cl_link);AN_global.cl_link = 0;
1214            exit(0);
1215        }
1216
1217        printf("There is another active nameserver. I try to kill it..\n");
1218        aisc_nput(AN_global.cl_link, AN_MAIN, AN_global.cl_main,
1219                  MAIN_SHUTDOWN, "ldfiojkherjkh",
1220                  NULL);
1221        aisc_close(AN_global.cl_link);AN_global.cl_link=0;
1222        sleep(1);
1223    }
1224    if( ((strcmp(argv[1],"-kill")==0)) ||
1225        ((argc==3) && (strcmp(argv[2],"-kill")==0))){
1226        printf("ARB_name_server: Now I kill myself!\n");
1227        exit(0);
1228    }
1229    for (i=0, so=0; (i<MAX_TRY) && (!so); i++){
1230        so = open_aisc_server(name, NAME_SERVER_SLEEP*1000L ,0);
1231        if (!so) sleep(RETRY_SLEEP);
1232    }
1233    if (!so) {
1234        printf("AN_SERVER: Gave up on opening the communication socket!\n");
1235        exit(0);
1236    }
1237    AN_global.server_communication = so;
1238
1239    aisc_main->server_file     = strdup(params->default_file);
1240    aisc_main->server_filedate = GB_time_of_file(aisc_main->server_file);
1241
1242    GB_ERROR error = server_load(aisc_main);
1243
1244    if (!error) {
1245        const char *field         = params->field;
1246        const char *field_default = params->field_default;
1247
1248        if (field == 0) {
1249            field         = "";                     // default to no field
1250            field_default = "";
1251        }
1252        else {
1253            if (!params->field_default) {
1254                error = GBS_global_string("Missing default value for add.field (option has to be -f%s=defaultValue)", field);
1255            }
1256        }
1257
1258        if (!error) {
1259            if (aisc_main->add_field == 0) {        // add. field was not defined yet
1260                if (field[0]) {
1261                    printf("* add. field not defined yet -> using '%s'\n", field);
1262                }
1263                else {
1264                    fputs("* using no add. field\n", stdout);
1265                }
1266                aisc_main->add_field = strdup(field);
1267                aisc_main->touched   = 1;
1268            }
1269            else {
1270                if (strcmp(aisc_main->add_field, field) != 0) { // add. field changed
1271                    if (aisc_main->add_field[0]) {
1272                        error = GBS_global_string("This names-DB has to be started with -f%s", aisc_main->add_field);
1273                    }
1274                    else {
1275                        error = "This names-DB has to be started w/o -f option";
1276                    }
1277                }
1278            }
1279        }
1280
1281        if (!error) {
1282            char *field_default_alnum = make_alnum(field_default);
1283
1284            if (aisc_main->add_field_default == 0) { // previously no default was defined for add.field
1285                reassign(aisc_main->add_field_default, field_default_alnum);
1286                set_empty_addids(aisc_main);
1287                aisc_main->touched           = 1;
1288            }
1289            else {
1290                if (strcmp(aisc_main->add_field_default, field_default_alnum) != 0) {
1291                    error = GBS_global_string("Default for add.field previously was '%s' (called with '%s')\n"
1292                                              "If you really need to change this - delete the names DB",
1293                                              aisc_main->add_field_default, field_default_alnum);
1294                }
1295            }
1296            free(field_default_alnum);
1297        }
1298    }
1299
1300    long accept_calls_init = NAME_SERVER_TIMEOUT/long(NAME_SERVER_SLEEP);
1301    long accept_calls      = accept_calls_init;
1302    bool isTimeout         = true;
1303
1304    if (!error && aisc_main->touched) server_save(aisc_main, 0);
1305
1306    while (!error && accept_calls>0) {
1307        aisc_accept_calls(so);
1308
1309        if (aisc_main->ploc_st.cnt <=0) { // timeout passed and no clients
1310            accept_calls--;
1311
1312            long server_date = GB_time_of_file(aisc_main->server_file);
1313            if (server_date == 0 && aisc_main->server_filedate != 0) {
1314                fprintf(stderr, "ARB_name_server data has been deleted.\n");
1315                accept_calls = 0;
1316                isTimeout    = false;
1317            }
1318            if (server_date>aisc_main->server_filedate) {
1319                fprintf(stderr, "ARB_name_server data changed on disc.\n");
1320                accept_calls = 0;
1321                isTimeout    = false;
1322            }
1323            else if (aisc_main->touched) {
1324                server_save(aisc_main,0);
1325                accept_calls = accept_calls_init;
1326            }
1327        }
1328        else { // got a client
1329            accept_calls = accept_calls_init;
1330        }
1331    }
1332
1333    if (error) {
1334        char *message = GBS_global_string_copy("Error in ARB_name_server: %s", error);
1335        char *send    = GBS_global_string_copy("arb_message \"%s\" &", message); // send async (otherwise deadlock!)
1336
1337        fprintf(stderr, "%s\n", message);
1338        if (system(send) != 0) fprintf(stderr, "Failed to send error message to ARB\n");
1339
1340        free(send);
1341        free(message);
1342    }
1343    else if (accept_calls == 0) {
1344        if (isTimeout) {
1345            fprintf(stderr, "Been idle for %i minutes.\n", int(NAME_SERVER_TIMEOUT/60));
1346        }
1347    }
1348
1349    printf("ARB_name_server terminating...\n");
1350    if (nameModHash) GBS_free_hash(nameModHash);
1351    names_server_shutdown(error ? EXIT_FAILURE : EXIT_SUCCESS); // never returns
1352}
Note: See TracBrowser for help on using the repository browser.