WvStreams
uniclientconn.cc
00001 /*
00002  * Worldvisions Tunnel Vision Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  *
00005  * Manages a connection between the UniConf client and daemon.
00006  */
00007 #include "uniclientconn.h"
00008 #include "wvaddr.h"
00009 #include "wvtclstring.h"
00010 #include "strutils.h"
00011 
00012 /***** UniClientConn *****/
00013 
00014 /* This table is _very_ important!!!
00015  *
00016  * With UniConf, we promise to never remove or modify the behaviour of
00017  * any of the commands listed here.  If you want to modify anything,
00018  * you'd better just add a new command instead.  We keep track of the
00019  * version of the UniConf protocol by the number of commands supported
00020  * by the server.
00021  *
00022  * @see UniClientConn::Commands
00023  */
00024 const UniClientConn::CommandInfo UniClientConn::cmdinfos[
00025     UniClientConn::NUM_COMMANDS] = {
00026     // requests
00027     { "noop", "noop: verify that the connection is active" },
00028     { "get", "get <key>: get the value of a key" },
00029     { "set", "set <key> <value>: sets the value of a key" },
00030     { "setv", "setv <key> <value> ...: set multiple key-value pairs" },
00031     { "del", "del <key>: deletes the key" },
00032     { "subt", "subt <key> <recurse?>: enumerates the children of a key" },
00033     { "hchild", "hchild <key>: returns whether a key has children" },
00034     { "commit", "commit: commits changes to disk" },
00035     { "refresh", "refresh: refresh contents from disk" },
00036     { "quit", "quit: kills the session nicely" },
00037     { "help", "help: returns this help text" },
00038 
00039     // command completion replies
00040     { "OK", "OK <payload>: reply on command success" },
00041     { "FAIL", "FAIL <payload>: reply on command failure" },
00042     { "CHILD", "CHILD <key> TRUE / FALSE: key has children or not" },
00043     { "ONEVAL", "ONEVAL <key> <value>: reply to a get" },
00044 
00045     // partial replies
00046     { "VAL", "VAL <key> <value>: intermediate reply value of a key" },
00047     { "TEXT", "TEXT <text>: intermediate reply of a text message" },
00048 
00049     // events
00050     { "HELLO", "HELLO <version> <message>: sent by server on connection" },
00051     { "NOTICE", "NOTICE <key> <oldval> <newval>: forget key and its children" },
00052 };
00053 
00054 
00055 UniClientConn::UniClientConn(IWvStream *_s, WvStringParm dst) :
00056     WvStreamClone(_s),
00057     log(WvString("UniConf to %s", dst.isnull() && _s->src() ? *_s->src() : WvString(dst)),
00058     WvLog::Debug5), closed(false), version(-1), payloadbuf("")
00059 {
00060     log("Opened\n");
00061 }
00062 
00063 
00064 UniClientConn::~UniClientConn()
00065 {
00066     close();
00067 }
00068 
00069 
00070 void UniClientConn::close()
00071 {
00072     if (!closed)
00073     {
00074         closed = true;
00075         WvStreamClone::close();
00076         log("Closed\n");
00077     }
00078 }
00079 
00080 
00081 WvString UniClientConn::readmsg()
00082 {
00083     WvString word;
00084     while ((word = wvtcl_getword(msgbuf,
00085                                  WVTCL_NASTY_NEWLINES,
00086                                  false)).isnull())
00087     {
00088         // use lots of readahead to prevent unnecessary runs through select()
00089         // during heavy data transfers.
00090         char *line = getline(0, '\n', 20480);
00091         if (line)
00092         {
00093             msgbuf.putstr(line);
00094             msgbuf.put('\n');
00095         }
00096         else
00097         {
00098             if (!WvStreamClone::isok())
00099             {
00100                 // possibly left some incomplete command behind
00101                 msgbuf.zap();
00102             }
00103             return WvString::null;
00104         }
00105     }
00106     if (!!word && 0)
00107         log("Read: %s\n", word);
00108     return word;
00109 }
00110 
00111 
00112 void UniClientConn::writemsg(WvStringParm msg)
00113 {
00114     write(msg);
00115     write("\n");
00116     // log("Wrote: %s\n", msg);
00117 }
00118 
00119 
00120 UniClientConn::Command UniClientConn::readcmd()
00121 {
00122     WvString buf;
00123     return readcmd(buf);
00124 }
00125 
00126 UniClientConn::Command UniClientConn::readcmd(WvString &command)
00127 {
00128     WvString msg(readmsg());
00129     if (msg.isnull())
00130         return NONE;
00131 
00132     // extract command, leaving the remainder in payloadbuf
00133     payloadbuf.reset(msg);
00134     command = readarg();
00135 
00136     if (command.isnull())
00137         return NONE;
00138 
00139     for (int i = 0; i < NUM_COMMANDS; ++i)
00140         if (strcasecmp(cmdinfos[i].name, command.cstr()) == 0)
00141             return Command(i);
00142     return INVALID;
00143 }
00144 
00145 
00146 WvString UniClientConn::readarg()
00147 {
00148     return wvtcl_getword(payloadbuf);
00149 }
00150 
00151 
00152 void UniClientConn::writecmd(UniClientConn::Command cmd, WvStringParm msg)
00153 {
00154     if (msg)
00155         write(WvString("%s %s\n", cmdinfos[cmd].name, msg));
00156     else
00157         write(WvString("%s\n", cmdinfos[cmd].name));
00158 }
00159 
00160 
00161 void UniClientConn::writeok(WvStringParm payload)
00162 {
00163     writecmd(REPLY_OK, payload);
00164 }
00165 
00166 
00167 void UniClientConn::writefail(WvStringParm payload)
00168 {
00169     writecmd(REPLY_FAIL, payload);
00170 }
00171 
00172 
00173 void UniClientConn::writevalue(const UniConfKey &key, WvStringParm value)
00174 {
00175     if (value == WvString::null)
00176         writecmd(PART_VALUE, wvtcl_escape(key));
00177     else
00178         writecmd(PART_VALUE, spacecat(wvtcl_escape(key), wvtcl_escape(value)));
00179 }
00180 
00181 
00182 void UniClientConn::writeonevalue(const UniConfKey &key, WvStringParm value)
00183 {
00184     writecmd(REPLY_ONEVAL, spacecat(wvtcl_escape(key), wvtcl_escape(value)));
00185 }
00186 
00187 
00188 void UniClientConn::writetext(WvStringParm text)
00189 {
00190     writecmd(PART_TEXT, wvtcl_escape(text));
00191 }
00192 
00193