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
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048 #include <stdio.h>
00049 #include <string.h>
00050 #include <stdlib.h>
00051 #include <sys/types.h>
00052 #include <sys/ipc.h>
00053 #include <unistd.h>
00054 #include <fcntl.h>
00055 #include <time.h>
00056
00057 #include "../../sr_module.h"
00058 #include "../../dprint.h"
00059 #include "../../ut.h"
00060 #include "../../timer.h"
00061 #include "../../mem/shm_mem.h"
00062 #include "../../lib/srdb2/db.h"
00063 #include "../../parser/parse_from.h"
00064 #include "../../parser/parse_content.h"
00065 #include "../../parser/contact/parse_contact.h"
00066 #include "../../resolve.h"
00067 #include "../../id.h"
00068
00069 #include "../../modules/tm/tm_load.h"
00070
00071 #define CONTACT_PREFIX "Content-Type: text/plain"CRLF"Contact: <"
00072 #define CONTACT_SUFFIX ">;msilo=yes"CRLF
00073 #define CONTACT_PREFIX_LEN (sizeof(CONTACT_PREFIX)-1)
00074 #define CONTACT_SUFFIX_LEN (sizeof(CONTACT_SUFFIX)-1)
00075 #define OFFLINE_MESSAGE "] is offline. The message will be delivered when user goes online."
00076 #define OFFLINE_MESSAGE_LEN (sizeof(OFFLINE_MESSAGE)-1)
00077
00078 #include "ms_msg_list.h"
00079 #include "msfuncs.h"
00080
00081 char *sc_mid = "mid";
00082 char *sc_from = "from_hdr";
00083 char *sc_to = "to_hdr";
00084 char *sc_ruri = "ruri";
00085 char *sc_uid = "uid";
00086 char *sc_body = "body";
00087 char *sc_ctype = "ctype";
00088 char *sc_exp_time = "exp_time";
00089 char *sc_inc_time = "inc_time";
00090
00091 MODULE_VERSION
00092
00093
00095 static db_ctx_t* ctx = NULL;
00096 static db_cmd_t* store = NULL;
00097 static db_cmd_t* load = NULL;
00098 static db_cmd_t* del_mid = NULL;
00099 static db_cmd_t* del_expired = NULL;
00100
00102 msg_list ml = NULL;
00103
00105 struct tm_binds tmb;
00106
00109 char *ms_db_url=DEFAULT_DB_URL;
00110 char *ms_db_table="silo";
00111 char *ms_registrar=NULL;
00112 int ms_expire_time=259200;
00113 int ms_check_time=30;
00114 int ms_clean_period=5;
00115 int ms_use_contact=1;
00116
00117 str msg_type = STR_STATIC_INIT("MESSAGE");
00118
00119 str reg_addr;
00120
00122 static int mod_init(void);
00123 static int child_init(int);
00124
00125 static int m_store(struct sip_msg*, char*, char*);
00126 static int m_dump(struct sip_msg*, char*, char*);
00127
00128 static void destroy(void);
00129
00130 void m_clean_silo(unsigned int ticks, void *);
00131
00133 static void m_tm_callback( struct cell *t, int type, struct tmcb_params *ps);
00134
00135 static cmd_export_t cmds[]={
00136 {"m_store", m_store, 2, 0, REQUEST_ROUTE | FAILURE_ROUTE},
00137 {"m_store", m_store, 1, 0, REQUEST_ROUTE | FAILURE_ROUTE},
00138 {"m_dump", m_dump, 1, 0, REQUEST_ROUTE},
00139 {"m_dump", m_dump, 0, 0, REQUEST_ROUTE},
00140 {0,0,0,0,0}
00141 };
00142
00143
00144 static param_export_t params[]={
00145 {"db_url", PARAM_STRING, &ms_db_url},
00146 {"db_table", PARAM_STRING, &ms_db_table},
00147 {"registrar", PARAM_STRING, &ms_registrar},
00148 {"expire_time", PARAM_INT, &ms_expire_time},
00149 {"check_time", PARAM_INT, &ms_check_time},
00150 {"clean_period", PARAM_INT, &ms_clean_period},
00151 {"use_contact", PARAM_INT, &ms_use_contact},
00152 {"sc_mid", PARAM_STRING, &sc_mid},
00153 {"sc_from", PARAM_STRING, &sc_from},
00154 {"sc_to", PARAM_STRING, &sc_to},
00155 {"sc_ruri", PARAM_STRING, &sc_ruri},
00156 {"sc_uid", PARAM_STRING, &sc_uid},
00157 {"sc_body", PARAM_STRING, &sc_body},
00158 {"sc_ctype", PARAM_STRING, &sc_ctype},
00159 {"sc_exp_time", PARAM_STRING, &sc_exp_time},
00160 {"sc_inc_time", PARAM_STRING, &sc_inc_time},
00161 {0,0,0}
00162 };
00163
00164
00166 struct module_exports exports= {
00167 "msilo",
00168 cmds,
00169 0,
00170 params,
00171
00172 mod_init,
00173 (response_function) 0,
00174 (destroy_function) destroy,
00175 0,
00176 child_init
00177 };
00178
00182 static int mod_init(void)
00183 {
00184 load_tm_f load_tm;
00185
00186 DBG("MSILO: initializing ...\n");
00187
00188
00189 if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT, 0))) {
00190 LOG(L_ERR, "ERROR: msilo: mod_init: can't import load_tm\n");
00191 return -1;
00192 }
00193
00194 if (load_tm( &tmb )==-1)
00195 return -1;
00196
00197 ml = msg_list_init();
00198 if(!ml)
00199 {
00200 DBG("ERROR: msilo: mod_init: can't initialize msg list\n");
00201 return -1;
00202 }
00203 register_timer( m_clean_silo, 0, ms_check_time);
00204
00205 reg_addr.s = ms_registrar;
00206 reg_addr.len = (ms_registrar)?strlen(ms_registrar):0;
00207
00208 return 0;
00209 }
00210
00211
00212 void msilo_db_close(void)
00213 {
00214 if (store) db_cmd_free(store);
00215 store = NULL;
00216
00217 if (load) db_cmd_free(load);
00218 load = NULL;
00219
00220 if (del_mid) db_cmd_free(del_mid);
00221 del_mid = NULL;
00222
00223 if (del_expired) db_cmd_free(del_expired);
00224 del_expired = NULL;
00225
00226 if (ctx) {
00227 db_disconnect(ctx);
00228 db_ctx_free(ctx);
00229 ctx = NULL;
00230 }
00231 }
00232
00233
00234 int msilo_db_init(char* db_url)
00235 {
00236 db_fld_t del_mid_param[] = {
00237 {.name = sc_mid, .type = DB_INT},
00238 {.name = 0}
00239 };
00240
00241 db_fld_t del_expired_param[] = {
00242 {.name = sc_exp_time, .type = DB_DATETIME, .op = DB_LEQ},
00243 {.name = 0}
00244 };
00245
00246 db_fld_t store_param[] = {
00247 {.name = sc_to, .type = DB_STR },
00248 {.name = sc_from, .type = DB_STR },
00249 {.name = sc_ruri, .type = DB_STR },
00250 {.name = sc_uid, .type = DB_STR },
00251 {.name = sc_body, .type = DB_BLOB },
00252 {.name = sc_ctype, .type = DB_STR },
00253 {.name = sc_exp_time, .type = DB_DATETIME},
00254 {.name = sc_inc_time, .type = DB_DATETIME},
00255 {.name = 0}
00256 };
00257
00258 db_fld_t load_match[] = {
00259 {.name = sc_uid, .type = DB_STR},
00260 {.name = 0}
00261 };
00262
00263 db_fld_t load_cols[] = {
00264 {.name = sc_mid, .type = DB_INT},
00265 {.name = sc_from, .type = DB_STR},
00266 {.name = sc_to, .type = DB_STR},
00267 {.name = sc_body, .type = DB_BLOB},
00268 {.name = sc_ctype, .type = DB_STR},
00269 {.name = sc_inc_time, .type = DB_DATETIME},
00270 {.name = sc_ruri, .type = DB_STR},
00271 {.name = 0}
00272 };
00273
00274 ctx = db_ctx("msilo");
00275 if (!ctx) goto error;
00276 if (db_add_db(ctx, db_url) < 0) goto error;
00277 if (db_connect(ctx) < 0) goto error;
00278
00279 store = db_cmd(DB_PUT, ctx, ms_db_table, NULL, NULL, store_param);
00280 if (!store) goto error;
00281
00282 load = db_cmd(DB_GET, ctx, ms_db_table, load_cols, load_match, NULL);
00283 if (!store) goto error;
00284
00285 del_mid = db_cmd(DB_DEL, ctx, ms_db_table, NULL, del_mid_param, NULL);
00286 if (!del_mid) goto error;
00287
00288 del_expired = db_cmd(DB_DEL, ctx, ms_db_table, NULL, del_expired_param, NULL);
00289 if (!store) goto error;
00290
00291 return 0;
00292
00293 error:
00294 msilo_db_close();
00295 ERR("msilo: Error while initializing database layer\n");
00296 return -1;
00297 }
00298
00299
00300
00301
00302
00303
00307 static int child_init(int rank)
00308 {
00309
00310 if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN)
00311 return 0;
00312
00313 DBG("MSILO: init_child #%d / pid <%d>\n", rank, getpid());
00314 if (msilo_db_init(ms_db_url) < 0) return -1;
00315 return 0;
00316 }
00317
00318
00322 static void destroy(void)
00323 {
00324 DBG("MSILO: destroy module ...\n");
00325 msg_list_free(ml);
00326
00327 msilo_db_close();
00328 }
00329
00330
00331
00332
00340 static int m_store(struct sip_msg* msg, char* str1, char* str2)
00341 {
00342 str body, str_hdr, ctaddr, uri, uid;
00343 struct to_body* to, *from;
00344
00345 int val, lexpire;
00346 t_content_type ctype;
00347 static char buf[512];
00348 static char buf1[1024];
00349 int mime, mode;
00350 str next_hop = STR_NULL;
00351 uac_req_t uac_r;
00352
00353 DBG("MSILO: m_store: ------------ start ------------\n");
00354
00355 if (!str1) {
00356 LOG(L_ERR, "MSILO:m_store: Invalid parameter value\n");
00357 goto error;
00358 }
00359 mode = str1[0] - '0';
00360 if (str2) {
00361 next_hop.s = str2;
00362 next_hop.len = strlen(str2);
00363 }
00364
00365 if (get_to_uid(&uid, msg) < 0) {
00366 LOG(L_ERR, "MSILO:m_store: Unable to find out identity of user\n");
00367 goto error;
00368 }
00369
00370
00371 body.s = get_body( msg );
00372 if (body.s==0) {
00373 LOG(L_ERR,"MSILO:m_store: ERROR cannot extract body from msg\n");
00374 goto error;
00375 }
00376
00377
00378 if (!msg->content_length) {
00379 LOG(L_ERR,"MSILO:m_store: ERROR no Content-Length header found!\n");
00380 goto error;
00381 }
00382 body.len = get_content_length(msg);
00383
00384
00385 if(body.len <= 0) {
00386 DBG("MSILO:m_store: body of the message is empty!\n");
00387 goto error;
00388 }
00389
00390 to = get_to(msg);
00391 if (!to) {
00392 LOG(L_ERR, "MSILO:m_store: Cannot get To header\n");
00393 goto error;
00394 }
00395
00396 if (parse_from_header(msg) < 0) {
00397 LOG(L_ERR, "MSILO:m_store: Error while Parsing From header\n");
00398 goto error;
00399 }
00400 from = get_from(msg);
00401 if (!from) {
00402 LOG(L_ERR, "MSILO:m_store: Cannot find From header\n");
00403 goto error;
00404 }
00405
00406 store->vals[0].v.lstr = to->uri;
00407 store->vals[1].v.lstr = from->uri;
00408
00409 switch(mode) {
00410 case 0:
00411 uri = *GET_RURI(msg);
00412
00413 break;
00414 case 1:
00415
00416 uri = msg->first_line.u.request.uri;
00417 break;
00418 case 2:
00419 uri = to->uri;
00420 break;
00421 default:
00422 LOG(L_ERR, "MSILO:m_store: Unrecognized parameter value: %s\n", str1);
00423 goto error;
00424 }
00425
00426 store->vals[2].v.lstr = uri;
00427 store->vals[3].v.lstr = uid;
00428
00429 store->vals[4].v.blob = body;
00430
00431 lexpire = ms_expire_time;
00432
00433 if ((mime=parse_content_type_hdr(msg))<1 )
00434 {
00435 LOG(L_ERR,"MSILO:m_store: ERROR cannot parse Content-Type header\n");
00436 goto error;
00437 }
00438
00439 store->vals[5].v.lstr.s = "text/plain";
00440 store->vals[5].v.lstr.len = 10;
00441
00443 if( mime!=(TYPE_TEXT<<16)+SUBTYPE_PLAIN
00444 && mime!=(TYPE_MESSAGE<<16)+SUBTYPE_CPIM )
00445 {
00446 if(m_extract_content_type(msg->content_type->body.s,
00447 msg->content_type->body.len, &ctype, CT_TYPE) != -1)
00448 {
00449 DBG("MSILO:m_store: 'content-type' found\n");
00450 store->vals[5].v.lstr = ctype.type;
00451 }
00452 }
00453
00454
00455 if(msg->expires && msg->expires->body.len > 0)
00456 {
00457 DBG("MSILO:m_store: 'expires' found\n");
00458 val = atoi(msg->expires->body.s);
00459 if(val > 0)
00460 lexpire = (ms_expire_time<=val)?ms_expire_time:val;
00461 }
00462
00463
00464 val = (int)time(NULL);
00465
00466
00467 store->vals[6].v.time = val + lexpire;
00468 store->vals[7].v.time = val;
00469
00470 if (db_exec(NULL, store) < 0) {
00471 LOG(L_ERR, "MSILO:m_store: error storing message\n");
00472 goto error;
00473 }
00474 DBG("MSILO:m_store: message stored. uid:<%.*s> F:<%.*s>\n",
00475 uid.len, uid.s, from->uri.len, ZSW(from->uri.s));
00476
00477 if(reg_addr.len > 0
00478 && reg_addr.len+CONTACT_PREFIX_LEN+CONTACT_SUFFIX_LEN+1<1024)
00479 {
00480 DBG("MSILO:m_store: sending info message.\n");
00481 strcpy(buf1, CONTACT_PREFIX);
00482 strncat(buf1,reg_addr.s,reg_addr.len);
00483 strncat(buf1, CONTACT_SUFFIX, CONTACT_SUFFIX_LEN);
00484 str_hdr.len = CONTACT_PREFIX_LEN+reg_addr.len+CONTACT_SUFFIX_LEN;
00485 str_hdr.s = buf1;
00486
00487 strncpy(buf, "User [", 6);
00488 body.len = 6;
00489 if(uri.len+OFFLINE_MESSAGE_LEN+7 < 512)
00490 {
00491 strncpy(buf+body.len, uri.s, uri.len);
00492 body.len += uri.len;
00493 }
00494 strncpy(buf+body.len, OFFLINE_MESSAGE, OFFLINE_MESSAGE_LEN);
00495 body.len += OFFLINE_MESSAGE_LEN;
00496
00497 body.s = buf;
00498
00499
00500 ctaddr.s = NULL;
00501 if(ms_use_contact && msg->contact!=NULL && msg->contact->body.s!=NULL
00502 && msg->contact->body.len > 0)
00503 {
00504 DBG("MSILO:m_store: contact header found\n");
00505 if((msg->contact->parsed!=NULL
00506 && ((contact_body_t*)(msg->contact->parsed))->contacts!=NULL)
00507 || (parse_contact(msg->contact)==0
00508 && msg->contact->parsed!=NULL
00509 && ((contact_body_t*)(msg->contact->parsed))->contacts!=NULL))
00510 {
00511 DBG("MSILO:m_store: using contact header for info msg\n");
00512 ctaddr.s =
00513 ((contact_body_t*)(msg->contact->parsed))->contacts->uri.s;
00514 ctaddr.len =
00515 ((contact_body_t*)(msg->contact->parsed))->contacts->uri.len;
00516
00517 if(!ctaddr.s || ctaddr.len < 6 || strncmp(ctaddr.s, "sip:", 4)
00518 || ctaddr.s[4]==' ')
00519 ctaddr.s = NULL;
00520 else
00521 DBG("MSILO:m_store: feedback contact [%.*s]\n",
00522 ctaddr.len,ctaddr.s);
00523 }
00524 }
00525
00526 set_uac_req(&uac_r,
00527 &msg_type,
00528 &str_hdr,
00529 &body,
00530 0,
00531 0,
00532 0,
00533 0
00534 );
00535
00536 tmb.t_request(&uac_r,
00537 (ctaddr.s)?&ctaddr:&from->uri,
00538 &from->uri,
00539 ®_addr,
00540 next_hop.len ? &next_hop: NULL
00541 );
00542 }
00543
00544 return 1;
00545 error:
00546 return -1;
00547 }
00548
00552 static int m_dump(struct sip_msg* msg, char* str1, char* str2)
00553 {
00554 db_res_t* res = NULL;
00555 db_rec_t* rec;
00556 int i, mid, n;
00557 char hdr_buf[1024], body_buf[1024];
00558
00559 str str_vals[5], hdr_str , body_str, uid;
00560 time_t rtime;
00561 str next_hop = STR_NULL;
00562 uac_req_t uac_r;
00563
00564 i=0;
00565 if (str1) {
00566 next_hop.s = str1;
00567 next_hop.len = strlen(str1);
00568 }
00569
00570 DBG("MSILO:m_dump: ------------ start ------------\n");
00571
00572 if (get_to_uid(&uid, msg) < 0) {
00573 LOG(L_ERR, "MSILO:m_dump: Unable to retrieve identity of user\n");
00574 goto error;
00575 }
00576
00577 hdr_str.s = hdr_buf;
00578 hdr_str.len = 1024;
00579 body_str.s = body_buf;
00580 body_str.len = 1024;
00581
00585 if(parse_headers(msg, HDR_EXPIRES_F, 0) >= 0)
00586 {
00587
00588 if(msg->expires && msg->expires->body.len > 0)
00589 {
00590 i = atoi(msg->expires->body.s);
00591 if(i <= 0)
00592 {
00593 DBG("MSILO:m_dump: user <%.*s> goes offline - expires=%d\n",
00594 uid.len, uid.s, i);
00595 goto error;
00596 }
00597 else
00598 DBG("MSILO:m_dump: user <%.*s> online - expires=%d\n",
00599 uid.len, uid.s, i);
00600 }
00601 }
00602 else
00603 {
00604 DBG("MSILO:m_dump: 'expires' threw error at parsing\n");
00605 goto error;
00606 }
00607
00608 load->match[0].v.lstr = uid;
00609
00610 if (db_exec(&res, load) < 0) {
00611 ERR("msilo: Error while loading messages from database\n");
00612 goto error;
00613 }
00614 if (!res || !(rec = db_first(res))) {
00615 DBG("MSILO:m_dump: no stored message for <%.*s>!\n", STR_FMT(&uid));
00616 goto done;
00617 }
00618
00619 for(; rec; rec = db_next(res)) {
00620 if (rec->fld[0].flags & DB_NULL) {
00621 ERR("msilo: Database returned message with NULL msgid, skipping\n");
00622 continue;
00623 }
00624 mid = rec->fld[0].v.int4;
00625 if(msg_list_check_msg(ml, mid))
00626 {
00627 DBG("MSILO:m_dump: message[%d] mid=%d already sent.\n",
00628 i, mid);
00629 continue;
00630 }
00631
00632 memset(str_vals, 0, 4*sizeof(str));
00633 if (!(rec->fld[1].flags & DB_NULL)) str_vals[0] = rec->fld[1].v.lstr;
00634 if (!(rec->fld[2].flags & DB_NULL)) str_vals[1] = rec->fld[2].v.lstr;
00635 if (!(rec->fld[3].flags & DB_NULL)) str_vals[2] = rec->fld[3].v.lstr;
00636 if (!(rec->fld[4].flags & DB_NULL)) str_vals[3] = rec->fld[4].v.lstr;
00637 if (!(rec->fld[6].flags & DB_NULL)) str_vals[4] = rec->fld[6].v.lstr;
00638
00639 hdr_str.len = 1024;
00640 if(m_build_headers(&hdr_str, str_vals[3] ,
00641 str_vals[0]) < 0)
00642 {
00643 DBG("MSILO:m_dump: headers building failed!!!\n");
00644 msg_list_set_flag(ml, mid, MS_MSG_ERRO);
00645 goto error;
00646 }
00647
00648 DBG("MSILO:m_dump: msg [%d-%d] for: %.*s\n", i+1, mid,
00649 uid.len, ZSW(uid.s));
00650
00652 body_str.len = 1024;
00653 if (rec->fld[5].flags & DB_NULL) {
00654 rtime = 0;
00655 } else {
00656 rtime = rec->fld[5].v.time;
00657 }
00658 n = m_build_body(&body_str, rtime, str_vals[2]);
00659 if(n<0)
00660 DBG("MSILO:m_dump: sending simple body\n");
00661 else
00662 DBG("MSILO:m_dump: sending composed body\n");
00663
00664 set_uac_req(&uac_r,
00665 &msg_type,
00666 &hdr_str,
00667 (n<0)?&str_vals[2]:&body_str,
00668 0,
00669 TMCB_LOCAL_COMPLETED,
00670 m_tm_callback,
00671 (void*)(long)mid
00672 );
00673 tmb.t_request(&uac_r,
00674 &str_vals[4],
00675 &str_vals[1],
00676 &str_vals[0],
00677 next_hop.len ? &next_hop: NULL
00678 );
00679 }
00680
00681 done:
00686 if (res) db_res_free(res);
00687 return 1;
00688 error:
00689 if (res) db_res_free(res);
00690 return -1;
00691 }
00692
00697 void m_clean_silo(unsigned int ticks, void *param)
00698 {
00699 msg_list_el mle = NULL, p;
00700
00701 DBG("MSILO:clean_silo: cleaning stored messages - %d\n", ticks);
00702
00703 msg_list_check(ml);
00704 mle = p = msg_list_reset(ml);
00705 while(p) {
00706 if(p->flag & MS_MSG_DONE) {
00707 del_mid->match[0].v.int4 = p->msgid;
00708 DBG("MSILO:clean_silo: cleaning sent message [%d]\n", p->msgid);
00709 if (db_exec(NULL, del_mid) < 0) {
00710 DBG("MSILO:clean_silo: error while cleaning message %d.\n", p->msgid);
00711 }
00712 }
00713 p = p->next;
00714 }
00715
00716 msg_list_el_free_all(mle);
00717
00718
00719 if(ticks % (ms_check_time * ms_clean_period) < ms_check_time) {
00720 DBG("MSILO:clean_silo: cleaning expired messages\n");
00721 del_expired->match[0].v.time = (int)time(NULL);
00722 if (db_exec(NULL, del_expired) < 0) {
00723 DBG("MSILO:clean_silo: ERROR cleaning expired messages\n");
00724 }
00725 }
00726 }
00727
00728
00732 void m_tm_callback( struct cell *t, int type, struct tmcb_params *ps)
00733 {
00734 int mid = -1;
00735
00736 DBG("MSILO:m_tm_callback: completed with status %d\n", ps->code);
00737 if(!ps->param)
00738 {
00739 DBG("MSILO m_tm_callback: message id not received\n");
00740 goto done;
00741 }
00742 mid = (int)(long)(*ps->param);
00743 if(ps->code < 200 || ps->code >= 300)
00744 {
00745
00746 DBG("MSILO:m_tm_callback: message <%d> was not sent successfully\n",
00747 mid);
00748 msg_list_set_flag(ml, mid, MS_MSG_ERRO);
00749 goto done;
00750 }
00751
00752 msg_list_set_flag(ml, mid, MS_MSG_DONE);
00753
00754 done:
00755 return;
00756 }