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

bb_http.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  * bb_http.c : bearerbox http adminstration commands
00059  *
00060  * NOTE: this is a special bearerbox module - it does call
00061  *   functions from core module! (other modules are fully
00062  *    encapsulated, and only called outside)
00063  *
00064  * Kalle Marjola <rpr@wapit.com> 2000 for project Kannel
00065  */
00066 
00067 #include <errno.h>
00068 #include <signal.h>
00069 #include <unistd.h>
00070 
00071 #include "gwlib/gwlib.h"
00072 #include "bearerbox.h"
00073 
00074 /* passed from bearerbox core */
00075 
00076 extern volatile sig_atomic_t bb_status;
00077 
00078 /* our own thingies */
00079 
00080 static volatile sig_atomic_t httpadmin_running;
00081 
00082 static long ha_port;
00083 static Octstr *ha_interface;
00084 static Octstr *ha_password;
00085 static Octstr *ha_status_pw;
00086 static Octstr *ha_allow_ip;
00087 static Octstr *ha_deny_ip;
00088 
00089 
00090 /*---------------------------------------------------------
00091  * static functions
00092  */
00093 
00094 /*
00095  * check if the password matches. Return NULL if
00096  * it does (or is not required)
00097  */
00098 static Octstr *httpd_check_authorization(List *cgivars, int status)
00099 {
00100     Octstr *password;
00101     static double sleep = 0.01;
00102 
00103     password = http_cgi_variable(cgivars, "password");
00104 
00105     if (status) {
00106     if (ha_status_pw == NULL)
00107         return NULL;
00108 
00109     if (password == NULL)
00110         goto denied;
00111 
00112     if (octstr_compare(password, ha_password)!=0
00113         && octstr_compare(password, ha_status_pw)!=0)
00114         goto denied;
00115     }
00116     else {
00117     if (password == NULL || octstr_compare(password, ha_password)!=0)
00118         goto denied;
00119     }
00120     sleep = 0.0;
00121     return NULL;    /* allowed */
00122 denied:
00123     gwthread_sleep(sleep);
00124     sleep += 1.0;       /* little protection against brute force
00125                  * password cracking */
00126     return octstr_create("Denied");
00127 }
00128 
00129 /*
00130  * check if we still have time to do things
00131  */
00132 static Octstr *httpd_check_status(void)
00133 {
00134     if (bb_status == BB_SHUTDOWN || bb_status == BB_DEAD)
00135     return octstr_create("Avalanche has already started, too late to "
00136                          "save the sheeps");
00137     return NULL;
00138 }
00139     
00140 static Octstr *httpd_status(List *cgivars, int status_type)
00141 {
00142     Octstr *reply;
00143     if ((reply = httpd_check_authorization(cgivars, 1))!= NULL) return reply;
00144     return bb_print_status(status_type);
00145 }
00146 
00147 static Octstr *httpd_store_status(List *cgivars, int status_type)
00148 {
00149     Octstr *reply;
00150     if ((reply = httpd_check_authorization(cgivars, 1))!= NULL) return reply;
00151     return store_status(status_type);
00152 }
00153 
00154 static Octstr *httpd_loglevel(List *cgivars, int status_type)
00155 {
00156     Octstr *reply;
00157     Octstr *level;
00158     int new_loglevel;
00159     
00160     if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply;
00161     if ((reply = httpd_check_status())!= NULL) return reply;
00162  
00163     /* check if new loglevel is given */
00164     level = http_cgi_variable(cgivars, "level");
00165     if (level) {
00166         new_loglevel = atoi(octstr_get_cstr(level));
00167         log_set_log_level(new_loglevel);
00168         return octstr_format("log-level set to %d", new_loglevel);
00169     }
00170     else {
00171         return octstr_create("New level not given");
00172     }
00173 }
00174 
00175 static Octstr *httpd_shutdown(List *cgivars, int status_type)
00176 {
00177     Octstr *reply;
00178     if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply;
00179     if (bb_status == BB_SHUTDOWN)
00180     bb_status = BB_DEAD;
00181     else {
00182     bb_shutdown();
00183         gwthread_wakeup(MAIN_THREAD_ID);
00184     }
00185     return octstr_create("Bringing system down");
00186 }
00187 
00188 static Octstr *httpd_isolate(List *cgivars, int status_type)
00189 {
00190     Octstr *reply;
00191     if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply;
00192     if ((reply = httpd_check_status())!= NULL) return reply;
00193 
00194     if (bb_isolate() == -1)
00195     return octstr_create("Already isolated");
00196     else
00197     return octstr_create(GW_NAME " isolated from message providers");
00198 }
00199 
00200 static Octstr *httpd_suspend(List *cgivars, int status_type)
00201 {
00202     Octstr *reply;
00203     if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply;
00204     if ((reply = httpd_check_status())!= NULL) return reply;
00205 
00206     if (bb_suspend() == -1)
00207     return octstr_create("Already suspended");
00208     else
00209     return octstr_create(GW_NAME " suspended");
00210 }
00211 
00212 static Octstr *httpd_resume(List *cgivars, int status_type)
00213 {
00214     Octstr *reply;
00215     if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply;
00216     if ((reply = httpd_check_status())!= NULL) return reply;
00217  
00218     if (bb_resume() == -1)
00219     return octstr_create("Already running");
00220     else
00221     return octstr_create("Running resumed");
00222 }
00223 
00224 static Octstr *httpd_restart(List *cgivars, int status_type)
00225 {
00226     Octstr *reply;
00227     if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply;
00228     if ((reply = httpd_check_status())!= NULL) return reply;
00229  
00230     if (bb_status == BB_SHUTDOWN) {
00231         bb_status = BB_DEAD;
00232         gwthread_wakeup_all();
00233         return octstr_create("Trying harder to restart");
00234     }
00235     bb_restart();
00236     return octstr_create("Restarting.....");
00237 }
00238 
00239 static Octstr *httpd_flush_dlr(List *cgivars, int status_type)
00240 {
00241     Octstr *reply;
00242     if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply;
00243     if ((reply = httpd_check_status())!= NULL) return reply;
00244 
00245     if (bb_flush_dlr() == -1)
00246     return octstr_create("Suspend " GW_NAME " before trying to flush DLR queue");
00247     else
00248     return octstr_create("DLR queue flushed");
00249 }
00250 
00251 static Octstr *httpd_stop_smsc(List *cgivars, int status_type)
00252 {
00253     Octstr *reply;
00254     Octstr *smsc;
00255     if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply;
00256     if ((reply = httpd_check_status())!= NULL) return reply;
00257 
00258     /* check if the smsc id is given */
00259     smsc = http_cgi_variable(cgivars, "smsc");
00260     if (smsc) {
00261         if (bb_stop_smsc(smsc) == -1)
00262             return octstr_format("Could not shut down smsc-id `%s'", octstr_get_cstr(smsc));
00263         else
00264             return octstr_format("SMSC `%s' shut down", octstr_get_cstr(smsc));
00265     } else
00266         return octstr_create("SMSC id not given");
00267 }
00268 
00269 static Octstr *httpd_restart_smsc(List *cgivars, int status_type)
00270 {
00271     Octstr *reply;
00272     Octstr *smsc;
00273     if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply;
00274     if ((reply = httpd_check_status())!= NULL) return reply;
00275 
00276     /* check if the smsc id is given */
00277     smsc = http_cgi_variable(cgivars, "smsc");
00278     if (smsc) {
00279         if (bb_restart_smsc(smsc) == -1)
00280             return octstr_format("Could not re-start smsc-id `%s'", octstr_get_cstr(smsc));
00281         else
00282             return octstr_format("SMSC `%s' re-started", octstr_get_cstr(smsc));
00283     } else
00284         return octstr_create("SMSC id not given");
00285 }
00286 
00287 /* Known httpd commands and their functions */
00288 static struct httpd_command {
00289     const char *command;
00290     Octstr * (*function)(List *cgivars, int status_type);
00291 } httpd_commands[] = {
00292     { "status", httpd_status },
00293     { "store-status", httpd_store_status },
00294     { "log-level", httpd_loglevel },
00295     { "shutdown", httpd_shutdown },
00296     { "suspend", httpd_suspend },
00297     { "isolate", httpd_isolate },
00298     { "resume", httpd_resume },
00299     { "restart", httpd_restart },
00300     { "flush-dlr", httpd_flush_dlr },
00301     { "stop-smsc", httpd_stop_smsc },
00302     { "start-smsc", httpd_restart_smsc },
00303     { NULL , NULL } /* terminate list */
00304 };
00305 
00306 static void httpd_serve(HTTPClient *client, Octstr *ourl, List *headers,
00307                         Octstr *body, List *cgivars)
00308 {
00309     Octstr *reply, *final_reply, *url;
00310     char *content_type;
00311     char *header, *footer;
00312     int status_type;
00313     int i;
00314     long pos;
00315 
00316     reply = final_reply = NULL; /* for compiler please */
00317     url = octstr_duplicate(ourl);
00318 
00319     /* Set default reply format according to client
00320      * Accept: header */
00321     if (http_type_accepted(headers, "text/vnd.wap.wml")) {
00322     status_type = BBSTATUS_WML;
00323     content_type = "text/vnd.wap.wml";
00324     }
00325     else if (http_type_accepted(headers, "text/html")) {
00326     status_type = BBSTATUS_HTML;
00327     content_type = "text/html";
00328     }
00329     else if (http_type_accepted(headers, "text/xml")) {
00330     status_type = BBSTATUS_XML;
00331     content_type = "text/xml";
00332     } else {
00333     status_type = BBSTATUS_TEXT;
00334     content_type = "text/plain";
00335     }
00336 
00337     /* kill '/cgi-bin' prefix */
00338     pos = octstr_search(url, octstr_imm("/cgi-bin/"), 0);
00339     if (pos != -1)
00340         octstr_delete(url, pos, 9);
00341     else if (octstr_get_char(url, 0) == '/')
00342         octstr_delete(url, 0, 1);
00343 
00344     /* look for type and kill it */
00345     pos = octstr_search_char(url, '.', 0);
00346     if (pos != -1) {
00347         Octstr *tmp = octstr_copy(url, pos+1, octstr_len(url) - pos - 1);
00348         octstr_delete(url, pos, octstr_len(url) - pos);
00349 
00350         if (octstr_str_compare(tmp, "txt") == 0)
00351             status_type = BBSTATUS_TEXT;
00352         else if (octstr_str_compare(tmp, "html") == 0)
00353             status_type = BBSTATUS_HTML;
00354         else if (octstr_str_compare(tmp, "xml") == 0)
00355             status_type = BBSTATUS_XML;
00356         else if (octstr_str_compare(tmp, "wml") == 0)
00357             status_type = BBSTATUS_WML;
00358 
00359         octstr_destroy(tmp);
00360     }
00361 
00362     for (i=0; httpd_commands[i].command != NULL; i++) {
00363         if (octstr_str_compare(url, httpd_commands[i].command) == 0) {
00364             reply = httpd_commands[i].function(cgivars, status_type);
00365             break;
00366         }
00367     }
00368 
00369     /* check if command found */
00370     if (httpd_commands[i].command == NULL) {
00371         char *lb = bb_status_linebreak(status_type);
00372     reply = octstr_format("Unknown command `%S'.%sPossible commands are:%s",
00373             ourl, lb, lb);
00374         for (i=0; httpd_commands[i].command != NULL; i++)
00375             octstr_format_append(reply, "%s%s", httpd_commands[i].command, lb);
00376     }
00377 
00378     gw_assert(reply != NULL);
00379 
00380     if (status_type == BBSTATUS_HTML) {
00381     header = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n"
00382         "<html>\n<title>" GW_NAME "</title>\n<body>\n<p>";
00383     footer = "</p>\n</body></html>\n";
00384     content_type = "text/html";
00385     } else if (status_type == BBSTATUS_WML) {
00386     header = "<?xml version=\"1.0\"?>\n"
00387             "<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\" "
00388             "\"http://www.wapforum.org/DTD/wml_1.1.xml\">\n"
00389             "\n<wml>\n <card>\n  <p>";
00390     footer = "  </p>\n </card>\n</wml>\n";
00391     content_type = "text/vnd.wap.wml";
00392     } else if (status_type == BBSTATUS_XML) {
00393     header = "<?xml version=\"1.0\"?>\n"
00394             "<gateway>\n";
00395         footer = "</gateway>\n";
00396     } else {
00397     header = "";
00398     footer = "";
00399     content_type = "text/plain";
00400     }
00401     final_reply = octstr_create(header);
00402     octstr_append(final_reply, reply);
00403     octstr_append_cstr(final_reply, footer);
00404     
00405     /* debug("bb.http", 0, "Result: '%s'", octstr_get_cstr(final_reply));
00406      */
00407     http_destroy_headers(headers);
00408     headers = gwlist_create();
00409     http_header_add(headers, "Content-Type", content_type);
00410 
00411     http_send_reply(client, HTTP_OK, headers, final_reply);
00412 
00413     octstr_destroy(url);
00414     octstr_destroy(ourl);
00415     octstr_destroy(body);
00416     octstr_destroy(reply);
00417     octstr_destroy(final_reply);
00418     http_destroy_headers(headers);
00419     http_destroy_cgiargs(cgivars);
00420 }
00421 
00422 static void httpadmin_run(void *arg)
00423 {
00424     HTTPClient *client;
00425     Octstr *ip, *url, *body;
00426     List *headers, *cgivars;
00427 
00428     while(bb_status != BB_DEAD) {
00429     if (bb_status == BB_SHUTDOWN)
00430         bb_shutdown();
00431         client = http_accept_request(ha_port, &ip, &url, &headers, &body, 
00432                                  &cgivars);
00433     if (client == NULL)
00434         break;
00435     if (is_allowed_ip(ha_allow_ip, ha_deny_ip, ip) == 0) {
00436         info(0, "HTTP admin tried from denied host <%s>, disconnected",
00437          octstr_get_cstr(ip));
00438         http_close_client(client);
00439         continue;
00440     }
00441         httpd_serve(client, url, headers, body, cgivars);
00442     octstr_destroy(ip);
00443     }
00444 
00445     httpadmin_running = 0;
00446 }
00447 
00448 
00449 /*-------------------------------------------------------------
00450  * public functions
00451  *
00452  */
00453 
00454 int httpadmin_start(Cfg *cfg)
00455 {
00456     CfgGroup *grp;
00457     int ssl = 0; 
00458 #ifdef HAVE_LIBSSL
00459     Octstr *ssl_server_cert_file;
00460     Octstr *ssl_server_key_file;
00461 #endif /* HAVE_LIBSSL */
00462     
00463     if (httpadmin_running) return -1;
00464 
00465 
00466     grp = cfg_get_single_group(cfg, octstr_imm("core"));
00467     if (cfg_get_integer(&ha_port, grp, octstr_imm("admin-port")) == -1)
00468     panic(0, "Missing admin-port variable, cannot start HTTP admin");
00469 
00470     ha_interface = cfg_get(grp, octstr_imm("admin-interface"));
00471     ha_password = cfg_get(grp, octstr_imm("admin-password"));
00472     if (ha_password == NULL)
00473     panic(0, "You MUST set HTTP admin-password");
00474     
00475     ha_status_pw = cfg_get(grp, octstr_imm("status-password"));
00476 
00477     ha_allow_ip = cfg_get(grp, octstr_imm("admin-allow-ip"));
00478     ha_deny_ip = cfg_get(grp, octstr_imm("admin-deny-ip"));
00479 
00480 #ifdef HAVE_LIBSSL
00481     cfg_get_bool(&ssl, grp, octstr_imm("admin-port-ssl"));
00482     
00483     /*
00484      * check if SSL is desired for HTTP servers and then
00485      * load SSL client and SSL server public certificates 
00486      * and private keys
00487      */    
00488     ssl_server_cert_file = cfg_get(grp, octstr_imm("ssl-server-cert-file"));
00489     ssl_server_key_file = cfg_get(grp, octstr_imm("ssl-server-key-file"));
00490     if (ssl_server_cert_file != NULL && ssl_server_key_file != NULL) {
00491         /* we are fine here, the following call is now in conn_config_ssl(),
00492          * so there is no reason to do this twice.
00493 
00494         use_global_server_certkey_file(ssl_server_cert_file, 
00495             ssl_server_key_file);
00496         */
00497     } else if (ssl) {
00498        panic(0, "You MUST specify cert and key files within core group for SSL-enabled HTTP servers!");
00499     }
00500 
00501     octstr_destroy(ssl_server_cert_file);
00502     octstr_destroy(ssl_server_key_file);
00503 #endif /* HAVE_LIBSSL */
00504 
00505     http_open_port_if(ha_port, ssl, ha_interface);
00506 
00507     if (gwthread_create(httpadmin_run, NULL) == -1)
00508     panic(0, "Failed to start a new thread for HTTP admin");
00509 
00510     httpadmin_running = 1;
00511     return 0;
00512 }
00513 
00514 
00515 void httpadmin_stop(void)
00516 {
00517     http_close_all_ports();
00518     gwthread_join_every(httpadmin_run);
00519     octstr_destroy(ha_interface);    
00520     octstr_destroy(ha_password);
00521     octstr_destroy(ha_status_pw);
00522     octstr_destroy(ha_allow_ip);
00523     octstr_destroy(ha_deny_ip);
00524     ha_password = NULL;
00525     ha_status_pw = NULL;
00526     ha_allow_ip = NULL;
00527     ha_deny_ip = NULL;
00528 }
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.