1    | /***************************************
2    |   $Revision: 1.29 $
3    | 
4    |   Functions to process data stream( file, network socket, etc.)
5    | 
6    |   Status: NOT REVUED, NOT TESTED
7    | 
8    |  Author(s):       Chris Ottrey, Andrei Robachevsky
9    | 
10   |   ******************/ /******************
11   |   Modification History:
12   |         andrei (17/01/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   | #include <sys/types.h>
34   | #include <sys/socket.h>
35   | #include <netdb.h>
36   | #include <arpa/inet.h>
37   | #include <unistd.h>
38   | #include <sys/stat.h>
39   | #include <fcntl.h>
40   | #include <string.h>
41   | #include "constants.h"
42   | #include "query_command.h"
43   | #include "ud.h"
44   | #include "ud_int.h"
45   | 
46   | typedef enum _Line_Type_t {
47   |  LINE_ATTRIBUTE,
48   |  LINE_COMMENT,
49   |  LINE_EMPTY,
50   |  LINE_EOF,
51   |  LINE_ADD,
52   |  LINE_UPD,
53   |  LINE_DEL,
54   |  LINE_OVERRIDE_ADD,
55   |  LINE_OVERRIDE_UPD,
56   |  LINE_OVERRIDE_DEL,
57   |  LINE_PLUS
58   | } Line_Type_t;
59   | 
60   | /* Maximum number of objects(serials) we can consume at a time */
61   | #define SBUNCH 1000
62   | 
63   | static int report_transaction(Transaction_t *tr, Log_t *log, char *obj_name, char *reason);
64   | static int process_nrtm(UD_stream_t *ud_stream, Transaction_t *tr, char *object_name, int operation);
65   | static int process_updates(UD_stream_t *ud_stream, Transaction_t *tr, char *object_name, int operation);
66   | static int process_transaction(UD_stream_t *ud_stream,Object_t *obj,char *object_name,nic_handle_t *nh, int operation);
67   |                                                                                                 
68   | /* Delimiters that separate list members, both RPS(,) and legacy( ) */
69   | #define ATTR_DELIMITERS " ,"
70   | 
71   | static GSList *split_attribute(GSList *attr_list, A_Type_t attr_type, char *attr_value){
72   | char *token;
73   | char *split;
74   | char *value, *n;
75   | Attribute_t *attr_split;
76   | GSList *the_list = attr_list;
77   | 
78   |   /* check for end-of-line comments */
79   |   n = index(attr_value, '#');
80   |       /* if there is no comment check for trailing \n */
81   |   if(n == NULL) n = index(attr_value, '\n');
82   |   /* now copy the clean value into the attribute */
83   |   if(n == NULL) value = g_strdup(attr_value); 
84   |   else  value = g_strndup(attr_value, (n - attr_value));
85   |      
86   |   token=value;
87   |   while((split=strsep(&token, ATTR_DELIMITERS))){
88   |      attr_split = attribute_new1(attr_type, split);
89   |      if (attr_split) the_list = g_slist_append(the_list, attr_split);
90   |   }
91   |   free(value);
92   |  return(the_list);
93   | }
94   | 
95   | /************************************************************
96   | *                                                           *
97   | * The function to reorder attributes in the List            *
98   | * nic-hdl and mnt-by should come first                      *
99   | *                                                           *
100  | * should return 0 if they are equal, a negative value if    * 
101  | * the first element comes before the second, or a positive  *
102  | * value if the first element comes after the second         *
103  | *                                                           *
104  | ************************************************************/
105  | static gint reorder_attributes(const void *element1, const void *element2)
106  | {
107  | Attribute_t *attr1 = (Attribute_t *)element1;
108  | Attribute_t *attr2 = (Attribute_t *)element2;
109  | gint order = -1;
110  |   
111  |   if(attr2->type == A_MB) order= 1;
112  |   if(attr1->type == A_MB) order= -1;
113  |   if(attr2->type == A_NH) order= 1;
114  |   if(attr1->type == A_NH) order= -1;
115  | 
116  |   return(order);
117  | 
118  | }
119  | 
120  | /* XXX */
121  | static void each_attribute_print(void *element_data, void *tr_ptr)
122  | {
123  | 
124  | Attribute_t *attr = (Attribute_t *)element_data;
125  | 
126  |  fprintf(stderr, "[%d|%s]\n", attr->type, attr->value);
127  | 
128  | }
129  | 
130  | /* XXX */
131  | static void print_object(Object_t *obj)
132  | {
133  |   g_slist_foreach(obj->attributes, each_attribute_print, NULL);	
134  |   fprintf(stderr, ">>>>>\n%s\n", obj->object->str);
135  | }
136  | 
137  | 
138  | /******************************************************************
139  | * GString *escape_apostrophes()                                   *
140  | * Escapes apostrophes in the text so they do not confuse printf   *
141  | * functions and don't corrupt SQL queries                         *
142  | *                                                                 *
143  | * *****************************************************************/
144  | GString *escape_apostrophes(GString *text) {
145  |   int i;
146  |   for (i=0; i < text->len; i++) {
147  |     if ((text->str[i] == '\'') || (text->str[i] == '\\')) {
148  |       text = g_string_insert_c(text, i, '\\');
149  |       i++;
150  |     }
151  |   }
152  |  return(text); 
153  | } /* escape_apostrophes() */
154  | 
155  | 
156  | /******************************************************************
157  | * Line_Type_t line_type(e)                                        *
158  | * Determines the line type analysing the first letters            *
159  | *                                                                 *
160  | * ****************************************************************/
161  | static Line_Type_t line_type(const char *line) {
162  |   Line_Type_t result = -1;
163  | 
164  |   if (strncmp(line, "# EOF", 4) == 0) {
165  |     result = LINE_EOF;
166  |   }
167  |   else if (strncmp(line, "#", 1) == 0) {
168  |     result = LINE_COMMENT;
169  |   }
170  |   else if (strcmp(line, "\n") == 0) {
171  |     result = LINE_EMPTY;
172  |   }
173  |   else if (strcmp(line, "ADD\n") == 0) {
174  |     result = LINE_ADD;
175  |   }
176  |   else if (strcmp(line, "UPD\n") == 0) {
177  |     result = LINE_UPD;
178  |   }
179  |   else if (strcmp(line, "DEL\n") == 0) {
180  |     result = LINE_DEL;
181  |   }
182  |   else if (strcmp(line, "ADD_OVERRIDE\n") == 0) {
183  |     result = LINE_OVERRIDE_ADD;
184  |   }
185  |   else if (strcmp(line, "UPD_OVERRIDE\n") == 0) {
186  |     result = LINE_OVERRIDE_UPD;
187  |   }
188  |   else if (strcmp(line, "DEL_OVERRIDE\n") == 0) {
189  |     result = LINE_OVERRIDE_DEL;
190  |   }
191  |   else if (strncmp(line, "+", 1) == 0) {
192  |     result = LINE_PLUS;
193  |   }  
194  |   else {
195  |     result = LINE_ATTRIBUTE;
196  |   }
197  | 
198  |   return result;
199  | } /* line_type() */
200  | 
201  | 
202  | /******************************************************************
203  | * report_transaction()                                            *
204  | *                                                                 * 
205  | * Prints error report to the log                                  *
206  | *                                                                 *
207  | * reason - additional message that will be included               *
208  | *                                                                 *
209  | * *****************************************************************/
210  | static int report_transaction(Transaction_t *tr, Log_t *log, char *obj_name, char *reason)
211  | {
212  | int result=0;
213  | 
214  |  if(tr->succeeded==0) {
215  |   result=tr->error;
216  |   log->num_failed++;
217  |   fprintf(stderr, "FAILED[%s][%s(%d)](%d/%d)\n ", obj_name, reason, result, log->num_failed, (log->num_failed)+(log->num_ok)); 
218  |   fprintf(log->logfile, "*FAILED[%s][%s](%d/%d)\n ", obj_name, reason, log->num_failed, (log->num_failed)+(log->num_ok));
219  |   if(result & ERROR_U_MEM) fprintf(log->logfile, "\t*Memory allocation error\n");
220  |   if(result & ERROR_U_DBS) fprintf(log->logfile, "\t*Database (SQL) error\n");
221  |   if(result & ERROR_U_OBJ) fprintf(log->logfile, "\t*Object (RF) error\n");
222  |   if(result & ERROR_U_AUT) fprintf(log->logfile, "\t*Object authentication error\n");
223  |   if(result & ERROR_U_BADOP) fprintf(log->logfile, "\t*Bad operation\n");
224  |   if(result & ERROR_U_COP) fprintf(log->logfile, "\t*Conflicting operation\n");
225  |   if(result & ERROR_U_NSUP) fprintf(log->logfile, "\t*Object of this type is not supported\n");
226  |   if(result & ERROR_U_BUG) fprintf(log->logfile, "\t*Software bug - report to <ripe-dbm@ripe.net>\n");
227  |   fprintf(log->logfile, "%s", (tr->error_script)->str);
228  |   result=(-1)*result;                                                
229  |   fflush(log->logfile);
230  |  }
231  |  else {
232  |   result=1;
233  |   log->num_ok++;
234  |   fprintf(stderr, "OK(%d/%d)\n", log->num_ok, (log->num_failed)+(log->num_ok));
235  |  }
236  |                                                                                                                                                 
237  |  return(result);
238  | }/* report_transaction() */
239  | 
240  | 
241  | 
242  | /************************************************************
243  | * process_nrtm()                                            *
244  | *                                                           *
245  | * Process object in NRTM client mode                        *
246  | *                                                           *
247  | * nrtm - pointer to _nrtm structure                         *
248  | * log - pointer to Log_t structure                          *
249  | * object_name - name of the object                          * 
250  | * operation - operation code (OP_ADD/OP_DEL)                *
251  | *                                                           *
252  | * Returns:                                                  *
253  | * 1  - okay                                                 *
254  | * <0 - error                                                *
255  | *                                                           *
256  | ************************************************************/
257  | 
258  | static int process_nrtm(UD_stream_t *ud_stream, Transaction_t *tr, char *object_name, int operation)
259  | {
260  | int result=0;
261  | int dummy=0;
262  | struct _nrtm *nrtm = ud_stream->nrtm;
263  | Log_t *log_ptr= &(ud_stream->log);
264  | 
265  |   /* We allow NRTM updates for some inconsistent objects                  */
266  |   /* One of the examples is reference by name which looks like nic-handle */
267  |   /* For this purpose we allow dummy creation when updating an object     */
268  |   /* We also check for dummy allowance when deleting an object            */
269  |   /* this is done to allow deletion of person objects referenced by name  */
270  | 
271  |   tr->dummy=1;
272  |   
273  |   switch (operation) {
274  |   
275  |   case OP_ADD:
276  |     if(nrtm->tr){ /* DEL ADD => saved*/
277  |       if(tr->object_id==0) { 
278  | 	/* object does not exist in the DB */      
279  |         object_process(nrtm->tr); /* delete the previous(saved) object*/
280  |         result=report_transaction(nrtm->tr, log_ptr, nrtm->object_name, "NRTM:DEL:While deleting previous(saved)");
281  |         /* create DEL serial */
282  | 	create_serial(nrtm->tr);
283  |         object_free(nrtm->tr->object);
284  |         transaction_free(nrtm->tr); nrtm->tr=NULL;
285  |         /* Create an object and update NHR */
286  |         tr->action=(TA_CREATE | TA_UPD_NHR);
287  | /*      fprintf(stderr,"CREATE next\n"); */
288  |         object_process(tr); /* create a new one*/
289  |         result=report_transaction(tr, log_ptr, object_name, "NRTM:ADD:While creating new");
290  | 	/* create ADD serial */
291  |         create_serial(tr);
292  |       }
293  |       else { 
294  |       /* object already exists in the DB - update or dummy replacement*/
295  |         if(tr->object_id==nrtm->tr->object_id) {/*compare the two, may be we may collapse operations*/
296  |           object_free(nrtm->tr->object);
297  |           transaction_free(nrtm->tr); nrtm->tr=NULL;
298  | /*        fprintf(stderr,"DEL-ADD ->> UPDATE\n");*/
299  |           tr->action=TA_UPDATE;
300  |           object_process(tr);
301  |           report_transaction(tr, log_ptr, object_name,"NRTM:upd");
302  |           result=report_transaction(tr, log_ptr, object_name,"NRTM:upd");
303  | 	  /* create DEL+ADD serial records */
304  | 	  tr->action=TA_DELETE; create_serial(tr);
305  |           tr->action=TA_CREATE; create_serial(tr);
306  |         }
307  |         else { /* this should be a dummy object in the database(that we are going to replace with the real one */
308  |         /* or an interleaved operation*/
309  | /*        fprintf(stderr,"DEL previous\n");*/
310  |           object_process(nrtm->tr); /* delete the previous(saved) object*/
311  |           result=report_transaction(nrtm->tr, log_ptr, nrtm->object_name, "NRTM:DEL:While deleting previous(saved)");
312  |           /* create a DEL serial record */
313  | 	  create_serial(nrtm->tr);
314  |           object_free(nrtm->tr->object);
315  |           transaction_free(nrtm->tr); nrtm->tr=NULL;
316  |           tr->action=TA_UPDATE;
317  |           dummy=isdummy(tr);
318  |           /* If we are replacing dummy with a real object update NHR */
319  |           if(dummy==1) tr->action |= TA_UPD_NHR;
320  | /*        fprintf(stderr,"UPDATE next(dummy)\n"); */
321  |           object_process(tr); /* create a new one*/
322  |           result=report_transaction(tr, log_ptr, object_name, "NRTM:ADD:While creating new");
323  | 	  /* For serials this is CREATE operation */
324  |           if(dummy==1) tr->action=TA_CREATE;
325  | 	  /* create ADD serial record */
326  |           create_serial(tr);
327  |         }
328  |       }
329  |     }
330  |     else { /* ADD ADD =>brand new object*/
331  |       if(tr->object_id==0) {
332  | /*      fprintf(stderr,"CREATE new\n");*/
333  |         /* Create an object and update NHR */
334  |         tr->action=(TA_CREATE | TA_UPD_NHR);
335  |         object_process(tr);
336  |         result=report_transaction(tr, log_ptr, object_name,"NRTM:ADD:While creating new");
337  |         /* create ADD serial */
338  | 	create_serial(tr);
339  |       }
340  |       else { /* object already exists in the database */
341  | 	/* this may happen because of dummies*/
342  | 	/* or with some implementations of mirroring protocol that have atomic update */
343  | 	/* instead of add + del */
344  | /*      fprintf(stderr,"CREATE new\n");*/
345  |         tr->action=TA_UPDATE;
346  |         dummy=isdummy(tr);
347  |  /* If we are replacing dummy with a real object update NHR */
348  |         if(dummy==1) tr->action |= TA_UPD_NHR;
349  |         object_process(tr);
350  |         result=report_transaction(tr, log_ptr, object_name,"NRTM:ADD:While creating new");
351  |         if(dummy==1) tr->action=TA_CREATE; /* we don't want to generate DEL serial for dummy replacement*/
352  |         /* create ADD serial record */
353  | 	create_serial(tr);
354  |       } 
355  |     }
356  |     break;
357  |     
358  |   case OP_DEL:
359  |     if(nrtm->tr){ /*DEL DEL =>saved */
360  | /*    fprintf(stderr,"DEL previous\n");*/
361  |       object_process(nrtm->tr); /* delete the previous(saved) object*/
362  |       result=report_transaction(nrtm->tr, log_ptr, nrtm->object_name, "NRTM:DEL:While deleting previous(saved) object");
363  |       /* create DEL serial record */
364  |       create_serial(nrtm->tr);
365  |       object_free(nrtm->tr->object);
366  |       transaction_free(nrtm->tr); nrtm->tr=NULL;
367  |     }
368  |     if(tr->object_id>0){ /* save the object*/
369  |       fprintf(stderr,"SAVED\n");
370  |       tr->action=TA_DELETE;
371  |       nrtm->tr=tr;
372  |       strcpy(nrtm->object_name, object_name);
373  |       return(1);
374  |     }
375  |     else { /* this is an error - Trying to DEL non-existing object*/
376  |       tr->succeeded=0; tr->error|=ERROR_U_COP;
377  |       result=report_transaction(tr, log_ptr, object_name, "NRTM:OOS:Trying to DEL non-existing object");
378  |       /* create DEL serial record anyway */
379  |       tr->action=TA_DELETE;
380  |       create_serial(tr);
381  |     }
382  |     break;
383  |   
384  |   default:
385  |     tr->succeeded=0; tr->error |=ERROR_U_BADOP;
386  |     break;  
387  |   }
388  | 
389  |  /* Free resources */  
390  |   object_free(tr->object);
391  |   transaction_free(tr);
392  |   
393  |   return(result);
394  | } /* process_nrtm() */
395  | 
396  | 
397  | 
398  | /************************************************************
399  | * process_updates()                                         *
400  | *                                                           *
401  | * Process object in update mode                             *
402  | *                                                           *
403  | * ud_stream - pointer to UD_stream structure                *
404  | * object_name - name of the object                          *
405  | * operation - operation code (OP_ADD/OP_DEL)                *
406  | *                                                           *
407  | * Note:                                                     *
408  | * Frees tr and tr->obj on exit                              *
409  | *                                                           *
410  | * Returns:                                                  *
411  | * 1  - okay                                                 *
412  | * <0 - error                                                *
413  | *                                                           * 
414  | ************************************************************/
415  | 
416  | static int process_updates(UD_stream_t *ud_stream, Transaction_t *tr, char *object_name, int operation)
417  | {
418  | int result=0;
419  | Log_t *log_ptr= &(ud_stream->log);
420  | int dummy=0;
421  | 
422  |     switch(operation) {
423  |     /* Compare operations and report an error if they do not match */    
424  |     case OP_ADD:
425  |              if(tr->object_id!=0) { /* trying to create, but object exists */
426  |         tr->succeeded=0; tr->error|=ERROR_U_COP;
427  |       } else {
428  |        /* Action: create the object and update NHR */
429  |         tr->action=(TA_CREATE | TA_UPD_NHR);
430  |         object_process(tr);
431  |       }
432  |       break;
433  |     case OP_UPD:
434  |       if(tr->object_id==0) { /* trying to update non-existing object*/
435  |         tr->succeeded=0; tr->error|=ERROR_U_COP;
436  |       } else {
437  |         tr->action=TA_UPDATE;
438  |         dummy=isdummy(tr);
439  |         /* If we are replacing dummy with a real object update NHR */
440  |         if(dummy==1) tr->action |= TA_UPD_NHR;
441  |         object_process(tr);
442  |       }
443  |       break;
444  | 
445  |     case OP_DEL:        
446  |       if(tr->object_id==0) { /* trying t delete non-existing object*/
447  |         tr->succeeded=0; tr->error|=ERROR_U_COP;
448  |       } else {
449  |         tr->action=TA_DELETE;
450  |         object_process(tr);
451  |       }
452  |       break;
453  |                 
454  |     default:                
455  |       /* bad operation for this mode if not standalone */
456  |       if(tr->standalone) {
457  |         if(tr->object_id==0)tr->action=TA_CREATE; else tr->action=TA_UPDATE;
458  |         object_process(tr);
459  |       }
460  |       else {
461  |         tr->succeeded=0; 
462  |         tr->error|=ERROR_U_BADOP; 
463  |       }
464  |       break;
465  |     }
466  |    /* Make a report */
467  |     result=report_transaction(tr, log_ptr, object_name, "RIPupd:");
468  | 
469  |    /* If not in standalone mode create serial and copy error transcript */ 
470  |     if(!tr->standalone) {
471  |       if(result==1)create_serial(tr);
472  |       ud_stream->error_script=g_strdup((tr->error_script)->str);
473  |     }  
474  | 
475  |    /* Free resources */   
476  |     object_free(tr->object);
477  |     transaction_free(tr);
478  |     
479  |     return(result);
480  |         
481  | } /* process_updates() */
482  | 
483  | 
484  | /************************************************************
485  | *                                                           *
486  | * int process_transaction()                                 *
487  | *                                                           *
488  | * Processes the transaction                                 *
489  | *                                                           *
490  | * ud_stream - pointer to UD_stream_t structure              *
491  | *                                                           *
492  | * Returns:                                                  *
493  | * 1 - no error                                              *
494  | * <0- errors                                                *
495  | *                                                           *
496  | ************************************************************/
497  | 
498  | /* It frees the obj */
499  | 
500  | static int process_transaction(UD_stream_t *ud_stream, 
501  |                         Object_t *obj, 
502  |                         char *object_name, 
503  |                         nic_handle_t *nh,
504  |                         int operation)
505  | {
506  | Transaction_t *tr = NULL;
507  | Log_t *log_ptr = &(ud_stream->log);
508  | Attribute_t *attr=NULL;
509  | int result;
510  | 
511  | /* start new transaction now */ 
512  |  tr = transaction_new(ud_stream->db_connection, obj->type);
513  | 
514  | /* Return with error if transaction cannot be created */ 
515  |  if (tr == NULL) return(-1);
516  |  
517  |  tr->standalone=IS_STANDALONE(ud_stream->ud_mode);
518  |  tr->dummy=IS_DUMMY_ALLOWED(ud_stream->ud_mode);
519  |  tr->load_pass=ud_stream->load_pass;
520  |  tr->object=obj;
521  |  tr->nh=nh;
522  |  tr->source_hdl=ud_stream->source_hdl;
523  |  
524  | /* We perform no commit/rollback in the loader mode, so thread_id should be set to 0 */
525  |  if(ud_stream->load_pass!=0) { tr->thread_ins=0; tr->thread_upd=0; }
526  |     
527  | /* For the first load pass we only create objects */ 
528  |  if(ud_stream->load_pass==1) tr->object_id=0;
529  |   else tr->object_id=get_object_id(tr);
530  |  
531  | /* Object cannot be retrieved */
532  |  if(tr->object_id==-1) { /* DB error*/
533  |     tr->succeeded=0;
534  |     tr->error |= ERROR_U_DBS;
535  |     report_transaction(tr, log_ptr, object_name, "Object cannot be retrieved");
536  |     transaction_free(tr);
537  |     object_free(obj);
538  |     return(-1);
539  |  }
540  | /* save the name of person/role as we need it for referential */
541  | /* integrity check when deleting the object against names. */
542  | /* This is needed to support legacy references by name rather */
543  | /* then by nic_hdl */
544  |   if((tr->class_type==C_PN) || (tr->class_type==C_RO)){
545  |      attr = attribute_new(object_name);
546  |      if (attr==NULL) {
547  |        tr->succeeded=0;
548  |        tr->error |= ERROR_U_MEM;
549  |        report_transaction(tr, log_ptr, object_name, "Cannot allocate memery");
550  |        transaction_free(tr);
551  |        object_free(obj);
552  |        return(-1);
553  |     }
554  |     /* Save the value */
555  |     tr->save=g_strdup(attr->value);
556  |   }
557  |                                                
558  | /* Process transaction. tr and obj are freed inside the process_* functions */
559  | 
560  |  if(IS_UPDATE(ud_stream->ud_mode))
561  |  /* We are in update mode */
562  |     result=process_updates(ud_stream, tr, object_name, operation);
563  |  else
564  |  /* We are in NRTM mode */   
565  |     result=process_nrtm(ud_stream, tr, object_name, operation);
566  |  
567  |  /* free attr if has been allocated */   
568  |  if(attr) attribute_free(attr, NULL);
569  |  
570  |  return(result);
571  | 
572  | }          
573  |           
574  | 
575  | /************************************************************
576  | *                                                           *
577  | * int UD_process_stream(UD_stream_t *ud_stream)             *
578  | *                                                           *
579  | * Processes the stream                                      *
580  | *                                                           *
581  | * ud_stream - pointer to UD_stream_t structure              *
582  | *                                                           *
583  | * Returns:                                                  *
584  | * in update mode (!standalone)(1 object processed):         *
585  | * 1 - no error                                              *
586  | * <0- errors                                                *
587  | *                                                           *
588  | * in NRTM & standalone modes                                *
589  | * total number of object processed                          *
590  | *                                                           *
591  | ************************************************************/
592  | 
593  | int UD_process_stream(UD_stream_t *ud_stream)
594  | {
595  |   char line_buff[STR_XXL], object_name[STR_XXL];
596  |   GString *g_line_buff; // needed to escape apostrophes
597  |   GSList *class_attr_list = NULL;
598  |   Attribute_t *class_attr;
599  |   Attribute_t *attr;
600  |   nic_handle_t *nh_ptr = NULL; /* To save  NIC handle structure */
601  |   Object_t *obj = NULL;
602  |   SQ_connection_t *sql_connection;
603  |   int start_object;
604  |   int a_type;
605  |   char *a_value;
606  |   char *ptr;
607  |   /* here we will store the parsed nic-hdl in required format */
608  |   char nic[MAX_NH_LENGTH];
609  |   struct _nrtm *nrtm;
610  |   Log_t *log_ptr= &(ud_stream->log);
611  |   time_t stime, ftime;
612  |   double obj_second1, obj_second10;
613  |   int result;
614  |   int operation=0;
615  |   int interrupt=0;
616  |   int do_update;
617  |   int default_ud_mode = ud_stream->ud_mode;
618  |   Line_Type_t linetype;
619  |   
620  |   nrtm=ud_stream->nrtm;
621  |   start_object = 1;
622  |   a_type=-1; 
623  | 
624  | 
625  |   /* Allocate line bufer */
626  |   if ((g_line_buff = g_string_sized_new(STR_XXL)) == NULL){ 
627  |     fprintf(stderr, "E: cannot allocate gstring\n"); 
628  |     return(-1); 
629  |   }
630  | 
631  |   /* Check connection to the database */
632  |   if(mysql_ping(ud_stream->db_connection)) {
633  |    fprintf(stderr, "D: ERROR: no SQL connection\n");
634  |    g_string_free(g_line_buff, TRUE);
635  |    return(-1);
636  |   }
637  |    
638  |   fprintf(stderr, "OK\n");
639  |   sql_connection=ud_stream->db_connection;
640  | 
641  | /* This is useful for loading DB from huge disk file. */
642  | /* We may start from <num_skip>th object */
643  | /*  num_skip=ud_stream->num_skip; */
644  | /*  if(num_skip>0) fprintf(stderr, "skipping %lu records\n", num_skip); */
645  | 
646  |   /* Start timer for statistics */
647  |   stime=time(NULL);
648  | 
649  |  /* Main loop. Reading input stream line by line */
650  |  /* Empty line signals to start processing an object, if we have it */ 
651  |   while (fgets(line_buff, STR_XXL, ud_stream->stream) != NULL) {
652  | 
653  |     switch (linetype=line_type(line_buff)) {
654  |       case LINE_PLUS:
655  |       case LINE_ATTRIBUTE:
656  |        if (start_object == 1) {
657  |           /* This is for loading stuff */
658  | /*    if(num_skip>0){ fprintf(stderr, "\r%10lu", num_skip); num_skip--; log_ptr->num_ok++; break; } */
659  |           obj = object_new(line_buff);
660  |        }
661  |       if (obj) {
662  |           g_string_sprintf(g_line_buff, "%s", line_buff);
663  |    /* escape apostrophes in the input line */
664  |           g_line_buff=escape_apostrophes(g_line_buff);
665  |    
666  |        if(start_object){
667  |    /* If this is the first attribute(==object name/type) */   
668  |           start_object=0;
669  |           strncpy(object_name, g_line_buff->str, g_line_buff->len-1);
670  |           *(object_name+g_line_buff->len-1)='\0';
671  |           fprintf(stderr, "D: object: [%s] ", object_name);
672  | 	  /* Create an attribute - the first one determines a class */
673  | 	  class_attr_list=NULL; /* Initialize the list that will hold splitted class attribute */
674  | 	  class_attr = attribute_new(g_line_buff->str);
675  | 	  if (class_attr == NULL) die; /* Should not happen */
676  | 	  if((class_attr->type==A_PN)||(class_attr->type==A_RO)){
677  | 		/* split names */  
678  | 		class_attr_list = split_attribute(class_attr_list, class_attr->type, class_attr->value);  
679  | 		attribute_free(class_attr, NULL);
680  |           } else {
681  | 		class_attr_list = g_slist_append(class_attr_list, class_attr); 
682  |           }
683  | 	  /* do nothing more with this attribute - we will prepend it at the end */
684  |        }
685  |        else {
686  | 	
687  | 	attr = attribute_new(g_line_buff->str);
688  | 	
689  |         if (attr) {
690  |           a_type=attr->type;   
691  | 	  a_value=attr->value;
692  |           if(a_type==A_NH) {
693  |           /*  Parse the string into nh structure */
694  |           /*  In case of an AUTO NIC handle check the ID in the database */
695  |           /* Possible errors leave to core processing */
696  |            if(NH_parse(attr->value, &nh_ptr) == 0) { 
697  | /*         fprintf(stderr, "D:parsing NIC: [%s]\n", attr->value); */
698  |            /* Check if we can allocate it */  
699  |             if((result = NH_check(nh_ptr, sql_connection))>0){
700  |             /* Convert nh to the database format */  
701  |               NH_convert(nic, nh_ptr);
702  | /*      fprintf(stderr, "D:NIC:[%s]\n", nic); */
703  |               /* Replace NIC handle in the string which is copied to the text object */
704  |               sprintf(line_buff, g_line_buff->str);
705  |               ptr = strstr(line_buff, attr->value);
706  |               /* compose new attribute string */
707  |               strcpy(ptr, nic);
708  |               g_string_sprintf(g_line_buff, line_buff);
709  |               g_string_sprintfa(g_line_buff, "\n");
710  |               /* Update the attribute */
711  |               attribute_upd(attr, attr->type, nic); 
712  | /*      fprintf(stderr, "D:attribute updated\n"); */
713  |             }
714  |            }
715  |           } /* NHR stuff */
716  |         }
717  | 	else {
718  | 	   if(linetype==LINE_PLUS)a_value=g_line_buff->str+1; /* skip '+' sign */ 
719  | 	   else a_value=g_line_buff->str;
720  | 	}
721  |         if (a_type>=0) { /* This indicates that the input line contains the value of the attribute */
722  | 	 switch	(a_type) {
723  |             /*these attributes may appear several on the line - split them*/   
724  |             case A_PN: /* person */
725  |             case A_RO: /* role */
726  |             case A_MR: /* mbrs-by-ref */
727  |             case A_MB: /* mnt-by */
728  |             case A_MO: /* member-of */
729  |             case A_SD: /* sub-dom */
730  |             case A_RZ: /* rev-srv */
731  |             case A_NS: /* nserver */
732  |                 obj->attributes = split_attribute(obj->attributes, a_type, a_value);
733  |                 if (attr) attribute_free(attr, NULL);
734  |              attr=NULL;
735  |              break; 
736  |             default: break;  
737  |          }
738  | /*       g_string_sprintfa(obj->object, "%s", g_line_buff->str); */
739  |          if(attr)obj->attributes = g_slist_append(obj->attributes, attr);  
740  |         }
741  |        } /* if not start_object (not the first/class attribute) */
742  |         /* copy the line into object no matter whether it is an attribute or not (continualtion, etc.) */
743  |         g_string_sprintfa(obj->object, "%s", g_line_buff->str);
744  |       }/* if (obj) */
745  |       break;
746  | 
747  |       case LINE_COMMENT:
748  |       break;
749  | 
750  |       case LINE_EOF:
751  |       break;
752  |       
753  |       case LINE_ADD:
754  |       /* restore the default operation mode */
755  |        operation=OP_ADD;
756  |        ud_stream->ud_mode=default_ud_mode;
757  |       break;
758  |       
759  |       case LINE_OVERRIDE_ADD:
760  |       /* for override - switch the dummy bit on */
761  |        operation=OP_ADD;
762  |        ud_stream->ud_mode=default_ud_mode|B_DUMMY;
763  |       break;
764  |       
765  |       case LINE_UPD:
766  |       /* restore the default operation mode */
767  |        operation=OP_UPD;
768  |        ud_stream->ud_mode=default_ud_mode;
769  |       break;  
770  | 
771  |       case LINE_OVERRIDE_UPD:
772  |       /* for override - switch the dummy bit on */
773  |        operation=OP_UPD;
774  |        ud_stream->ud_mode=default_ud_mode|B_DUMMY;
775  |       break;
776  |       
777  |       case LINE_DEL:
778  |       /* restore the default operation mode */
779  |        operation=OP_DEL;
780  |        ud_stream->ud_mode=default_ud_mode;
781  |       break; 
782  | 
783  |       case LINE_OVERRIDE_DEL:
784  |       /* for override - switch the dummy bit on */
785  |        operation=OP_DEL;
786  |        ud_stream->ud_mode=default_ud_mode|B_DUMMY;
787  |       break;
788  |  
789  |       case LINE_EMPTY:
790  |        /* Indicate that we have complete object, so a new one will be collected later */ 
791  |         start_object=1;
792  | 	a_type=-1;
793  |        /* start processing the object */ 
794  |         if (obj == NULL) break;  /* may be the previous lines were just garbage*/
795  | 	/* reorder some attributes */
796  |         obj->attributes = g_slist_sort(obj->attributes, reorder_attributes);
797  | 	/* prepend the class attribute */
798  | 	obj->attributes = g_slist_concat(class_attr_list, obj->attributes);
799  | 	/* forget the location */
800  | 	class_attr=NULL;
801  | 
802  | 	/* XXX */
803  | 	/*  print_object(obj); */
804  | 
805  |   /* start new transaction now */ 
806  |         result=process_transaction(ud_stream, obj, object_name, nh_ptr, operation);
807  |           
808  |         /* process_transaction() frees tr and obj structures, */
809  |         /* so make sure we'll not reference these objects in the future */
810  |         obj=NULL; operation=OP_NOOP; nh_ptr=NULL;
811  |         ud_stream->ud_mode=default_ud_mode;
812  |           
813  |         /* this is a good place for quick interrupt */
814  |         do_update=CO_get_do_update();
815  |         if (do_update) interrupt=0; else interrupt=1;
816  |         /* we still need to exit in update server mode (only 1 object at a time */ 
817  |         if (IS_UPDATE(ud_stream->ud_mode) && (!IS_STANDALONE(ud_stream->ud_mode))) interrupt=1;
818  | 
819  |       break;
820  | 
821  |       default:
822  |         fprintf(stderr, "ERROR: Bad line type\n");
823  |     } /* switch */
824  |     
825  |     /* Finish processing if interrupt has been set */
826  |     if (interrupt) break;
827  |   } /* while */
828  |  
829  |  /* Some postprocessing */
830  |   if(!IS_UPDATE(ud_stream->ud_mode)){
831  |   /* We are in NRTM mode */
832  |   /* Clean up */
833  |    fclose(ud_stream->stream);
834  |   /* In NRTM mode there may be a saved object that is unprocessed */   
835  |    if(nrtm->tr){ /*saved backlog?*/
836  |     object_process(nrtm->tr); /* delete the previous(saved) object*/
837  |     result=report_transaction(nrtm->tr, &(ud_stream->log), nrtm->object_name, 
838  |                               "NRTM:DEL:While deleting previous(saved) object");
839  |     /* create DEL serial record no matter what the result is */
840  |     create_serial(nrtm->tr);
841  |     object_free(nrtm->tr->object);
842  |     transaction_free(nrtm->tr); nrtm->tr=NULL;
843  |    } 
844  |   }
845  | 
846  |  /* That's all. Free GString */
847  |   g_string_free(g_line_buff, TRUE);
848  |                                                                                                        
849  |  /* Calculate some statistics */
850  |   ftime=time(NULL);
851  |   obj_second1 = (float)(log_ptr->num_ok)/(ftime-stime);
852  |   obj_second10 = (float)(log_ptr->num_ok+log_ptr->num_failed)/(ftime-stime);
853  |   
854  |   /* Print the report */
855  |   if(IS_STANDALONE(ud_stream->ud_mode) || (!IS_UPDATE(ud_stream->ud_mode))) {
856  | /*   printf("\n\n******** report **********\n%d objects OK\n%d objects failed\n", 
857  |             log_ptr->num_ok, log_ptr->num_failed); */
858  |    fprintf(log_ptr->logfile,"\n******** report **********\n");
859  |    fprintf(log_ptr->logfile," %d objects OK (%5.2f obj/s)\n", log_ptr->num_ok, obj_second1);
860  |    fprintf(log_ptr->logfile," %d objects failed\n", log_ptr->num_failed);
861  |    fprintf(log_ptr->logfile," average processing time %5.2f obj/s (%5.2f obj/min)\n", 
862  |                           obj_second10, obj_second10*60);
863  |    result=log_ptr->num_ok+log_ptr->num_failed;
864  |   }
865  |   return(result);
866  | 
867  | } /* UD_process_stream */
868  |