Statistics
| Branch: | Revision:

napa-baselibs / tests / nvtest1 / nvtest1.c @ 507372bb

History | View | Annotate | Download (18.2 KB)

1
/*
2
 * Compile with:
3
 * gcc  -o test test.c chunk.c -levent
4
 */
5
#include "chunk.h"
6

    
7
#ifndef DISABLE_ML
8
#include "ml.h"
9
void receive_outconn_cb (int connectionID, void *arg);
10
#endif
11

    
12
#define URLPARTLEN 50
13
#define HTTP_PREFIX "http://"
14
#define DEFAULT_CHUNK_PATH "/NapaTest/chunk"
15

    
16

    
17
struct event_base *evb;
18

    
19
/** invoked when HTTP client session is closed
20
 *  this is superfluous right now, may be removed 
21
 */ 
22
void  http_close_cb(struct evhttp_connection *conn, void *arg) {
23
      ev_uint16_t port;
24
      char* namebuf;
25
      evhttp_connection_get_peer(conn, &namebuf, &port);
26
fprintf(stderr, "HTTP Request complete: %s : %d\n", namebuf, port);
27

    
28
}
29

    
30
/** invoked when HTTP client session is answered
31
 *  apart from debugging, this is superfluous right now and may be removed 
32
 */ 
33
void http_request_cb(struct evhttp_request *req, void *arg) {
34
      struct evbuffer *inb = evhttp_request_get_input_buffer(req);
35
      fprintf(stderr, "HTTP Request (%s) callback %d, %s\n",
36
                      req->kind == EVHTTP_REQUEST ? "REQ" : "RESP",
37
                      req->response_code, req->response_code_line);
38
      char *cc;
39
      while((cc = evbuffer_readln(inb, NULL, EVBUFFER_EOL_ANY)) != NULL) {
40
        printf("Line X: %s!\n",cc);
41
      }
42

    
43
}
44

    
45
void chunk_to_http_post_callback(struct chunk *chunk, void *arg) {
46
        struct evhttp_connection *htc = (struct evhttp_connection *)arg;
47
    struct evhttp_request *reqobj = evhttp_request_new (http_request_cb, "VAVAVAV");
48
        char uri[50], nbuf[10];
49

    
50
        printf("HTTP EJECT %d\n", chunk->len);
51
        evbuffer_add(reqobj->output_buffer,chunk->buf, chunk->len);
52

    
53
    reqobj->kind = EVHTTP_REQUEST;
54
          {
55
                        char nbuf[50];
56
                        char *p;
57
                        uint16_t port;
58
                        evhttp_connection_get_peer(htc,&p,&port);
59
                        sprintf(nbuf,"%s:%d",p, port);
60

    
61
                        evhttp_add_header(reqobj->output_headers,"Host",nbuf);
62
                        printf("Chunk transmission: %d %d %s\n", chunk->len, chunk->chunk_num, nbuf);
63
          }
64
        sprintf(nbuf,"%d", chunk->len);
65
        evhttp_add_header(reqobj->output_headers,"Content-Length",nbuf);
66
        sprintf(uri,"%s/%d", DEFAULT_CHUNK_PATH, chunk->chunk_num);
67

    
68
    evhttp_make_request(htc, reqobj, EVHTTP_REQ_POST, DEFAULT_CHUNK_PATH);
69
}
70

    
71
/**
72
        * break up uri string to hostname, port and path-prefix 
73
        * valid formats is [[<address>]:]<port>[/[<path-prefix>]]
74
        * optional address and path-prefix fields, if not detected, are not assigned a value
75
        * output parameters may be NULL, meaning no assignment requested
76
        *
77
        * @param url the original string
78
        * @param host a buffer to receive the host part (URIPARTLEN bytes minimum). NULL means ignore.
79
        * @param port this will receive the port part. NULL means ignore.
80
        * @param path a buffer to receive the path part (URIPARTLEN bytes minimum). NULL means ignore.
81
        * @return the number of fields detected: 1: port only 2: hostname + port, 3 all fields.
82
*/
83

    
84

    
85
int     breakup_url(const char *url, char *host, int *port, char *path) {
86
                        char hostpart[URLPARTLEN + 1], pathpart[URLPARTLEN + 1];
87
                        int portpart = -1;
88
                        if(!strncmp(url, "http://", 7)) url += 7;
89

    
90
                        int fields = sscanf(url,"%50[^:]:%d/%50s", hostpart, &portpart, pathpart);
91
                        if(fields < 2) {
92
                                fields = sscanf(url + (*url == ':'),"%d",&portpart);
93
                        }
94
                        if( portpart <= 0 || portpart > 65535) return 0; 
95
                        switch(fields) {
96
                                        break;
97
                                case 3:
98
                                        if(path != NULL) { *path = '/'; strcpy(path+1, pathpart); }
99
                                case 2:
100
                                        if(host != NULL) strcpy(host, hostpart);
101
                                case 1:
102
                                        if(port != NULL) *port = portpart;
103
                                        break;
104
                                default: return 0;
105
                        }
106
                        return fields;
107
}
108

    
109

    
110
/**
111
* Setup a HTTP ejector, a component that will transmit a delayed replay of chunks via HTTP post
112
*
113
 * @param url   the <hostname>:<port>[/<pathprefix>] format URL. (<pathprefix> is currently unused)
114
 * @param delay requested delay in sec relative to the original timestamps.
115
 * @return -1 if error (i.e. params failed), 0 otherwise
116
*/
117

    
118
int        setup_http_ejector(const char const *url, double delay) {
119
                char host[URLPARTLEN+1]; 
120
                int port; 
121
                if(breakup_url(url, host, &port, NULL) < 2) return -1;
122

    
123

    
124

    
125
                printf("Registering HTTP ejector toward %s:%d  (delay: %f)\n", host, port, delay);
126
        struct evhttp_connection *htc = evhttp_connection_base_new(evb, host, port);
127
        evhttp_connection_set_closecb(htc, http_close_cb, "DUMMY");
128
// TODO: THIS SEEMS LOGICAL, BUT CAUSES COREDUMP
129
//                evhttp_connection_set_timeout(htc,1);
130
                chbuf_add_listener((int)(delay*1000000), chunk_to_http_post_callback, htc);
131
                return 0;
132
}
133

    
134

    
135
/**
136
* send a test event of 50 bytes to the specified destination
137
*
138
* @param addr: address or hostname
139
* @param port: port number
140
* @param addr: url prefix (/1111) is appended in request
141
* 
142
*/
143
void send_test_http_request(const char *addr, int port, char *pprefix) {
144
                fprintf(stdout, "Sending HTTP request to %s:%d\n", addr, port);
145
                  int res;
146
                  struct evhttp_connection *htc = evhttp_connection_base_new(evb, addr, port);
147
                  evhttp_connection_set_closecb(htc, http_close_cb, "ABCABCT");
148
                  struct evhttp_request *reqobj = evhttp_request_new (http_request_cb, "VAVAVAV");
149
                  reqobj->kind = EVHTTP_REQUEST;
150

    
151
                                  evbuffer_add(reqobj->output_buffer,"aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa ",50);
152

    
153
                                  {
154
                                                char nbuf[50];
155
                                                char *p;
156
                                                uint16_t port;
157
                                                evhttp_connection_get_peer(htc,&p,&port);
158
                                                sprintf(nbuf,"%s:%d",p, port);
159

    
160
                                                evhttp_add_header(reqobj->output_headers,"Host",nbuf);
161
                                  }
162
                  evhttp_add_header(reqobj->output_headers,"Content-Length","50");
163
              //    evbuffer_add(reqobj->output_buffer, "\n\n",2);
164

    
165
                                  char path[100];
166
                                  sprintf(path, "%s/1111", pprefix);
167
                  evhttp_make_request(htc, reqobj, EVHTTP_REQ_POST, path);
168
}
169

    
170
        
171
static double parse_double_param(const char *param, double def_val) {
172
                                        double ret = def_val;
173
                                        if(param != NULL) {
174
                                                ret = atof(param);
175
                                                if(ret == 0 && strncmp("0.00000000", param, strlen(param))) {
176
                                                        ret = def_val;
177
                                                }
178
                                        }
179
                                        return ret;
180
}
181

    
182
#include <sys/queue.h>
183
#include <event2/event_struct.h>
184

    
185
/**
186
 * Callback for http injector
187
 *
188
 * @param req contains info about the request
189
 * @param contains the path prefix
190
 */
191
static void http_receive(struct evhttp_request *req, void *dummy_arg) {
192
                char *path_pat = (char *)dummy_arg;
193
        char *path = req->uri;
194
fprintf(stdout, "Http request received: %s %d\n", req->uri, req->type);
195
        if(!strncmp(path, HTTP_PREFIX , strlen(HTTP_PREFIX))) {  // skip "http://host:port" part if present
196
                          path = strchr(path + strlen(HTTP_PREFIX),'/');
197
        }
198
                // TODO is it good?
199
                struct evkeyvalq params;
200
                evhttp_parse_query(req->uri, &params);
201
#if(1)
202
                {
203
                struct evkeyval *header;
204
        TAILQ_FOREACH(header,  &params, next) {
205
                        printf("Param %s : %s\n", header->key, header->value);
206

    
207
        }
208
                }
209
#endif
210
        if(req->type == EVHTTP_REQ_POST || req->type == EVHTTP_REQ_PUT) {
211

    
212
            if(!strncmp(path, path_pat , strlen(path_pat))) {
213
                                struct timeval tv = {0,0};
214
                     int chunk_num;
215
                     int slashed;
216
                     path += strlen(path_pat);
217
                     if(slashed = (*path == '/'))  path++;
218
                     if(*path == '\0') {
219
                         chunk_num = chbuf_get_current() + 1;
220
                     }
221
                     else {
222
                                                 int p;
223
                                                 if(sscanf(path, "%u%n",&chunk_num, &p) != 1) { 
224
bad_url:
225
                                 evhttp_send_reply(req,400,"BAD URI",NULL);
226
                                                                 fprintf(stdout,"Bad URL extension in [%s]. Required format: %s/<nnn>.\n", req->uri, path_pat);
227

    
228
                                 goto finally;
229
                         }
230
                                                 if(p < strlen(path)) {
231
                                                         if(path[p] == '?') {
232
                                                                 const char *time = evhttp_find_header(&params,"timestamp");
233
                                                                 if(time != NULL) {
234
                                                                         if(isdigit(time[0])) {
235
                                                                         double t = parse_double_param(time, 0);
236
                                                                         if(t > 0.0) {
237
                                                                                 tv.tv_sec = (unsigned int)t;
238
                                                                                 tv.tv_usec = (unsigned int)(1000000.0 * (t - tv.tv_sec));
239
                                                                         }
240
                                                                         }
241
                                                                         else {
242
                                                                                 struct tm tmm;
243
                                                                                 strptime(time, "%a, %y %b %d %T GMT", &tmm);
244
                                                                                 time_t ltime = mktime(&tmm);
245
                                                                                 if(ltime > 0) tv.tv_sec = ltime;
246
                                                                         }
247
                                                                 }
248
                                                         }
249
                                                         else goto bad_url;
250
                                                 }
251
                     }
252
                                         fprintf(stdout, "Contains data: %d bytes\n", evbuffer_get_length(req->input_buffer));
253

    
254
                                         if(tv.tv_sec > 0) {
255
                                                 char buf[100];
256
                                                 strftime(buf, 100, "%a, %y %b %d %T GMT", gmtime(&tv.tv_sec));
257
                                                 printf("Request contains time: %ld.%ld -> %s\n", tv.tv_sec, tv.tv_usec, buf);
258
                                         }
259
                                         chbuf_enqueue_from_evbuf(req->input_buffer, chunk_num, tv.tv_sec > 0 ? &tv : NULL);
260
                     evhttp_send_reply(req,200,"OK",NULL);
261
                                         goto finally;
262
                }
263
        }
264
        if(req->type == EVHTTP_REQ_POST || req->type == EVHTTP_REQ_GET) {
265
#define REGFORM_FILE_NAME "register-receptor.html"
266
                        char *register_path = "/register-receptor";
267
            if(!strncmp(path, register_path , strlen(register_path))) {
268
                                const char *url = evhttp_find_header(&params,"url");
269
                                if(url != NULL) {
270
                                        double delay = parse_double_param(evhttp_find_header(&params,"delay"), 5.0);
271
                                        double validity = parse_double_param(evhttp_find_header(&params,"validity"), 300);
272
                                        if(setup_http_ejector(url,delay)) {
273
                                                printf("Could not start up ejector");
274
                                                evhttp_send_reply(req,305,"REQUEST PROCESSING FAILED",NULL);
275
                                                goto finally;
276
                                        }
277
                                        evbuffer_add_printf(req->output_buffer,"<html><head><title>NAPA - Registration</title></head>"
278
                                                "<body><h3>Successfully registered to %s (delay: %f)</h3></body></html>", url, delay);
279
                                }
280
                                else {
281
                                        int fd = open(REGFORM_FILE_NAME,O_RDONLY);
282
                                        if(fd > 0) evbuffer_add_file(req->output_buffer, fd, 0, 10000);
283
                                        else evbuffer_add_printf(req->output_buffer,"<html><head><title>NAPA - Error</title></head>"
284
                                                "<body><h3>Error!! Cannot load file %s</h3></body></html>", REGFORM_FILE_NAME);
285
                                }
286
                                evhttp_send_reply(req,200,"OK",NULL);
287
                                goto finally;
288
                        }
289
                }
290
                fprintf(stdout,"Unknown HTTP request");
291
        evhttp_send_reply(req,404,"NOT FOUND",NULL);
292

    
293
finally:
294
                evhttp_clear_headers(&params);
295
}
296

    
297

    
298
/**
299
 *  Setup a HTTP injector, a component that will transmit a delayed replay of chunks via HTTP post
300
 *  @todo TODO: return error if fisten failed
301
 * @param url   in <hostname>:<port>/<pathprefix> or <hostname>:<port> or <port> format (default for hostname is 0.0.0.0 and default for pathprefix is "/NapaTest/chunk"
302
 * @return -1 if error (url format failed) 0 otherwise
303

304
*/
305

    
306
int        setup_http_injector(const char const *url) {
307
                char host[URLPARTLEN+1] = "0.0.0.0";
308
                char *path = malloc(URLPARTLEN+1);
309
                int port;
310

    
311
                sprintf(path, DEFAULT_CHUNK_PATH);
312
                if(breakup_url(url, host, &port, path) < 1) return -1;
313

    
314
                printf("Starting HTTP injector listening on %s:%d for uri prefix is %s\n", host, port, path);
315

    
316
                struct evhttp* evh = evhttp_new(evb);
317
                evhttp_bind_socket(evh,host,port);
318
                evhttp_set_gencb(evh, http_receive, path);
319
                return 0;
320
}
321

    
322

    
323
/**
324
 * Keyboard command listening loop
325
 *
326
 * Commands available:
327
 *        i [<address>:]<port>[/<prefix>] : start a HTTP injector
328
 *        e <address>:<port>[/<prefix>] : start a HTTP ejector
329
 *         s <address>:<port>/<prefix> : send a test message
330
 *        q : quit
331
 *        h : help
332
 *
333
 * @param fd this is normally the stdin
334
 * @param unused_event is unused
335
 * @param contains the event
336
 */
337

    
338
void
339
stdin_read(int fd, short unused_event, void *arg)
340
{
341
        char buf[255];
342
        int len;
343
        struct event *ev = arg;
344

    
345

    
346
        len = read(fd, buf, sizeof(buf) - 1);
347

    
348
        if (len == -1) {
349
                perror("read");
350
                return;
351
        } else if (len == 0) {
352
                fprintf(stderr, "Connection closed\n");
353
                event_del(ev);
354

    
355
                return;
356
        }
357

    
358
        buf[len] = '\0';
359
        if(buf[0] == 'i') {
360
                char url[100];
361
                sprintf(url,"localhost:9955");
362
                sscanf(buf+1," %s",url);
363
                if(setup_http_injector(url)) printf("Could not start up injector");
364
        }
365
        else if(buf[0] == 'e') {
366
                double delay = 5.0;
367
                char url[100];
368
                sprintf(url,"localhost:9955");
369
                sscanf(buf+1," %s %lf",url, &delay);
370
                if(setup_http_ejector(url,delay)) printf("Could not start up ejector");
371
        }
372
        else if(buf[0] == 's') {
373
                char host[URLPARTLEN+1] = "0.0.0.0";
374
                char path[URLPARTLEN+1] = DEFAULT_CHUNK_PATH;
375
                int port;
376
                if(breakup_url(buf+2, host, &port, path) < 2) printf("s command requires a url: <host>:<port>[/<pathprefix>] !\n");
377
                else send_test_http_request(host, port, path);
378
        }
379
#ifndef DISABLE_ML
380
        else if(buf[0] == 'c') {
381
                  socketID_handle remote_socketID = malloc(SOCKETID_SIZE);
382
                  mlStringToSocketID(buf+2, remote_socketID);
383
                        send_params sParams;
384
                  if(mlOpenConnection(remote_socketID,&receive_outconn_cb,"DUMMY ARG",sParams) != 0) {
385
                    printf("ML open_connection failed\n");
386
                  }
387
        }
388
#endif
389
        else if(buf[0] == 'q') {
390
                fprintf(stdout, "Bye!\n", buf);
391
                exit(-1);
392
//                event_del(ev);
393
        }
394
        else if(buf[0] == 'h') {
395
                printf("Commands:\n");
396
                printf(" q: quit\n");
397
                printf(" i [<address>:]<port>[/<prefix>] : start a HTTP injector \n");
398
                printf(" e <address>:<port>[/<prefix>] [<delay>]: start a HTTP ejector. Delay is in seconds, default=5.0 .\n");
399
                printf(" s <address>:<port>/<prefix> : send a test message \n");
400
                printf(" c <remote_socket_id>        : connect to peer and forward all chunks to it\n");
401
                printf(" h : help\n");
402
        }
403
        else fprintf(stdout, "Unknown command: %s, use 'h' for help\n", buf);
404
}
405

    
406

    
407

    
408

    
409
/** ********************** MESSAGING LAYER OPERATIONS ************************ */
410

    
411
#ifndef DISABLE_ML
412

    
413
void receive_local_socketID_cb(socketID_handle local_socketID,int errorstatus){
414

    
415
  char buf[100];
416
  mlSocketIDToString(local_socketID,buf, 100);
417

    
418
    printf("--Monitoring module: received a local ID <%s>\n", buf);
419

    
420
}
421

    
422
static connID;
423
#define CHUNK_MSG 15
424

    
425
void chunk_transmit_callback(struct chunk *chunk, void *arg) {
426
        printf("Sending chunk with data of length: %d\n", chunk->len);
427
        mlSendData((int)arg,(char *)chunk,sizeof(struct chunk) + chunk->len,CHUNK_MSG,NULL);
428
}
429

    
430

    
431
/**
432
  * A callback function that tells a connection has been established.
433
  * @param connectionID The connection ID
434
  * @param *arg An argument for data about the connection
435
  */
436
void receive_outconn_cb (int connectionID, void *arg){
437

    
438
      printf("ML: Successfully set up outgoing connection connectionID %d  \n",connectionID );
439

    
440
      /// transmit data to peer 2
441
      int size = 10000;
442
      char buffer[size];
443
      memset(buffer,'a',size);
444
      unsigned char msgtype = 12;
445
      //strcpy(buffer, "Message for peer 2 \n");
446
  
447
      mlSendData(connID,buffer,size,msgtype,NULL);
448

    
449
             chbuf_add_listener(1000000, chunk_transmit_callback, (void *)connID);
450

    
451
}
452

    
453

    
454
/**
455
  * A callback function that tells a connection has been established. 
456
  * @param connectionID The connection ID
457
  * @param *arg An argument for data about the connection 
458
  */
459
void receive_conn_cb (int connectionID, void *arg){
460

    
461
  printf("ML: Received incoming connection connectionID %d  \n",connectionID );
462
  connID=connectionID;
463
} 
464

    
465
/**
466
  * A funtion that prints a connection establishment has failed
467
  * @param connectionID The connection ID
468
  * @param *arg An argument for data about the connection
469
  */
470
void conn_fail_cb(int connectionID,void *arg){
471

    
472
  printf("--echoServer: ConnectionID %d  could not be established \n ",connectionID);
473

    
474
}
475

    
476
/**
477
  * The peer receives data per callback from the messaging layer. 
478
  * @param *buffer A pointer to the buffer
479
  * @param buflen The length of the buffer
480
  * @param msgtype The message type 
481
  * @param *arg An argument that receives metadata about the received data
482
  */
483
void recv_data_from_peer_cb(char *buffer,int buflen,unsigned char msgtype,void *arg){
484

    
485
  printf("--echoServer: Received data from callback: \n\t \t msgtype %d \t buflen %d \n",msgtype,buflen ); 
486

    
487
}
488

    
489
void recv_chunk_from_peer_cb(char *buffer,int buflen,unsigned char msgtype,void *arg){
490
  printf("--echoServer: Received chunk from callback: \n\t \t msgtype %d \t buflen %d \n",msgtype,buflen ); 
491
  if(buflen != ((struct chunk *)buffer)->len + sizeof(struct chunk)) {
492
          printf("ERROR!!! received inconsistent chunk with bad length: %d <--> %d \n",((struct chunk *)buffer)->len,buflen ); 
493
  }
494
  chbuf_enqueue_chunk((struct chunk *)buffer);
495
}
496
#endif
497

    
498
/**
499
 * Main program for simple transmission tests.
500
 *
501
 * Sets up a HTTP injector based on the -i option (<address>:<port>, but typically 0.0.0.0:<port>)
502
 * sets up a HTTP ejector based on the argument s to the -e option. Args: <address>:<port>[/<prefix>] <delay_in_usec>
503
 * by default, no sender or receiver is set up
504
 *
505
 * @param argc cmdline arguments count (includes program name)
506
 * @param argv cmdline arguments (argv[0] is the program name)
507
 * @return command execution return value
508
 */
509

    
510
int main (int argc, char **argv) {
511
                evb = event_base_new();
512

    
513

    
514
                /* Initalize stdin event loop */
515
                {
516
                        struct event *evfifo;
517
                        evfifo = event_new(evb,0, EV_READ | EV_PERSIST, stdin_read, &evfifo);
518

    
519
                /* Add it to the active events, without a timeout */
520
                        event_add(evfifo, NULL);
521
                        fprintf(stdout, "Console Command Interpreter. Enter 'h' for help!\n");
522

    
523
                }
524

    
525
                int argc_pos = 1;
526
                while(argc > argc_pos) {
527
                        if(argc >= argc_pos + 2 && !strcmp(argv[argc_pos],"-i")) {
528
                        /* Initialize HTTP listener */
529
                                char host[50];
530
                                int port;
531

    
532
                                breakup_url(argv[argc_pos +1], host, &port, NULL);
533
                                setup_http_injector(argv[argc_pos+1]);
534

    
535
                                argc_pos += 2;
536
                        }
537

    
538
                        else if(argc >= argc_pos + 3  && !strcmp(argv[argc_pos],"-e")) {
539
                                double delay = 5.0;
540
                                sscanf(argv[argc_pos+2],"%lf",&delay);
541
                                setup_http_ejector(argv[argc_pos+1],delay);
542
                                argc_pos += 3;
543
                        }
544
#ifndef DISABLE_ML
545
                        else if(argc >= argc_pos + 2  && !strcmp(argv[argc_pos],"-m")) {
546
                                struct timeval timeout_value = { 3, 0 };
547

    
548
                                mlInit(true,timeout_value,atoi(argv[argc_pos+1]),strdup(mlAutodetectIPAddress()),3478,"stun.ideasip.com",&receive_local_socketID_cb,evb);
549
                            mlRegisterRecvConnectionCb(&receive_conn_cb);
550
                mlRegisterErrorConnectionCb(&conn_fail_cb);
551
                            mlRegisterRecvDataCb(&recv_data_from_peer_cb,12);
552
                            mlRegisterRecvDataCb(&recv_chunk_from_peer_cb,15);
553
                            argc_pos += 2;
554
                        }
555
#endif
556
                        else {
557
                                printf("Usage: %s [-i <listen-url> ] [-m <port>] [ -e <transmit-url> [<transmit-delay>] ]", argv[0]);
558
                                printf("        -m: startup Messaging Layer on given UDP port\n");
559
                                printf("        -i: startup a HTTP injector\n");
560
                                printf("        -e: startup a HTTP ejector with delay as specified. Delay is in seconds, default 5.0 .\n");
561
                                exit(-1);
562
                        }
563
                }
564

    
565
                char buf[100];
566
                time_t ppp;
567
                time(&ppp);
568
                strftime(buf, 100, "%a, %y %b %d %T GMT", gmtime(&ppp));
569
                printf("Time now: (%s)\n",buf);
570

    
571
                event_base_dispatch(evb);
572
                return (0);
573
}
574