Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

bb_store_spool.c

Go to the documentation of this file.
00001 /* ====================================================================
00002  * The Kannel Software License, Version 1.0
00003  *
00004  * Copyright (c) 2001-2008 Kannel Group
00005  * Copyright (c) 1998-2001 WapIT Ltd.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions
00010  * are met:
00011  *
00012  * 1. Redistributions of source code must retain the above copyright
00013  *    notice, this list of conditions and the following disclaimer.
00014  *
00015  * 2. Redistributions in binary form must reproduce the above copyright
00016  *    notice, this list of conditions and the following disclaimer in
00017  *    the documentation and/or other materials provided with the
00018  *    distribution.
00019  *
00020  * 3. The end-user documentation included with the redistribution,
00021  *    if any, must include the following acknowledgment:
00022  *       "This product includes software developed by the
00023  *        Kannel Group (http://www.kannel.org/)."
00024  *    Alternately, this acknowledgment may appear in the software itself,
00025  *    if and wherever such third-party acknowledgments normally appear.
00026  *
00027  * 4. The names "Kannel" and "Kannel Group" must not be used to
00028  *    endorse or promote products derived from this software without
00029  *    prior written permission. For written permission, please
00030  *    contact org@kannel.org.
00031  *
00032  * 5. Products derived from this software may not be called "Kannel",
00033  *    nor may "Kannel" appear in their name, without prior written
00034  *    permission of the Kannel Group.
00035  *
00036  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00038  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00039  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
00040  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
00041  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
00042  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
00043  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
00044  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
00045  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
00046  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00047  * ====================================================================
00048  *
00049  * This software consists of voluntary contributions made by many
00050  * individuals on behalf of the Kannel Group.  For more information on
00051  * the Kannel Group, please see <http://www.kannel.org/>.
00052  *
00053  * Portions of this software are based upon software originally written at
00054  * WapIT Ltd., Helsinki, Finland for the Kannel project.
00055  */
00056 
00063 #include "gw-config.h"
00064 
00065 #include <unistd.h>
00066 #include <sys/types.h>
00067 #include <sys/stat.h>
00068 #include <fcntl.h>
00069 #include <dirent.h>
00070 #include <errno.h>
00071 
00072 #include "gwlib/gwlib.h"
00073 #include "msg.h"
00074 #include "sms.h"
00075 #include "bearerbox.h"
00076 #include "bb_store.h"
00077 
00078 
00079 /* how much subdirs allowed ? */
00080 #define MAX_DIRS 100
00081 
00082 static Octstr *spool;
00083 static Counter *counter;
00084 static List *loaded;
00085 
00086 
00087 static int store_spool_dump()
00088 {
00089     /* nothing todo */
00090     return 0;
00091 }
00092 
00093 
00094 static long store_spool_messages()
00095 {
00096     return counter ? counter_value(counter) : -1;
00097 }
00098 
00099 
00100 static int for_each_file(const Octstr *dir_s, int ignore_err, void(*cb)(const Octstr*, void*), void *data)
00101 {
00102     DIR *dir;
00103     struct dirent *ent;
00104     struct stat stat;
00105     int ret = 0;
00106 
00107     if ((dir = opendir(octstr_get_cstr(dir_s))) == NULL) {
00108         error(errno, "Could not open directory `%s'", octstr_get_cstr(dir_s));
00109         return -1;
00110     }
00111     while((ent = readdir(dir)) != NULL) {
00112         Octstr *filename;
00113         if (*(ent->d_name) == '.') /* skip hidden files */
00114             continue;
00115         filename = octstr_format("%S/%s", dir_s, ent->d_name);
00116         if (lstat(octstr_get_cstr(filename), &stat) == -1) {
00117             if (!ignore_err)
00118                 error(errno, "Could not get stat for `%s'", octstr_get_cstr(filename));
00119             ret = -1;
00120         } else if (S_ISDIR(stat.st_mode) && for_each_file(filename, ignore_err, cb, data) == -1) {
00121             ret = -1;
00122         } else if (S_ISREG(stat.st_mode) && cb != NULL)
00123             cb(filename, data);
00124         octstr_destroy(filename);
00125         if (ret == -1 && ignore_err)
00126             ret = 0;
00127         else if (ret == -1)
00128             break;
00129     }
00130     closedir(dir);
00131 
00132     return ret;
00133 }
00134 
00135 
00136 struct status {
00137     const char *format;
00138     Octstr *status;
00139 };
00140 
00141 
00142 static void status_cb(const Octstr *filename, void *d)
00143 {
00144     struct status *data = d;
00145     struct tm tm;
00146     char id[UUID_STR_LEN + 1];
00147     Octstr *msg_s;
00148     Msg *msg;
00149 
00150     msg_s = octstr_read_file(octstr_get_cstr(filename));
00151     msg = store_msg_unpack(msg_s);
00152     octstr_destroy(msg_s);
00153     if (msg == NULL)
00154         return;
00155 
00156     /* transform the time value */
00157 #if LOG_TIMESTAMP_LOCALTIME
00158     tm = gw_localtime(msg->sms.time);
00159 #else
00160     tm = gw_gmtime(msg->sms.time);
00161 #endif
00162     if (msg->sms.udhdata)
00163         octstr_binary_to_hex(msg->sms.udhdata, 1);
00164     if (msg->sms.msgdata &&
00165         (msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2 ||
00166         (msg->sms.coding == DC_UNDEF && msg->sms.udhdata)))
00167         octstr_binary_to_hex(msg->sms.msgdata, 1);
00168 
00169     uuid_unparse(msg->sms.id, id);
00170 
00171     octstr_format_append(data->status, data->format,
00172         id,
00173         (msg->sms.sms_type == mo ? "MO" :
00174         msg->sms.sms_type == mt_push ? "MT-PUSH" :
00175         msg->sms.sms_type == mt_reply ? "MT-REPLY" :
00176         msg->sms.sms_type == report_mo ? "DLR-MO" :
00177         msg->sms.sms_type == report_mt ? "DLR-MT" : ""),
00178         tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
00179         tm.tm_hour, tm.tm_min, tm.tm_sec,
00180         (msg->sms.sender ? octstr_get_cstr(msg->sms.sender) : ""),
00181         (msg->sms.receiver ? octstr_get_cstr(msg->sms.receiver) : ""),
00182         (msg->sms.smsc_id ? octstr_get_cstr(msg->sms.smsc_id) : ""),
00183         (msg->sms.boxc_id ? octstr_get_cstr(msg->sms.boxc_id) : ""),
00184         (msg->sms.udhdata ? octstr_get_cstr(msg->sms.udhdata) : ""),
00185         (msg->sms.msgdata ? octstr_get_cstr(msg->sms.msgdata) : ""));
00186 
00187     msg_destroy(msg);
00188 }
00189 
00190 
00191 static Octstr *store_spool_status(int status_type)
00192 {
00193     Octstr *ret = octstr_create("");
00194     const char *format;
00195     struct status data;
00196 
00197     /* check if we are active */
00198     if (spool == NULL)
00199         return ret;
00200 
00201     /* set the type based header */
00202     if (status_type == BBSTATUS_HTML) {
00203         octstr_append_cstr(ret, "<table border=1>\n"
00204             "<tr><td>SMS ID</td><td>Type</td><td>Time</td><td>Sender</td><td>Receiver</td>"
00205             "<td>SMSC ID</td><td>BOX ID</td><td>UDH</td><td>Message</td>"
00206             "</tr>\n");
00207 
00208         format = "<tr><td>%s</td><td>%s</td>"
00209                 "<td>%04d-%02d-%02d %02d:%02d:%02d</td>"
00210                 "<td>%s</td><td>%s</td><td>%s</td>"
00211                 "<td>%s</td><td>%s</td><td>%s</td></tr>\n";
00212     } else if (status_type == BBSTATUS_XML) {
00213         format = "<message>\n\t<id>%s</id>\n\t<type>%s</type>\n\t"
00214                 "<time>%04d-%02d-%02d %02d:%02d:%02d</time>\n\t"
00215                 "<sender>%s</sender>\n\t"
00216                 "<receiver>%s</receiver>\n\t<smsc-id>%s</smsc-id>\n\t"
00217                 "<box-id>%s</box-id>\n\t"
00218                 "<udh-data>%s</udh-data>\n\t<msg-data>%s</msg-data>\n\t"
00219                 "</message>\n";
00220     } else {
00221         octstr_append_cstr(ret, "[SMS ID] [Type] [Time] [Sender] [Receiver] [SMSC ID] [BOX ID] [UDH] [Message]\n");
00222         format = "[%s] [%s] [%04d-%02d-%02d %02d:%02d:%02d] [%s] [%s] [%s] [%s] [%s] [%s]\n";
00223     }
00224 
00225     data.format = format;
00226     data.status = ret;
00227     /* ignore error because files may disappear */
00228     for_each_file(spool, 1, status_cb, &data);
00229 
00230     /* set the type based footer */
00231     if (status_type == BBSTATUS_HTML) {
00232         octstr_append_cstr(ret,"</table>");
00233     }
00234 
00235     return ret;
00236 }
00237 
00238 
00239 static void dispatch(const Octstr *filename, void *data)
00240 {
00241     Octstr *msg_s;
00242     Msg *msg;
00243     void(*receive_msg)(Msg*) = data;
00244 
00245     /* debug("", 0, "dispatch(%s,...) called", octstr_get_cstr(filename)); */
00246 
00247     msg_s = octstr_read_file(octstr_get_cstr(filename));
00248     if (msg_s == NULL)
00249         return;
00250     msg = store_msg_unpack(msg_s);
00251     octstr_destroy(msg_s);
00252     if (msg != NULL) {
00253         receive_msg(msg);
00254         counter_increase(counter);
00255     } else {
00256         error(0, "Could not unpack message `%s'", octstr_get_cstr(filename));
00257     }
00258 }
00259 
00260 
00261 static int store_spool_load(void(*receive_msg)(Msg*))
00262 {
00263     int rc;
00264 
00265     /* check if we are active */
00266     if (spool == NULL)
00267         return 0;
00268         
00269     /* sanity check */
00270     if (receive_msg == NULL)
00271         return -1;
00272 
00273     rc = for_each_file(spool, 0, dispatch, receive_msg);
00274 
00275     info(0, "Loaded %ld messages from store.", counter_value(counter));
00276 
00277     /* allow using of storage */
00278     gwlist_remove_producer(loaded);
00279 
00280     return rc;
00281 }
00282 
00283 
00284 static int store_spool_save(Msg *msg)
00285 {
00286     char id[UUID_STR_LEN + 1];
00287     Octstr *id_s;
00288 
00289     /* always set msg id and timestamp */
00290     if (msg_type(msg) == sms && uuid_is_null(msg->sms.id))
00291         uuid_generate(msg->sms.id);
00292 
00293     if (msg_type(msg) == sms && msg->sms.time == MSG_PARAM_UNDEFINED)
00294         time(&msg->sms.time);
00295 
00296     if (spool == NULL)
00297         return 0;
00298 
00299     /* blocke here if store still not loaded */
00300     gwlist_consume(loaded);
00301 
00302     switch(msg_type(msg)) {
00303         case sms:
00304         {
00305             Octstr *os = store_msg_pack(msg);
00306             Octstr *filename, *dir;
00307             int fd;
00308             size_t wrc;
00309 
00310             if (os == NULL) {
00311                 error(0, "Could not pack message.");
00312                 return -1;
00313             }
00314             uuid_unparse(msg->sms.id, id);
00315             id_s = octstr_create(id);
00316             dir = octstr_format("%S/%ld", spool, octstr_hash_key(id_s) % MAX_DIRS);
00317             octstr_destroy(id_s);
00318             if (mkdir(octstr_get_cstr(dir), S_IRUSR|S_IWUSR|S_IXUSR) == -1 && errno != EEXIST) {
00319                 error(errno, "Could not create directory `%s'.", octstr_get_cstr(dir));
00320                 octstr_destroy(dir);
00321                 octstr_destroy(os);
00322                 return -1;
00323             }
00324             filename = octstr_format("%S/%s", dir, id);
00325             octstr_destroy(dir);
00326             if ((fd = open(octstr_get_cstr(filename), O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR)) == -1) {
00327                 error(errno, "Could not open file `%s'.", octstr_get_cstr(filename));
00328                 octstr_destroy(filename);
00329                 octstr_destroy(os);
00330                 return -1;
00331             }
00332             for (wrc = 0; wrc < octstr_len(os); ) {
00333                 size_t rc = write(fd, octstr_get_cstr(os) + wrc, octstr_len(os) - wrc);
00334                 if (rc == -1) {
00335                     /* remove file */
00336                     error(errno, "Could not write message to `%s'.", octstr_get_cstr(filename));
00337                     close(fd);
00338                     if (unlink(octstr_get_cstr(filename)) == -1)
00339                         error(errno, "Oops, Could not remove failed file `%s'.", octstr_get_cstr(filename));
00340                     octstr_destroy(os);
00341                     octstr_destroy(filename);
00342                     return -1;
00343                 }
00344                 wrc += rc;
00345             }
00346             close(fd);
00347             counter_increase(counter);
00348             octstr_destroy(filename);
00349             octstr_destroy(os);
00350             break;
00351         }
00352         case ack:
00353         {
00354             Octstr *filename;
00355             uuid_unparse(msg->ack.id, id);
00356             id_s = octstr_create(id);
00357             filename = octstr_format("%S/%ld/%s", spool, octstr_hash_key(id_s) % MAX_DIRS, id);
00358             octstr_destroy(id_s);
00359             if (unlink(octstr_get_cstr(filename)) == -1) {
00360                 error(errno, "Could not unlink file `%s'.", octstr_get_cstr(filename));
00361                 octstr_destroy(filename);
00362                 return -1;
00363             }
00364             counter_decrease(counter);
00365             octstr_destroy(filename);
00366             break;
00367         }
00368         default:
00369             return -1;
00370     }
00371 
00372     return 0;
00373 }
00374 
00375 
00376 static int store_spool_save_ack(Msg *msg, ack_status_t status)
00377 {
00378     int ret;
00379     Msg *nack = msg_create(ack);
00380 
00381     nack->ack.nack = status;
00382     uuid_copy(nack->ack.id, msg->sms.id);
00383     nack->ack.time = msg->sms.time;
00384     ret = store_spool_save(nack);
00385     msg_destroy(nack);
00386 
00387     return ret;
00388 }
00389 
00390 
00391 static void store_spool_shutdown()
00392 {
00393     if (spool == NULL)
00394         return;
00395         
00396     counter_destroy(counter);
00397     octstr_destroy(spool);
00398     gwlist_destroy(loaded, NULL);
00399 }
00400 
00401 
00402 int store_spool_init(const Octstr *store_dir)
00403 {
00404     DIR *dir;
00405 
00406     store_messages = store_spool_messages;
00407     store_save = store_spool_save;
00408     store_save_ack = store_spool_save_ack;
00409     store_load = store_spool_load;
00410     store_dump = store_spool_dump;
00411     store_shutdown = store_spool_shutdown;
00412     store_status = store_spool_status;
00413 
00414     if (store_dir == NULL)
00415         return 0;
00416 
00417     /* check if we can open directory */
00418     if ((dir = opendir(octstr_get_cstr(store_dir))) == NULL) {
00419         error(errno, "Could not open directory `%s'", octstr_get_cstr(store_dir));
00420         return -1;
00421     }
00422     closedir(dir);
00423 
00424     loaded = gwlist_create();
00425     gwlist_add_producer(loaded);
00426     spool = octstr_duplicate(store_dir);
00427     counter = counter_create();
00428 
00429     return 0;
00430 }
00431 
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.