## bin/dbupdate/dbupdate.cc

/* [<][>][^][v][top][bottom][index][help] */

## FUNCTIONS

This source file includes following functions.

1. error_init
2. delete_key
3. import_key
4. get_keyowner_fingerpr
5. process_object
6. process_file
7. generate_upd_file
8. create_lock_file
9. remove_lock_file
10. write_checkpoint
11. remove_checkpoint
12. main

       1 /***************************************
       2   $Revision: 1.75 $
       3
       4   DBupdate
       5
       6   Status: NOT REVIEWED, TESTED
       7
       8   Author(s):       Engin Gunduz
       9
      10   ******************/ /******************
      11   Modification History:
      12         engin (01/03/2000) Created.
      13   ******************/ /******************
      14   Copyright (c) 2000                              RIPE NCC
      15
      16   All Rights Reserved
      17
      18   Permission to use, copy, modify, and distribute this software and its
      19   documentation for any purpose and without fee is hereby granted,
      20   provided that the above copyright notice appear in all copies and that
      21   both that copyright notice and this permission notice appear in
      22   supporting documentation, and that the name of the author not be
      23   used in advertising or publicity pertaining to distribution of the
      24   software without specific, written prior permission.
      25
      26   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
      27   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
      28   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
      29   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
      30   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      31   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      32  ***************************************/
      33
      34
      35
      36
      37 #include <config.h>
      38 #include "dbupdate.h"
      39 #include "UP_extrnl_syntax.h"
      40 #include "UP_subject.h"
      41 #include "er_yacc_helper.h"
      42 #include "erroutines.h"
      43 #include "ca_configFns.h"
      44 #include "ca_dictionary.h"
      45 #include "ca_macros.h"
      46 #include "ca_srcAttribs.h"
      47 #include "notification.h"
      48 #include "gpg.h"
      49 #include "mail_parser.h"
      50 #include "process.h"
      51
      52 #ifdef HAVE_SYS_PARAM_H
      53 #include <sys/param.h>
      54 #endif
      55
      56
      57 int tracing = 0;
      58 int test_mode = 0;
      59 int supress_ack_notif = 0;
      60 int print_out_ack = 0;
      61
      62 /* do we process a mail */
      63 int reading_from_mail = 0;
      64
      65 /* are we processing networkupdate? */
      66 int networkupdate = 0;
      67
      68 /* IP of the networkupdate client */
      69 char * netupdclientIP = NULL;
      70
      71
      72 /* sender of the mail, in case of a mail update */
      73 char *update_mail_sender = NULL;
      74 char *update_mail_subject = NULL;
      75 char *update_mail_date = NULL;
      76 char *update_mail_ID = NULL;
      77 char *update_mail_cc = NULL;
      78
      79 /* required configuration variables */
      80 char *tmpdir = NULL;
      81 char *lockdir = NULL;
      82 char *mailcmd = NULL;
      83 char *notitxt = NULL;
      84 char *notimailtxt = NULL;
      85 char *notinetworktxt = NULL;
      86 char *fwtxt   = NULL;
      87 char *fwmailtxt = NULL;
      88 char *mtfwheader = NULL;
      89 char *mtfwtxt = NULL;
      90 char *mailtxt = NULL;
      91 char *acksig = NULL;
      92 char *defmail = NULL;
      93 char *updlog = NULL;
      94 char *notiflog = NULL;
      95 char *crosslog = NULL;
      96 char *acklog = NULL;
      97 char *forwlog = NULL;
      98 char *humailbox = NULL;
      99 char *autobox = NULL;
     100 char *copyright_notice = NULL;
     101 char *overridecryptedpw = NULL;
     102 char *country = NULL;
     103 char *countries[400];
     104 char *sources[100];
     105 char *pgppath = NULL;
     106 char *gpgcmd = NULL;
     107 char *pgp_public_key_ring = NULL;
     108 char *autodbmhelp = NULL;
     109 char *update_host = NULL;
     110 int  update_port;
     111 char *query_host = NULL;
     112 int  query_port;
     113 char *cn_subject_add = NULL;
     114 char *cn_subject_del = NULL;
     115 char *cn_explain_add = NULL;
     116 char *cn_explain_del = NULL;
     117 char *cn_overlap_add = NULL;
     118 char *cn_overlap_del = NULL;
     119 char *cno_subject_add = NULL;
     120 char *cno_subject_del = NULL;
     121 char *cno_explain_add = NULL;
     122 char *cno_explain_del = NULL;
     123 char *cno_overlap_add = NULL;
     124 char *cno_overlap_del = NULL;
     125 char *mheader = NULL;
     126 char *DBhost = NULL;
     127 int  DBport;
     128 char *DBuser = NULL;
     129 char *DBname = NULL;
     130 char *DBpasswd = NULL;
     131 /* end of config variables */
     132
     133 char * fingerprint = NULL;
     134 char * keyowner = NULL;
     135
     136 /* result of subject line processing is kept in the following struct. */
     137 up_subject_struct subject_result;
     138
     139
     140 /* hostname and pid are used all over the program, so we save them into these variables */
     141 char hostname[MAXHOSTNAMELEN];
     142 //char * hostname;
     143 int pid;
     144
     145 /* name of the lock file, which is used for the crash recovery mechanism */
     146 char * lock_file_name;
     147
     148 void error_init(int argc, char ** argv) {
         /* [<][>][^][v][top][bottom][index][help] */
     149
     150   ER_init("dbupdate", 1);
     151
     152
     153 } /* error_init() */
     154
     155
     156
     157
     158
     159
     160 /* 'lockfile' struct is for keeping both the name of the lock file and the file descriptor
     161     of it, which is open during the execution of dbupdate. We need the filedes to close it,
     162     when dbupdate finishes, and the name to delete the file. */
     163 typedef struct {
     164    char * lockname;
     165    int filedes;
     166 } lockfilestruct;
     167
     168
     169 lockfilestruct lockfile;
     170
     171
     172
     173 /* Deletes the key defined in the incoming object (a key-cert object)
     174    from the public keyring. Returns NULL if there was no error,
     175    returns an error message if there is an error */
     176 char * delete_key(char * obj){
         /* [<][>][^][v][top][bottom][index][help] */
     177
     178   struct ImportKeyObject iKO;
     179   char * obj_keyID;
     180   char * key_cert_attr;
     181   GSList * templist, * certiflist, * next;
     182   u32 keyID;
     183   char * tempfile;
     184   char ** lines;
     185   int i;
     186   FILE * key_file;
     187   char * temp, * temp2;
     188   char * error_string;
     189
     190   templist = get_attr_list(obj, "key-cert");
     191   key_cert_attr = strdup((char *)templist->data);
     192   g_slist_free(templist);
     193
     194   tempfile = (char *)malloc(strlen(tmpdir) + strlen("tmp-key.") + 32);
     195   sprintf(tempfile, "%s/tmp-key.%i", tmpdir, pid /*getpid()*/);
     196
     197   /* now we must write certif attribute(s) of this key-certif into the tempfile */
     198   /* get the certif first */
     199   certiflist = get_attr_list(obj, "certif");
     200   if(( key_file = fopen(tempfile, "w")) == NULL){
     201      //fprintf(stderr, "Can't open temporary file, %s", tempfile);
     202      ER_perror(FAC_UP, UP_CANTOPEN, "Can't open temporary file, %s", tempfile);
     203      exit(1);
     204   }
     205   for( next = certiflist; next != NULL ; next = g_slist_next(next) ){
     206     lines = g_strsplit((char *)next->data, "\n", 0);
     207     if(lines[0] == NULL){/* if this was an empty attribute, just print an empty line */
     208       fprintf(key_file, "\n");
     209     }
     210     for(i = 0; lines[i] != NULL; i++){
     211       temp = strdup(lines[i]);
     212       if(i != 0 && temp[0] == '+'){/* if it begins with a plus */
     213         temp2 = strdup(temp + 1);
     214         g_strstrip(temp2);
     215         fprintf(key_file, "%s\n", temp2);
     216         free(temp);free(temp2);
     217       }else{
     218         g_strstrip(temp);
     219         fprintf(key_file, "%s\n", temp);
     220         free(temp);
     221       }
     222     }
     223     g_strfreev(lines);
     224     //fprintf(key_file, "%s\n", (char *)next->data);
     225   }
     226   fclose(key_file);
     227   g_slist_free(certiflist);
     228
     229   strcpy(iKO.iFilename, tempfile);
     230
     231   if(tracing){
     232     printf("TRACING: delete_key: key_cert_attr: [%s]\n", key_cert_attr);
     233   }
     234
     235   obj_keyID = strdup(key_cert_attr + strlen("PGPKEY-"));
     236
     237   if(tracing){
     238     printf("TRACING: delete_key: obj_keyID: [%s]\n", obj_keyID);
     239   }
     240
     241   keyID = strtoul(obj_keyID, NULL, 16);
     242
     243   if(tracing){
     244     printf("TRACING: delete_key: keyID is: %u, %X\n", keyID, keyID);
     245   }
     246
     247
     248
     249   strcpy(iKO.keyRing, pgp_public_key_ring);
     250   PA_RemoveKey(&iKO);
     251
     252   if(tracing){
     253     printf("TRACING: importKeyObj status:\n");
     254     printf("TRACING: isValid: %d\n", iKO.rc);
     255   }
     256
     257
     258
     259   unlink(tempfile);
     260   if(iKO.rc == iKO_OK){/* if PA_RemoveKey returned OK */
     261     return NULL;
     262   }else{/* if PA_RemoveKey returned not OK */
     263       switch(iKO.rc){
     264         case iKO_UNCHANGED:      error_string = strdup("the key is already in the keyring");break;
     265         case iKO_NOUSERID:       error_string = strdup("no user ID could be extracted");break;
     266         case iKO_GENERAL:        error_string = strdup("general PGP error");break;
     267         case iKO_NOTVALIDUSERID: error_string = strdup("no valid user ID ");break;
     268         case iKO_NOPUBLICKEY:    error_string = strdup("no public key in the object");break;
     269         case iKO_NODEFAULTPUBLICKEYRING: error_string = strdup("general PGP error");break;
     270         case iKO_CRC_ERROR:      error_string = strdup("CRC error in the certificate");break;
     271         case iKO_NO_OPENPGP_DATA:error_string = strdup("no OpenPGP data in the object");break;
     272         case iKO_NO_IN_FILES:    error_string = strdup("general PGP error");break;
     273         case iKO_GENERALFAILURE: error_string = strdup("general PGP error");break;
     274         default:            error_string = strdup("general PGP error");
     275       }
     276       return error_string;
     277   }
     278
     279   return NULL;
     280 }
     281
     282
     283 /* Takes a key-certif object, extracts its 'certif' attribute and adds
     284    the key into public keyring
     285    If there is no problem, it returns NULL
     286    If there is a problem, then it returns a string which contains an error
     287    message */
     288 char * import_key(char *obj){
         /* [<][>][^][v][top][bottom][index][help] */
     289
     290   char * tempfile;
     291   struct ImportKeyObject iKO;
     292   GSList * certiflist, * next, * templist;
     293   FILE * key_file;
     294   char keyID[9];
     295   char * obj_keyID, * key_cert_attr;
     296   char * error_string = NULL;
     297   char ** lines;
     298   int i;
     299   char * temp, * temp2;
     300
     301   tempfile = (char *)malloc(strlen(tmpdir) + strlen("tmp-key.") + 32);
     302   sprintf(tempfile, "%s/tmp-key.%i", tmpdir, pid );
     303
     304   /* now we must write certif attribute(s) of this key-certif into the tempfile */
     305   /* get the certif first */
     306   certiflist = get_attr_list(obj, "certif");
     307   if(( key_file = fopen(tempfile, "w")) == NULL){
     308      ER_perror(FAC_UP, UP_CANTOPEN, "Can't open temporary file, %s", tempfile);
     309      exit(1);
     310   }
     311   for( next = certiflist; next != NULL ; next = g_slist_next(next) ){
     312     lines = g_strsplit((char *)next->data, "\n", 0);
     313     if(lines[0] == NULL){/* if this was an empty attribute, just print an empty line */
     314       fprintf(key_file, "\n");
     315     }
     316     for(i = 0; lines[i] != NULL; i++){
     317       temp = strdup(lines[i]);
     318       if(i != 0 && temp[0] == '+'){/* if it begins with a plus */
     319         temp2 = strdup(temp + 1);
     320         g_strstrip(temp2);
     321         fprintf(key_file, "%s\n", temp2);
     322         free(temp);free(temp2);
     323       }else{
     324         g_strstrip(temp);
     325         fprintf(key_file, "%s\n", temp);
     326         free(temp);
     327       }
     328     }
     329     g_strfreev(lines);
     330   }
     331   fclose(key_file);
     332   g_slist_free(certiflist);
     333
     334   strcpy(iKO.iFilename, tempfile);
     335   strcpy(iKO.keyRing, pgp_public_key_ring);
     336   PA_ImportKey(&iKO);
     337
     338   if(tracing){
     339     printf("importKeyObj status:\n");
     340
     341     printf("isValid: %d\n", iKO.rc);
     342     printf("keyID: %08lX\n", iKO.keyID);
     343   }
     344   snprintf(keyID, 9, "%08lX", iKO.keyID);
     345
     346   if(tracing){
     347     printf("keyID: [%s]\n", keyID);
     348   }
     349
     350   unlink(tempfile);
     351   free(tempfile);
     352
     353
     354   templist = get_attr_list(obj, "key-cert");
     355   key_cert_attr = strdup((char *)templist->data);
     356   g_slist_free(templist);
     357
     358   if(tracing){
     359     printf("key_cert_attr: [%s]\n", key_cert_attr);
     360   }
     361   obj_keyID = strdup(key_cert_attr + strlen("PGPKEY-"));
     362   if(tracing){
     363     printf("obj_keyID: [%s]\n", obj_keyID);
     364   }
     365   if(iKO.rc == iKO_OK && (strcasecmp(obj_keyID, keyID) == 0)){/* if PA_ImportKey returned OK
     366                                                             and the real keyID is equal to the
     367                                                             keyID in the 'key-cert' attribute */
     368     fingerprint = strdup(iKO.fingerPrint);
     369     keyowner    = strdup(iKO.keyOwner);
     370     return NULL;
     371   }else{/* if PA_ImportKey returned not OK or obj_keyID, keyID didn't match */
     372     if(iKO.rc != iKO_OK){
     373       switch(iKO.rc){
     374         case iKO_UNCHANGED:      error_string = strdup("the key is already in the keyring");break;
     375         case iKO_NOUSERID:       error_string = strdup("no user ID could be extracted");break;
     376         case iKO_GENERAL:        error_string = strdup("general PGP error");break;
     377         case iKO_NOTVALIDUSERID: error_string = strdup("no valid user ID ");break;
     378         case iKO_NOPUBLICKEY:    error_string = strdup("no public key in the object");break;
     379         case iKO_NODEFAULTPUBLICKEYRING: error_string = strdup("general PGP error");break;
     380         case iKO_CRC_ERROR:      error_string = strdup("CRC error in the certificate");break;
     381         case iKO_NO_OPENPGP_DATA:error_string = strdup("no OpenPGP data in the object");break;
     382         case iKO_NO_IN_FILES:    error_string = strdup("general PGP error");break;
     383         case iKO_GENERALFAILURE: error_string = strdup("general PGP error");break;
     384         default:            error_string = strdup("general PGP error");
     385       }
     386       return error_string;
     387     }else{
     388       error_string = (char *)malloc(1024);/* this should be enough */
     389       sprintf(error_string, "Keyid for this certificate (%s) is not the same as the PGPKEY field (%s)",
     390                  keyID, obj_keyID);
     391       return error_string;
     392     }
     393   }
     394
     395 }
     396
     397
     398
     399
     400
     401 /* Gets the keyowner and fingerprint of a PGP key
     402    from the keycert object. The keycert object must already
     403    be in the database (thus, in the keyring)
     404    The fingerprint and keyowner will be saved in global variables */
     405
     406 void * get_keyowner_fingerpr(char *obj){
         /* [<][>][^][v][top][bottom][index][help] */
     407
     408   GSList * templist = NULL;
     409   char * obj_keyID = NULL;
     410   struct ImportKeyObject iKO;
     411   u32 keyID;
     412   char * key_cert_attr;
     413
     414
     415   templist = get_attr_list(obj, "key-cert");
     416   key_cert_attr = strdup((char *)templist->data);
     417   g_slist_free(templist);
     418
     419   if(tracing){
     420     printf("get_keyowner_fingerpr: key_cert_attr is [%s]\n", key_cert_attr);
     421   }
     422   obj_keyID = strdup(key_cert_attr + strlen("PGPKEY-"));
     423   if(tracing){
     424     printf("get_keyowner_fingerpr: obj_keyID is [%s]\n", obj_keyID);
     425   }
     426
     427   sscanf(obj_keyID, "%8X", &keyID);
     428   if(tracing){
     429     printf("get_keyowner_fingerpr: keyID is [%s]\n", keyID);
     430   }
     431
     432   /* set appropriate fields of iKO */
     433   iKO.keyID = keyID;
     434   strcpy(iKO.keyRing, pgp_public_key_ring);
     435
     436   GetFingerPrint(&iKO);
     437   fingerprint = strdup(iKO.fingerPrint);
     438
     439   GetKeyOwner(&iKO);
     440   keyowner = strdup(iKO.keyOwner);
     441
     442
     443   if(tracing){
     444     if(fingerprint != NULL){
     445       printf("get_keyowner_fingerpr: fingerprint is [%s]\n", fingerprint);
     446     }
     447     if(keyowner){
     448       printf("get_keyowner_fingerpr: keyowner is [%s]\n", keyowner);
     449     }
     450   }
     451 }
     452
     453
     454
     455
     456
     457
     458 /* Checks the object's syntax, retrives the old version of it from the db,
     459    and checks auth2. If everything is OK, then sends it to RIPdb, where referential
     460    integrity is checked, and the object is really committed to the db.
     461
     462      Arguments:
     463         char * arg: The object,
     464         credentials_struct credentials: The struct containing the credentials, such as
     465           'From:' field of the e-mail update,
     466         GHashTable * NIC_hdl_hash: A hash containing
     467         char * ack_file_name:  The file name, to be used to store ACK message
     468 */
     469
     470
     471
     472 int process_object(char * incoming, credentials_struct credentials,
         /* [<][>][^][v][top][bottom][index][help] */
     473                    GHashTable * NIC_hdl_hash, char * ack_file_name,
     474                    GHashTable * ntfy_hash, GHashTable * forw_hash, GHashTable * cross_hash){
     475
     476     bool code = true;
     477     Object *o;
     478     char * old_version = NULL;
     479     o = new Object;
     480     int result = 0;
     481     //int result_from_RIPupd = 0;
     482     up_ripupd_result_struct * result_from_RIPupd;
     483     char * result_from_import_key = NULL;
     484     char * result_from_delete_key = NULL;
     485     char * auto_nic = NULL;
     486     char * changed_obj = NULL;
     487     char * obj_with_AUTO_NIC_hdl;
     488     char * assigned_NIC;
     489     char * formatted_object;
     490     char * type;
     491     char * arg;
     492     char * arg2;
     493     char * generated_object;
     494     external_syntax_struct * external_syntax_results;
     495
     496
     497     char * value = NULL;/* these two are for */
     498     Attr * attr;        /* ack messages only */
     499
     500     arg = strdup(incoming);
     501
     502     if(has_ref_to_AUTO_nic_hdl(arg)){/* if this object has refs to AUTO NIC hdls*/
     503        /* then first replace AUTO NIC hdls with assigned NIC hdls (in NIC_hdl_hash) */
     504        if((changed_obj = replace_refs_to_AUTO_NIC_hdl(changed_obj, arg, NIC_hdl_hash)) == NULL){
     505          /* try to parse the object only for reporting purposes */
     506          o->scan_silent(arg,strlen(arg));
     507          AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] Unknown AUTO NIC handle referenced\n%s\n",
     508                  (o && o->type) ? o->type->getName() : "unknown-type", arg);
     509          delete(o);
     510          return UP_ANE; /* AUTO NIC hdl error */
     511
     512        }else{ /* in this case, we must use changed_obj instead of arg */
     513
     514          free(arg);
     515          arg = changed_obj;
     516
     517        };
     518     }
     519
     520
     521     code = o->scan_silent(arg,strlen(arg));
     522     if(code){
     523       type = get_class_type(o);
     524       /* is the object to be deleted? */
     525       if(o->isDeleted){
     526         old_version = get_old_version(arg);
     527         if(old_version == NULL){ /* the object doesn't exist in the db! */
     528           AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\nEntry not found\n\n%s\n",
     529                         o->type->getName(), get_search_key(o, o->type->getName(), arg), arg);
     530           return UP_NSO; /* no such object */
     531         }else {/* the object is in the db */
     532           if(identical(old_version, arg) /* if the old & new versions are identical */
     533              || (strcmp(o->type->getName(), "key-cert") == 0)  /* or it is a key-cert object  */
     534              || (strcmp(o->type->getName(), "inet6num") == 0)){/* or it is an inet6num object */
     535             result = check_auth(NULL, old_version, o->type->getName(), credentials);
     536             if(result == UP_AUTH_OK){
     537               if(tracing) {
     538                 printf("TRACING: Will send the obj to be deleted\n");
     539               }
     540               if(strcmp(type, "key-cert") == 0){
     541                 result_from_delete_key = delete_key(arg);
     542               }else{
     543                 result_from_delete_key = NULL;
     544               }
     545               /* if there was no problem with key deletion from the key-ring */
     546               if(result_from_delete_key == NULL){
     547                 result_from_RIPupd = send_object_db(arg, NULL, "DEL");
     548                 if(result_from_RIPupd->result == 0){
     549                   AK_add_to_ack(ack_file_name, "\nDelete OK: [%s] %s\n",
     550                                 o->type->getName(), get_search_key(o, o->type->getName(), arg));
     551                   NT_write_all_ntfs(old_version, NULL, NULL, tmpdir, ntfy_hash, forw_hash, cross_hash,
     552                                     credentials.from);
     553                 }else{
     554                   AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\n%s\n",
     555                                 o->type->getName(), get_search_key(o, o->type->getName(), arg),
     556                                 result_from_RIPupd->error_str);
     557                 }
     558                 result_from_RIPupd->result = 0;
     559               }else{
     560                  AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\n%s\n",
     561                                 o->type->getName(), get_search_key(o, o->type->getName(), arg), result_from_delete_key);
     562               }
     563             }else{ /* auth failed */
     564               if(tracing) {
     565                 printf("TRACING: Auth failed\n");
     566               }
     567
     568               AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\nAuthorisation failed, request forwarded to maintainer.\n%s\n",
     569                             o->type->getName(), get_search_key(o, o->type->getName(), arg), arg);
     570               NT_write_all_frwds(arg, NULL, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);
     571               return UP_AUF; /* Auth failed */
     572             }
     573           }else{/* the new & old versions do not match */
     574             AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\nnew & old versions do not match\n%s\n",
     575                  o->type->getName(), get_search_key(o, o->type->getName(), arg), arg);
     576             return UP_NOM; /* new & old versions do not match */
     577           }
     578         }
     579       }else {/* the object is _not_ to be deleted */
     580
     581         if(has_AUTO_NIC_hdl(arg)){/* if the object has an AUTO NIC hdl */
     582           external_syntax_results = UP_check_external_syntax(o, arg);
     583           if(     external_syntax_results->result != UP_EXTSYN_ERR
     584                && external_syntax_results->result != UP_EXTSYN_ERR_WARN){/* if there is no error */
     585             /* then its nic-hdl attribute must be modified so that RIPupdate
     586                would understand that it must assign a NIC handle to it */
     587             /* but first check the auth */
     588             result = check_auth(arg, NULL, o->type->getName(), credentials);
     589             if(result == UP_AUTH_OK){
     590               if(tracing) {
     591                   printf("TRACING: Will send the obj to be created with AUTO NIC hdl\n");
     592               }
     593               auto_nic = (char *)malloc(1024); /* should be enough for a NIC hdl */
     594               obj_with_AUTO_NIC_hdl = replace_AUTO_NIC_hdl(external_syntax_results->new_obj, auto_nic);
     595               if(tracing) {
     596                 printf("TRACING:  Called replace_AUTO_NIC_hdl, get [%s]\n", obj_with_AUTO_NIC_hdl);
     597                 printf("TRACING: Will send the obj to be added\n");
     598               }
     599               assigned_NIC = (char *)malloc(128); /* this should be enough for a NIC hdl */
     600               result_from_RIPupd = send_object_db(obj_with_AUTO_NIC_hdl, assigned_NIC, "ADD");
     601               if(result_from_RIPupd->result == 0){
     602                 AK_add_to_ack(ack_file_name, "\nNew OK: [%s] %s\n",
     603                               o->type->getName(), assigned_NIC);
     604                 /* replace the AUTO nic hdl with the assigned one (for reporting purposes, in the notif mesg) */
     605                 formatted_object = UP_put_assigned_NIC(external_syntax_results->new_obj, assigned_NIC);
     606                 formatted_object = delete_override(formatted_object);
     607                 arg2 = delete_override(arg);
     608                 NT_write_all_ntfs(NULL, arg2, formatted_object, tmpdir, ntfy_hash, forw_hash, cross_hash,
     609                                   credentials.from);
     610               }else{
     611                 AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s]\n%s\n%s\n",
     612                               o->type->getName(), arg,
     613                               result_from_RIPupd->error_str);
     614               }
     615               result_from_RIPupd->result = 0;
     616               if(tracing && assigned_NIC != NULL) {
     617                 printf("TRACING: send_object_db returned [%s] as assigned NIC hdl\n", assigned_NIC);
     618               }
     619               if(assigned_NIC != NULL){
     620                 if(tracing){
     621                   printf("TRACING: auto_nic=[%s], assigned_NIC=[%s]\n", auto_nic, assigned_NIC);
     622                 }
     623                 g_hash_table_insert(NIC_hdl_hash, auto_nic, assigned_NIC);
     624                 if(tracing){
     625                   printf("TRACING: NIC_hdl_hash has %i pairs\n",g_hash_table_size(NIC_hdl_hash));
     626                 }
     627               }
     628
     629             }else{
     630               /* auth failed ! */
     631               if(tracing) {
     632                 printf("TRACING: Auth failed\n");
     633               }
     634
     635               AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\nAuthorisation failed, request forwarded to maintainer.\n%s\n",
     636                             o->type->getName(), get_search_key(o, o->type->getName(), arg), arg);
     637               arg2 = delete_override(arg);
     638               NT_write_all_frwds(NULL, arg2, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);
     639               return UP_AUF; /* Auth failed */
     640             }
     641           }else{/* external syntax check failed */
     642             AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s\n%s%s\n",
     643                    o->type->getName(), get_search_key(o, o->type->getName(), arg),
     644                    arg, external_syntax_results->error_str);
     645           }
     646         }
     647         else{
     648           old_version = get_old_version(arg);
     649           if(old_version != NULL){/* so, this is an update operation */
     650             if( (!reading_from_mail) ||
     651                 (subject_result.result != UP_SUBJ_NEW_ENFORCED)){/* If the user didn't enforce
     652                                                                     creation in the subject line */
     653               external_syntax_results = UP_check_external_syntax(o, arg);
     654               if(    external_syntax_results->result != UP_EXTSYN_ERR
     655                   && external_syntax_results->result != UP_EXTSYN_ERR_WARN){/* if there is no error */
     656                 /* if the old version & the new one are not identical */
     657                 if(identical(old_version, arg) != 1){
     658                   result = check_auth(arg, old_version, o->type->getName(), credentials);
     659                   if(result == UP_AUTH_OK){
     660                     if(tracing) {
     661                       printf("TRACING: Will send the obj to be updated\n");
     662                     }
     663
     664                     if(strcasecmp(o->type->getName(), "key-cert") == 0){
     665                       get_keyowner_fingerpr(arg);
     666                       generated_object = UP_generate_kc_attrs(o, arg);
     667                       external_syntax_results->new_obj = generated_object;
     668                     }
     669
     670                     result_from_RIPupd = send_object_db(external_syntax_results->new_obj, NULL, "UPD");
     671                     if(result_from_RIPupd->result == 0){
     672                       AK_add_to_ack(ack_file_name, "\nUpdate OK: [%s] %s\n",
     673                                     o->type->getName(), get_search_key(o, o->type->getName(), arg));
     674                       arg2 = delete_override(arg);
     675                       NT_write_all_ntfs(old_version, arg2, NULL, tmpdir, ntfy_hash, forw_hash, cross_hash,
     676                                         credentials.from);
     677                     }else{
     678                       AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s\n%s\n",
     679
     680                                     o->type->getName(), get_search_key(o, o->type->getName(), arg),
     681                                     result_from_RIPupd->error_str);
     682                     }
     683                     result_from_RIPupd->result = 0;
     684                   }else{
     685                     /* auth failed ! */
     686                     if(tracing) {
     687                       printf("TRACING: Auth failed\n");
     688                     }
     689
     690                     AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s\nAuthorisation failed, request forwarded to maintainer.\n%s\n",
     691                                   o->type->getName(), get_search_key(o, o->type->getName(), arg), arg);
     692                     arg2 = delete_override(arg);
     693                     NT_write_all_frwds(old_version, arg2, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);
     694                     return UP_AUF; /* Auth failed */
     695                   }
     696
     697                 }else{/* if the old and new versions of the object are the same */
     698                   if(tracing) {
     699                       printf("TRACING: The obj sent is identical to the one in the DB (NOOP)\n");
     700                   }
     701                   AK_add_to_ack(ack_file_name, "\nUpdate NOOP: [%s] %s\n",
     702                                     o->type->getName(), get_search_key(o, o->type->getName(), arg));
     703
     704                 }
     705               }else{/* if there is an error in external syntax checks */
     706                 AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s\n%s%s\n",
     707                      o->type->getName(), get_search_key(o, o->type->getName(), arg),
     708                      arg, external_syntax_results->error_str);
     709               }
     710
     711             }else{/* if the user enforced creation (using NEW keyword) in the subject line */
     712
     713               AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s\n%s\n"
     714                    "***ERROR:  Object already exists\n",
     715                    o->type->getName(), get_search_key(o, o->type->getName(), arg),
     716                    arg);
     717
     718             }
     719           }else { /* old_version  == NULL, so, creation */
     720             external_syntax_results = UP_check_external_syntax(o, arg);
     721             if(    external_syntax_results->result != UP_EXTSYN_ERR
     722                 && external_syntax_results->result != UP_EXTSYN_ERR_WARN){/* if there is no error */
     723               result = check_auth(arg, NULL, o->type->getName(), credentials);
     724               if(result == UP_AUTH_OK){
     725                 if(tracing) {
     726                   printf("TRACING: Will send the obj to be added\n");
     727                 }
     728                  /* if the object is a key-cert object, then we must import the PGP key */
     729                 if(strcmp(type, "key-cert") == 0){
     730                   result_from_import_key = import_key(arg);
     731                 }else{
     732                   result_from_import_key = NULL;
     733                 }
     734                 if(result_from_import_key == NULL){/* no PGP problem */
     735                   if(strcmp(type, "key-cert") == 0){/* if the object is a key-cert object */
     736
     737                     generated_object = UP_generate_kc_attrs(o, arg);
     738                     result_from_RIPupd = send_object_db(generated_object, NULL, "ADD");
     739                   }else{
     740
     741                     result_from_RIPupd = send_object_db(external_syntax_results->new_obj, NULL, "ADD");
     742
     743                   }
     744                   if(result_from_RIPupd->result == 0){/* if there was no problem */
     745                     AK_add_to_ack(ack_file_name, "\nNew OK: [%s] %s\n",
     746                                   o->type->getName(), get_search_key(o, o->type->getName(), arg));
     747                     arg2 = delete_override(arg);
     748                     NT_write_all_ntfs(NULL, arg2, NULL, tmpdir, ntfy_hash, forw_hash, cross_hash,
     749                                       credentials.from);
     750
     751                   }else{
     752                     //AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\nReferential integrity failure\n",
     753                     AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\n%s\n",
     754                                   o->type->getName(), get_search_key(o, o->type->getName(), arg),
     755                                   result_from_RIPupd->error_str);
     756                   }
     757                   result_from_RIPupd = 0;
     758                 }else{/* there was a problem with PGP key import */
     759                   AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\n%s\n",
     760                                   o->type->getName(), get_search_key(o, o->type->getName(), arg),
     761                                   result_from_import_key);
     762                 }
     763               }else if(result == UP_FWD){ /* this was a maintainer or as-block creation request, so
     764                                              forward it to <HUMAILBOX> */
     765
     766                 if(tracing){
     767
     768                   printf("TRACING: Maintainer or as-block request will be forwarded to <HUMAILBOX>\n");
     769
     770                 }
     771
     772                 AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\n%s\n"
     773                               "*ERROR*:     %s objects cannot be created automatically\n"
     774                               "*ERROR*:     This object has been forwarded to %s\n"
     775                               "*ERROR*:     for authorisation.\n"
     776                               "*ERROR*:     No further action from your part is required\n",
     777                               o->type->getName(), get_search_key(o, o->type->getName(), arg),
     778                               external_syntax_results->new_obj, o->type->getName(), humailbox);
     779
     780                 /* and forward this creation request to <HUMAILBOX> */
     781                 NT_forw_create_req(external_syntax_results->new_obj);
     782
     783               }else if(result == UP_HOF){/* hierarchical authorisation failed */
     784                 if(tracing) {
     785                   printf("TRACING: Auth failed\n");
     786                 }
     787
     788                 AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\nHierarchical authorisation failed, request forwarded to maintainer.\n%s\n",
     789                               o->type->getName(), get_search_key(o, o->type->getName(), arg), arg);
     790                 arg2 = delete_override(arg);
     791                 NT_write_all_frwds(NULL, arg2, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);
     792                 return UP_AUF;
     793
     794
     795               }else{
     796                 /* auth failed ! */
     797                 if(tracing) {
     798                   printf("TRACING: Auth failed\n");
     799                 }
     800
     801                 AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\nAuthorisation failed, request forwarded to maintainer.\n%s\n",
     802                               o->type->getName(), get_search_key(o, o->type->getName(), arg), arg);
     803                 arg2 = delete_override(arg);
     804                 NT_write_all_frwds(NULL, arg2, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);
     805                 return UP_AUF; /* Auth failed */
     806               }
     807             }else{
     808               AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\n%s%s\n",
     809                               o->type->getName(), get_search_key(o, o->type->getName(), arg),
     810                               arg, external_syntax_results->error_str);
     811             }
     812           }
     813         }
     814
     815       }
     816     }else{/* even if obj doesn't parse properly, it may be a legacy object
     817             which the user wants to delete... */
     818        if(tracing){
     819          printf("TRACING: Object didn't parse\n");
     820        }
     821        /* if it is for deletion */
     822        if(o->isDeleted){
     823          /* here delete it */
     824          old_version = get_old_version(arg);
     825          if(old_version == NULL){ /* the object doesn't exist in the db! */
     826             AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\nEntry not found\n\n%s\n",
     827                           o->type->getName(), get_search_key(o, o->type->getName(), arg), arg);
     828             return UP_NSO; /* no such object */
     829           }else {/* the object is in the db */
     830             if(identical(old_version, arg)){/* if the old & new versions are identical */
     831               result = check_auth(NULL, old_version, o->type->getName(), credentials);
     832               if(result == UP_AUTH_OK){
     833                 if(tracing) {
     834                   printf("TRACING: Will send the obj to be deleted\n");
     835                 }
     836                 if(strcmp(type, "key-cert") == 0){
     837                   result_from_delete_key = delete_key(arg);
     838                 }else{
     839                   result_from_delete_key = NULL;
     840                 }
     841                 /* if there was no problem with key deletion from the key-ring */
     842                 if(result_from_delete_key == NULL){
     843                   result_from_RIPupd = send_object_db(arg, NULL, "DEL");
     844                   if(result_from_RIPupd->result == 0){
     845                     AK_add_to_ack(ack_file_name, "\nDelete OK: [%s] %s\n",
     846                                   o->type->getName(), get_search_key(o, o->type->getName(), arg));
     847                     NT_write_all_ntfs(arg, NULL, NULL, tmpdir, ntfy_hash, forw_hash, cross_hash,
     848                                       credentials.from);
     849                   }else{
     850                     AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\n%s\n",
     851                                   o->type->getName(), get_search_key(o, o->type->getName(), arg),
     852                                   result_from_RIPupd->error_str);
     853                   }
     854                   result_from_RIPupd = 0;
     855                 }else{
     856                    AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\n%s\n",
     857                                   o->type->getName(), get_search_key(o, o->type->getName(), arg), result_from_delete_key);
     858                 }
     859               }else{ /* auth failed */
     860                 if(tracing) {
     861                   printf("TRACING: Auth failed\n");
     862                 }
     863
     864                 AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\nAuthorisation failed, request forwarded to maintainer.\n%s\n",
     865                               o->type->getName(), get_search_key(o, o->type->getName(), arg), arg);
     866                 NT_write_all_frwds(arg, NULL, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);
     867                 return UP_AUF; /* Auth failed */
     868               }
     869             }else{/* the new & old versions do not match */
     870               AK_add_to_ack(ack_file_name, "\nDelete FAILED: new & old versions do not match\n%s\n", arg);
     871               return UP_NOM; /* new & old versions do not match */
     872             }
     873           }
     874
     875        }else{/* syntax error AND not deletion */
     876          AK_add_to_ack(ack_file_name, "\nUpdate FAILED: Syntax error in object\n");
     877
     878          if(o->attrs.head() != NULL){
     879            for(attr = o->attrs.head(); attr; attr = o->attrs.next(attr)){
     880              if(attr->len > 0){
     881                value = (char*)malloc(attr->len);
     882                strncpy(value, (char *)(arg+attr->offset) ,
     883                  attr->len - 1);
     884                value[attr->len - 1] = '\0';
     885                AK_add_to_ack(ack_file_name, "%s\n", value);
     886                if(!attr->errors.empty()){
     887                  AK_add_to_ack_string(ack_file_name, attr->errors);
     888                }
     889                free(value);
     890              }else{
     891                if(!attr->errors.empty()){
     892                  AK_add_to_ack_string(ack_file_name, attr->errors);
     893                }
     894              }
     895            }
     896          }
     897          if(o->has_error){
     898            AK_add_to_ack_string(ack_file_name, o->errors);
     899          }
     900          AK_add_to_ack(ack_file_name, "\n");
     901
     902          return UP_SYN; /* syntax error */
     903        }
     904     }
     905 }
     906
     907
     908
     909
     910
     911 /* processes the objects in the given file */
     912 void process_file(char * filename, credentials_struct credentials,
         /* [<][>][^][v][top][bottom][index][help] */
     913                   GHashTable * AUTO_NIC_hdl_hash, char * ack_file_name,
     914                   GHashTable * ntfy_hash, GHashTable * forw_hash, GHashTable * cross_hash){
     915
     916 FILE * input_file;
     917 GSList *list_of_objects = NULL, *list_of_objects2 = NULL;
     918 GSList *next = NULL;
     919 int object_count = 0;
     920 char *object = NULL;
     921 char * line;
     922 char * lwrcase_line;
     923 int result = 0;
     924 struct VerifySignObject vSO, *pvSO;
     925
     926
     927
     928     line = (char *)malloc(1024);
     929
     930     if((input_file = fopen(filename, "r")) == NULL){
     931         ER_perror(FAC_UP, UP_CANTOPEN, "Couldn't open the file %s: %s\n", filename, strerror(errno));
     932         exit(1);
     933     }
     934
     935
     936     while(fgets(line, 1023, input_file) != NULL){
     937       /* first, if it is a pasword, save it, but do not regard it as an attrib */
     938       lwrcase_line = strdup(line);
     939       g_strdown(lwrcase_line);
     940       if(strstr(lwrcase_line, "password:") == lwrcase_line){
     941         if(tracing){
     942           printf("TRACING: This is a password\n");
     943         }
     944         credentials.password_list = g_slist_append(credentials.password_list,
     945                                       g_strstrip(strdup(line + strlen("password:"))));
     946         continue;
     947       }
     948       free(lwrcase_line);
     949
     950       line = UP_remove_EOLs(line); /* remove '\n's and '\r' first */
     951       /* remove trailing white space */
     952       line = g_strchomp(line);
     953       if(strlen(line) == 0){/* then, this was an empty line */
     954         if(object != NULL){
     955            list_of_objects = g_slist_append(list_of_objects, object);
     956            if(tracing){
     957              printf("TRACING: added an object: [%s]\n", object);
     958            }
     959            object = NULL;
     960         }
     961       }else{
     962         if(object == NULL && strlen(line) != 0){
     963           object = (char *)malloc(strlen(line) + 2);
     964           object = strcpy(object, line);
     965           object = strcat(object, "\n"); /* add EOL again (we removed it before) */
     966         }
     967         else{
     968           object = (char *)realloc(object, strlen(object) + strlen(line) + 2);
     969           object = strcat(object, line);
     970           object = strcat(object, "\n");
     971         }
     972       }
     973
     974     }
     975     fclose(input_file);
     976
     977     /* now, if at the very and of the input file there wasn't an
     978        empty line, we have to add the remaining object in the 'object'
     979        variable */
     980     if(object != NULL){
     981        list_of_objects = g_slist_append(list_of_objects, object);
     982        object = NULL;
     983     }
     984
     985
     986
     987     if(tracing) {
     988        printf("TRACING: Will process the objects in the list\n");
     989     }
     990     next = list_of_objects;
     991     object_count = 0;
     992     for( next = list_of_objects; next != NULL ; next = g_slist_next(next) ){
     993       object_count++;
     994
     995       if(tracing) {
     996         cout << "TRACING: Got an object from the list" << endl;
     997         cout << (char *)next->data << endl;
     998       }
     999
    1000       if(has_ref_to_AUTO_nic_hdl((char *)next->data)){/* defer the processing */
    1001         if(tracing) {
    1002           printf("TRACING: this object has a ref to an AUTO NIC hdl\n");
    1003         }
    1004         list_of_objects2 = g_slist_append(list_of_objects2, strdup((char *)next->data));
    1005       }else{
    1006         result = 0;
    1007         result = process_object((char *)next->data, credentials, AUTO_NIC_hdl_hash, ack_file_name,
    1008                                  ntfy_hash, forw_hash, cross_hash);
    1009       }
    1010     }
    1011
    1012     if(tracing) {
    1013       printf("TRACING: list_of_objects2 has %d entries\n", g_slist_length(list_of_objects2));
    1014     }
    1015
    1016     if(tracing) {
    1017       printf("TRACING: will start to process the second list\n");
    1018     }
    1019
    1020     for( next = list_of_objects2; next != NULL ; next = g_slist_next(next) ){
    1021       if(tracing) {
    1022         printf("TRACING: Will process object: %s\n", (char *)next->data);
    1023       }
    1024       result = process_object((char *)next->data, credentials, AUTO_NIC_hdl_hash, ack_file_name,
    1025                                ntfy_hash, forw_hash, cross_hash);
    1026     }
    1027
    1028 }/* process_file */
    1029
    1030
    1031
    1032
    1033
    1034
    1035 /*  Generates a unique file name and returns the full path of the filename
    1036     for storing notification message.  */
    1037
    1038 char * generate_upd_file(){
         /* [<][>][^][v][top][bottom][index][help] */
    1039
    1040    char * name;
    1041
    1042    /* allocate space for name.  32 should be enough for PID */
    1043    name = (char*)malloc(strlen(tmpdir) + strlen("/dbupdate-tmp.") + 32 );
    1044
    1045    sprintf(name, "%s/dbupdate-tmp.%i", tmpdir, pid /*getpid()*/);
    1046
    1047
    1048    return name;
    1049
    1050 }
    1051
    1052
    1053 /* create_lock_file: creates a lock file in lockdir and locks it. This is a
    1054    part of crash recovery. Must be called in the beginning of the run. At the
    1055    end, the file must be removed. */
    1056 /* The idea: Create the "lock" file, and lock it. When another process starts
    1057    running, it checks the existing lock files. If some exists, then it checks
    1058    if it is locked or not. It not locked, then assumes that the corresponding
    1059    dbupdate is alredy running. If not locked, assumes that it has crashed.
    1060    (note: when a process crashes, the kernel releases all the files locked by
    1061    this process [by the OS])
    1062    Problem: locking doesn't work properly on some NFS implementations. */
    1063
    1064 lockfilestruct create_lock_file(){
         /* [<][>][^][v][top][bottom][index][help] */
    1065
    1066   lockfilestruct lock;
    1067   int file;
    1068   int length;
    1069
    1070   /* allocate space for file name */
    1071   length = strlen(lockdir) +  strlen(hostname) + 32;
    1072   lock.lockname = (char *)malloc(length + 1);
    1073
    1074   snprintf(lock.lockname, length, "%s/dbupdate.%s.%ld", lockdir, hostname, pid /*getpid()*/);
    1075
    1076   /* we will lock the file, so we have to use open(), but not fopen() (see man
    1077      page of lockf(3C)) */
    1078   if(( file = open(lock.lockname, O_RDWR|O_CREAT)) == -1){
    1079     ER_perror(FAC_UP, UP_CANTOPEN, "Can't open lock file, %s", lock.lockname);
    1080     exit(1);
    1081   }
    1082
    1083   if(lockf(file, F_LOCK, 0) == -1){
    1084     ER_perror(FAC_UP, UP_CANTLOCK, "Can't lock the file, %s", lock.lockname);
    1085     exit(1);
    1086   };
    1087
    1088   lock.filedes = file;
    1089
    1090   return lock;
    1091
    1092 }
    1093
    1094
    1095
    1096
    1097
    1098 /* remove_lock_file(): unlocks and removes the file  */
    1099 void remove_lock_file(lockfilestruct lockfile){
         /* [<][>][^][v][top][bottom][index][help] */
    1100
    1101   close(lockfile.filedes); /* this will remove the lock at the same time */
    1102   unlink(lockfile.lockname);
    1103
    1104 }
    1105
    1106
    1107
    1108 /* writes the checkpoint file with the specified state */
    1109 void write_checkpoint(int state){
         /* [<][>][^][v][top][bottom][index][help] */
    1110
    1111   char * filename;
    1112   char * tmpfilename;
    1113   int length;
    1114   FILE * file;
    1115
    1116   if(tracing){
    1117     printf("TRACING: write_checkpoint, state=[%i]\n", state);
    1118   }
    1119   length = strlen(lockdir) +  strlen(hostname) + 64;
    1120   filename    = (char *)malloc(length + 1);
    1121   tmpfilename = (char *)malloc(length + 5);
    1122
    1123   snprintf(filename,    length, "%s/dbupdate.checkpoint.%s.%ld",     lockdir, hostname, pid );
    1124   snprintf(tmpfilename, length, "%s/dbupdate.checkpoint.%s.%ld.tmp", lockdir, hostname, pid );
    1125
    1126   if(( file = fopen(tmpfilename, "w")) == NULL){
    1127     //fprintf(stderr, "Can't open temp checkpoint file, %s", tmpfilename);
    1128     ER_perror(FAC_UP, UP_CANTOPEN, "Can't open temp checkpoint file, %s", tmpfilename);
    1129     exit(1);
    1130   }
    1131
    1132   fprintf(file, "[STATE]\n%i\n", state);
    1133
    1134   fprintf(file, "[FLAGS]\n");
    1135   /* should print the flags here */
    1136
    1137   fprintf(file, "[PARTS]\n");
    1138   /* should print the parts (filenames) here */
    1139
    1140   fprintf(file, "[OBJECTS1]\n");
    1141
    1142   fprintf(file, "[OBJECTS2]\n");
    1143
    1144   fprintf(file, "[ACKFILE]\n");
    1145
    1146   fprintf(file, "[NOTIFFILES]\n");
    1147
    1148   fprintf(file, "[TIDS1]\n");
    1149
    1150   fprintf(file, "[TIDS2]\n");
    1151
    1152   fprintf(file, "[NICHDLHASH]\n");
    1153
    1154   fprintf(file, "[CURRENTOBJECT]\n");
    1155
    1156   fprintf(file, "[CURRENTPART]\n");
    1157
    1158
    1159   fclose(file);
    1160
    1161   rename(tmpfilename, filename);
    1162
    1163   /* free the char *'s */
    1164   free(tmpfilename);
    1165   free(filename);
    1166
    1167 }
    1168
    1169
    1170
    1171
    1172 /* removes check point file */
    1173 void remove_checkpoint(){
         /* [<][>][^][v][top][bottom][index][help] */
    1174
    1175   char * filename;
    1176   int length;
    1177   FILE * file;
    1178
    1179   if(tracing){
    1180     printf("TRACING: remove_checkpoint\n");
    1181   }
    1182
    1183   length = strlen(lockdir) +  strlen(hostname) + 64;
    1184   filename    = (char *)malloc(length + 1);
    1185
    1186   snprintf(filename,    length, "%s/dbupdate.checkpoint.%s.%ld",     lockdir, hostname, pid );
    1187
    1188   unlink(filename);
    1189
    1190   /* free the char * */
    1191   free(filename);
    1192
    1193 }
    1194
    1195
    1196
    1197
    1198
    1199
    1200
    1201 /* main */
    1202 void main(int argc, char **argv, char **envp){
         /* [<][>][^][v][top][bottom][index][help] */
    1203   //init_and_set_options(argc, argv, envp);
    1204
    1205   int count = 0;
    1206   int i,j;
    1207   int no_of_updateables = 0;
    1208   char ** temp_vector;
    1209   char * temp;
    1210   char * temp_upd_file = NULL;
    1211   char *input_file_name = NULL;
    1212   GHashTable *AUTO_NIC_hdl_hash;
    1213   credentials_struct credentials;
    1214   FILE * upd_file;
    1215   char c;
    1216   char * mheader_replaced = NULL;
    1217   char * mailtxt_replaced = NULL;
    1218
    1219   /* temp variables to read from conf */
    1220   char * source = NULL, * canupd = NULL;
    1221   ca_dbSource_t *source_hdl;
    1222   ca_updDbSource_t *upd_source_hdl;
    1223
    1224   GHashTable *ntfy_hash, *forw_hash, *cross_hash;
    1225
    1226
    1227   char *mail_command_line, * ack_file_name;
    1228   char *config_file_name = NULL;
    1229
    1230
    1231
    1232   /* to use EP module */
    1233   EP_Mail_DescrPtr p;
    1234   EPTokenPtr pt;
    1235   EPTokenPtr list_item;
    1236   EPTokenKeysPtr ptk;
    1237
    1238   char * temp_keyid;
    1239
    1240   /* a variable to be used to know if the part is pgp_signed or not */
    1241   int pgp_signed = 0;
    1242
    1243   long debug = 0;
    1244
    1245   /* optarg & optind are necessary to use getopt(3C) */
    1246   extern char *optarg;
    1247   extern int optind;
    1248
    1249
    1250   /* create notification hashes */
    1251   ntfy_hash = g_hash_table_new(g_str_hash, g_str_equal);
    1252   forw_hash = g_hash_table_new(g_str_hash, g_str_equal);
    1253   cross_hash = g_hash_table_new(g_str_hash, g_str_equal);
    1254
    1255   credentials.password_list = NULL;
    1256   credentials.from = NULL;
    1257   int ch;
    1258   char * to_address = NULL;
    1259   char * subject = NULL;
    1260   char * reply_to = NULL;
    1261
    1262   AUTO_NIC_hdl_hash = g_hash_table_new(g_str_hash, g_str_equal);
    1263
    1264
    1265
    1266
    1267   while ((ch = getopt(argc, argv, "MtSTf:c:sn")) != -1){
    1268           switch(ch) {
    1269           case 'M':
    1270                   reading_from_mail = 1;
    1271                   break;
    1272           case 'f':
    1273                   input_file_name = strdup(optarg);
    1274                   break;
    1275           case 'c':
    1276                   config_file_name = strdup(optarg);
    1277                   break;
    1278           case 't':
    1279                   tracing = 1;
    1280                   break;
    1281           /* Test mode? In test mode, creation of mntners and as-blocks is possible, without overriding */
    1282           case 'T':
    1283                   test_mode = 1;
    1284                   break;
    1285           /* Supress acks and notifications? If yes, the acks and notifs will go to DEFMAIL config var */
    1286           case 'S':
    1287                   supress_ack_notif = 1;
    1288                   break;
    1289           /* Print out the ack to stdout? */
    1290           case 's':
    1291                   print_out_ack = 1;
    1292                   break;
    1293           /* are we processing networkupdate? (invoked via inetd) */
    1294           case 'n':
    1295                   networkupdate = 1;
    1296                   break;
    1297           case '?':
    1298           default:
    1299                   printf("Unknown option\n"); exit(1);
    1300           }
    1301   }
    1302
    1303
    1304   /* config stuff */
    1305   /* if -c flag is given, use the named file as config file, otherwise use
    1306      default filename */
    1307   if( config_file_name != NULL){
    1308     /*ca_readConfig(config_file_name, confVars, VARS);*/
    1309     ca_init(config_file_name);
    1310   }else{
    1311     /*ca_readConfig("dbupdate.conf", confVars, VARS);*/
    1312     ca_init("dbupdate.conf");
    1313   }
    1314
    1315   error_init(argc, argv);
    1316
    1317
    1318   tmpdir = ca_get_tmpdir;
    1319   tmpdir = g_strstrip(tmpdir);
    1320   lockdir = ca_get_lockdir;
    1321   mailcmd = ca_get_mailcmd;
    1322   mailcmd = g_strstrip(mailcmd);
    1323   notitxt = ca_get_notitxt;
    1324   mailtxt = ca_get_mailtxt;
    1325   defmail = ca_get_defmail; defmail = UP_remove_EOLs(defmail);
    1326   crosslog = ca_get_crosslog;
    1327   fwtxt = ca_get_fwtxt;
    1328   acksig = ca_get_acksig;
    1329   humailbox = ca_get_humailbox;
    1330   humailbox = g_strstrip(humailbox);
    1331   autobox = ca_get_autobox;
    1332   overridecryptedpw = ca_get_overridecryptedpw;
    1333   overridecryptedpw = g_strstrip(overridecryptedpw);
    1334   updlog = ca_get_updlog;
    1335   acklog = ca_get_acklog;
    1336   notiflog = ca_get_notiflog;
    1337   notimailtxt = ca_get_notimailtxt;
    1338   notinetworktxt = ca_get_notinetworktxt;
    1339   forwlog = ca_get_forwlog;
    1340   fwmailtxt = ca_get_fwmailtxt;
    1341   mtfwheader = ca_get_mtfwheader;
    1342   mtfwtxt = ca_get_mtfwtxt;
    1343   country = ca_get_country;
    1344   pgppath = ca_get_pgppath;
    1345   gpgcmd = ca_get_gpgcmd;
    1346   autodbmhelp = ca_get_autodbmhelp;
    1347   cn_subject_add = ca_get_cn_subject_add; cn_subject_add = UP_remove_EOLs(cn_subject_add);
    1348   cn_subject_del = ca_get_cn_subject_del; cn_subject_del = UP_remove_EOLs(cn_subject_del);
    1349   cn_explain_add = ca_get_cn_explain_add;
    1350   cn_explain_del = ca_get_cn_explain_del;
    1351   cn_overlap_add = ca_get_cn_overlap_add;
    1352   cn_overlap_del = ca_get_cn_overlap_del;
    1353   cno_subject_add = ca_get_cno_subject_add; cno_subject_add = UP_remove_EOLs(cno_subject_add);
    1354   cno_subject_del = ca_get_cno_subject_del; cno_subject_del = UP_remove_EOLs(cno_subject_del);
    1355   cno_explain_add = ca_get_cno_explain_add;
    1356   cno_explain_del = ca_get_cno_explain_del;
    1357   cno_overlap_add = ca_get_cno_overlap_add;
    1358   cno_overlap_del = ca_get_cno_overlap_del;
    1359   copyright_notice = ca_get_pw_resp_header;
    1360   mheader = ca_get_mheader;
    1361   pgp_public_key_ring = (char *)malloc(strlen(pgppath) + strlen("/pubring.gpg") + 2);
    1362   sprintf(pgp_public_key_ring ,"%s/pubring.gpg", pgppath);
    1363   if(test_mode != 1){/* if it is not already set to 1 (from command line), read from config */
    1364
    1365     test_mode = ca_get_testmode;
    1366   }
    1367   /* retrieve source variables */
    1368   upd_source_hdl = ca_get_UpdSourceHandle(CA_UPDSOURCE);
    1369
    1370   if(upd_source_hdl == NULL){
    1371     printf("There must be one updateable source in the config file. Exiting.\n");
    1372     ER_perror(FAC_UP, UP_CONFERR, "There must be one updateable source in"
    1373                                   " the config file. Exiting.");
    1374     exit(1);
    1375   }else{
    1376     if(tracing){
    1377       printf("\nTRACING: The upd_source_hdl is: %s\n", upd_source_hdl->name);
    1378     }
    1379     sources[0] = strdup(upd_source_hdl->name);
    1380     update_host = upd_source_hdl->whoisd_host;
    1381     query_host = strdup(update_host);
    1382     update_port = upd_source_hdl->updPort;
    1383     query_port = upd_source_hdl->qryPort;
    1384     DBhost = upd_source_hdl->updDb.host;
    1385     DBport = upd_source_hdl->updDb.port;
    1386     DBname = upd_source_hdl->updDb.dbName;
    1387     DBuser = upd_source_hdl->updDb.user;
    1388     DBpasswd = upd_source_hdl->updDb.password;
    1389   }
    1390
    1391
    1392
    1393   /* construct country array from country string variable */
    1394
    1395   temp_vector = g_strsplit(country, "\n", 0);
    1396   for(i=0, j=0; temp_vector[i] != NULL; i++){
    1397     temp_vector[i] == g_strstrip(temp_vector[i]);
    1398     if(strlen(temp_vector[i]) > 0){
    1399       countries[j] = strdup(temp_vector[i]);
    1400       g_strup(countries[j]);
    1401       j++;
    1402     }
    1403   }
    1404   countries[j] = NULL; /* mark the end of array */
    1405
    1406   if(tracing){
    1407     /* print out the config variables for debugging */
    1408     printf("TRACING: countries[%i] = NULL\n", j);
    1409
    1410     printf("TMPDIR is: [%s]\n", tmpdir);
    1411     printf("MAILCMD is: [%s]\n", mailcmd);
    1412     printf("NOTITXT is: [%s]\n", notitxt);
    1413     printf("CROSSLOG is: [%s]\n", crosslog);
    1414     printf("FWTXT is: [%s]\n", fwtxt);
    1415     printf("HUMAILBOX is: [%s]\n", humailbox);
    1416     printf("AUTOBOX is: [%s]\n", autobox);
    1417     printf("OVERRIDECRYPTEDPW is: [%s]\n", overridecryptedpw);
    1418     printf("ACKLOG is: [%s]\n", acklog);
    1419     printf("NOTIFLOG is: [%s]\n", notiflog);
    1420     printf("FORWLOG is: [%s]\n", forwlog);
    1421     printf("NOTIMAILTXT is: [%s]\n", notimailtxt);
    1422     printf("FWMAILTXT is: [%s]\n", fwmailtxt);
    1423     printf("COUNTRY is: [%s]\n", country);
    1424     printf("PGPPATH is: [%s]\n", pgppath);
    1425     printf("UPDATE_HOST is: [%s]\n", update_host);
    1426     printf("UPDATE_PORT is: [%i]\n", update_port);
    1427     printf("QUERY_HOST is: [%s]\n",  query_host);
    1428     printf("QUERY_PORT is: [%i]\n",  query_port);
    1429     printf("LOCKDIR is: [%s]\n", lockdir);
    1430     printf("TESTMODE is: [%i]\n", test_mode);
    1431     printf("CNO_SUBJECT_ADD is: [%s]\n", cno_subject_add);
    1432     printf("CNO_SUBJECT_DEL is: [%s]\n", cno_subject_del);
    1433   }
    1434   /* end of config stuff */
    1435
    1436
    1437   /* set hostname global variable */
    1438   gethostname(hostname, MAXHOSTNAMELEN);
    1439
    1440   /* set pid global variable */
    1441   pid = getpid();
    1442
    1443   /* create the lock file and lock it */
    1444   //lockfile = create_lock_file();
    1445
    1446
    1447   /* initialize the parser */
    1448   schema.initialize();
    1449
    1450
    1451   /* Generate a name for temporary file for storing acks (AK_ack_file_name_generate
    1452       also creates it) */
    1453   ack_file_name = AK_ack_file_name_generate(tmpdir, ACK_FILE_PREFIX);
    1454
    1455   /* initialize credentials.pgp_key_list */
    1456   credentials.pgp_key_list = NULL;
    1457
    1458
    1459
    1460
    1461   if(reading_from_mail){
    1462     if(input_file_name != NULL){
    1463       temp_upd_file = generate_upd_file();
    1464       if(tracing){
    1465         printf("TRACING: temp_upd_file is [%s]\n", temp_upd_file);
    1466       }
    1467
    1468       /* first log the input in the upd log file */
    1469       UP_add_to_upd_log(input_file_name);
    1470
    1471
    1472       MM_store(input_file_name, temp_upd_file, 0);
    1473       p = EP_ParseMail(input_file_name, tmpdir, pgp_public_key_ring, gpgcmd);
    1474
    1475     }else{/* input_file_name == NULL */
    1476       temp_upd_file = generate_upd_file();
    1477       MM_store("-", temp_upd_file, 0);
    1478
    1479       /* first log the input in the upd log file */
    1480       UP_add_to_upd_log(temp_upd_file);
    1481
    1482       p = EP_ParseMail(temp_upd_file, tmpdir, pgp_public_key_ring, gpgcmd);
    1483
    1484     }
    1485
    1486     /* write off the checkpoint file */
    1487     write_checkpoint(1);
    1488
    1489     /* the new stuff using the EP module's interface */
    1490     if(tracing){
    1491       printf("\nTRACING: From field is: [%s]\n", p->from->field);
    1492     }
    1493
    1494     temp = (char *)malloc(strlen(p->from->field) + strlen("From: ") + 1);
    1495     sprintf(temp, "From: %s", p->from->field);
    1496     /* cut off the '\n's and '\r's at the end of temp */
    1497     UP_remove_EOLs(temp);
    1498
    1499     credentials.from = temp;
    1500     credentials.from_email = strdup(p->from->field);
    1501
    1502     update_mail_sender = strdup(p->from->field);
    1503     update_mail_sender = UP_remove_EOLs(update_mail_sender);
    1504
    1505     if(p->subject != NULL && p->subject->field != NULL){
    1506       subject = strdup(p->subject->field);
    1507     }else{
    1508       subject = strdup("");
    1509     }
    1510
    1511     /* cut off the '\n' and '\r' from the end */
    1512     UP_remove_EOLs(subject);
    1513
    1514     update_mail_subject = strdup(subject);
    1515
    1516     /* parse the subject line */
    1517     subject_result = UP_subject_process(update_mail_subject);
    1518
    1519
    1520     if(p->reply_to != NULL && p->reply_to->field != NULL){
    1521       reply_to = strdup(p->reply_to->field);
    1522     }else{
    1523       reply_to = strdup("");
    1524     }
    1525
    1526
    1527     /* cut off the '\n' and '\r' from the end */
    1528     UP_remove_EOLs(reply_to);
    1529
    1530
    1531
    1532     to_address = find_email_address(credentials.from);
    1533
    1534     /* if Reply_To was available in the incoming header, then use it */
    1535     if(strlen(reply_to) > 0){
    1536       to_address = (char *)realloc(to_address, strlen(reply_to) + 1);
    1537       to_address = strcpy(to_address, reply_to);
    1538       to_address = find_email_address(to_address); /* so that we take only the email address */
    1539     }
    1540
    1541     if(p->message_id != NULL && p->message_id->field != NULL){
    1542       update_mail_ID = strdup(p->message_id->field);
    1543     }else{
    1544       update_mail_ID = strdup("");
    1545     }
    1546
    1547     /* cut off the '\n' and '\r' from the end */
    1548     UP_remove_EOLs(update_mail_ID);
    1549
    1550
    1551     if(p->date != NULL && p->date->field != NULL){
    1552       update_mail_date = strdup(p->date->field);
    1553     }else{
    1554       update_mail_date = strdup("");
    1555     }
    1556
    1557     /* cut off the '\n' and '\r' from the end  */
    1558     UP_remove_EOLs(update_mail_date);
    1559
    1560
    1561     if(tracing){
    1562       printf("\nEP_ShowTree outputs:\n");
    1563       EP_ShowTree(p->tree);
    1564     }
    1565
    1566     pt = EP_GetTokens(p->tree, NULL, NULL);
    1567
    1568     if(tracing){
    1569       /* Print the list out (debugging) */
    1570       printf("\nEP_PrintTokens outputs:\n");
    1571       EP_PrintTokens(pt);
    1572     }
    1573
    1574     /* replace the global variables in mheader */
    1575     mheader_replaced = UP_replace_globals(mheader);
    1576     /* replace the global variables in mailtxt */
    1577     mailtxt_replaced = UP_replace_globals(mailtxt);
    1578
    1579     /* If this wasn't only a help request, then we need to process the input */
    1580     if(subject_result.result != UP_SUBJ_HELP_REQ){
    1581
    1582       /* Print out the header of the ackonwledgement */
    1583       AK_add_to_ack(ack_file_name, "To: %s\n%s\n\nAcknowledgement message from"
    1584         " database software\n\n%s\n", to_address, mheader_replaced, mailtxt_replaced);
    1585
    1586       /* ... and now process the items in the list */
    1587       list_item = pt;
    1588       while (list_item != NULL) {
    1589         if(tracing){
    1590           printf("\n\nWill process: %s, MIMEtype: %d\n", list_item->file, list_item->MIMEContentType);
    1591         }
    1592         /* initialize pgp_key_list (XXX This should be a proper freeing of the list) */
    1593         credentials.pgp_key_list = NULL;
    1594         ptk = list_item->keys;
    1595         if(ptk != NULL){
    1596           AK_add_to_ack(ack_file_name, "==== BEGIN PGP SIGNED PART (keyID(s):");
    1597           pgp_signed = 1;
    1598           while (ptk != NULL) {
    1599             if(tracing){
    1600               printf("TRACING:     key: %.8X, isValid: %i\n",
    1601                    ptk->keyID, ptk->isValidPGPSignature);
    1602             }
    1603             temp_keyid = (char *)malloc(10);
    1604             sprintf(temp_keyid, "%.8X", ptk->keyID);
    1605             if(tracing){
    1606               printf("TRACING: This key will be added to the list: [%s]\n", temp_keyid);
    1607             }
    1608             AK_add_to_ack(ack_file_name, " %s", temp_keyid);
    1609             credentials.pgp_key_list = g_slist_append (credentials.pgp_key_list, temp_keyid);
    1610             ptk = ptk->next;
    1611             if(ptk != NULL){
    1612               AK_add_to_ack(ack_file_name, ",");
    1613             }else{
    1614               AK_add_to_ack(ack_file_name, ") ====\n");
    1615             }
    1616           }
    1617         }
    1618         process_file(list_item->file, credentials,
    1619                      AUTO_NIC_hdl_hash, ack_file_name,
    1620                      ntfy_hash, forw_hash, cross_hash);
    1621         if(pgp_signed){
    1622           AK_add_to_ack(ack_file_name, "==== END PGP SIGNED PART ====\n\n");
    1623           pgp_signed = 0;
    1624         }
    1625         list_item = list_item->next;
    1626       }
    1627
    1628     }else{/* this was only a help request (inferred from the "Subject" line of the upd message) */
    1629
    1630       /* Print out the header of the acknowledgement */
    1631       AK_add_to_ack(ack_file_name, "To: %s\n%s\n\nHelp file requested so body of message ignored.\n\n\n"
    1632           "============================================================\n\n",
    1633           to_address, mheader_replaced);
    1634
    1635       AK_add_file_to_ack(ack_file_name, autodbmhelp);
    1636       AK_add_to_ack(ack_file_name, "\n============================================================");
    1637
    1638     }
    1639
    1640     EP_CleanTokens(pt);
    1641
    1642     EP_MailDescrCleanUp(p);
    1643
    1644     /* if we have created a temporary file for update, delete it */
    1645     if(temp_upd_file != NULL){
    1646
    1647       unlink(temp_upd_file);
    1648
    1649     }
    1650
    1651   }else if(networkupdate){
    1652
    1653     /* process networkupdate. Since we use inetd, we just process stdin */
    1654     process_networkupdate(credentials, AUTO_NIC_hdl_hash, ack_file_name,
    1655                           ntfy_hash, forw_hash, cross_hash);
    1656
    1657   }else{/* not reading from the mail message or from network */
    1658     if(input_file_name != NULL){
    1659
    1660       /* first log the input in the upd log file */
    1661       UP_add_to_upd_log(input_file_name);
    1662
    1663
    1664       write_checkpoint(1);
    1665       process_file(input_file_name, credentials,
    1666                   AUTO_NIC_hdl_hash, ack_file_name,
    1667                   ntfy_hash, forw_hash, cross_hash);
    1668     }else{/* the filename is not given, so we have to write
    1669              stdin to a temp file, and give it to process_file */
    1670        temp_upd_file = generate_upd_file();
    1671        if(tracing){
    1672          printf("TRACING: main: temp_upd_file=%s\n", temp_upd_file);
    1673        }
    1674        if(( upd_file = fopen(temp_upd_file, "a")) == NULL){
    1675          ER_perror(FAC_UP, UP_CANTOPENW, "Can't open ack file, %s", temp_upd_file);
    1676        }
    1677
    1678        while((c = getchar()) != EOF){
    1679          fprintf(upd_file, "%c",c);
    1680        }
    1681        fclose(upd_file);
    1682
    1683        write_checkpoint(1);
    1684        process_file(temp_upd_file, credentials,
    1685                   AUTO_NIC_hdl_hash, ack_file_name,
    1686                   ntfy_hash, forw_hash, cross_hash);
    1687        unlink(temp_upd_file);
    1688
    1689     }
    1690
    1691   }
    1692
    1693
    1694   /* send the ack */
    1695   if(reading_from_mail && to_address != NULL){
    1696     AK_send_ack(ack_file_name, to_address, mailcmd);
    1697   }
    1698
    1699   /* if our update wasn't a mail update OR we have been asked explicitely
    1700      to print out the ack to the stdout, print it */
    1701   if(!reading_from_mail || print_out_ack){
    1702     AK_print_ack(ack_file_name);
    1703   }
    1704
    1705   AK_log_ack(ack_file_name, acklog);
    1706   AK_delete_ack(ack_file_name);
    1707
    1708   NT_send_ntfy_list(ntfy_hash, mailcmd);
    1709   NT_log_ntfy_list(ntfy_hash, notiflog);
    1710   NT_delete_ntfy_list(ntfy_hash);
    1711
    1712   NT_send_ntfy_list(forw_hash, mailcmd);
    1713   NT_log_ntfy_list(forw_hash, forwlog);
    1714   NT_delete_ntfy_list(forw_hash);
    1715
    1716
    1717   NT_send_ntfy_list(cross_hash, mailcmd);
    1718   NT_log_ntfy_list(cross_hash, crosslog);
    1719   NT_delete_ntfy_list(cross_hash);
    1720
    1721   /* remove the lock file */
    1722   //remove_lock_file(lockfile);
    1723
    1724
    1725   /* remove checkpoint file */
    1726   remove_checkpoint();
    1727
    1728
    1729   if(tracing) {
    1730     printf("TRACING: END\n");
    1731   }
    1732
    1733
    1734 }

/* [<][>][^][v][top][bottom][index][help] */