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
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071 #include <errno.h>
00072 #include <stdlib.h>
00073 #include <stdio.h>
00074 #include <time.h>
00075 #include <string.h>
00076 #include <sys/time.h>
00077 #include <sys/types.h>
00078 #include <sys/socket.h>
00079 #include <unistd.h>
00080 #include <signal.h>
00081
00082 #include "gwlib/gwlib.h"
00083 #include "msg.h"
00084 #include "bearerbox.h"
00085 #include "sms.h"
00086
00087 static FILE *file = NULL;
00088 static Octstr *filename = NULL;
00089 static Octstr *newfile = NULL;
00090 static Octstr *bakfile = NULL;
00091 static Mutex *file_mutex = NULL;
00092 static long cleanup_thread = -1;
00093 static long dump_frequency = 0;
00094
00095 static Dict *sms_dict = NULL;
00096
00097 static int active = 1;
00098 static time_t last_dict_mod = 0;
00099 static List *loaded;
00100
00101
00102 static void write_msg(Msg *msg)
00103 {
00104 Octstr *pack;
00105 unsigned char buf[4];
00106
00107 pack = store_msg_pack(msg);
00108 encode_network_long(buf, octstr_len(pack));
00109 octstr_insert_data(pack, 0, (char*)buf, 4);
00110
00111 octstr_print(file, pack);
00112 fflush(file);
00113
00114 octstr_destroy(pack);
00115 }
00116
00117
00118 static int read_msg(Msg **msg, Octstr *os, long *off)
00119 {
00120 unsigned char buf[4];
00121 long i;
00122 Octstr *pack;
00123
00124 gw_assert(*off >= 0);
00125 if (*off + 4 > octstr_len(os)) {
00126 error(0, "Packet too short while unpacking Msg.");
00127 return -1;
00128 }
00129
00130 octstr_get_many_chars((char*)buf, os, *off, 4);
00131 i = decode_network_long(buf);
00132 *off += 4;
00133
00134 pack = octstr_copy(os, *off, i);
00135 *off += octstr_len(pack);
00136 *msg = store_msg_unpack(pack);
00137 octstr_destroy(pack);
00138
00139 if (!*msg)
00140 return -1;
00141
00142 return 0;
00143 }
00144
00145
00146 static int open_file(Octstr *name)
00147 {
00148 file = fopen(octstr_get_cstr(name), "w");
00149 if (file == NULL) {
00150 error(errno, "Failed to open '%s' for writing, cannot create store-file",
00151 octstr_get_cstr(name));
00152 return -1;
00153 }
00154 return 0;
00155 }
00156
00157
00158 static int rename_store(void)
00159 {
00160 if (rename(octstr_get_cstr(filename), octstr_get_cstr(bakfile)) == -1) {
00161 if (errno != ENOENT) {
00162 error(errno, "Failed to rename old store '%s' as '%s'",
00163 octstr_get_cstr(filename), octstr_get_cstr(bakfile));
00164 return -1;
00165 }
00166 }
00167 if (rename(octstr_get_cstr(newfile), octstr_get_cstr(filename)) == -1) {
00168 error(errno, "Failed to rename new store '%s' as '%s'",
00169 octstr_get_cstr(newfile), octstr_get_cstr(filename));
00170 return -1;
00171 }
00172 return 0;
00173 }
00174
00175
00176 static int do_dump(void)
00177 {
00178 Octstr *key;
00179 Msg *msg;
00180 List *sms_list;
00181 long l;
00182
00183 if (filename == NULL)
00184 return 0;
00185
00186
00187
00188
00189 if (open_file(newfile)==-1)
00190 return -1;
00191
00192 sms_list = dict_keys(sms_dict);
00193 for (l=0; l < gwlist_len(sms_list); l++) {
00194 key = gwlist_get(sms_list, l);
00195 msg = dict_get(sms_dict, key);
00196 if (msg != NULL)
00197 write_msg(msg);
00198 }
00199 fflush(file);
00200 gwlist_destroy(sms_list, octstr_destroy_item);
00201
00202
00203
00204
00205 return rename_store();
00206 }
00207
00208
00209
00210
00211
00212
00213 static void store_dumper(void *arg)
00214 {
00215 time_t now;
00216 int busy = 0;
00217
00218 while (active) {
00219 now = time(NULL);
00220
00221
00222
00223
00224 if (now - last_dict_mod > dump_frequency || busy) {
00225 store_dump();
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236 last_dict_mod = time(NULL) + 3600*24;
00237 busy = 0;
00238 } else {
00239 busy = (now - last_dict_mod) > 0;
00240 }
00241 gwthread_sleep(dump_frequency);
00242 }
00243 store_dump();
00244 if (file != NULL)
00245 fclose(file);
00246 octstr_destroy(filename);
00247 octstr_destroy(newfile);
00248 octstr_destroy(bakfile);
00249 mutex_destroy(file_mutex);
00250
00251 dict_destroy(sms_dict);
00252
00253 filename = newfile = bakfile = NULL;
00254 file_mutex = NULL;
00255 sms_dict = NULL;
00256 }
00257
00258
00259
00260
00261 static Octstr *store_file_status(int status_type)
00262 {
00263 char *frmt;
00264 Octstr *ret, *key;
00265 unsigned long l;
00266 struct tm tm;
00267 Msg *msg;
00268 List *keys;
00269 char id[UUID_STR_LEN + 1];
00270
00271 ret = octstr_create("");
00272
00273
00274 if (status_type == BBSTATUS_HTML) {
00275 octstr_append_cstr(ret, "<table border=1>\n"
00276 "<tr><td>SMS ID</td><td>Type</td><td>Time</td><td>Sender</td><td>Receiver</td>"
00277 "<td>SMSC ID</td><td>BOX ID</td><td>UDH</td><td>Message</td>"
00278 "</tr>\n");
00279 } else if (status_type == BBSTATUS_TEXT) {
00280 octstr_append_cstr(ret, "[SMS ID] [Type] [Time] [Sender] [Receiver] [SMSC ID] [BOX ID] [UDH] [Message]\n");
00281 }
00282
00283
00284 if (filename == NULL)
00285 goto finish;
00286
00287 keys = dict_keys(sms_dict);
00288
00289 for (l = 0; l < gwlist_len(keys); l++) {
00290 key = gwlist_get(keys, l);
00291 msg = dict_get(sms_dict, key);
00292 if (msg == NULL)
00293 continue;
00294
00295 if (msg_type(msg) == sms) {
00296
00297 if (status_type == BBSTATUS_HTML) {
00298 frmt = "<tr><td>%s</td><td>%s</td>"
00299 "<td>%04d-%02d-%02d %02d:%02d:%02d</td>"
00300 "<td>%s</td><td>%s</td><td>%s</td>"
00301 "<td>%s</td><td>%s</td><td>%s</td></tr>\n";
00302 } else if (status_type == BBSTATUS_XML) {
00303 frmt = "<message>\n\t<id>%s</id>\n\t<type>%s</type>\n\t"
00304 "<time>%04d-%02d-%02d %02d:%02d:%02d</time>\n\t"
00305 "<sender>%s</sender>\n\t"
00306 "<receiver>%s</receiver>\n\t<smsc-id>%s</smsc-id>\n\t"
00307 "<box-id>%s</box-id>\n\t"
00308 "<udh-data>%s</udh-data>\n\t<msg-data>%s</msg-data>\n\t"
00309 "</message>\n";
00310 } else {
00311 frmt = "[%s] [%s] [%04d-%02d-%02d %02d:%02d:%02d] [%s] [%s] [%s] [%s] [%s] [%s]\n";
00312 }
00313
00314
00315 #if LOG_TIMESTAMP_LOCALTIME
00316 tm = gw_localtime(msg->sms.time);
00317 #else
00318 tm = gw_gmtime(msg->sms.time);
00319 #endif
00320 if (msg->sms.udhdata)
00321 octstr_binary_to_hex(msg->sms.udhdata, 1);
00322 if (msg->sms.msgdata &&
00323 (msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2 ||
00324 (msg->sms.coding == DC_UNDEF && msg->sms.udhdata)))
00325 octstr_binary_to_hex(msg->sms.msgdata, 1);
00326
00327 uuid_unparse(msg->sms.id, id);
00328
00329 octstr_format_append(ret, frmt, id,
00330 (msg->sms.sms_type == mo ? "MO" :
00331 msg->sms.sms_type == mt_push ? "MT-PUSH" :
00332 msg->sms.sms_type == mt_reply ? "MT-REPLY" :
00333 msg->sms.sms_type == report_mo ? "DLR-MO" :
00334 msg->sms.sms_type == report_mt ? "DLR-MT" : ""),
00335 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
00336 tm.tm_hour, tm.tm_min, tm.tm_sec,
00337 (msg->sms.sender ? octstr_get_cstr(msg->sms.sender) : ""),
00338 (msg->sms.receiver ? octstr_get_cstr(msg->sms.receiver) : ""),
00339 (msg->sms.smsc_id ? octstr_get_cstr(msg->sms.smsc_id) : ""),
00340 (msg->sms.boxc_id ? octstr_get_cstr(msg->sms.boxc_id) : ""),
00341 (msg->sms.udhdata ? octstr_get_cstr(msg->sms.udhdata) : ""),
00342 (msg->sms.msgdata ? octstr_get_cstr(msg->sms.msgdata) : ""));
00343
00344 if (msg->sms.udhdata)
00345 octstr_hex_to_binary(msg->sms.udhdata);
00346 if (msg->sms.msgdata &&
00347 (msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2 ||
00348 (msg->sms.coding == DC_UNDEF && msg->sms.udhdata)))
00349 octstr_hex_to_binary(msg->sms.msgdata);
00350 }
00351 }
00352 gwlist_destroy(keys, octstr_destroy_item);
00353
00354 finish:
00355
00356 if (status_type == BBSTATUS_HTML) {
00357 octstr_append_cstr(ret,"</table>");
00358 }
00359
00360 return ret;
00361 }
00362
00363
00364 static long store_file_messages(void)
00365 {
00366 return (sms_dict ? dict_key_count(sms_dict) : -1);
00367 }
00368
00369
00370 static int store_to_dict(Msg *msg)
00371 {
00372 Msg *copy;
00373 Octstr *uuid_os;
00374 char id[UUID_STR_LEN + 1];
00375
00376
00377 if (msg_type(msg) == sms && uuid_is_null(msg->sms.id))
00378 uuid_generate(msg->sms.id);
00379
00380 if (msg_type(msg) == sms && msg->sms.time == MSG_PARAM_UNDEFINED)
00381 time(&msg->sms.time);
00382
00383 if (msg_type(msg) == sms) {
00384 copy = msg_duplicate(msg);
00385
00386 uuid_unparse(copy->sms.id, id);
00387 uuid_os = octstr_create(id);
00388
00389 dict_put(sms_dict, uuid_os, copy);
00390 octstr_destroy(uuid_os);
00391 last_dict_mod = time(NULL);
00392 } else if (msg_type(msg) == ack) {
00393 uuid_unparse(msg->ack.id, id);
00394 uuid_os = octstr_create(id);
00395 copy = dict_remove(sms_dict, uuid_os);
00396 octstr_destroy(uuid_os);
00397 if (copy == NULL) {
00398 warning(0, "bb_store: get ACK of message not found "
00399 "from store, strange?");
00400 } else {
00401 msg_destroy(copy);
00402 last_dict_mod = time(NULL);
00403 }
00404 } else
00405 return -1;
00406 return 0;
00407 }
00408
00409 static int store_file_save(Msg *msg)
00410 {
00411 if (filename == NULL)
00412 return 0;
00413
00414
00415 gwlist_consume(loaded);
00416
00417
00418 mutex_lock(file_mutex);
00419 if (store_to_dict(msg) == -1) {
00420 mutex_unlock(file_mutex);
00421 return -1;
00422 }
00423
00424
00425 write_msg(msg);
00426 fflush(file);
00427 mutex_unlock(file_mutex);
00428
00429 return 0;
00430 }
00431
00432
00433 static int store_file_save_ack(Msg *msg, ack_status_t status)
00434 {
00435 Msg *mack;
00436 int ret;
00437
00438
00439 if (!msg || msg_type(msg) != sms)
00440 return -1;
00441
00442 if (filename == NULL)
00443 return 0;
00444
00445 mack = msg_create(ack);
00446 if (!mack)
00447 return -1;
00448
00449 mack->ack.time = msg->sms.time;
00450 uuid_copy(mack->ack.id, msg->sms.id);
00451 mack->ack.nack = status;
00452
00453 ret = store_save(mack);
00454 msg_destroy(mack);
00455
00456 return ret;
00457 }
00458
00459
00460 static int store_file_load(void(*receive_msg)(Msg*))
00461 {
00462 List *keys;
00463 Octstr *store_file, *key;
00464 Msg *msg;
00465 int retval, msgs;
00466 long end, pos;
00467
00468 if (filename == NULL)
00469 return 0;
00470
00471 mutex_lock(file_mutex);
00472 if (file != NULL) {
00473 fclose(file);
00474 file = NULL;
00475 }
00476
00477 store_file = octstr_read_file(octstr_get_cstr(filename));
00478 if (store_file != NULL)
00479 info(0, "Loading store file `%s'", octstr_get_cstr(filename));
00480 else {
00481 store_file = octstr_read_file(octstr_get_cstr(newfile));
00482 if (store_file != NULL)
00483 info(0, "Loading store file `%s'", octstr_get_cstr(newfile));
00484 else {
00485 store_file = octstr_read_file(octstr_get_cstr(bakfile));
00486 if (store_file != NULL)
00487 info(0, "Loading store file `%s'", octstr_get_cstr(bakfile));
00488 else {
00489 info(0, "Cannot open any store file, starting a new one");
00490 retval = open_file(filename);
00491 goto end;
00492 }
00493 }
00494 }
00495
00496 info(0, "Store-file size %ld, starting to unpack%s", octstr_len(store_file),
00497 octstr_len(store_file) > 10000 ? " (may take awhile)" : "");
00498
00499
00500 pos = 0;
00501 msgs = 0;
00502 end = octstr_len(store_file);
00503
00504 while (pos < end) {
00505 if (read_msg(&msg, store_file, &pos) == -1) {
00506 error(0, "Garbage at store-file, skipped.");
00507 continue;
00508 }
00509 if (msg_type(msg) == sms) {
00510 store_to_dict(msg);
00511 msgs++;
00512 } else if (msg_type(msg) == ack) {
00513 store_to_dict(msg);
00514 } else {
00515 warning(0, "Strange message in store-file, discarded, "
00516 "dump follows:");
00517 msg_dump(msg, 0);
00518 }
00519 msg_destroy(msg);
00520 }
00521 octstr_destroy(store_file);
00522
00523 info(0, "Retrieved %d messages, non-acknowledged messages: %ld",
00524 msgs, dict_key_count(sms_dict));
00525
00526
00527
00528 keys = dict_keys(sms_dict);
00529 while ((key = gwlist_extract_first(keys)) != NULL) {
00530 msg = dict_remove(sms_dict, key);
00531 if (store_to_dict(msg) != -1) {
00532 receive_msg(msg);
00533 } else {
00534 error(0, "Found unknown message type in store file.");
00535 msg_dump(msg, 0);
00536 msg_destroy(msg);
00537 }
00538 octstr_destroy(key);
00539 }
00540 gwlist_destroy(keys, octstr_destroy_item);
00541
00542
00543 retval = do_dump();
00544
00545 end:
00546 mutex_unlock(file_mutex);
00547
00548
00549 gwlist_remove_producer(loaded);
00550
00551
00552 if ((cleanup_thread = gwthread_create(store_dumper, NULL))==-1)
00553 panic(0, "Failed to create a cleanup thread!");
00554
00555 return retval;
00556 }
00557
00558
00559 static int store_file_dump(void)
00560 {
00561 int retval;
00562
00563 debug("bb.store", 0, "Dumping %ld messages to store",
00564 dict_key_count(sms_dict));
00565 mutex_lock(file_mutex);
00566 if (file != NULL) {
00567 fclose(file);
00568 file = NULL;
00569 }
00570 retval = do_dump();
00571 mutex_unlock(file_mutex);
00572
00573 return retval;
00574 }
00575
00576
00577 static void store_file_shutdown(void)
00578 {
00579 if (filename == NULL)
00580 return;
00581
00582 active = 0;
00583 gwthread_wakeup(cleanup_thread);
00584
00585 if (cleanup_thread != -1)
00586 gwthread_join(cleanup_thread);
00587
00588 gwlist_destroy(loaded, NULL);
00589 }
00590
00591
00592 int store_file_init(const Octstr *fname, long dump_freq)
00593 {
00594
00595 store_messages = store_file_messages;
00596 store_save = store_file_save;
00597 store_save_ack = store_file_save_ack;
00598 store_load = store_file_load;
00599 store_dump = store_file_dump;
00600 store_shutdown = store_file_shutdown;
00601 store_status = store_file_status;
00602
00603 if (fname == NULL)
00604 return 0;
00605
00606 if (octstr_len(fname) > (FILENAME_MAX-5))
00607 panic(0, "Store file filename too long: `%s', failed to init.",
00608 octstr_get_cstr(fname));
00609
00610 filename = octstr_duplicate(fname);
00611 newfile = octstr_format("%s.new", octstr_get_cstr(filename));
00612 bakfile = octstr_format("%s.bak", octstr_get_cstr(filename));
00613
00614 sms_dict = dict_create(1024, msg_destroy_item);
00615
00616 if (dump_freq > 0)
00617 dump_frequency = dump_freq;
00618 else
00619 dump_frequency = BB_STORE_DEFAULT_DUMP_FREQ;
00620
00621 file_mutex = mutex_create();
00622 active = 1;
00623
00624 loaded = gwlist_create();
00625 gwlist_add_producer(loaded);
00626
00627 return 0;
00628 }
See file LICENSE for details about the license agreement for using,
modifying, copying or deriving work from this software.