00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00036 #include "ucontact.h"
00037 #include <string.h>
00038 #include "../../mem/shm_mem.h"
00039 #include "../../ut.h"
00040 #include "../../ip_addr.h"
00041 #include "../../socket_info.h"
00042 #include "../../dprint.h"
00043 #include "../../lib/srdb1/db.h"
00044 #include "p_usrloc_mod.h"
00045 #include "../usrloc/ul_callback.h"
00046 #include "urecord.h"
00047 #include "ucontact.h"
00048 #include "ul_db_layer.h"
00049 #include "dlist.h"
00050
00059 ucontact_t* new_ucontact(str* _dom, str* _aor, str* _contact, ucontact_info_t* _ci)
00060 {
00061 ucontact_t *c;
00062
00063 c = (ucontact_t*)shm_malloc(sizeof(ucontact_t));
00064 if (!c) {
00065 LM_ERR("no more shm memory\n");
00066 return 0;
00067 }
00068 memset(c, 0, sizeof(ucontact_t));
00069
00070 if (shm_str_dup( &c->c, _contact) < 0) goto error;
00071 if (shm_str_dup( &c->callid, _ci->callid) < 0) goto error;
00072 if (shm_str_dup( &c->user_agent, _ci->user_agent) < 0) goto error;
00073
00074 if (_ci->received.s && _ci->received.len) {
00075 if (shm_str_dup( &c->received, &_ci->received) < 0) goto error;
00076 }
00077 if (_ci->path && _ci->path->len) {
00078 if (shm_str_dup( &c->path, _ci->path) < 0) goto error;
00079 }
00080
00081 c->domain = _dom;
00082 c->aor = _aor;
00083 c->expires = _ci->expires;
00084 c->q = _ci->q;
00085 c->sock = _ci->sock;
00086 c->cseq = _ci->cseq;
00087 c->state = CS_NEW;
00088 c->flags = _ci->flags;
00089 c->cflags = _ci->cflags;
00090 c->methods = _ci->methods;
00091 c->last_modified = _ci->last_modified;
00092
00093 return c;
00094 error:
00095 LM_ERR("no more shm memory\n");
00096 if (c->path.s) shm_free(c->path.s);
00097 if (c->received.s) shm_free(c->received.s);
00098 if (c->user_agent.s) shm_free(c->user_agent.s);
00099 if (c->callid.s) shm_free(c->callid.s);
00100 if (c->c.s) shm_free(c->c.s);
00101 shm_free(c);
00102 return 0;
00103 }
00104
00105
00106
00111 void free_ucontact(ucontact_t* _c)
00112 {
00113 if (!_c) return;
00114 if (_c->path.s) shm_free(_c->path.s);
00115 if (_c->received.s) shm_free(_c->received.s);
00116 if (_c->user_agent.s) shm_free(_c->user_agent.s);
00117 if (_c->callid.s) shm_free(_c->callid.s);
00118 if (_c->c.s) shm_free(_c->c.s);
00119 shm_free( _c );
00120 }
00121
00122
00128 void print_ucontact(FILE* _f, ucontact_t* _c)
00129 {
00130 time_t t = time(0);
00131 char* st;
00132
00133 switch(_c->state) {
00134 case CS_NEW: st = "CS_NEW"; break;
00135 case CS_SYNC: st = "CS_SYNC"; break;
00136 case CS_DIRTY: st = "CS_DIRTY"; break;
00137 default: st = "CS_UNKNOWN"; break;
00138 }
00139
00140 fprintf(_f, "~~~Contact(%p)~~~\n", _c);
00141 fprintf(_f, "domain : '%.*s'\n", _c->domain->len, ZSW(_c->domain->s));
00142 fprintf(_f, "aor : '%.*s'\n", _c->aor->len, ZSW(_c->aor->s));
00143 fprintf(_f, "Contact : '%.*s'\n", _c->c.len, ZSW(_c->c.s));
00144 fprintf(_f, "Expires : ");
00145 if (_c->expires == 0) {
00146 fprintf(_f, "Permanent\n");
00147 } else if (_c->expires == UL_EXPIRED_TIME) {
00148 fprintf(_f, "Deleted\n");
00149 } else if (t > _c->expires) {
00150 fprintf(_f, "Expired\n");
00151 } else {
00152 fprintf(_f, "%u\n", (unsigned int)(_c->expires - t));
00153 }
00154 fprintf(_f, "q : %s\n", q2str(_c->q, 0));
00155 fprintf(_f, "Call-ID : '%.*s'\n", _c->callid.len, ZSW(_c->callid.s));
00156 fprintf(_f, "CSeq : %d\n", _c->cseq);
00157 fprintf(_f, "User-Agent: '%.*s'\n",
00158 _c->user_agent.len, ZSW(_c->user_agent.s));
00159 fprintf(_f, "received : '%.*s'\n",
00160 _c->received.len, ZSW(_c->received.s));
00161 fprintf(_f, "Path : '%.*s'\n",
00162 _c->path.len, ZSW(_c->path.s));
00163 fprintf(_f, "State : %s\n", st);
00164 fprintf(_f, "Flags : %u\n", _c->flags);
00165 if (_c->sock) {
00166 fprintf(_f, "Sock : %.*s (%p)\n",
00167 _c->sock->sock_str.len,_c->sock->sock_str.s,_c->sock);
00168 } else {
00169 fprintf(_f, "Sock : none (null)\n");
00170 }
00171 fprintf(_f, "Methods : %u\n", _c->methods);
00172 fprintf(_f, "next : %p\n", _c->next);
00173 fprintf(_f, "prev : %p\n", _c->prev);
00174 fprintf(_f, "~~~/Contact~~~~\n");
00175 }
00176
00177
00184 int mem_update_ucontact(ucontact_t* _c, ucontact_info_t* _ci)
00185 {
00186 #define update_str(_old,_new) \
00187 do{\
00188 if ((_old)->len < (_new)->len) { \
00189 ptr = (char*)shm_malloc((_new)->len); \
00190 if (ptr == 0) { \
00191 LM_ERR("no more shm memory\n"); \
00192 return -1; \
00193 }\
00194 memcpy(ptr, (_new)->s, (_new)->len);\
00195 if ((_old)->s) shm_free((_old)->s);\
00196 (_old)->s = ptr;\
00197 } else {\
00198 memcpy((_old)->s, (_new)->s, (_new)->len);\
00199 }\
00200 (_old)->len = (_new)->len;\
00201 } while(0)
00202
00203 char* ptr;
00204
00205
00206
00207
00208 update_str( &_c->user_agent, _ci->user_agent);
00209
00210 if (_ci->received.s && _ci->received.len) {
00211 update_str( &_c->received, &_ci->received);
00212 } else {
00213 if (_c->received.s) shm_free(_c->received.s);
00214 _c->received.s = 0;
00215 _c->received.len = 0;
00216 }
00217
00218 if (_ci->path) {
00219 update_str( &_c->path, _ci->path);
00220 } else {
00221 if (_c->path.s) shm_free(_c->path.s);
00222 _c->path.s = 0;
00223 _c->path.len = 0;
00224 }
00225
00226 _c->sock = _ci->sock;
00227 _c->expires = _ci->expires;
00228 _c->q = _ci->q;
00229 _c->cseq = _ci->cseq;
00230 _c->methods = _ci->methods;
00231 _c->last_modified = _ci->last_modified;
00232 _c->flags = _ci->flags;
00233 _c->cflags = _ci->cflags;
00234
00235 return 0;
00236 }
00237
00238
00239
00240
00245 void st_update_ucontact(ucontact_t* _c)
00246 {
00247 switch(_c->state) {
00248 case CS_NEW:
00249
00250
00251
00252
00253 break;
00254
00255 case CS_SYNC:
00256
00257
00258
00259
00260
00261
00262 if (db_mode == WRITE_BACK || db_mode == WRITE_THROUGH) {
00263 _c->state = CS_DIRTY;
00264 }
00265 break;
00266
00267 case CS_DIRTY:
00268
00269
00270
00271 break;
00272 }
00273 }
00274
00275
00281 int st_delete_ucontact(ucontact_t* _c)
00282 {
00283 switch(_c->state) {
00284 case CS_NEW:
00285
00286
00287
00288
00289 return 1;
00290
00291 case CS_SYNC:
00292 case CS_DIRTY:
00293
00294
00295
00296
00297
00298
00299
00300 if (db_mode == WRITE_BACK) {
00301 _c->expires = UL_EXPIRED_TIME;
00302 return 0;
00303 } else {
00304
00305
00306
00307
00308
00309 return 1;
00310 }
00311 }
00312
00313 return 0;
00314 }
00315
00316
00322 int st_expired_ucontact(ucontact_t* _c)
00323 {
00324
00325
00326
00327
00328
00329 switch(_c->state) {
00330 case CS_NEW:
00331
00332
00333
00334 return 0;
00335
00336 case CS_SYNC:
00337 case CS_DIRTY:
00338
00339 return 1;
00340 }
00341
00342 return 0;
00343 }
00344
00345
00351 int st_flush_ucontact(ucontact_t* _c)
00352 {
00353 switch(_c->state) {
00354 case CS_NEW:
00355
00356
00357
00358
00359 _c->state = CS_SYNC;
00360 return 1;
00361
00362 case CS_SYNC:
00363
00364
00365
00366 return 0;
00367
00368 case CS_DIRTY:
00369
00370
00371
00372
00373 _c->state = CS_SYNC;
00374 return 2;
00375 }
00376
00377 return 0;
00378 }
00379
00380
00381
00382
00388 int db_insert_ucontact(ucontact_t* _c)
00389 {
00390 char* dom;
00391 db_key_t keys[15];
00392 db_val_t vals[15];
00393 int nr_cols = 0;
00394 int nr_cols_key = 0;
00395 struct udomain * _d;
00396 str user={0, 0};
00397 str domain={0, 0};
00398
00399 if (_c->flags & FL_MEM) {
00400 return 0;
00401 }
00402
00403 if(register_udomain(_c->domain->s, &_d) < 0){
00404 return -1;
00405 }
00406 LM_INFO("Domain set for contact %.*s\n", _c->domain->len, _c->domain->s);
00407
00408 keys[nr_cols] = &user_col;
00409 vals[nr_cols].type = DB1_STR;
00410 vals[nr_cols].nul = 0;
00411 vals[nr_cols].val.str_val = *_c->aor;
00412 nr_cols++;
00413
00414 keys[nr_cols] = &contact_col;
00415 vals[nr_cols].type = DB1_STR;
00416 vals[nr_cols].nul = 0;
00417 vals[nr_cols].val.str_val = _c->c;
00418 nr_cols++;
00419
00420 if(use_domain) {
00421 keys[nr_cols] = &domain_col;
00422 vals[nr_cols].type = DB1_STR;
00423 vals[nr_cols].nul = 0;
00424
00425 dom = memchr(_c->aor->s, '@', _c->aor->len);
00426 if (dom==0) {
00427 LM_INFO("*** use domain and AOR does not contain @\n");
00428 vals[nr_cols].val.str_val.len = 0;
00429 vals[nr_cols].val.str_val.s = 0;
00430 } else {
00431 vals[0].val.str_val.len = dom - _c->aor->s;
00432 vals[nr_cols].val.str_val.s = dom + 1;
00433 vals[nr_cols].val.str_val.len = _c->aor->s + _c->aor->len - dom - 1;
00434 }
00435 domain = vals[nr_cols].val.str_val;
00436 LM_DBG("** Username=%.*s Domain=%.*s\n", vals[0].val.str_val.len, vals[0].val.str_val.s,
00437 vals[nr_cols].val.str_val.len, vals[nr_cols].val.str_val.s);
00438 nr_cols++;
00439 }
00440 nr_cols_key = nr_cols;
00441 user = vals[0].val.str_val;
00442
00443 keys[nr_cols] = &expires_col;
00444 vals[nr_cols].type = DB1_DATETIME;
00445 vals[nr_cols].nul = 0;
00446 vals[nr_cols].val.time_val = _c->expires;
00447 nr_cols++;
00448
00449 keys[nr_cols] = &q_col;
00450 vals[nr_cols].type = DB1_DOUBLE;
00451 vals[nr_cols].nul = 0;
00452 vals[nr_cols].val.double_val = q2double(_c->q);
00453 nr_cols++;
00454
00455 keys[nr_cols] = &callid_col;
00456 vals[nr_cols].type = DB1_STR;
00457 vals[nr_cols].nul = 0;
00458 vals[nr_cols].val.str_val = _c->callid;
00459 nr_cols++;
00460
00461 keys[nr_cols] = &cseq_col;
00462 vals[nr_cols].type = DB1_INT;
00463 vals[nr_cols].nul = 0;
00464 vals[nr_cols].val.int_val = _c->cseq;
00465 nr_cols++;
00466
00467 keys[nr_cols] = &flags_col;
00468 vals[nr_cols].type = DB1_INT;
00469 vals[nr_cols].nul = 0;
00470 vals[nr_cols].val.bitmap_val = _c->flags;
00471 nr_cols++;
00472
00473 keys[nr_cols] = &cflags_col;
00474 vals[nr_cols].type = DB1_INT;
00475 vals[nr_cols].nul = 0;
00476 vals[nr_cols].val.bitmap_val = _c->cflags;
00477 nr_cols++;
00478
00479 keys[nr_cols] = &user_agent_col;
00480 vals[nr_cols].type = DB1_STR;
00481 vals[nr_cols].nul = 0;
00482 vals[nr_cols].val.str_val = _c->user_agent;
00483 nr_cols++;
00484
00485 keys[nr_cols] = &received_col;
00486 vals[nr_cols].type = DB1_STR;
00487 if (_c->received.s == 0) {
00488 vals[nr_cols].nul = 1;
00489 } else {
00490 vals[nr_cols].nul = 0;
00491 vals[nr_cols].val.str_val = _c->received;
00492 }
00493 nr_cols++;
00494
00495 keys[nr_cols] = &path_col;
00496 vals[nr_cols].type = DB1_STR;
00497 if (_c->path.s == 0) {
00498 vals[nr_cols].nul = 1;
00499 } else {
00500 vals[nr_cols].nul = 0;
00501 vals[nr_cols].val.str_val = _c->path;
00502 }
00503 nr_cols++;
00504
00505 keys[nr_cols] = &sock_col;
00506 vals[nr_cols].type = DB1_STR;
00507 if (_c->sock) {
00508 vals[nr_cols].val.str_val = _c->sock->sock_str;
00509 vals[nr_cols].nul = 0;
00510 } else {
00511 vals[nr_cols].nul = 1;
00512 }
00513 nr_cols++;
00514
00515 keys[nr_cols] = &methods_col;
00516 vals[nr_cols].type = DB1_BITMAP;
00517 if (_c->methods == 0xFFFFFFFF) {
00518 vals[nr_cols].nul = 1;
00519 } else {
00520 vals[nr_cols].val.bitmap_val = _c->methods;
00521 vals[nr_cols].nul = 0;
00522 }
00523 nr_cols++;
00524
00525 keys[nr_cols] = &last_mod_col;
00526 vals[nr_cols].type = DB1_DATETIME;
00527 vals[nr_cols].nul = 0;
00528 vals[nr_cols].val.time_val = _c->last_modified;
00529
00530
00531
00532 if (ul_db_layer_replace(_d, &user, &domain, keys, vals, nr_cols, nr_cols_key) <0) {
00533 LM_ERR("inserting contact in db failed\n");
00534 return -1;
00535 }
00536
00537 return 0;
00538 }
00539
00540
00546 int db_update_ucontact(ucontact_t* _c)
00547 {
00548 char* dom;
00549 db_key_t keys1[4];
00550 db_val_t vals1[4];
00551
00552 db_key_t keys2[11];
00553 db_val_t vals2[11];
00554
00555 if (_c->flags & FL_MEM) {
00556 return 0;
00557 }
00558 struct udomain * _d;
00559 if(register_udomain(_c->domain->s, &_d) < 0){
00560 return -1;
00561 }
00562
00563 keys1[0] = &user_col;
00564 keys1[1] = &contact_col;
00565 keys1[2] = &callid_col;
00566 keys1[3] = &domain_col;
00567 keys2[0] = &expires_col;
00568 keys2[1] = &q_col;
00569 keys2[2] = &cseq_col;
00570 keys2[3] = &flags_col;
00571 keys2[4] = &cflags_col;
00572 keys2[5] = &user_agent_col;
00573 keys2[6] = &received_col;
00574 keys2[7] = &path_col;
00575 keys2[8] = &sock_col;
00576 keys2[9] = &methods_col;
00577 keys2[10] = &last_mod_col;
00578
00579 vals1[0].type = DB1_STR;
00580 vals1[0].nul = 0;
00581 vals1[0].val.str_val = *_c->aor;
00582
00583 vals1[1].type = DB1_STR;
00584 vals1[1].nul = 0;
00585 vals1[1].val.str_val = _c->c;
00586
00587 vals1[2].type = DB1_STR;
00588 vals1[2].nul = 0;
00589 vals1[2].val.str_val = _c->callid;
00590
00591 vals2[0].type = DB1_DATETIME;
00592 vals2[0].nul = 0;
00593 vals2[0].val.time_val = _c->expires;
00594
00595 vals2[1].type = DB1_DOUBLE;
00596 vals2[1].nul = 0;
00597 vals2[1].val.double_val = q2double(_c->q);
00598
00599 vals2[2].type = DB1_INT;
00600 vals2[2].nul = 0;
00601 vals2[2].val.int_val = _c->cseq;
00602
00603 vals2[3].type = DB1_INT;
00604 vals2[3].nul = 0;
00605 vals2[3].val.bitmap_val = _c->flags;
00606
00607 vals2[4].type = DB1_INT;
00608 vals2[4].nul = 0;
00609 vals2[4].val.bitmap_val = _c->cflags;
00610
00611 vals2[5].type = DB1_STR;
00612 vals2[5].nul = 0;
00613 vals2[5].val.str_val = _c->user_agent;
00614
00615 vals2[6].type = DB1_STR;
00616 if (_c->received.s == 0) {
00617 vals2[6].nul = 1;
00618 } else {
00619 vals2[6].nul = 0;
00620 vals2[6].val.str_val = _c->received;
00621 }
00622
00623 vals2[7].type = DB1_STR;
00624 if (_c->path.s == 0) {
00625 vals2[7].nul = 1;
00626 } else {
00627 vals2[7].nul = 0;
00628 vals2[7].val.str_val = _c->path;
00629 }
00630
00631 vals2[8].type = DB1_STR;
00632 if (_c->sock) {
00633 vals2[8].val.str_val = _c->sock->sock_str;
00634 vals2[8].nul = 0;
00635 } else {
00636 vals2[8].nul = 1;
00637 }
00638
00639 vals2[9].type = DB1_BITMAP;
00640 if (_c->methods == 0xFFFFFFFF) {
00641 vals2[9].nul = 1;
00642 } else {
00643 vals2[9].val.bitmap_val = _c->methods;
00644 vals2[9].nul = 0;
00645 }
00646
00647 vals2[10].type = DB1_DATETIME;
00648 vals2[10].nul = 0;
00649 vals2[10].val.time_val = _c->last_modified;
00650
00651 if (use_domain) {
00652 vals1[3].type = DB1_STR;
00653 vals1[3].nul = 0;
00654 dom = memchr(_c->aor->s, '@', _c->aor->len);
00655 if (dom==0) {
00656 vals1[0].val.str_val.len = 0;
00657 vals1[3].val.str_val = *_c->aor;
00658 } else {
00659 vals1[0].val.str_val.len = dom - _c->aor->s;
00660 vals1[3].val.str_val.s = dom + 1;
00661 vals1[3].val.str_val.len = _c->aor->s + _c->aor->len - dom - 1;
00662 }
00663 }
00664
00665 if (ul_db_layer_update(_d, &vals1[0].val.str_val, &vals1[3].val.str_val, keys1, 0, vals1, keys2, vals2,
00666 (use_domain) ? (4) : (3), 11) < 0) {
00667 LM_ERR("updating database failed\n");
00668 return -1;
00669 }
00670
00671 return 0;
00672 }
00673
00674
00680 int db_delete_ucontact(ucontact_t* _c)
00681 {
00682 char* dom;
00683 db_key_t keys[4];
00684 db_val_t vals[4];
00685
00686 if (_c->flags & FL_MEM) {
00687 return 0;
00688 }
00689 struct udomain * _d;
00690 if(register_udomain(_c->domain->s, &_d) < 0){
00691 return -1;
00692 }
00693
00694 keys[0] = &user_col;
00695 keys[1] = &contact_col;
00696 keys[2] = &callid_col;
00697 keys[3] = &domain_col;
00698
00699 vals[0].type = DB1_STR;
00700 vals[0].nul = 0;
00701 vals[0].val.str_val = *_c->aor;
00702
00703 vals[1].type = DB1_STR;
00704 vals[1].nul = 0;
00705 vals[1].val.str_val = _c->c;
00706
00707 vals[2].type = DB1_STR;
00708 vals[2].nul = 0;
00709 vals[2].val.str_val = _c->callid;
00710
00711 if (use_domain) {
00712 vals[3].type = DB1_STR;
00713 vals[3].nul = 0;
00714 dom = memchr(_c->aor->s, '@', _c->aor->len);
00715 if (dom==0) {
00716 vals[0].val.str_val.len = 0;
00717 vals[3].val.str_val = *_c->aor;
00718 } else {
00719 vals[0].val.str_val.len = dom - _c->aor->s;
00720 vals[3].val.str_val.s = dom + 1;
00721 vals[3].val.str_val.len = _c->aor->s + _c->aor->len - dom - 1;
00722 }
00723 }
00724
00725 if (ul_db_layer_delete(_d, &vals[0].val.str_val, &vals[3].val.str_val, keys, 0, vals, (use_domain) ? (4) : (3)) < 0) {
00726 LM_ERR("deleting from database failed\n");
00727 return -1;
00728 }
00729
00730 return 0;
00731 }
00732
00733
00739 static inline void unlink_contact(struct urecord* _r, ucontact_t* _c)
00740 {
00741 if (_c->prev) {
00742 _c->prev->next = _c->next;
00743 if (_c->next) {
00744 _c->next->prev = _c->prev;
00745 }
00746 } else {
00747 _r->contacts = _c->next;
00748 if (_c->next) {
00749 _c->next->prev = 0;
00750 }
00751 }
00752 }
00753
00754
00760 static inline void update_contact_pos(struct urecord* _r, ucontact_t* _c)
00761 {
00762 ucontact_t *pos, *ppos;
00763
00764 if (desc_time_order) {
00765
00766 if (_c->prev==0)
00767 return;
00768 unlink_contact(_r, _c);
00769
00770 _c->next = _r->contacts;
00771 _c->prev = 0;
00772 _r->contacts->prev = _c;
00773 _r->contacts = _c;
00774 } else {
00775
00776 if ( (_c->prev==0 || _c->q<=_c->prev->q)
00777 && (_c->next==0 || _c->q>=_c->next->q) )
00778 return;
00779
00780 unlink_contact(_r, _c);
00781 _c->next = _c->prev = 0;
00782 for(pos=_r->contacts,ppos=0;pos&&pos->q<_c->q;ppos=pos,pos=pos->next);
00783 if (pos) {
00784 if (!pos->prev) {
00785 pos->prev = _c;
00786 _c->next = pos;
00787 _r->contacts = _c;
00788 } else {
00789 _c->next = pos;
00790 _c->prev = pos->prev;
00791 pos->prev->next = _c;
00792 pos->prev = _c;
00793 }
00794 } else if (ppos) {
00795 ppos->next = _c;
00796 _c->prev = ppos;
00797 } else {
00798 _r->contacts = _c;
00799 }
00800 }
00801 }
00802
00803
00811 int update_ucontact(struct urecord* _r, ucontact_t* _c, ucontact_info_t* _ci)
00812 {
00813
00814
00815 if (mem_update_ucontact( _c, _ci) < 0) {
00816 LM_ERR("failed to update memory\n");
00817 return -1;
00818 }
00819
00820
00821 if (exists_ulcb_type(UL_CONTACT_UPDATE))
00822 {
00823 LM_DBG("exists callback for type= UL_CONTACT_UPDATE\n");
00824 run_ul_callbacks( UL_CONTACT_UPDATE, _c);
00825 }
00826
00827 if (_r && db_mode!=DB_ONLY)
00828 update_contact_pos( _r, _c);
00829
00830 st_update_ucontact(_c);
00831
00832 if (db_mode == WRITE_THROUGH || db_mode==DB_ONLY) {
00833
00834
00835
00836
00837
00838
00839
00840 if (db_insert_ucontact(_c) < 0) {
00841 LM_ERR("failed to insert_update database\n");
00842 return -1;
00843 } else {
00844 _c->state = CS_SYNC;
00845 }
00846 }
00847 return 0;
00848 }