WvStreams
wvprotostream.cc
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * WvProtoStream is a framework that makes it easy to communicate using
00006  * common command-response driven protocols.  This is supposed to be flexible
00007  * enough to handle FTP, HTTP, SMTP, tunnelv, Weaver rcmd, and many others.
00008  */
00009 #include "wvprotostream.h"
00010 #include "wvlog.h"
00011 #include "strutils.h"
00012 #include <ctype.h>
00013 #include <assert.h>
00014 
00015 
00016 WvProtoStream::WvProtoStream(WvStream *_cloned, WvLog *_debuglog)
00017                 : WvStreamClone(_cloned)
00018 {
00019     if (_debuglog)
00020         logp = new WvLog(_debuglog->split(WvLog::Debug4));
00021     else
00022         logp = NULL;
00023     
00024     log_enable = true;
00025     state = 0;
00026 }
00027 
00028 
00029 WvProtoStream::~WvProtoStream()
00030 {
00031     close();
00032     WVRELEASE(logp);
00033 }
00034 
00035 
00036 /* Just like a WvStream::uwrite(), but it copies all output to WvLog if
00037  * log_enable==true.
00038  */
00039 size_t WvProtoStream::uwrite(const void *buf, size_t size)
00040 {
00041     if (logp && log_enable)
00042     {
00043         (*logp)("Sent: ");
00044         logp->write(buf, size);
00045         (*logp)("\n");
00046     }
00047     
00048     return WvStreamClone::uwrite(buf, size);
00049 }
00050 
00051 
00052 WvProtoStream::Token *WvProtoStream::next_token()
00053 {
00054     static unsigned char whitespace[] = " \t\r\n";
00055     size_t len;
00056     
00057     // find and remove up to first non-whitespace
00058     tokbuf.get(tokbuf.match(whitespace, sizeof(whitespace)));
00059 
00060     // return a token up to the first whitespace character
00061     len = tokbuf.notmatch(whitespace, sizeof(whitespace));
00062     return len ? new Token(tokbuf.get(len), len) : NULL;
00063 }
00064 
00065 
00066 WvString WvProtoStream::next_token_str()
00067 {
00068     Token *t = next_token();
00069     if (!t) return WvString("");
00070     
00071     WvString s(t->data);
00072     delete t;
00073     return s;
00074 }
00075 
00076 
00077 WvString WvProtoStream::token_remaining()
00078 {
00079     tokbuf.put('\0');
00080     return trim_string((char *)tokbuf.get(tokbuf.used()));
00081 }
00082 
00083 
00084 /* Default input tokenizer.  "line" is NULL-terminated, and individual string
00085  * tokens are separated by any amount of whitespace.
00086  */
00087 WvProtoStream::TokenList *WvProtoStream::tokenize()
00088 {
00089     TokenList *tl = new TokenList;
00090     Token *t;
00091 
00092     while ((t = next_token()) != NULL)
00093         tl->append(t, true);
00094 #if 0 
00095     if (logp && log_enable && !tl->isempty())
00096     {
00097         (*logp)("Read: ");
00098         TokenList::Iter i(*tl);
00099         for (i.rewind(); i.next(); )
00100             (*logp)("(%s) ", i.data);
00101         (*logp)("\n");
00102     }
00103 #endif
00104     return tl;
00105 }
00106 
00107 
00108 /* convert a TokenList to an array of Token.
00109  * The TokenList becomes invalid after this operation!
00110  * Remember to free the array afterwards!
00111  */
00112 size_t WvProtoStream::list_to_array(TokenList *tl, Token **array)
00113 {
00114     size_t total = tl->count(), count;
00115     
00116     assert(array);
00117     *array = new Token[total];
00118     
00119     TokenList::Iter i(*tl);
00120     for (count = 0, i.rewind(); i.next(); count++)
00121     {
00122         Token &t = *i;
00123         (*array)[count].fill((unsigned char *)(const char *)t.data, t.length);
00124     }
00125     
00126     delete tl;
00127     return count;
00128 }
00129 
00130 
00131 /* Retrieve an input line and parse its first token.
00132  * This is the usual high-level interface to the input tokenizer. Remember
00133  * to free the array afterwards!
00134  * Ths input line is specifically allowed to be a NULL pointer.  In that case,
00135  * the returned token will be NULL also.
00136  */
00137 WvProtoStream::Token *WvProtoStream::tokline(const char *line)
00138 { 
00139     if (!line) return NULL;
00140     
00141     char *newline = strdup(line);
00142     
00143     tokbuf.zap();
00144     tokbuf.put(line, strlen(line));
00145 
00146     if (logp && log_enable)
00147     {
00148         if (strlen(trim_string(newline)) > 0)
00149             (*logp)("Read: %s\n", trim_string(newline));
00150     }
00151 
00152     free(newline);
00153     
00154     return next_token();
00155 }
00156 
00157 
00158 /* returns -1 if t is not in lookup[], or else the index into lookup where
00159  * the token was found.
00160  */
00161 int WvProtoStream::tokanal(const Token &t, const char **lookup,
00162                            bool case_sensitive)
00163 {
00164     assert(lookup);
00165     
00166     const char **i;
00167     
00168     for (i = lookup; *i; i++)
00169     {
00170         if ( (!case_sensitive && !strcasecmp(t.data, *i))
00171           || ( case_sensitive && !strcmp(t.data, *i)) )
00172             return i - lookup;
00173     }
00174     
00175     return -1;
00176 }
00177 
00178 
00179 void WvProtoStream::do_state(Token &)
00180 {
00181 }
00182 
00183 
00184 void WvProtoStream::switch_state(int newstate)
00185 {
00186     state = newstate;
00187 }
00188 
00189 
00190 /* Default execute() function -- process a line of input, and handle it
00191  * (based on the current system state) using do_state().
00192  */
00193 void WvProtoStream::execute()
00194 {
00195     WvStreamClone::execute();
00196     
00197     Token *t1 = tokline(getline());
00198     
00199     if (t1)
00200     {
00201         do_state(*t1);
00202         delete t1;
00203     }
00204 }
00205 
00206 
00207 
00209 
00210 
00211 
00212 WvProtoStream::Token::Token()
00213 {
00214     // leave empty -- you should call fill() manually later!
00215 }
00216 
00217 
00218 WvProtoStream::Token::Token(const unsigned char *_data, size_t _length)
00219 {
00220     fill(_data, _length);
00221 }
00222 
00223 
00224 void WvProtoStream::Token::fill(const unsigned char *_data,
00225                                 size_t _length)
00226 {
00227     length = _length;
00228     
00229     data.setsize(length + 1);
00230     memcpy(data.edit(), _data, length);
00231     data.edit()[length] = 0;
00232 }
00233 
00234 
00235 WvProtoStream::Token::~Token()
00236 {
00237     // 'data' member is freed automatically
00238 }