smsc_wrapper.c
上传用户:gzpyjq
上传日期:2013-01-31
资源大小:1852k
文件大小:10k
源码类别:

手机WAP编程

开发平台:

WINDOWS

  1. /*
  2.  * SMSC Connection wrapper
  3.  *
  4.  * Interface to old SMS center implementations
  5.  *
  6.  * Kalle Marjola 2000
  7.  */
  8. #include "gwlib/gwlib.h"
  9. #include "smscconn.h"
  10. #include "smscconn_p.h"
  11. #include "bb_smscconn_cb.h"
  12. #include "smsc.h"
  13. #include "smsc_p.h"
  14. typedef struct smsc_wrapper {
  15.     SMSCenter *smsc;
  16.     List *outgoing_queue;
  17.     List *stopped; /* list-trick for suspend/isolate */ 
  18.     long      receiver_thread;
  19.     long sender_thread;
  20.     Mutex *reconnect_mutex;
  21. } SmscWrapper;
  22. static void smscwrapper_destroy(SmscWrapper *wrap)
  23. {
  24.     if (wrap == NULL)
  25. return;
  26.     list_destroy(wrap->outgoing_queue, NULL);
  27.     list_destroy(wrap->stopped, NULL);
  28.     mutex_destroy(wrap->reconnect_mutex);
  29.     if (wrap->smsc != NULL)
  30. smsc_close(wrap->smsc);
  31.     gw_free(wrap);
  32. }
  33. static int reconnect(SMSCConn *conn)
  34. {
  35.     SmscWrapper *wrap = conn->data;
  36.     Msg *msg;
  37.     int ret;
  38.     int wait = 1;
  39.     /* disable double-reconnect
  40.      * NOTE: it is still possible that we do double-connect if
  41.      *   first thread gets through this if-statement and then
  42.      *   execution switches to another thread.. this can be avoided
  43.      *   via double-mutex system, but I do not feel it is worth it,
  44.      *   maybe later --rpr
  45.      */
  46.     if (conn->status == SMSCCONN_RECONNECTING) {
  47. mutex_lock(wrap->reconnect_mutex); /* wait here */
  48. mutex_unlock(wrap->reconnect_mutex);
  49. return 0;
  50.     }
  51.     mutex_lock(wrap->reconnect_mutex);
  52.     debug("bb.sms", 0, "smsc_wrapper <%s>: reconnect started",
  53.   octstr_get_cstr(conn->name));
  54.     while((msg = list_extract_first(wrap->outgoing_queue))!=NULL) {
  55. bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_TEMPORARILY);
  56.     }
  57.     conn->status = SMSCCONN_RECONNECTING;
  58.     
  59.     while(conn->why_killed == SMSCCONN_ALIVE) {
  60. ret = smsc_reopen(wrap->smsc);
  61. if (ret == 0) {
  62.     info(0, "Re-open of %s succeeded.", octstr_get_cstr(conn->name));
  63.     mutex_lock(conn->flow_mutex);
  64.     conn->status = SMSCCONN_ACTIVE;
  65.     conn->connect_time = time(NULL);
  66.     mutex_unlock(conn->flow_mutex);
  67.     bb_smscconn_connected(conn);
  68.     break;
  69. }
  70. else if (ret == -2) {
  71.     error(0, "Re-open of %s failed permanently",
  72.   octstr_get_cstr(conn->name));
  73.     mutex_lock(conn->flow_mutex);
  74.     conn->status = SMSCCONN_DISCONNECTED;
  75.     mutex_unlock(wrap->reconnect_mutex);
  76.     mutex_unlock(conn->flow_mutex);
  77.     return -1; /* permanent failure */
  78. }
  79. else {
  80.     error(0, "Re-open to <%s> failed, retrying after %d minutes...",
  81.   octstr_get_cstr(conn->name), wait);
  82.     gwthread_sleep(wait*60.0);
  83.     wait = wait > 10 ? 10 : wait * 2 + 1;
  84. }
  85.     }
  86.     mutex_unlock(wrap->reconnect_mutex);
  87.     return 0;
  88. }
  89. static Msg *sms_receive(SMSCConn *conn)
  90. {
  91.     SmscWrapper *wrap = conn->data;
  92.     int ret;
  93.     Msg *newmsg = NULL;
  94.     if (smscenter_pending_smsmessage(wrap->smsc) == 1) {
  95.         ret = smscenter_receive_msg(wrap->smsc, &newmsg);
  96.         if (ret == 1) {
  97.             /* if any smsc_id available, use it */
  98.             newmsg->sms.smsc_id = octstr_duplicate(conn->id);
  99.     return newmsg;
  100.         } else if (ret == 0) { /* "NEVER" happens */
  101.             warning(0, "SMSC %s: Pending message returned '1', "
  102.                     "but nothing to receive!", octstr_get_cstr(conn->name));
  103.             msg_destroy(newmsg);
  104.             return NULL;
  105.         } else {
  106.             msg_destroy(newmsg);
  107.     if (reconnect(conn) == -1)
  108. smscconn_shutdown(conn, 0);
  109.     return NULL;
  110.         }
  111.     }
  112.     return NULL;
  113. }
  114. static void wrapper_receiver(void *arg)
  115. {
  116.     Msg  *msg;
  117.     SMSCConn  *conn = arg;
  118.     SmscWrapper *wrap = conn->data;
  119.     /* SmscWrapper *wrap = conn->data; ** non-used */
  120.     double  sleep = 0.0001;
  121.     
  122.     
  123.     /* remove messages from SMSC until we are killed */
  124.     while(conn->why_killed == SMSCCONN_ALIVE) {
  125.         list_consume(wrap->stopped); /* block here if suspended/isolated */
  126. msg = sms_receive(conn);
  127. if (msg) {
  128.             debug("bb.sms", 0, "smscconn (%s): new message received",
  129.   octstr_get_cstr(conn->name));
  130.             sleep = 0.0001;
  131.     bb_smscconn_receive(conn, msg);
  132.         }
  133.         else {
  134.     /* note that this implementations means that we sleep even
  135.      * when we fail connection.. but time is very short, anyway
  136.      */
  137.             gwthread_sleep(sleep);
  138.             /* gradually sleep longer and longer times until something starts to
  139.              * happen - this of course reduces response time, but that's better than
  140.              * extensive CPU usage when it is not used
  141.              */
  142.             sleep *= 2;
  143.             if (sleep >= 2.0)
  144.                 sleep = 1.999999;
  145.         }
  146.     }
  147.     conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
  148.     /* this thread is joined at sender */
  149. }
  150. static int sms_send(SMSCConn *conn, Msg *msg)
  151. {
  152.     SmscWrapper *wrap = conn->data;
  153.     int ret;
  154.     debug("bb.sms", 0, "smscconn_sender (%s): sending message",
  155.   octstr_get_cstr(conn->name));
  156.         
  157.     ret = smscenter_submit_msg(wrap->smsc, msg);
  158.     if (ret == -1) {
  159. bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_REJECTED);
  160. if (reconnect(conn) == -1)
  161.     smscconn_shutdown(conn, 0);
  162.         return -1;
  163.     } else {
  164. bb_smscconn_sent(conn, msg);
  165.         return 0;
  166.     }
  167. }
  168. static void wrapper_sender(void *arg)
  169. {
  170.     Msg  *msg;
  171.     SMSCConn  *conn = arg;
  172.     SmscWrapper *wrap = conn->data;
  173.     /* send messages to SMSC until our putgoing_list is empty and
  174.      * no producer anymore (we are set to shutdown) */
  175.     while(conn->status != SMSCCONN_DEAD) {
  176. if ((msg = list_consume(wrap->outgoing_queue)) == NULL)
  177.             break;
  178.         if (octstr_search_char(msg->sms.receiver, ' ', 0) != -1) {
  179.             /*
  180.              * multi-send: this should be implemented in corresponding
  181.              *  SMSC protocol, but while we are waiting for that...
  182.              */
  183.             int i;
  184.     Msg *newmsg;
  185.             /* split from spaces: in future, split with something more sensible,
  186.              * this is dangerous... (as space is url-encoded as '+')
  187.              */
  188.             List *nlist = octstr_split_words(msg->sms.receiver);
  189.     debug("bb.sms", 0, "Handling multi-receiver message");
  190.             for(i=0; i < list_len(nlist); i++) {
  191. newmsg = msg_duplicate(msg);
  192.                 octstr_destroy(newmsg->sms.receiver);
  193.                 newmsg->sms.receiver = list_get(nlist, i);
  194.                 sms_send(conn, newmsg);
  195.             }
  196.             list_destroy(nlist, NULL);
  197.             msg_destroy(msg);
  198.         }
  199.         else
  200.     sms_send(conn,msg);
  201.     }
  202.     /* cleanup, we are now dying */
  203.     debug("bb.sms", 0, "SMSCConn %s sender died, waiting for receiver",
  204.   octstr_get_cstr(conn->name));
  205.     
  206.     conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
  207.     if (conn->is_stopped) {
  208. list_remove_producer(wrap->stopped);
  209. conn->is_stopped = 0;
  210.     }
  211.     gwthread_wakeup(wrap->receiver_thread);
  212.     gwthread_join(wrap->receiver_thread);
  213.     /* call 'failed' to all messages still in queue */
  214.     
  215.     mutex_lock(conn->flow_mutex);
  216.     conn->status = SMSCCONN_DEAD;
  217.     while((msg = list_extract_first(wrap->outgoing_queue))!=NULL) {
  218. bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_SHUTDOWN);
  219.     }
  220.     smscwrapper_destroy(wrap);
  221.     conn->data = NULL;
  222.     
  223.     mutex_unlock(conn->flow_mutex);
  224.     bb_smscconn_killed();
  225. }
  226. static int wrapper_add_msg(SMSCConn *conn, Msg *sms)
  227. {
  228.     SmscWrapper *wrap = conn->data;
  229.     Msg *copy;
  230.     copy = msg_duplicate(sms);
  231.     list_produce(wrap->outgoing_queue, copy);
  232.     return 0;
  233. }
  234. static int wrapper_shutdown(SMSCConn *conn, int finish_sending)
  235. {
  236.     SmscWrapper *wrap = conn->data;
  237.     debug("bb.sms", 0, "Shutting down SMSCConn %s, %s",
  238.   octstr_get_cstr(conn->name), finish_sending ? "slow" : "instant");
  239.     
  240.     if (finish_sending == 0) {
  241. Msg *msg; 
  242. while((msg = list_extract_first(wrap->outgoing_queue))!=NULL) {
  243.     bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_SHUTDOWN);
  244. }
  245.     }
  246.     list_remove_producer(wrap->outgoing_queue);
  247.     gwthread_wakeup(wrap->sender_thread);
  248.     gwthread_wakeup(wrap->receiver_thread);
  249.     return 0;
  250. }
  251. static void wrapper_stop(SMSCConn *conn)
  252. {
  253.     SmscWrapper *wrap = conn->data;
  254.     debug("smscconn", 0, "Stopping wrapper");
  255.     list_add_producer(wrap->stopped);
  256. }
  257. static void wrapper_start(SMSCConn *conn)
  258. {
  259.     SmscWrapper *wrap = conn->data;
  260.     debug("smscconn", 0, "Starting wrapper");
  261.     list_remove_producer(wrap->stopped);
  262. }
  263. static long wrapper_queued(SMSCConn *conn)
  264. {
  265.     SmscWrapper *wrap = conn->data;
  266.     long ret = list_len(wrap->outgoing_queue);
  267.     /* use internal queue as load, maybe something else later */
  268.     
  269.     conn->load = ret;
  270.     return ret;
  271. }
  272. int smsc_wrapper_create(SMSCConn *conn, CfgGroup *cfg)
  273. {
  274.     /* 1. Call smsc_open()
  275.      * 2. create sender/receiver threads
  276.      * 3. fill up the conn
  277.      *
  278.      * XXX open() SHOULD be done in distinct thread, not here!
  279.      */
  280.     SmscWrapper *wrap;
  281.     wrap = gw_malloc(sizeof(SmscWrapper));
  282.     wrap->smsc = NULL;
  283.     conn->data = wrap;
  284.     conn->send_msg = wrapper_add_msg;
  285.     
  286.     
  287.     wrap->outgoing_queue = list_create();
  288.     wrap->stopped = list_create();
  289.     wrap->reconnect_mutex = mutex_create();
  290.     list_add_producer(wrap->outgoing_queue);
  291.     
  292.     if ((wrap->smsc = smsc_open(cfg)) == NULL)
  293. goto error;
  294.     conn->name = octstr_create(smsc_name(wrap->smsc));
  295.     conn->status = SMSCCONN_ACTIVE;
  296.     conn->connect_time = time(NULL);
  297.     if (conn->is_stopped)
  298. list_add_producer(wrap->stopped);
  299.     /* XXX here we could fail things... especially if the second one
  300.      *     fails.. so fix this ASAP
  301.      *
  302.      * moreover, open should be in sender/receiver, so that we can continue
  303.      * while tyring to open... maybe move this, or just wait for new
  304.      * implementations of various SMSC protocols
  305.      */
  306.     
  307.     if ((wrap->receiver_thread = gwthread_create(wrapper_receiver, conn))==-1)
  308. goto error;
  309.     if ((wrap->sender_thread = gwthread_create(wrapper_sender, conn))==-1)
  310. goto error;
  311.     conn->shutdown = wrapper_shutdown;
  312.     conn->queued = wrapper_queued;
  313.     conn->stop_conn = wrapper_stop;
  314.     conn->start_conn = wrapper_start;
  315.     
  316.     return 0;
  317. error:
  318.     error(0, "Failed to create Smsc wrapper");
  319.     conn->data = NULL;
  320.     smscwrapper_destroy(wrap);
  321.     conn->why_killed = SMSCCONN_KILLED_CANNOT_CONNECT;
  322.     conn->status = SMSCCONN_DEAD;
  323.     return -1;
  324. }