WvStreams
|
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 }