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

dlr.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 
00057 /*
00058  * gw/dlr.c
00059  *
00060  * Implementation of handling delivery reports (DLRs)
00061  *
00062  * Andreas Fink <andreas@fink.org>, 18.08.2001
00063  * Stipe Tolj <stolj@wapme.de>, 22.03.2002
00064  * Alexander Malysh <a.malysh@centrium.de> 2003
00065  *
00066  * Changes:
00067  * 2001-12-17: andreas@fink.org:
00068  *     implemented use of mutex to avoid two mysql calls to run at the same time
00069  * 2002-03-22: stolj@wapme.de:
00070  *     added more abstraction to fit for several other storage types
00071  * 2002-08-04: stolj@wapme.de:
00072  *     added simple database library (sdb) support
00073  * 2002-11-14: stolj@wapme.de:
00074  *     added re-routing info for DLRs to route via bearerbox to the same smsbox
00075  *     instance. This is required if you use state conditioned smsboxes or smppboxes
00076  *     via one bearerbox. Previously bearerbox was simple ignoring to which smsbox
00077  *     connection a msg is passed. Now we can route the messages inside bearerbox.
00078  */
00079  
00080 #include <ctype.h>
00081 #include <time.h>
00082 #include <errno.h>
00083 #include <limits.h>
00084 #include <string.h>
00085 
00086 #include <unistd.h>
00087 
00088 #include "gwlib/gwlib.h"
00089 #include "sms.h"
00090 #include "dlr.h"
00091 #include "dlr_p.h"
00092 
00093 /* Our callback functions */
00094 static struct dlr_storage *handles = NULL;
00095 
00096 /*
00097  * Function to allocate a new struct dlr_entry entry
00098  * and intialize it to zero
00099  */
00100 struct dlr_entry *dlr_entry_create(void)
00101 {
00102     struct dlr_entry *dlr;
00103 
00104     dlr = gw_malloc(sizeof(*dlr));
00105     gw_assert(dlr != NULL);
00106 
00107     /* set all values to NULL */
00108     memset(dlr, 0, sizeof(*dlr));
00109 
00110     return dlr;
00111 }
00112 
00113 /* 
00114  * Duplicate dlr entry
00115  */
00116 struct dlr_entry *dlr_entry_duplicate(const struct dlr_entry *dlr)
00117 {
00118     struct dlr_entry *ret;
00119 
00120     if (dlr == NULL)
00121         return NULL;
00122 
00123     ret = dlr_entry_create();
00124     ret->smsc = octstr_duplicate(dlr->smsc);
00125     ret->timestamp = octstr_duplicate(dlr->timestamp);
00126     ret->source = octstr_duplicate(dlr->source);
00127     ret->destination = octstr_duplicate(dlr->destination);
00128     ret->service = octstr_duplicate(dlr->service);
00129     ret->url = octstr_duplicate(dlr->url);
00130     ret->boxc_id = octstr_duplicate(dlr->boxc_id);
00131     ret->mask = dlr->mask;
00132 
00133     return ret;
00134 }
00135 
00136 /*
00137  * Function to destroy the struct dlr_entry entry
00138  */
00139 void dlr_entry_destroy(struct dlr_entry *dlr)
00140 {
00141     /* sanity check */
00142     if (dlr == NULL)
00143         return;
00144 
00145 #define O_DELETE(a)  { if (a) octstr_destroy(a); a = NULL; }
00146 
00147     O_DELETE(dlr->smsc);
00148     O_DELETE(dlr->timestamp);
00149     O_DELETE(dlr->source);
00150     O_DELETE(dlr->destination);
00151     O_DELETE(dlr->service);
00152     O_DELETE(dlr->url);
00153     O_DELETE(dlr->boxc_id);
00154 
00155 #undef O_DELETE
00156 
00157     dlr->mask = 0;
00158     gw_free(dlr);
00159 }
00160 
00161 /*
00162  * Load all configuration directives that are common for all database
00163  * types that use the 'dlr-db' group to define which attributes are 
00164  * used in the table
00165  */
00166 struct dlr_db_fields *dlr_db_fields_create(CfgGroup *grp)
00167 {
00168     struct dlr_db_fields *ret = NULL;
00169 
00170     ret = gw_malloc(sizeof(*ret));
00171     gw_assert(ret != NULL);
00172     memset(ret, 0, sizeof(*ret));
00173 
00174     if (!(ret->table = cfg_get(grp, octstr_imm("table"))))
00175         panic(0, "DLR: DB: directive 'table' is not specified!");
00176     if (!(ret->field_smsc = cfg_get(grp, octstr_imm("field-smsc"))))
00177         panic(0, "DLR: DB: directive 'field-smsc' is not specified!");
00178     if (!(ret->field_ts = cfg_get(grp, octstr_imm("field-timestamp"))))
00179         panic(0, "DLR: DB: directive 'field-timestamp' is not specified!");
00180     if (!(ret->field_src = cfg_get(grp, octstr_imm("field-source"))))
00181         panic(0, "DLR: DB: directive 'field-source' is not specified!");
00182     if (!(ret->field_dst = cfg_get(grp, octstr_imm("field-destination"))))
00183         panic(0, "DLR: DB: directive 'field-destination' is not specified!");
00184     if (!(ret->field_serv = cfg_get(grp, octstr_imm("field-service"))))
00185         panic(0, "DLR: DB: directive 'field-service' is not specified!");
00186     if (!(ret->field_url = cfg_get(grp, octstr_imm("field-url"))))
00187         panic(0, "DLR: DB: directive 'field-url' is not specified!");
00188     if (!(ret->field_mask = cfg_get(grp, octstr_imm("field-mask"))))
00189         panic(0, "DLR: DB: directive 'field-mask' is not specified!");
00190     if (!(ret->field_status = cfg_get(grp, octstr_imm("field-status"))))
00191         panic(0, "DLR: DB: directive 'field-status' is not specified!");
00192     if (!(ret->field_boxc = cfg_get(grp, octstr_imm("field-boxc-id"))))
00193         panic(0, "DLR: DB: directive 'field-boxc-id' is not specified!");
00194 
00195     return ret;
00196 }
00197 
00198 void dlr_db_fields_destroy(struct dlr_db_fields *fields)
00199 {
00200     /* sanity check */
00201     if (fields == NULL)
00202         return;
00203 
00204 #define O_DELETE(a)  { if (a) octstr_destroy(a); a = NULL; }
00205 
00206     O_DELETE(fields->table);
00207     O_DELETE(fields->field_smsc);
00208     O_DELETE(fields->field_ts);
00209     O_DELETE(fields->field_src);
00210     O_DELETE(fields->field_dst);
00211     O_DELETE(fields->field_serv);
00212     O_DELETE(fields->field_url);
00213     O_DELETE(fields->field_mask);
00214     O_DELETE(fields->field_status);
00215     O_DELETE(fields->field_boxc);
00216 
00217 #undef O_DELETE
00218 
00219     gw_free(fields);
00220 }
00221 
00222 
00223 /*
00224  * Initialize specifically dlr storage. If defined storage is unknown
00225  * then panic.
00226  */
00227 void dlr_init(Cfg* cfg)
00228 {
00229     CfgGroup *grp;
00230     Octstr *dlr_type;
00231 
00232     /* check which DLR storage type we are using */
00233     grp = cfg_get_single_group(cfg, octstr_imm("core"));
00234     if(grp == NULL)
00235     panic(0, "DLR: can't find group core");
00236 
00237     dlr_type = cfg_get(grp, octstr_imm("dlr-storage"));
00238 
00239     /* 
00240      * assume we are using internal memory in case no directive
00241      * has been specified, warn the user anyway
00242      */
00243     if (dlr_type == NULL) {
00244         dlr_type = octstr_imm("internal");
00245         warning(0, "DLR: using default 'internal' for storage type.");
00246     }
00247 
00248     /* call the sub-init routine */
00249     if (octstr_compare(dlr_type, octstr_imm("mysql")) == 0) {
00250         handles = dlr_init_mysql(cfg);
00251     } else if (octstr_compare(dlr_type, octstr_imm("sdb")) == 0) {
00252         handles = dlr_init_sdb(cfg);
00253     } else if (octstr_compare(dlr_type, octstr_imm("oracle")) == 0) {
00254         handles = dlr_init_oracle(cfg);
00255     } else if (octstr_compare(dlr_type, octstr_imm("internal")) == 0) {
00256         handles = dlr_init_mem(cfg);
00257     } else if (octstr_compare(dlr_type, octstr_imm("pgsql")) == 0) {
00258         handles = dlr_init_pgsql(cfg);
00259     }
00260 
00261     /*
00262      * add aditional types here
00263      */
00264 
00265      if (handles == NULL) {
00266         panic(0, "DLR: storage type '%s' is not supported!", octstr_get_cstr(dlr_type));
00267     }
00268 
00269     /* check needed function pointers */
00270     if (handles->dlr_add == NULL || handles->dlr_get == NULL || handles->dlr_remove == NULL)
00271         panic(0, "DLR: storage type '%s' don't implement needed functions", octstr_get_cstr(dlr_type));
00272 
00273     /* get info from storage */
00274     info(0, "DLR using storage type: %s", handles->type);
00275 
00276     /* cleanup */
00277     octstr_destroy(dlr_type);
00278 }
00279 
00280 /*
00281  * Shutdown dlr storage
00282  */
00283 void dlr_shutdown()
00284 {
00285     if (handles != NULL && handles->dlr_shutdown != NULL)
00286         handles->dlr_shutdown();
00287 }
00288 
00289 /* 
00290  * Return count waiting delivery entries or -1 if error occurs
00291  */
00292 long dlr_messages(void)
00293 {
00294     if (handles != NULL && handles->dlr_messages != NULL)
00295         return handles->dlr_messages();
00296 
00297     return -1;
00298 }
00299 
00300 /*
00301  * Return type of used dlr storage
00302  */
00303 const char* dlr_type(void)
00304 {
00305     if (handles != NULL && handles->type != NULL)
00306         return handles->type;
00307 
00308     return "unknown";
00309 }
00310  
00311 /*
00312  * Add new dlr entry into dlr storage
00313  */
00314 void dlr_add(const Octstr *smsc, const Octstr *ts, const Msg *msg)
00315 {
00316     struct dlr_entry *dlr = NULL;
00317 
00318     if(octstr_len(smsc) == 0) {
00319     warning(0, "DLR[%s]: Can't add a dlr without smsc-id", dlr_type());
00320         return;
00321     }
00322 
00323     /* sanity check */
00324     if (handles == NULL || handles->dlr_add == NULL || msg == NULL)
00325         return;
00326 
00327     /* check if delivery receipt requested */
00328     if (!DLR_IS_ENABLED(msg->sms.dlr_mask))
00329         return;
00330 
00331      /* allocate new struct dlr_entry struct */
00332     dlr = dlr_entry_create();
00333     gw_assert(dlr != NULL);
00334 
00335     /* now copy all values, we are interested in */
00336     dlr->smsc = (smsc ? octstr_duplicate(smsc) : octstr_create(""));
00337     dlr->timestamp = (ts ? octstr_duplicate(ts) : octstr_create(""));
00338     dlr->source = (msg->sms.sender ? octstr_duplicate(msg->sms.sender) : octstr_create(""));
00339     dlr->destination = (msg->sms.receiver ? octstr_duplicate(msg->sms.receiver) : octstr_create(""));
00340     dlr->service = (msg->sms.service ? octstr_duplicate(msg->sms.service) : octstr_create(""));
00341     dlr->url = (msg->sms.dlr_url ? octstr_duplicate(msg->sms.dlr_url) : octstr_create(""));
00342     dlr->boxc_id = (msg->sms.boxc_id ? octstr_duplicate(msg->sms.boxc_id) : octstr_create(""));
00343     dlr->mask = msg->sms.dlr_mask;
00344 
00345     debug("dlr.dlr", 0, "DLR[%s]: Adding DLR smsc=%s, ts=%s, src=%s, dst=%s, mask=%d, boxc=%s",
00346               dlr_type(), octstr_get_cstr(dlr->smsc), octstr_get_cstr(dlr->timestamp),
00347               octstr_get_cstr(dlr->source), octstr_get_cstr(dlr->destination), dlr->mask, octstr_get_cstr(dlr->boxc_id));
00348     
00349     /* call registered function */
00350     handles->dlr_add(dlr);
00351 }
00352 
00353 /*
00354  * Return Msg* if dlr entry found in DB, otherwise NULL.
00355  * NOTE: If typ is end status (e.g. DELIVERED) then dlr entry
00356  *       will be removed from DB.
00357  */
00358 Msg *dlr_find(const Octstr *smsc, const Octstr *ts, const Octstr *dst, int typ)
00359 {
00360     Msg *msg = NULL;
00361     struct dlr_entry *dlr = NULL;
00362     
00363     if(octstr_len(smsc) == 0) {
00364     warning(0, "DLR[%s]: Can't find a dlr without smsc-id", dlr_type());
00365         return NULL;
00366     }
00367 
00368     /* check if we have handler registered */
00369     if (handles == NULL || handles->dlr_get == NULL)
00370         return NULL;
00371 
00372     debug("dlr.dlr", 0, "DLR[%s]: Looking for DLR smsc=%s, ts=%s, dst=%s, type=%d",
00373                                  dlr_type(), octstr_get_cstr(smsc), octstr_get_cstr(ts), octstr_get_cstr(dst), typ);
00374 
00375     dlr = handles->dlr_get(smsc, ts, dst);
00376     if (dlr == NULL)  {
00377         warning(0, "DLR[%s]: DLR from SMSC<%s> for DST<%s> not found.",
00378                 dlr_type(), octstr_get_cstr(smsc), octstr_get_cstr(dst));         
00379         return NULL;
00380     }
00381 
00382 #define O_SET(x, val) if (octstr_len(val) > 0) { x = val; val = NULL; }
00383 
00384     if ((typ & dlr->mask) > 0) {
00385         /* its an entry we are interested in */
00386         msg = msg_create(sms);
00387         msg->sms.sms_type = report_mo;
00388         msg->sms.dlr_mask = typ;
00389         O_SET(msg->sms.service, dlr->service);
00390         O_SET(msg->sms.smsc_id, dlr->smsc);
00391         O_SET(msg->sms.receiver, dlr->destination);
00392         O_SET(msg->sms.sender, dlr->source);
00393         /* if dlr_url was present, recode it here again */
00394         O_SET(msg->sms.dlr_url, dlr->url);
00395         /* 
00396          * insert original message to the data segment 
00397          * later in the smsc module 
00398          */
00399         msg->sms.msgdata = NULL;
00400         /* 
00401          * If a boxc_id is available, then instruct bearerbox to 
00402          * route this msg back to originating smsbox
00403          */
00404         O_SET(msg->sms.boxc_id, dlr->boxc_id);
00405 
00406         time(&msg->sms.time);
00407         debug("dlr.dlr", 0, "DLR[%s]: created DLR message for URL <%s>",
00408                       dlr_type(), (msg->sms.dlr_url?octstr_get_cstr(msg->sms.dlr_url):""));
00409     } else {
00410         debug("dlr.dlr", 0, "DLR[%s]: Ignoring DLR message because of mask type=%d dlr->mask=%d", dlr_type(), typ, dlr->mask);
00411         /* ok that was a status report but we where not interested in having it */
00412         msg = NULL;
00413     }
00414 
00415 #undef O_SET
00416  
00417     /* check for end status and if so remove from storage */
00418     if ((typ & DLR_BUFFERED) && ((dlr->mask & DLR_SUCCESS) || (dlr->mask & DLR_FAIL))) {
00419         debug("dlr.dlr", 0, "DLR[%s]: DLR not destroyed, still waiting for other delivery report", dlr_type());
00420         /* update dlr entry status if function defined */
00421         if (handles != NULL && handles->dlr_update != NULL)
00422             handles->dlr_update(smsc, ts, dst, typ);
00423     } else {
00424         if (handles != NULL && handles->dlr_remove != NULL) {
00425             /* it's not good for internal storage, but better for all others */
00426             handles->dlr_remove(smsc, ts, dst);
00427         } else {
00428             warning(0, "DLR[%s]: Storage don't have remove operation defined", dlr_type());
00429         }
00430     }
00431 
00432     /* destroy struct dlr_entry */
00433     dlr_entry_destroy(dlr);
00434 
00435     return msg;
00436 }
00437     
00438 void dlr_flush(void)
00439 {
00440     info(0, "Flushing all %ld queued DLR messages in %s storage", dlr_messages(), 
00441             dlr_type());
00442  
00443     if (handles != NULL && handles->dlr_flush != NULL)
00444         handles->dlr_flush();
00445 }
00446 
00447 
00448 Msg* create_dlr_from_msg(const Octstr *smsc, const Msg *msg, const Octstr *reply, long stat)
00449 {
00450     Msg *dlrmsg;
00451 
00452     if (msg == NULL)
00453         return NULL;
00454 
00455     /* generate DLR */
00456     debug("dlr.dlr", 0,"SMSC[%s]: creating DLR message",
00457                 (smsc ? octstr_get_cstr(smsc) : "UNKNOWN"));
00458 
00459     dlrmsg = msg_create(sms);
00460     gw_assert(dlrmsg != NULL);
00461 
00462     dlrmsg->sms.service = octstr_duplicate(msg->sms.service);
00463     dlrmsg->sms.dlr_mask = stat;
00464     dlrmsg->sms.sms_type = report_mo;
00465     dlrmsg->sms.smsc_id = octstr_duplicate(smsc ? smsc : msg->sms.smsc_id);
00466     dlrmsg->sms.sender = octstr_duplicate(msg->sms.sender);
00467     dlrmsg->sms.receiver = octstr_duplicate(msg->sms.receiver);
00468     dlrmsg->sms.dlr_url = octstr_duplicate(msg->sms.dlr_url);
00469     dlrmsg->sms.msgdata = octstr_duplicate(reply);
00470     dlrmsg->sms.boxc_id = octstr_duplicate(msg->sms.boxc_id);
00471     time(&dlrmsg->sms.time);
00472 
00473     debug("dlr.dlr", 0,"SMSC[%s]: DLR = %s",
00474                 (smsc ? octstr_get_cstr(smsc) : "UNKNOWN"),
00475                 (dlrmsg->sms.dlr_url ? octstr_get_cstr(dlrmsg->sms.dlr_url) : ""));
00476 
00477     return dlrmsg;
00478 }
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.