tAll clients now use NetworkBuffer code for connect()ing, to allow support for SOCKS; "run from fights" Jet dialog made more usable; SOCKS5 user/password authentication now supported by curses and GTK+ clients; "Sack Bitch" menu item no longer hard-coded - vaccinewars - be a doctor and try to vaccinate the world
 (HTM) git clone git://src.adamsgaard.dk/vaccinewars
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit d6e0ec90931c5375b3157cfbb9e4bb42c8cf13f6
 (DIR) parent ec4529cf865fafa618e04da4a5cc3214e2667cd5
 (HTM) Author: Ben Webb <ben@salilab.org>
       Date:   Mon, 15 Oct 2001 16:04:18 +0000
       
       All clients now use NetworkBuffer code for connect()ing, to allow support for
       SOCKS; "run from fights" Jet dialog made more usable; SOCKS5 user/password
       authentication now supported by curses and GTK+ clients; "Sack Bitch" menu
       item no longer hard-coded
       
       
       Diffstat:
         M src/AIPlayer.c                      |     104 +++++++++++++++++++++++++------
         M src/curses_client.c                 |     231 +++++++++++++++++++++++++------
         M src/gtk_client.c                    |     275 +++++++++++++++++++++++++------
         M src/message.c                       |      21 ---------------------
         M src/message.h                       |       1 -
         M src/network.c                       |     230 ++++++++++++++-----------------
         M src/network.h                       |      12 ++++++++----
         M src/serverside.c                    |       3 ++-
       
       8 files changed, 617 insertions(+), 260 deletions(-)
       ---
 (DIR) diff --git a/src/AIPlayer.c b/src/AIPlayer.c
       t@@ -60,6 +60,56 @@ static void AIHandleQuestion(char *Data,AICode AI,Player *AIPlay,Player *From);
        /*       out where these locations are for itself.          */
        int RealLoanShark,RealBank,RealGunShop,RealPub;
        
       +static void AIConnectFailed(NetworkBuffer *netbuf) {
       +  GString *errstr;
       +
       +  errstr = g_string_new(_("Connection closed by remote host"));
       +  if (netbuf->error) g_string_assign_error(errstr,netbuf->error);
       +  g_log(NULL,G_LOG_LEVEL_CRITICAL,
       +        _("Could not connect to dopewars server\n(%s)\n"
       +          "AI Player terminating abnormally."),errstr->str);
       +  g_string_free(errstr,TRUE);
       +}
       +
       +static void AIStartGame(Player *AIPlay) {
       +  Client=Network=TRUE;
       +  InitAbilities(AIPlay);
       +  SendAbilities(AIPlay);
       +
       +  AISetName(AIPlay);
       +  g_message(_("Connection established\n"));
       +}
       +
       +static void DisplayConnectStatus(NetworkBuffer *netbuf,NBStatus oldstatus,
       +                                 NBSocksStatus oldsocks) {
       +  NBStatus status;
       +  NBSocksStatus sockstat;
       +
       +  status = netbuf->status;
       +  sockstat = netbuf->sockstat;
       +  if (oldstatus==status && oldsocks==sockstat) return;
       +
       +  switch(status) {
       +    case NBS_PRECONNECT:
       +      break;
       +    case NBS_SOCKSCONNECT:
       +      switch(sockstat) {
       +        case NBSS_METHODS:
       +          g_print(_("Connected to SOCKS server %s...\n"),Socks.name);
       +          break;
       +        case NBSS_USERPASSWD:
       +          g_print(_("Authenticating with SOCKS server\n"));
       +          break;
       +        case NBSS_CONNECT:
       +          g_print(_("Asking SOCKS for connect to %s...\n"),ServerName);
       +          break;
       +      }
       +      break;
       +    case NBS_CONNECTED:
       +      break;
       +  }
       +}
       +
        void AIPlayerLoop() {
        /* Main loop for AI players. Connects to server, plays game, */
        /* and then disconnects.                                     */
       t@@ -67,45 +117,59 @@ void AIPlayerLoop() {
           gchar *msg;
           Player *AIPlay;
           fd_set readfs,writefs;
       -   gboolean DoneOK,QuitRequest;
       +   gboolean DoneOK,QuitRequest,datawaiting;
           int MaxSock;
       +   NBStatus oldstatus;
       +   NBSocksStatus oldsocks;
       +   NetworkBuffer *netbuf;
        
           errstr=g_string_new("");
           AIPlay=g_new(Player,1);
           FirstClient=AddPlayer(0,AIPlay,FirstClient);
           g_message(_("AI Player started; attempting to contact server at %s:%d..."),
                     ServerName,Port);
       -   if (!SetupNetwork(errstr)) {
       -      g_log(NULL,G_LOG_LEVEL_CRITICAL,
       -            _("Could not connect to dopewars server\n(%s)\n"
       -              "AI Player terminating abnormally."),errstr->str);
       -      g_string_free(errstr,TRUE);
       -      return;
       -   }
       -   BindNetworkBufferToSocket(&AIPlay->NetBuf,ClientSock);
       -
       -   InitAbilities(AIPlay);
       -   SendAbilities(AIPlay);
       -
       -   AISetName(AIPlay);
       -   g_message(_("Connection established\n"));
        
           /* Forget where the "special" locations are */
           RealLoanShark=RealBank=RealGunShop=RealPub=-1;
        
       +   netbuf = &AIPlay->NetBuf;
       +   oldstatus = netbuf->status;
       +   oldsocks  = netbuf->sockstat;
       +
       +   if (!StartNetworkBufferConnect(netbuf,ServerName,Port)) {
       +     AIConnectFailed(netbuf); return;
       +   } else if (netbuf->status==NBS_CONNECTED) {
       +     AIStartGame(AIPlay);
       +   } else {
       +     DisplayConnectStatus(netbuf,oldstatus,oldsocks);
       +   }
       +
           while (1) {
              FD_ZERO(&readfs);
              FD_ZERO(&writefs);
              MaxSock=0;
        
       -      SetSelectForNetworkBuffer(&AIPlay->NetBuf,&readfs,&writefs,NULL,&MaxSock);
       +      SetSelectForNetworkBuffer(netbuf,&readfs,&writefs,NULL,&MaxSock);
        
       +      oldstatus = netbuf->status;
       +      oldsocks  = netbuf->sockstat;
              if (bselect(MaxSock,&readfs,&writefs,NULL,NULL)==-1) {
                 if (errno==EINTR) continue;
                 printf("Error in select\n"); exit(1);
              }
        
       -      if (RespondToSelect(&AIPlay->NetBuf,&readfs,&writefs,NULL,&DoneOK)) {
       +      datawaiting=RespondToSelect(netbuf,&readfs,&writefs,NULL,&DoneOK);
       +
       +      if (oldstatus!=NBS_CONNECTED &&
       +          (netbuf->status==NBS_CONNECTED || !DoneOK)) {
       +        if (DoneOK) AIStartGame(AIPlay);
       +        else {
       +          AIConnectFailed(netbuf); break;
       +        }
       +      } else if (netbuf->status!=NBS_CONNECTED) {
       +        DisplayConnectStatus(netbuf,oldstatus,oldsocks);
       +      }
       +      if (datawaiting && netbuf->status==NBS_CONNECTED) {
                 QuitRequest=FALSE;
                 while ((msg=GetWaitingPlayerMessage(AIPlay))!=NULL) {
                    if (HandleAIMessage(msg,AIPlay)) {
       t@@ -113,7 +177,10 @@ void AIPlayerLoop() {
                       break;
                    }
                 }
       -         if (QuitRequest) break;
       +         if (QuitRequest) {
       +           g_print(_("AI Player terminated OK.\n"));
       +           break;
       +         }
              }
              if (!DoneOK) {
                 g_print(_("Connection to server lost!\n"));
       t@@ -123,7 +190,6 @@ void AIPlayerLoop() {
           ShutdownNetwork(AIPlay);
           g_string_free(errstr,TRUE);
           FirstClient=RemovePlayer(AIPlay,FirstClient);
       -   g_print(_("AI Player terminated OK.\n"));
        }
        
        void AISetName(Player *AIPlay) {
 (DIR) diff --git a/src/curses_client.c b/src/curses_client.c
       t@@ -73,13 +73,16 @@ static void display_message(char *buf);
        static void print_location(char *text);
        static void print_status(Player *Play,gboolean DispDrug);
        static char *nice_input(char *prompt,int sy,int sx,gboolean digitsonly,
       -                        char *displaystr);
       +                        char *displaystr,char passwdchar);
        static Player *ListPlayers(Player *Play,gboolean Select,char *Prompt);
        static void HandleClientMessage(char *buf,Player *Play);
        static void PrintMessage(const gchar *text);
        static void GunShop(Player *Play);
        static void LoanShark(Player *Play);
        static void Bank(Player *Play);
       +static void HttpAuthFunc(HttpConnection *conn,gboolean proxyauth,
       +                         gchar *realm,gpointer data);
       +static void SocksAuthFunc(NetworkBuffer *netbuf,gpointer data);
        
        static DispMode DisplayMode;
        static gboolean QuitRequest;
       t@@ -208,10 +211,10 @@ static void SelectServerManually(void) {
           mvaddstr(17,1,
        /* Prompts for hostname and port when selecting a server manually */
                    _("Please enter the hostname and port of a dopewars server:-"));
       -   text=nice_input(_("Hostname: "),18,1,FALSE,ServerName);
       +   text=nice_input(_("Hostname: "),18,1,FALSE,ServerName,'\0');
           AssignName(&ServerName,text); g_free(text);
           PortText=g_strdup_printf("%d",Port);
       -   text=nice_input(_("Port: "),19,1,TRUE,PortText);
       +   text=nice_input(_("Port: "),19,1,TRUE,PortText,'\0');
           Port=atoi(text);
           g_free(text); g_free(PortText);
        }
       t@@ -228,7 +231,7 @@ static gboolean SelectServerFromMetaServer(Player *Play,GString *errstr) {
           gint index;
           fd_set readfds,writefds;
           int maxsock;
       -   gboolean DoneOK;
       +   gboolean DoneOK,authOK;
           HttpConnection *MetaConn;
        
           attrset(TextAttr);
       t@@ -236,7 +239,10 @@ static gboolean SelectServerFromMetaServer(Player *Play,GString *errstr) {
           mvaddstr(17,1,_("Please wait... attempting to contact metaserver..."));
           refresh();
        
       -   if (!OpenMetaHttpConnection(&MetaConn)) {
       +   if (OpenMetaHttpConnection(&MetaConn)) {
       +      SetHttpAuthFunc(MetaConn,HttpAuthFunc,&authOK);
       +      SetNetworkBufferUserPasswdFunc(&MetaConn->NetBuf,SocksAuthFunc,&authOK);
       +   } else {
              g_string_assign_error(errstr,MetaConn->NetBuf.error);
              CloseHttpConnection(MetaConn);
              return FALSE;
       t@@ -260,10 +266,11 @@ static gboolean SelectServerFromMetaServer(Player *Play,GString *errstr) {
                if (c=='\f') wrefresh(curscr);
        #endif
              }
       +      authOK=TRUE; /* Gets set to FALSE if authentication fails */
              if (RespondToSelect(&MetaConn->NetBuf,&readfds,&writefds,NULL,&DoneOK)) {
                 while (HandleWaitingMetaServerData(MetaConn,&ServerList,&DoneOK)) {}
              }
       -      if (!DoneOK && HandleHttpCompletion(MetaConn)) {
       +      if ((!DoneOK || !authOK) && HandleHttpCompletion(MetaConn)) {
                 if (IsHttpError(MetaConn)) {
                    g_string_assign_error(errstr,MetaConn->NetBuf.error);
                    CloseHttpConnection(MetaConn);
       t@@ -331,11 +338,141 @@ static gboolean SelectServerFromMetaServer(Player *Play,GString *errstr) {
           return TRUE;
        }
        
       +static void DisplayConnectStatus(NetworkBuffer *netbuf,
       +                                 NBStatus oldstatus,NBSocksStatus oldsocks) {
       +  NBStatus status;
       +  NBSocksStatus sockstat;
       +  GString *text;
       +
       +  status = netbuf->status;
       +  sockstat = netbuf->sockstat;
       +
       +  if (oldstatus==status && oldsocks==sockstat) return;
       +
       +  text=g_string_new("");
       +
       +  switch(status) {
       +    case NBS_PRECONNECT:
       +      break;
       +    case NBS_SOCKSCONNECT:
       +      switch(sockstat) {
       +        case NBSS_METHODS:
       +          g_string_sprintf(text,_("Connected to SOCKS server %s..."),
       +                           Socks.name);
       +          break;
       +        case NBSS_USERPASSWD:
       +          g_string_assign(text,_("Authenticating with SOCKS server"));
       +          break;
       +        case NBSS_CONNECT:
       +          g_string_sprintf(text,_("Asking SOCKS for connect to %s..."),
       +                           ServerName);
       +          break;
       +      }
       +      break;
       +    case NBS_CONNECTED:
       +      break;
       +  }
       +  if (text->str[0]) {
       +    mvaddstr(17,1,text->str);
       +    refresh();
       +  }
       +  g_string_free(text,TRUE);
       +}
       +
       +void HttpAuthFunc(HttpConnection *conn,gboolean proxyauth,
       +                  gchar *realm,gpointer data) {
       +  gchar *text,*user,*password;
       +  gboolean *authOK;
       +
       +  authOK = (gboolean *)data;
       +
       +  attrset(TextAttr);
       +  clear_bottom();
       +  if (proxyauth) {
       +    text = g_strdup_printf(_("Proxy authentication required for realm %s"),
       +                           realm);
       +  } else {
       +    text = g_strdup_printf(_("Authentication required for realm %s"),realm);
       +  }
       +  mvaddstr(17,1,text);
       +  g_free(text);
       +  
       +  user=nice_input(_("User name: "),18,1,FALSE,NULL,'\0');
       +  password=nice_input(_("Password: "),19,1,FALSE,NULL,'*');
       +
       +  *authOK = SetHttpAuthentication(conn,proxyauth,user,password);
       +  g_free(user); g_free(password);
       +}
       +
       +void SocksAuthFunc(NetworkBuffer *netbuf,gpointer data) {
       +  gchar *user,*password;
       +  gboolean *authOK;
       +
       +  authOK = (gboolean *)data;
       +
       +  attrset(TextAttr);
       +  clear_bottom();
       +  mvaddstr(17,1,_("SOCKS authentication required"));
       +  
       +  user=nice_input(_("User name: "),18,1,FALSE,NULL,'\0');
       +  password=nice_input(_("Password: "),19,1,FALSE,NULL,'*');
       +
       +  *authOK = SendSocks5UserPasswd(netbuf,user,password);
       +  g_free(user); g_free(password);
       +}
       +
       +static gboolean DoConnect(Player *Play,GString *errstr) {
       +  NetworkBuffer *netbuf;
       +  fd_set readfds,writefds;
       +  int maxsock,c;
       +  gboolean doneOK=TRUE,authOK=TRUE;
       +  NBStatus oldstatus;
       +  NBSocksStatus oldsocks;
       +
       +  netbuf=&Play->NetBuf;
       +  oldstatus = netbuf->status;
       +  oldsocks  = netbuf->sockstat;
       +
       +  if (!StartNetworkBufferConnect(netbuf,ServerName,Port)) {
       +    doneOK=FALSE;
       +  } else {
       +    SetNetworkBufferUserPasswdFunc(netbuf,SocksAuthFunc,&authOK);
       +    if (netbuf->status!=NBS_CONNECTED) {
       +      DisplayConnectStatus(netbuf,oldstatus,oldsocks);
       +      do {
       +        FD_ZERO(&readfds); FD_ZERO(&writefds);
       +        FD_SET(0,&readfds); maxsock=1;
       +        SetSelectForNetworkBuffer(netbuf,&readfds,&writefds,NULL,&maxsock);
       +        if (bselect(maxsock,&readfds,&writefds,NULL,NULL)==-1) {
       +           if (errno==EINTR) { CheckForResize(Play); continue; }
       +           perror("bselect"); exit(1);
       +        }
       +        if (FD_ISSET(0,&readfds)) {
       +          /* So that Ctrl-L works */
       +          c = getch();
       +#ifndef CYGWIN
       +          if (c=='\f') wrefresh(curscr);
       +#endif
       +        }
       +        oldstatus = netbuf->status;
       +        oldsocks  = netbuf->sockstat;
       +        authOK=TRUE;
       +        RespondToSelect(netbuf,&readfds,&writefds,NULL,&doneOK);
       +        if (netbuf->status==NBS_CONNECTED) break;
       +        DisplayConnectStatus(netbuf,oldstatus,oldsocks);
       +      } while (doneOK && authOK);
       +    }
       +  }
       +
       +  if (!doneOK || !authOK) g_string_assign_error(errstr,netbuf->error);
       +  return (doneOK && authOK);
       +}
       +
        static gboolean ConnectToServer(Player *Play) {
        /* Connects to a dopewars server. Prompts the user to select a server */
        /* if necessary. Returns TRUE, unless the user elected to quit the    */
        /* program rather than choose a valid server.                         */
       -   gboolean MetaOK=TRUE,NetOK=TRUE;
       +   gboolean MetaOK=TRUE,NetOK=TRUE,firstrun=FALSE;
           GString *errstr;
           gchar *text;
           int c;
       t@@ -354,24 +491,27 @@ static gboolean ConnectToServer(Player *Play) {
              ConnectMethod=CM_SINGLE;
              g_string_free(errstr,TRUE);
              return TRUE;
       -   }
       +   } else firstrun=TRUE;
       +
           while (1) {
              attrset(TextAttr);
              clear_bottom();
       -      if (MetaOK) {
       +      if (MetaOK && !firstrun) {
                 mvaddstr(17,1,
                          _("Please wait... attempting to contact dopewars server..."));
                 refresh();
       -         NetOK=SetupNetwork(errstr);
       +         NetOK=DoConnect(Play,errstr);
              }
       -      if (!NetOK || !MetaOK) {
       +      if (!NetOK || !MetaOK || firstrun) {
       +         firstrun=FALSE;
       +         clear_line(16);
                 clear_line(17);
                 if (!MetaOK) {
        /* Display of an error while contacting the metaserver */
                    mvaddstr(16,1,_("Cannot get metaserver details"));
                    text=g_strdup_printf("   (%s)",errstr->str);
                    mvaddstr(17,1,text); g_free(text);
       -         } else {
       +         } else if (!NetOK) {
        /* Display of an error message while trying to contact a dopewars server
           (the error message itself is displayed on the next screen line) */
                    mvaddstr(16,1,_("Could not start multiplayer dopewars"));
       t@@ -381,16 +521,14 @@ static gboolean ConnectToServer(Player *Play) {
                 MetaOK=NetOK=TRUE;
                 attrset(PromptAttr);
                 mvaddstr(18,1,
       -                  _("Will you... C>onnect to a different host and/or port"));
       +                  _("Will you... C>onnect to a named dopewars server"));
                 mvaddstr(19,1,
                          _("            L>ist the servers on the metaserver, and "
                            "select one"));
                 mvaddstr(20,1,
       -                  _("            Q>uit (where you can start a server by "
       -                    "typing "));
       -         mvaddstr(21,1,
       -                  _("                   dopewars -s < /dev/null & )"));
       -         mvaddstr(22,1,_("         or P>lay single-player ? "));
       +                  _("            Q>uit (where you can start a server "
       +                    "by typing \"dopewars -s\")"));
       +         mvaddstr(21,1,_("         or P>lay single-player ? "));
                 attrset(TextAttr);
        
        /* Translate these 4 keys in line with the above options, keeping the order
       t@@ -399,19 +537,17 @@ static gboolean ConnectToServer(Player *Play) {
                 switch(c) {
                    case 'Q': g_string_free(errstr,TRUE);
                              return FALSE;
       -            case 'P': ConnectMethod=CM_SINGLE;
       -                      g_string_free(errstr,TRUE);
       +            case 'P': g_string_free(errstr,TRUE);
                              return TRUE;
       -            case 'L': ConnectMethod=CM_META;
       -                      MetaOK=SelectServerFromMetaServer(Play,errstr);
       +            case 'L': MetaOK=SelectServerFromMetaServer(Play,errstr);
                              break;
       -            case 'C': ConnectMethod=CM_PROMPT;
       -                      SelectServerManually();
       +            case 'C': SelectServerManually();
                              break;
                 }
              } else break;
           }
           g_string_free(errstr,TRUE);
       +   Client=Network=TRUE;
           return TRUE;
        }
        #endif /* NETWORKING */
       t@@ -493,7 +629,7 @@ static void DropDrugs(Player *Play) {
                 c--;
                 if (c<'A') {
                    addstr(Drug[i].Name);
       -            buf=nice_input(_("How many do you drop? "),23,8,TRUE,NULL);
       +            buf=nice_input(_("How many do you drop? "),23,8,TRUE,NULL,'\0');
                    c=atoi(buf); g_free(buf);
                    if (c>0) {
                       g_string_sprintf(text,"drug^%d^%d",i,-c);
       t@@ -547,7 +683,7 @@ static void DealDrugs(Player *Play,gboolean Buy) {
                                      CanAfford,CanCarry);
                 mvaddstr(23,2,text);
                 input=nice_input(_("How many do you buy? "),23,2+strlen(text),
       -                          TRUE,NULL);
       +                          TRUE,NULL,'\0');
                 c=atoi(input); g_free(input); g_free(text);
                 if (c>=0) {
                    text=g_strdup_printf("drug^%d^%d",DrugNum,c);
       t@@ -559,7 +695,7 @@ static void DealDrugs(Player *Play,gboolean Buy) {
                 text=g_strdup_printf(_("You have %d. "),Play->Drugs[DrugNum].Carried);
                 mvaddstr(23,2,text);
                 input=nice_input(_("How many do you sell? "),23,2+strlen(text),
       -                          TRUE,NULL);
       +                          TRUE,NULL,'\0');
                 c=atoi(input); g_free(input); g_free(text);
                 if (c>=0) {
                    text=g_strdup_printf("drug^%d^%d",DrugNum,-c);
       t@@ -655,7 +791,7 @@ static void change_name(Player *Play,gboolean nullname) {
           gchar *NewName;
        
        /* Prompt for player to change his/her name */
       -   NewName=nice_input(_("New name: "),23,0,FALSE,NULL);
       +   NewName=nice_input(_("New name: "),23,0,FALSE,NULL,'\0');
        
           if (NewName[0]) {
              if (nullname) {
       t@@ -990,7 +1126,8 @@ void LoanShark(Player *Play) {
              attrset(PromptAttr);
        
        /* Prompt for paying back loans from the loan shark */
       -      text=nice_input(_("How much money do you pay back? "),19,1,TRUE,NULL);
       +      text=nice_input(_("How much money do you pay back? "),19,1,
       +                      TRUE,NULL,'\0');
              attrset(TextAttr);
              money=strtoprice(text); g_free(text);
              if (money<0) money=0;
       t@@ -1029,7 +1166,7 @@ void Bank(Player *Play) {
              if (c=='L') return;
        
        /* Prompt for putting money in or taking money out of the bank */
       -      text=nice_input(_("How much money? "),19,1,TRUE,NULL);
       +      text=nice_input(_("How much money? "),19,1,TRUE,NULL,'\0');
        
              money=strtoprice(text); g_free(text);
              if (money<0) money=0;
       t@@ -1482,7 +1619,7 @@ Player *ListPlayers(Player *Play,gboolean Select,char *Prompt) {
        }
        
        char *nice_input(char *prompt,int sy,int sx,gboolean digitsonly,
       -                 char *displaystr) {
       +                 char *displaystr,char passwdchar) {
        /* Displays the given "prompt" (if non-NULL) at coordinates sx,sy and   */
        /* allows the user to input a string, which is returned. This is a      */
        /* dynamically allocated string, and so must be freed by the calling    */
       t@@ -1491,6 +1628,8 @@ char *nice_input(char *prompt,int sy,int sx,gboolean digitsonly,
        /* strtoprice routine understands this notation for a 1000000 or 1000   */
        /* multiplier) as well as a decimal point (. or ,)                      */
        /* If "displaystr" is non-NULL, it is taken as a default response.      */
       +/* If "passwdchar" is non-zero, it is displayed instead of the user's   */
       +/* keypresses (e.g. for entering passwords)                             */
           int i,c,x;
           gboolean DecimalPoint,Suffix;
           GString *text;
       t@@ -1505,7 +1644,11 @@ char *nice_input(char *prompt,int sy,int sx,gboolean digitsonly,
           }
           attrset(TextAttr);
           if (displaystr) {
       -      addstr(displaystr);
       +      if (passwdchar) {
       +        for (i=strlen(displaystr);i;i--) addch((guint)passwdchar);
       +      } else {
       +        addstr(displaystr);
       +      }
              i=strlen(displaystr);
              text=g_string_new(displaystr);
           } else {
       t@@ -1530,16 +1673,16 @@ char *nice_input(char *prompt,int sy,int sx,gboolean digitsonly,
                         (!digitsonly && c>=32 && c!='^' && c<127)) {
                    g_string_append_c(text,c);
                    i++;
       -            addch((guint)c);
       +            addch((guint)passwdchar ? passwdchar : c);
                 } else if (digitsonly && (c=='.' || c==',') && !DecimalPoint) {
                    g_string_append_c(text,'.');
       -            addch((guint)c);
       +            addch((guint)passwdchar ? passwdchar : c);
                    DecimalPoint=TRUE;
                 } else if (digitsonly && (c=='M' || c=='m' || c=='k' || c=='K')
                            && !Suffix) {
                    g_string_append_c(text,c);
                    i++;
       -            addch((guint)c);
       +            addch((guint)passwdchar ? passwdchar : c);
                    Suffix=TRUE;
                 }
              }
       t@@ -1574,6 +1717,7 @@ static void Curses_DoGame(Player *Play) {
           char HaveWorthless;
           Player *tmp;
           struct sigaction sact;
       +   gboolean justconnected=FALSE;
        
           DisplayMode=DM_NONE;
           QuitRequest=FALSE;
       t@@ -1596,12 +1740,12 @@ static void Curses_DoGame(Player *Play) {
           buf=NULL;
           do {
              g_free(buf);
       -      buf=nice_input(_("Hey dude, what's your name? "),17,1,FALSE,OldName);
       +      buf=nice_input(_("Hey dude, what's your name? "),17,1,FALSE,OldName,'\0');
           } while (buf[0]==0);
        #if NETWORKING
           if (WantNetwork) {
              if (!ConnectToServer(Play)) { end_curses(); exit(1); }
       -      BindNetworkBufferToSocket(&Play->NetBuf,ClientSock);
       +      justconnected=TRUE;
           }
        #endif /* NETWORKING */
           print_status(Play,TRUE);
       t@@ -1710,6 +1854,15 @@ static void Curses_DoGame(Player *Play) {
              FD_ZERO(&writefs);
              FD_SET(0,&readfs); MaxSock=1;
              if (Client) {
       +         if (justconnected) {
       +/* Deal with any messages that came in while we were connect()ing */
       +           justconnected=FALSE;
       +           while ((pt=GetWaitingPlayerMessage(Play))!=NULL) {
       +             HandleClientMessage(pt,Play);
       +             g_free(pt);
       +           }
       +           if (QuitRequest) return;
       +         }
                 SetSelectForNetworkBuffer(&Play->NetBuf,&readfs,&writefs,
                                           NULL,&MaxSock);
              }
       t@@ -1803,7 +1956,7 @@ static void Curses_DoGame(Player *Play) {
                       if (tmp) {
                          attrset(TextAttr); clear_line(22);
        /* Prompt for sending player-player messages */
       -                  TalkMsg=nice_input(_("Talk: "),22,0,FALSE,NULL);
       +                  TalkMsg=nice_input(_("Talk: "),22,0,FALSE,NULL,'\0');
                          if (TalkMsg[0]) {
                             SendClientMessage(Play,C_NONE,C_MSGTO,tmp,TalkMsg);
                             buf=g_strdup_printf("%s->%s: %s",GetPlayerName(Play),
       t@@ -1815,7 +1968,7 @@ static void Curses_DoGame(Player *Play) {
                       }
                    } else if (c=='T' && Client) {
                       attrset(TextAttr); clear_line(22);
       -               TalkMsg=nice_input(_("Talk: "),22,0,FALSE,NULL);
       +               TalkMsg=nice_input(_("Talk: "),22,0,FALSE,NULL,'\0');
                       if (TalkMsg[0]) {
                          SendClientMessage(Play,C_NONE,C_MSG,NULL,TalkMsg);
                          buf=g_strdup_printf("%s: %s",GetPlayerName(Play),TalkMsg);
 (DIR) diff --git a/src/gtk_client.c b/src/gtk_client.c
       t@@ -94,12 +94,17 @@ static void ListInventory(GtkWidget *widget,gpointer data);
        static void NewGameDialog(void);
        static void StartGame(void);
        static void EndGame(void);
       +static void Jet(GtkWidget *parent);
        static void UpdateMenus(void);
       +
       +#ifdef NETWORKING
       +static void DisplayConnectStatus(struct StartGameStruct *widgets,gboolean meta,
       +                                 NBStatus oldstatus,NBSocksStatus oldsocks);
        static void AuthDialog(HttpConnection *conn,
                               gboolean proxyauth,gchar *realm,
                               gpointer data);
       -
       -#ifdef NETWORKING
       +static void MetaSocksAuthDialog(NetworkBuffer *netbuf,gpointer data);
       +static void SocksAuthDialog(NetworkBuffer *netbuf,gpointer data);
        static void GetClientMessage(gpointer data,gint socket,
                                     GdkInputCondition condition);
        static void SocketStatus(NetworkBuffer *NetBuf,gboolean Read,gboolean Write);
       t@@ -127,7 +132,6 @@ static void UpdateInventory(struct InventoryWidgets *Inven,
                                    Inventory *Objects,int NumObjects,
                                    gboolean AreDrugs);
        static void JetButtonPressed(GtkWidget *widget,gpointer data);
       -static void Jet(void);
        static void DealDrugs(GtkWidget *widget,gpointer data);
        static void DealGuns(GtkWidget *widget,gpointer data);
        static void QuestionDialog(char *Data,Player *From);
       t@@ -170,7 +174,9 @@ static GtkItemFactoryEntry menu_items[] = {
           { N_("/_Errands"),NULL,NULL,0,"<Branch>" },
           { N_("/Errands/_Spy..."),NULL,SpyOnPlayer,0,NULL },
           { N_("/Errands/_Tipoff..."),NULL,TipOff,0,NULL },
       -   { N_("/Errands/Sack _Bitch..."),NULL,SackBitch,0,NULL },
       +/* N.B. "Sack Bitch" has to be recreated (and thus translated) at the start
       +   of each game, below, so is not marked for gettext here */
       +   { "/Errands/S_ack Bitch...",NULL,SackBitch,0,NULL },
           { N_("/Errands/_Get spy reports..."),NULL,GetSpyReports,0,NULL },
           { N_("/_Help"),NULL,NULL,0,"<LastBranch>" },
           { N_("/Help/_About..."),"F1",display_intro,0,NULL }
       t@@ -282,33 +288,45 @@ void GetClientMessage(gpointer data,gint socket,
                              GdkInputCondition condition) {
           gchar *pt;
           NetworkBuffer *NetBuf;
       -   gboolean DoneOK,Connecting;
       +   gboolean DoneOK,datawaiting;
       +   NBStatus status,oldstatus;
       +   NBSocksStatus oldsocks;
        
           NetBuf = &ClientData.Play->NetBuf;
       -   Connecting = NetBuf->status != NBS_CONNECTED;
       -   if (PlayerHandleNetwork(ClientData.Play,condition&GDK_INPUT_READ,
       -                           condition&GDK_INPUT_WRITE,&DoneOK) && !Connecting) {
       -      while ((pt=GetWaitingPlayerMessage(ClientData.Play))!=NULL) {
       -         HandleClientMessage(pt,ClientData.Play);
       -         g_free(pt);
       -      }
       +
       +   oldstatus = NetBuf->status;
       +   oldsocks = NetBuf->sockstat;
       +
       +   datawaiting = PlayerHandleNetwork(ClientData.Play,condition&GDK_INPUT_READ,
       +                                     condition&GDK_INPUT_WRITE,&DoneOK);
       +
       +   status = NetBuf->status;
       +
       +   if (status!=NBS_CONNECTED) {
       +/* The start game dialog isn't visible once we're connected... */
       +     DisplayConnectStatus((struct StartGameStruct *)data,FALSE,
       +                          oldstatus,oldsocks);
           }
       -   if (Connecting && (NetBuf->status==NBS_CONNECTED || !DoneOK)) {
       -      FinishServerConnect(data,DoneOK);
       -      if (DoneOK) {   /* Just in case, clean up any messages that came in */
       -         while ((pt=GetWaitingPlayerMessage(ClientData.Play))!=NULL) {
       -            HandleClientMessage(pt,ClientData.Play);
       -            g_free(pt);
       -         }
       -      }
       -   } else if (!DoneOK) {
       -      if (InGame) {
       +
       +   if (oldstatus!=NBS_CONNECTED && (status==NBS_CONNECTED || !DoneOK)) {
       +     FinishServerConnect(data,DoneOK);
       +   }
       +   if (status==NBS_CONNECTED && datawaiting) {
       +     while ((pt=GetWaitingPlayerMessage(ClientData.Play))!=NULL) {
       +       HandleClientMessage(pt,ClientData.Play);
       +       g_free(pt);
       +     }
       +   }
       +   if (!DoneOK) {
       +     if (status==NBS_CONNECTED) {
        /* The network connection to the server was dropped unexpectedly */
       -         g_warning(_("Connection to server lost - switching to "
       -                   "single player mode"));
       -         SwitchToSinglePlayer(ClientData.Play);
       -         UpdateMenus();
       -      }
       +       g_warning(_("Connection to server lost - switching to "
       +                 "single player mode"));
       +       SwitchToSinglePlayer(ClientData.Play);
       +       UpdateMenus();
       +     } else {
       +       ShutdownNetworkBuffer(&ClientData.Play->NetBuf);
       +     }
           }
        }
        
       t@@ -355,7 +373,6 @@ void HandleClientMessage(char *pt,Player *Play) {
                 DisplayFightMessage(Data); break;
              case C_PUSH:
        /* The server admin has asked us to leave - so warn the user, and do so */
       -         ShutdownNetworkBuffer(&Play->NetBuf);
                 g_warning(_("You have been pushed from the server.\n"
                             "Switching to single player mode."));
                 SwitchToSinglePlayer(Play);
       t@@ -363,7 +380,6 @@ void HandleClientMessage(char *pt,Player *Play) {
                 break;
              case C_QUIT:
        /* The server has sent us notice that it is shutting down */
       -         ShutdownNetworkBuffer(&Play->NetBuf);
                 g_warning(_("The server has terminated.\n"
                             "Switching to single player mode."));
                 SwitchToSinglePlayer(Play);
       t@@ -416,6 +432,15 @@ void HandleClientMessage(char *pt,Player *Play) {
                 break;
              case C_ENDLIST:
                 MenuItem=gtk_item_factory_get_widget(ClientData.Menu,
       +                                              "<main>/Errands/Sack Bitch...");
       +
       +/* Text for the Errands/Sack Bitch menu item */
       +         text=dpg_strdup_printf(_("%/Sack Bitch menu item/S_ack %Tde"),
       +                                Names.Bitch);
       +         SetAccelerator(MenuItem,text,NULL,NULL,NULL);
       +         g_free(text);
       +
       +         MenuItem=gtk_item_factory_get_widget(ClientData.Menu,
                                                      "<main>/Errands/Spy...");
        
        /* Text to update the Errands/Spy menu item with the price for spying */
       t@@ -656,8 +681,8 @@ static void FightCallback(GtkWidget *widget,gpointer data) {
                 if (CanRunHere) {
                    SendClientMessage(Play,C_NONE,C_FIGHTACT,NULL,"R");
                 } else {
       -            gtk_widget_hide(FightDialog);
       -            Jet();
       +/*          gtk_widget_hide(FightDialog);*/
       +            Jet(FightDialog);
                 }
                 break;
              case 'F': case 'S':
       t@@ -1135,11 +1160,11 @@ void JetButtonPressed(GtkWidget *widget,gpointer data) {
           if (ClientData.Play->Flags & FIGHTING) {
              DisplayFightMessage(NULL);
           } else {
       -      Jet();
       +      Jet(NULL);
           }
        }
        
       -void Jet(void) {
       +void Jet(GtkWidget *parent) {
           GtkWidget *dialog,*table,*button,*label,*vbox;
           GtkAccelGroup *accel_group;
           gint boxsize,i,row,col;
       t@@ -1155,7 +1180,8 @@ void Jet(void) {
           gtk_window_add_accel_group(GTK_WINDOW(dialog),accel_group);
           gtk_window_set_modal(GTK_WINDOW(dialog),TRUE);
           gtk_window_set_transient_for(GTK_WINDOW(dialog),
       -                                GTK_WINDOW(ClientData.window));
       +                                parent ? GTK_WINDOW(parent)
       +                                       : GTK_WINDOW(ClientData.window));
        
           vbox=gtk_vbox_new(FALSE,7);
        
       t@@ -1901,7 +1927,10 @@ _("Based on John E. Dell's old Drug Wars game, dopewars is a simulation of an\n"
        /* Label at the bottom of GTK+ 'about' dialog */
        _("\nFor information on the command line options, type dopewars -h at your\n"
        "Unix prompt. This will display a help screen, listing the available "
       -"options."));
       +"options.\n"));
       +   gtk_box_pack_start(GTK_BOX(vbox),label,FALSE,FALSE,0);
       +
       +   label=gtk_label_new("http://dopewars.sourceforge.net/");
           gtk_box_pack_start(GTK_BOX(vbox),label,FALSE,FALSE,0);
        
           hsep=gtk_hseparator_new();
       t@@ -1934,12 +1963,12 @@ static gboolean GetStartGamePlayerName(struct StartGameStruct *widgets,
           }
        }
        
       -#ifdef NETWORKING
        static void SetStartGameStatus(struct StartGameStruct *widgets,gchar *msg) {
           gtk_label_set_text(GTK_LABEL(widgets->status),
                              msg ? msg : _("Status: Waiting for user input"));
        }
        
       +#ifdef NETWORKING
        static void ConnectError(struct StartGameStruct *widgets,gboolean meta) {
           GString *neterr;
           gchar *text;
       t@@ -1948,10 +1977,13 @@ static void ConnectError(struct StartGameStruct *widgets,gboolean meta) {
           if (meta) error=widgets->MetaConn->NetBuf.error;
           else error=ClientData.Play->NetBuf.error;
        
       -   if (!error) return;
       -
           neterr = g_string_new("");
       -   g_string_assign_error(neterr,error);
       +
       +   if (error) {
       +     g_string_assign_error(neterr,error);
       +   } else {
       +     g_string_assign(neterr,_("Connection closed by remote host"));
       +   }
        
           if (meta) {
        /* Error: GTK+ client could not connect to the metaserver */
       t@@ -1978,19 +2010,28 @@ void FinishServerConnect(struct StartGameStruct *widgets,gboolean ConnectOK) {
        
        static void DoConnect(struct StartGameStruct *widgets) {
           gchar *text;
       +   NetworkBuffer *NetBuf;
       +   NBStatus oldstatus;
       +   NBSocksStatus oldsocks;
       +
       +   NetBuf=&ClientData.Play->NetBuf;
       +
        /* Message displayed during the attempted connect to a dopewars server */
           text=g_strdup_printf(_("Status: Attempting to contact %s..."),ServerName);
           SetStartGameStatus(widgets,text); g_free(text);
        
        /* Terminate any existing connection attempts */
       -   ShutdownNetworkBuffer(&ClientData.Play->NetBuf);
       +   ShutdownNetworkBuffer(NetBuf);
           if (widgets->MetaConn) {
              CloseHttpConnection(widgets->MetaConn); widgets->MetaConn=NULL;
           }
        
       -   if (StartNetworkBufferConnect(&ClientData.Play->NetBuf,ServerName,Port)) {
       -      SetNetworkBufferCallBack(&ClientData.Play->NetBuf,SocketStatus,
       -                               (gpointer)widgets);
       +   oldstatus = NetBuf->status;
       +   oldsocks = NetBuf->sockstat;
       +   if (StartNetworkBufferConnect(NetBuf,ServerName,Port)) {
       +      DisplayConnectStatus(widgets,FALSE,oldstatus,oldsocks);
       +      SetNetworkBufferUserPasswdFunc(NetBuf,SocksAuthDialog,(gpointer)widgets);
       +      SetNetworkBufferCallBack(NetBuf,SocketStatus,(gpointer)widgets);
           } else {
              ConnectError(widgets,FALSE);
           }
       t@@ -2051,15 +2092,19 @@ static void FillMetaServerList(struct StartGameStruct *widgets,
           gtk_clist_thaw(GTK_CLIST(metaserv));
        }
        
       -static void DisplayConnectStatus(struct StartGameStruct *widgets,
       -                                 gboolean meta,
       -                                 NBStatus oldstatus,NBSocksStatus oldsocks) {
       +void DisplayConnectStatus(struct StartGameStruct *widgets,gboolean meta,
       +                          NBStatus oldstatus,NBSocksStatus oldsocks) {
          NBStatus status;
          NBSocksStatus sockstat;
          gchar *text;
        
       -  status = widgets->MetaConn->NetBuf.status;
       -  sockstat = widgets->MetaConn->NetBuf.sockstat;
       +  if (meta) {
       +    status = widgets->MetaConn->NetBuf.status;
       +    sockstat = widgets->MetaConn->NetBuf.sockstat;
       +  } else {
       +    status = ClientData.Play->NetBuf.status;
       +    sockstat = ClientData.Play->NetBuf.sockstat;
       +  }
          if (oldstatus==status && sockstat==oldsocks) return;
        
          switch (status) {
       t@@ -2078,13 +2123,13 @@ static void DisplayConnectStatus(struct StartGameStruct *widgets,
                  break;
                case NBSS_CONNECT:
                  text=g_strdup_printf(_("Status: Asking SOCKS for connect to %s..."),
       -                               MetaServer.Name);
       +                               meta ? MetaServer.Name : ServerName);
                  SetStartGameStatus(widgets,text); g_free(text);
                  break;
              }
              break;
            case NBS_CONNECTED:
       -      SetStartGameStatus(widgets,
       +      if (meta) SetStartGameStatus(widgets,
                   _("Status: Obtaining server information from metaserver..."));
              break;
          }
       t@@ -2162,6 +2207,8 @@ static void UpdateMetaServerList(GtkWidget *widget,
           if (OpenMetaHttpConnection(&widgets->MetaConn)) {
              metaserv=widgets->metaserv;
              SetHttpAuthFunc(widgets->MetaConn,AuthDialog,(gpointer)widgets);
       +      SetNetworkBufferUserPasswdFunc(&widgets->MetaConn->NetBuf,
       +                                     MetaSocksAuthDialog,(gpointer)widgets);
              SetNetworkBufferCallBack(&widgets->MetaConn->NetBuf,
                                       MetaSocketStatus,(gpointer)widgets);
           } else {
       t@@ -2875,7 +2922,8 @@ void SackBitch(GtkWidget *widget,gpointer data) {
           if (ClientData.Play->Bitches.Carried<=0) return;
        
        /* Title of dialog to sack a bitch (%Tde = "Bitch" by default) */
       -   title=dpg_strdup_printf(_("Sack %Tde"),Names.Bitch);
       +   title=dpg_strdup_printf(_("%/Sack Bitch dialog title/Sack %Tde"),
       +                           Names.Bitch);
        
        /* Confirmation message for sacking a bitch. (%tde = "guns", "drugs",
           "bitch", respectively, by default) */
       t@@ -3168,6 +3216,7 @@ void DisplaySpyReports(Player *Play) {
           gtk_widget_show_all(notebook);
        }
        
       +#ifdef NETWORKING
        static void OKAuthDialog(GtkWidget *widget,GtkWidget *window) {
           gtk_object_set_data(GTK_OBJECT(window),"authok",GINT_TO_POINTER(TRUE));
           gtk_widget_destroy(window);
       t@@ -3239,7 +3288,7 @@ void AuthDialog(HttpConnection *conn,gboolean proxy,gchar *realm,
        
           vbox=gtk_vbox_new(FALSE,7);
        
       -   table=gtk_table_new(3,3,FALSE);
       +   table=gtk_table_new(3,2,FALSE);
           gtk_table_set_row_spacings(GTK_TABLE(table),10);
           gtk_table_set_col_spacings(GTK_TABLE(table),5);
        
       t@@ -3293,6 +3342,128 @@ void AuthDialog(HttpConnection *conn,gboolean proxy,gchar *realm,
           gtk_widget_show_all(window);
        }
        
       +static void OKSocksAuth(GtkWidget *widget,GtkWidget *window) {
       +   gtk_object_set_data(GTK_OBJECT(window),"authok",GINT_TO_POINTER(TRUE));
       +   gtk_widget_destroy(window);
       +}
       +
       +static void DestroySocksAuth(GtkWidget *window,gpointer data) {
       +   GtkWidget *userentry,*passwdentry;
       +   gchar *username=NULL,*password=NULL;
       +   gpointer authok,meta;
       +   NetworkBuffer *netbuf;
       +   struct StartGameStruct *widgets;
       +   NBStatus oldstatus;
       +   NBSocksStatus oldsocks;
       +
       +   authok = gtk_object_get_data(GTK_OBJECT(window),"authok");
       +   meta = gtk_object_get_data(GTK_OBJECT(window),"meta");
       +   userentry = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(window),"username");
       +   passwdentry = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(window),
       +                                                  "password");
       +   netbuf = (NetworkBuffer *)gtk_object_get_data(GTK_OBJECT(window),"netbuf");
       +   widgets = (struct StartGameStruct *)gtk_object_get_data(GTK_OBJECT(window),
       +                                                           "widgets");
       +
       +   g_assert(userentry && passwdentry && netbuf);
       +
       +   if (authok) {
       +     username = gtk_editable_get_chars(GTK_EDITABLE(userentry),0,-1);
       +     password = gtk_editable_get_chars(GTK_EDITABLE(passwdentry),0,-1);
       +   }
       +
       +   oldstatus = netbuf->status;
       +   oldsocks = netbuf->sockstat;
       +   if (!SendSocks5UserPasswd(netbuf,username,password)) {
       +     if (meta) MetaDone(widgets); else ConnectError(widgets,FALSE);
       +   } else {
       +     DisplayConnectStatus(widgets,GPOINTER_TO_INT(meta),oldstatus,oldsocks);
       +   }
       +   g_free(username); g_free(password);
       +}
       +
       +static void RealSocksAuthDialog(NetworkBuffer *netbuf,gboolean meta,
       +                                gpointer data) {
       +   GtkWidget *window,*button,*hsep,*vbox,*label,*entry,*table,*hbbox;
       +   struct StartGameStruct *widgets;
       +
       +   widgets = (struct StartGameStruct *)data;
       +
       +   window=gtk_window_new(GTK_WINDOW_DIALOG);
       +   gtk_signal_connect(GTK_OBJECT(window),"destroy",
       +                      GTK_SIGNAL_FUNC(DestroySocksAuth),NULL);
       +   gtk_object_set_data(GTK_OBJECT(window),"netbuf",(gpointer)netbuf);
       +   gtk_object_set_data(GTK_OBJECT(window),"meta",GINT_TO_POINTER(meta));
       +   gtk_object_set_data(GTK_OBJECT(window),"widgets",(gpointer)widgets);
       +
       +/* Title of dialog for authenticating with a SOCKS server */
       +   gtk_window_set_title(GTK_WINDOW(window),_("SOCKS Authentication Required"));
       +
       +   gtk_window_set_modal(GTK_WINDOW(window),TRUE);
       +   gtk_window_set_transient_for(GTK_WINDOW(window),
       +                                GTK_WINDOW(ClientData.window));
       +   gtk_container_set_border_width(GTK_CONTAINER(window),7);
       +
       +   vbox=gtk_vbox_new(FALSE,7);
       +
       +   table=gtk_table_new(2,2,FALSE);
       +   gtk_table_set_row_spacings(GTK_TABLE(table),10);
       +   gtk_table_set_col_spacings(GTK_TABLE(table),5);
       +
       +   label=gtk_label_new("User name:");
       +   gtk_table_attach_defaults(GTK_TABLE(table),label,0,1,0,1);
       +
       +   entry=gtk_entry_new();
       +   gtk_object_set_data(GTK_OBJECT(window),"username",(gpointer)entry);
       +   gtk_table_attach_defaults(GTK_TABLE(table),entry,1,2,0,1);
       +
       +   label=gtk_label_new("Password:");
       +   gtk_table_attach_defaults(GTK_TABLE(table),label,0,1,1,2);
       +
       +   entry=gtk_entry_new();
       +   gtk_object_set_data(GTK_OBJECT(window),"password",(gpointer)entry);
       +
       +#ifdef HAVE_FIXED_GTK
       +   /* GTK+ versions earlier than 1.2.10 do bad things with this */
       +   gtk_entry_set_visibility(GTK_ENTRY(entry),FALSE);
       +#endif
       +
       +   gtk_table_attach_defaults(GTK_TABLE(table),entry,1,2,1,2);
       +
       +   gtk_box_pack_start(GTK_BOX(vbox),table,TRUE,TRUE,0);
       +
       +   hsep=gtk_hseparator_new();
       +   gtk_box_pack_start(GTK_BOX(vbox),hsep,FALSE,FALSE,0);
       +
       +   hbbox = gtk_hbutton_box_new();
       +
       +   button=gtk_button_new_with_label(_("OK"));
       +   gtk_signal_connect(GTK_OBJECT(button),"clicked",
       +                      GTK_SIGNAL_FUNC(OKSocksAuth),(gpointer)window);
       +   gtk_box_pack_start(GTK_BOX(hbbox),button,TRUE,TRUE,0);
       +
       +   button=gtk_button_new_with_label(_("Cancel"));
       +   gtk_signal_connect_object(GTK_OBJECT(button),"clicked",
       +                             GTK_SIGNAL_FUNC(gtk_widget_destroy),
       +                             (gpointer)window);
       +   gtk_box_pack_start(GTK_BOX(hbbox),button,TRUE,TRUE,0);
       +
       +   gtk_box_pack_start(GTK_BOX(vbox),hbbox,TRUE,TRUE,0);
       +
       +   gtk_container_add(GTK_CONTAINER(window),vbox);
       +   gtk_widget_show_all(window);
       +}
       +
       +void MetaSocksAuthDialog(NetworkBuffer *netbuf,gpointer data) {
       +  RealSocksAuthDialog(netbuf,TRUE,data);
       +}
       +
       +void SocksAuthDialog(NetworkBuffer *netbuf,gpointer data) {
       +  RealSocksAuthDialog(netbuf,FALSE,data);
       +}
       +
       +#endif /* NETWORKING */
       +
        #else
        
        #include <glib.h>
 (DIR) diff --git a/src/message.c b/src/message.c
       t@@ -714,27 +714,6 @@ price_t GetNextPrice(gchar **Data,price_t Default) {
           if (Word) return strtoprice(Word); else return Default;
        }
        
       -#if NETWORKING
       -gboolean SetupNetwork(GString *errstr) {
       -/* Sets up the connection from the client to the server. If the connection */
       -/* is successful, Network and Client are set to TRUE, and ClientSock is a  */
       -/* file descriptor for the newly-opened socket. TRUE is returned. If the   */
       -/* connection fails, FALSE is returned, and errstr (if non-NULL) is filled */
       -/* with a descriptive error message.                                       */
       -   LastError *err;
       -
       -   Network=Client=Server=FALSE;
       -   if (StartConnect(&ClientSock,ServerName,Port,FALSE,&err)) {
       -     Client=Network=TRUE;
       -     return TRUE;
       -   } else {
       -     if (errstr) g_string_assign_error(errstr,err);
       -     FreeError(err);
       -     return FALSE;
       -   }
       -}
       -#endif /* NETWORKING */
       -
        void SwitchToSinglePlayer(Player *Play) {
        /* Called when the client is pushed off the server, or the server  */
        /* terminates. Using the client information, starts a local server */
 (DIR) diff --git a/src/message.h b/src/message.h
       t@@ -109,7 +109,6 @@ gchar *GetNextWord(gchar **Data,gchar *Default);
        void AssignNextWord(gchar **Data,gchar **Dest);
        int GetNextInt(gchar **Data,int Default);
        price_t GetNextPrice(gchar **Data,price_t Default);
       -gboolean SetupNetwork(GString *errstr);
        void ShutdownNetwork(Player *Play);
        void SwitchToSinglePlayer(Player *Play);
        int ProcessMessage(char *Msg,Player *Play,Player **Other,AICode *AI,
 (DIR) diff --git a/src/network.c b/src/network.c
       t@@ -66,9 +66,8 @@ typedef enum {
        
        static gboolean StartSocksNegotiation(NetworkBuffer *NetBuf,gchar *RemoteHost,
                                              unsigned RemotePort);
       -static gchar *GetWaitingData(NetworkBuffer *NetBuf,int numbytes);
       -static gchar *PeekWaitingData(NetworkBuffer *NetBuf,int numbytes);
       -static gchar *ExpandWriteBuffer(ConnBuf *conn,int numbytes);
       +static gboolean StartConnect(int *fd,gchar *RemoteHost,unsigned RemotePort,
       +                             gboolean *doneOK,LastError **error);
        
        #ifdef CYGWIN
        
       t@@ -186,16 +185,18 @@ void SetNetworkBufferCallBack(NetworkBuffer *NetBuf,NBCallBack CallBack,
        }
        
        void SetNetworkBufferUserPasswdFunc(NetworkBuffer *NetBuf,
       -                                    NBUserPasswd userpasswd) {
       +                                    NBUserPasswd userpasswd,gpointer data) {
        /* Sets the function used to obtain a username and password for SOCKS5 */
        /* username/password authentication                                    */
           NetBuf->userpasswd=userpasswd;
       +   NetBuf->userpasswddata=data;
        }
        
        void BindNetworkBufferToSocket(NetworkBuffer *NetBuf,int fd) {
        /* Sets up the given network buffer to handle data being sent/received */
        /* through the given socket                                            */
           NetBuf->fd=fd;
       +   SetBlocking(fd,FALSE); /* We only deal with non-blocking sockets */
           NetBuf->status=NBS_CONNECTED; /* Assume the socket is connected */
        }
        
       t@@ -209,6 +210,7 @@ gboolean StartNetworkBufferConnect(NetworkBuffer *NetBuf,gchar *RemoteHost,
                                           unsigned RemotePort) {
          gchar *realhost;
          unsigned realport;
       +  gboolean doneOK;
        
          ShutdownNetworkBuffer(NetBuf);
        
       t@@ -220,20 +222,22 @@ gboolean StartNetworkBufferConnect(NetworkBuffer *NetBuf,gchar *RemoteHost,
            realport = RemotePort;
          }
        
       -  if (StartConnect(&NetBuf->fd,realhost,realport,TRUE,&NetBuf->error)) {
       -    NetBuf->WaitConnect=TRUE;
       +  if (StartConnect(&NetBuf->fd,realhost,realport,&doneOK,&NetBuf->error)) {
       +/* If we connected immediately, then set status, otherwise signal that we're
       +   waiting for the connect to complete */
       +    if (doneOK) {
       +      NetBuf->status = NetBuf->socks ? NBS_SOCKSCONNECT : NBS_CONNECTED;
       +      NetBuf->sockstat = NBSS_METHODS;
       +    } else {
       +      NetBuf->WaitConnect=TRUE;
       +    }
        
       -    if (NetBuf->socks) {
       -      if (!StartSocksNegotiation(NetBuf,RemoteHost,RemotePort)) {
       -        NetBuf->WaitConnect=FALSE;
       -        return FALSE;
       -      } else {
       -        NetBuf->status   = NBS_SOCKSCONNECT;
       -        NetBuf->sockstat = NBSS_METHODS;
       -      }
       +    if (NetBuf->socks && !StartSocksNegotiation(NetBuf,RemoteHost,RemotePort)) {
       +      return FALSE;
            }
        
       -/* Notify the owner if necessary to check for the connection completing */
       +/* Notify the owner if necessary to check for the connection completing
       +   and/or for data to be writeable */
            NetBufCallBack(NetBuf);
        
            return TRUE;
       t@@ -392,7 +396,7 @@ static gboolean Socks5UserPasswd(NetworkBuffer *NetBuf) {
        /* Request a username and password (the callback function should in turn
           call SendSocks5UserPasswd when it's done) */
              NetBuf->sockstat = NBSS_USERPASSWD;
       -      (*NetBuf->userpasswd)(NetBuf);
       +      (*NetBuf->userpasswd)(NetBuf,NetBuf->userpasswddata);
              return TRUE;
           }
        }
       t@@ -403,13 +407,13 @@ gboolean SendSocks5UserPasswd(NetworkBuffer *NetBuf,gchar *user,
           guint addlen;
           ConnBuf *conn;
        
       -   if (!user || !password) {
       +   if (!user || !password || !user[0] || !password[0]) {
              SetError(&NetBuf->error,&ETSocks,SEC_USERCANCEL,NULL);
              return FALSE;
           }
           conn=&NetBuf->negbuf;
           addlen = 3 + strlen(user) + strlen(password);
       -   addpt = ExpandWriteBuffer(conn,addlen);
       +   addpt = ExpandWriteBuffer(conn,addlen,&NetBuf->error);
           if (!addpt || strlen(user)>255 || strlen(password)>255) {
              SetError(&NetBuf->error,ET_CUSTOM,E_FULLBUF,NULL);
              return FALSE;
       t@@ -419,13 +423,8 @@ gboolean SendSocks5UserPasswd(NetworkBuffer *NetBuf,gchar *user,
           strcpy(&addpt[2],user);
           addpt[2+strlen(user)] = strlen(password);
           strcpy(&addpt[3+strlen(user)],password);
       -   g_free(user); g_free(password);
        
       -   conn->DataPresent+=addlen;
       -
       -/* If the buffer was empty before, we may need to tell the owner to check
       -   the socket for write-ready status */
       -   if ((gchar *)addpt==conn->Data) NetBufCallBack(NetBuf);
       +   CommitWriteBuffer(NetBuf,conn,addpt,addlen);
        
           return TRUE;
        }
       t@@ -445,11 +444,8 @@ static gboolean Socks5Connect(NetworkBuffer *NetBuf) {
           g_assert(sizeof(netport)==2);
        
           addlen = hostlen + 7;
       -   addpt = ExpandWriteBuffer(conn,addlen);
       -   if (!addpt) {
       -      SetError(&NetBuf->error,ET_CUSTOM,E_FULLBUF,NULL);
       -      return FALSE;
       -   }
       +   addpt = ExpandWriteBuffer(conn,addlen,&NetBuf->error);
       +   if (!addpt) return FALSE;
           addpt[0] = 5;       /* SOCKS version 5 */
           addpt[1] = 1;       /* CONNECT */
           addpt[2] = 0;       /* reserved - must be zero */
       t@@ -459,13 +455,9 @@ static gboolean Socks5Connect(NetworkBuffer *NetBuf) {
           memcpy(&addpt[5+hostlen],&netport,sizeof(netport));
        
           NetBuf->sockstat = NBSS_CONNECT;
       -   g_print("FIXME: SOCKS5 CONNECT request sent\n");
       -
       -   conn->DataPresent+=addlen;
       +/* g_print("FIXME: SOCKS5 CONNECT request sent\n");*/
        
       -/* If the buffer was empty before, we may need to tell the owner to check
       -   the socket for write-ready status */
       -   if ((gchar *)addpt==conn->Data) NetBufCallBack(NetBuf);
       +   CommitWriteBuffer(NetBuf,conn,addpt,addlen);
        
           return TRUE;
        }
       t@@ -476,23 +468,18 @@ static gboolean HandleSocksReply(NetworkBuffer *NetBuf) {
           guint replylen;
           gboolean retval=TRUE;
           if (NetBuf->socks->version==5) {
       -g_print("Handling SOCKS5 reply\n");
              if (NetBuf->sockstat == NBSS_METHODS) {
                 data = GetWaitingData(NetBuf,2);
                 if (data) {
                    retval=FALSE;
       -            g_print("FIXME: Reply from SOCKS5 server: %d %d\n",data[0],data[1]);
                    if (data[0]!=5) {
                       SetError(&NetBuf->error,&ETSocks,SEC_VERSION,NULL);
       -            } else if (data[1]!=0 && data[1]!=2) {
       -               SetError(&NetBuf->error,&ETSocks,SEC_NOMETHODS,NULL);
       +            } else if (data[1]==SM_NOAUTH) {
       +               retval=Socks5Connect(NetBuf);
       +            } else if (data[1]==SM_USERPASSWD) {
       +               retval=Socks5UserPasswd(NetBuf);
                    } else {
       -               g_print("FIXME: Using SOCKS5 method %d\n",data[1]);
       -               if (data[1]==SM_NOAUTH) {
       -                  retval=Socks5Connect(NetBuf);
       -               } else if (data[1]==SM_USERPASSWD) {
       -                  retval=Socks5UserPasswd(NetBuf);
       -               }
       +               SetError(&NetBuf->error,&ETSocks,SEC_NOMETHODS,NULL);
                    }
                    g_free(data);
                 }
       t@@ -500,9 +487,7 @@ g_print("Handling SOCKS5 reply\n");
                 data = GetWaitingData(NetBuf,2);
                 if (data) {
                    retval=FALSE;
       -            if (data[0]!=5) {
       -               SetError(&NetBuf->error,&ETSocks,SEC_VERSION,NULL);
       -            } else if (data[1]!=0) {
       +            if (data[1]!=0) {
                       SetError(&NetBuf->error,&ETSocks,SEC_AUTHFAILED,NULL);
                    } else {
                       retval=Socks5Connect(NetBuf);
       t@@ -510,7 +495,6 @@ g_print("Handling SOCKS5 reply\n");
                    g_free(data);
                 }
              } else if (NetBuf->sockstat == NBSS_CONNECT) {
       -g_print("FIXME: SOCKS5 connect reply\n");
                 data = PeekWaitingData(NetBuf,5);
                 if (data) {
                    retval=FALSE;
       t@@ -531,10 +515,9 @@ g_print("FIXME: SOCKS5 connect reply\n");
                       else replylen+=data[4];   /* FQDN */
                       data = GetWaitingData(NetBuf,replylen);
                       if (data) {
       -                  g_print("FIXME: SOCKS5 successful connect\n");
       -               if (addrtype==1) g_print("IPv4 address %d.%d.%d.%d\n",data[4],data[5],data[6],data[7]);
       +/*               if (addrtype==1) g_print("IPv4 address %d.%d.%d.%d\n",data[4],data[5],data[6],data[7]);
                       else if (addrtype==4) g_print("IPv6 address\n");
       -               else g_print("FQDN\n");
       +               else g_print("FQDN\n");*/
                          NetBuf->status = NBS_CONNECTED;
                          g_free(data);
                          NetBufCallBack(NetBuf); /* status has changed */
       t@@ -584,13 +567,17 @@ static gboolean DoNetworkBufferStuff(NetworkBuffer *NetBuf,gboolean ReadReady,
                 retval=FinishConnect(NetBuf->fd,&NetBuf->error);
                 ConnectDone=TRUE;
                 NetBuf->WaitConnect=FALSE;
       -         if (!NetBuf->socks) {
       -g_print("FIXME: Non-SOCKS successful connect\n");
       -            NetBuf->status = NBS_CONNECTED;
       -         }
        
       -         if (!retval) {
       -            *WriteOK=FALSE;
       +         if (retval) {
       +           if (NetBuf->socks) {
       +             NetBuf->status   = NBS_SOCKSCONNECT;
       +             NetBuf->sockstat = NBSS_METHODS;
       +           } else {
       +             NetBuf->status = NBS_CONNECTED;
       +           }
       +         } else {
       +           NetBuf->status = NBS_PRECONNECT;
       +           *WriteOK=FALSE;
                 }
              }
           } else {
       t@@ -777,18 +764,30 @@ gboolean ReadDataFromWire(NetworkBuffer *NetBuf) {
           return TRUE;
        }
        
       -gchar *ExpandWriteBuffer(ConnBuf *conn,int numbytes) {
       -   int newlen;
       -   newlen = conn->DataPresent + numbytes;
       -   if (newlen > conn->Length) {
       -      conn->Length*=2;
       -      conn->Length=MAX(conn->Length,newlen);
       -      if (conn->Length > MAXWRITEBUF) conn->Length=MAXWRITEBUF;
       -      if (newlen > conn->Length) return NULL;
       -      conn->Data=g_realloc(conn->Data,conn->Length);
       -   }
       +gchar *ExpandWriteBuffer(ConnBuf *conn,int numbytes,LastError **error) {
       +  int newlen;
       +  newlen = conn->DataPresent + numbytes;
       +  if (newlen > conn->Length) {
       +    conn->Length*=2;
       +    conn->Length=MAX(conn->Length,newlen);
       +    if (conn->Length > MAXWRITEBUF) conn->Length=MAXWRITEBUF;
       +    if (newlen > conn->Length) {
       +      if (error) SetError(error,ET_CUSTOM,E_FULLBUF,NULL);
       +      return NULL;
       +    }
       +    conn->Data=g_realloc(conn->Data,conn->Length);
       +  }
        
       -   return (&conn->Data[conn->DataPresent]);
       +  return (&conn->Data[conn->DataPresent]);
       +}
       +
       +void CommitWriteBuffer(NetworkBuffer *NetBuf,ConnBuf *conn,
       +                       gchar *addpt,guint addlen) {
       +   conn->DataPresent+=addlen;
       +
       +/* If the buffer was empty before, we may need to tell the owner to check
       +   the socket for write-ready status */
       +   if (NetBuf && addpt==conn->Data) NetBufCallBack(NetBuf);
        }
        
        void QueueMessageForSend(NetworkBuffer *NetBuf,gchar *data) {
       t@@ -804,16 +803,13 @@ void QueueMessageForSend(NetworkBuffer *NetBuf,gchar *data) {
        
           if (!data) return;
           addlen = strlen(data)+1;
       -   addpt = ExpandWriteBuffer(conn,addlen);
       +   addpt = ExpandWriteBuffer(conn,addlen,NULL);
           if (!addpt) return;
        
           memcpy(addpt,data,addlen);
       -   conn->DataPresent+=addlen;
           addpt[addlen-1]=NetBuf->Terminator;
        
       -/* If the buffer was empty before, we may need to tell the owner to check
       -   the socket for write-ready status */
       -   if (addpt==conn->Data) NetBufCallBack(NetBuf);
       +   CommitWriteBuffer(NetBuf,conn,addpt,addlen);
        }
        
        static struct hostent *LookupHostname(gchar *host,LastError **error) {
       t@@ -837,8 +833,8 @@ gboolean StartSocksNegotiation(NetworkBuffer *NetBuf,gchar *RemoteHost,
           guint addlen,i;
           struct in_addr *haddr;
           unsigned short int netport;
       -#ifdef CYGWIN
           gchar *username=NULL;
       +#ifdef CYGWIN
           DWORD bufsize;
        #else
           struct passwd *pwd;
       t@@ -850,28 +846,19 @@ gboolean StartSocksNegotiation(NetworkBuffer *NetBuf,gchar *RemoteHost,
              num_methods=1;
              if (NetBuf->userpasswd) num_methods++;
              addlen=2+num_methods;
       -      addpt = ExpandWriteBuffer(conn,addlen);
       -      if (!addpt) {
       -         SetError(&NetBuf->error,ET_CUSTOM,E_FULLBUF,NULL);
       -         return FALSE;
       -      }
       +      addpt = ExpandWriteBuffer(conn,addlen,&NetBuf->error);
       +      if (!addpt) return FALSE;
              addpt[0] = 5;   /* SOCKS version 5 */
              addpt[1] = num_methods;
              i=2;
              addpt[i++] = SM_NOAUTH;
              if (NetBuf->userpasswd) addpt[i++] = SM_USERPASSWD;
        
       -   g_print("FIXME: SOCKS5 methods request sent\n");
       -
       -      conn->DataPresent+=addlen;
       -
              g_free(NetBuf->host);
              NetBuf->host = g_strdup(RemoteHost);
              NetBuf->port = RemotePort;
        
       -/* If the buffer was empty before, we may need to tell the owner to check
       -   the socket for write-ready status */
       -      if ((gchar *)addpt==conn->Data) NetBufCallBack(NetBuf);
       +      CommitWriteBuffer(NetBuf,conn,addpt,addlen);
        
              return TRUE;
           }
       t@@ -879,27 +866,33 @@ gboolean StartSocksNegotiation(NetworkBuffer *NetBuf,gchar *RemoteHost,
           he = LookupHostname(RemoteHost,&NetBuf->error);
           if (!he) return FALSE;
        
       -#ifdef CYGWIN
       -   bufsize=0;
       -   WNetGetUser(NULL,username,&bufsize);
       -   if (GetLastError()!=ERROR_MORE_DATA) {
       -     SetError(&NetBuf->error,ET_WIN32,GetLastError(),NULL);
       -     return FALSE;
       +   if (NetBuf->socks->user && NetBuf->socks->user[0]) {
       +     username = g_strdup(NetBuf->socks->user);
           } else {
       -     username=g_malloc(bufsize);
       -     if (WNetGetUser(NULL,username,&bufsize)!=NO_ERROR) {
       +#ifdef CYGWIN
       +     bufsize=0;
       +     WNetGetUser(NULL,username,&bufsize);
       +     if (GetLastError()!=ERROR_MORE_DATA) {
               SetError(&NetBuf->error,ET_WIN32,GetLastError(),NULL);
               return FALSE;
       +     } else {
       +       username=g_malloc(bufsize);
       +       if (WNetGetUser(NULL,username,&bufsize)!=NO_ERROR) {
       +         SetError(&NetBuf->error,ET_WIN32,GetLastError(),NULL);
       +         return FALSE;
       +       }
             }
       -   }
       -g_print("username %s\n",username);
       -   addlen=9+strlen(username);
        #else
       -   pwd = getpwuid(getuid());
       -   if (!pwd || !pwd->pw_name) return FALSE;
       -g_print("username %s\n",pwd->pw_name);
       -   addlen=9+strlen(pwd->pw_name);
       +     if (NetBuf->socks->numuid) {
       +       username=g_strdup_printf("%d",getuid());
       +     } else {
       +       pwd = getpwuid(getuid());
       +       if (!pwd || !pwd->pw_name) return FALSE;
       +       username=g_strdup(pwd->pw_name);
       +     }
        #endif
       +   }
       +   addlen=9+strlen(username);
        
           haddr = (struct in_addr *)he->h_addr;
           g_assert(sizeof(struct in_addr)==4);
       t@@ -907,30 +900,18 @@ g_print("username %s\n",pwd->pw_name);
           netport = htons(RemotePort);
           g_assert(sizeof(netport)==2);
        
       -   addpt = ExpandWriteBuffer(conn,addlen);
       -   if (!addpt) {
       -      SetError(&NetBuf->error,ET_CUSTOM,E_FULLBUF,NULL);
       -      return FALSE;
       -   }
       -
       +   addpt = ExpandWriteBuffer(conn,addlen,&NetBuf->error);
       +   if (!addpt) return FALSE;
        
           addpt[0] = 4;  /* SOCKS version */
           addpt[1] = 1;  /* CONNECT */
           memcpy(&addpt[2],&netport,sizeof(netport));
           memcpy(&addpt[4],haddr,sizeof(struct in_addr));
       -#ifdef CYGWIN
           strcpy(&addpt[8],username);
           g_free(username);
       -#else
       -   strcpy(&addpt[8],pwd->pw_name);
       -#endif
           addpt[addlen-1] = '\0';
        
       -   conn->DataPresent+=addlen;
       -
       -/* If the buffer was empty before, we may need to tell the owner to check
       -   the socket for write-ready status */
       -   if ((gchar *)addpt==conn->Data) NetBufCallBack(NetBuf);
       +   CommitWriteBuffer(NetBuf,conn,addpt,addlen);
        
           return TRUE;
        }
       t@@ -1094,7 +1075,7 @@ gboolean SetHttpAuthentication(HttpConnection *conn,gboolean proxy,
              ptuser=&conn->user; ptpassword=&conn->password;
           }
           g_free(*ptuser); g_free(*ptpassword);
       -   if (user && password) {
       +   if (user && password && user[0] && password[0]) {
              *ptuser = g_strdup(user);
              *ptpassword = g_strdup(password);
           } else {
       t@@ -1228,7 +1209,7 @@ gchar *ReadHttpResponse(HttpConnection *conn) {
        
        gboolean HandleHttpCompletion(HttpConnection *conn) {
           NBCallBack CallBack;
       -   gpointer CallBackData;
       +   gpointer CallBackData,userpasswddata;
           NBUserPasswd userpasswd;
           gboolean retry=FALSE;
           LastError **error;
       t@@ -1266,12 +1247,14 @@ gboolean HandleHttpCompletion(HttpConnection *conn) {
           if (retry) {
              CallBack=conn->NetBuf.CallBack;
              userpasswd=conn->NetBuf.userpasswd;
       +      userpasswddata=conn->NetBuf.userpasswddata;
              CallBackData=conn->NetBuf.CallBackData;
              ShutdownNetworkBuffer(&conn->NetBuf);
              if (StartHttpConnect(conn)) {
                 SendHttpRequest(conn);
                 SetNetworkBufferCallBack(&conn->NetBuf,CallBack,CallBackData);
       -         SetNetworkBufferUserPasswdFunc(&conn->NetBuf,userpasswd);
       +         SetNetworkBufferUserPasswdFunc(&conn->NetBuf,
       +                                        userpasswd,userpasswddata);
                 return FALSE;
              }
           } else if (conn->StatusCode>=300) {
       t@@ -1323,10 +1306,11 @@ gboolean BindTCPSocket(int sock,unsigned port,LastError **error) {
        }
        
        gboolean StartConnect(int *fd,gchar *RemoteHost,unsigned RemotePort,
       -                      gboolean NonBlocking,LastError **error) {
       +                      gboolean *doneOK,LastError **error) {
           struct sockaddr_in ClientAddr;
           struct hostent *he;
        
       +   if (doneOK) *doneOK=FALSE;
           he = LookupHostname(RemoteHost,error);
           if (!he) return FALSE;
        
       t@@ -1338,7 +1322,7 @@ gboolean StartConnect(int *fd,gchar *RemoteHost,unsigned RemotePort,
           ClientAddr.sin_addr=*((struct in_addr *)he->h_addr);
           memset(ClientAddr.sin_zero,0,sizeof(ClientAddr.sin_zero));
        
       -   SetBlocking(*fd,!NonBlocking);
       +   SetBlocking(*fd,FALSE);
        
           if (connect(*fd,(struct sockaddr *)&ClientAddr,
               sizeof(struct sockaddr))==SOCKET_ERROR) {
       t@@ -1353,7 +1337,7 @@ gboolean StartConnect(int *fd,gchar *RemoteHost,unsigned RemotePort,
              CloseSocket(*fd); *fd=-1;
              return FALSE;
           } else {
       -      SetBlocking(*fd,FALSE); /* All connected sockets should be nonblocking */
       +      if (doneOK) *doneOK=TRUE;
           }
           return TRUE;
        }
 (DIR) diff --git a/src/network.h b/src/network.h
       t@@ -65,7 +65,7 @@ typedef struct _NetworkBuffer NetworkBuffer;
        
        typedef void (*NBCallBack)(NetworkBuffer *NetBuf,gboolean Read,gboolean Write);
        
       -typedef void (*NBUserPasswd)(NetworkBuffer *NetBuf);
       +typedef void (*NBUserPasswd)(NetworkBuffer *NetBuf,gpointer data);
        
        /* Information about a SOCKS server */
        typedef struct _SocksServer {
       t@@ -108,6 +108,7 @@ struct _NetworkBuffer {
           SocksServer *socks;      /* If non-NULL, a SOCKS server to use */
           NBUserPasswd userpasswd; /* Function to supply username and password for
                                       SOCKS5 authentication */  
       +   gpointer userpasswddata; /* data to pass to the above function */
           gchar *host;             /* If non-NULL, the host to connect to */
           unsigned port;           /* If non-NULL, the port to connect to */
           LastError *error;        /* Any error from the last operation */
       t@@ -160,7 +161,7 @@ void InitNetworkBuffer(NetworkBuffer *NetBuf,char Terminator,char StripChar,
        void SetNetworkBufferCallBack(NetworkBuffer *NetBuf,NBCallBack CallBack,
                                      gpointer CallBackData);
        void SetNetworkBufferUserPasswdFunc(NetworkBuffer *NetBuf,
       -                                    NBUserPasswd userpasswd);
       +                                    NBUserPasswd userpasswd,gpointer data);
        gboolean IsNetworkBufferActive(NetworkBuffer *NetBuf);
        void BindNetworkBufferToSocket(NetworkBuffer *NetBuf,int fd);
        gboolean StartNetworkBufferConnect(NetworkBuffer *NetBuf,gchar *RemoteHost,
       t@@ -180,6 +181,11 @@ gint CountWaitingMessages(NetworkBuffer *NetBuf);
        gchar *GetWaitingMessage(NetworkBuffer *NetBuf);
        gboolean SendSocks5UserPasswd(NetworkBuffer *NetBuf,gchar *user,
                                      gchar *password);
       +gchar *GetWaitingData(NetworkBuffer *NetBuf,int numbytes);
       +gchar *PeekWaitingData(NetworkBuffer *NetBuf,int numbytes);
       +gchar *ExpandWriteBuffer(ConnBuf *conn,int numbytes,LastError **error);
       +void CommitWriteBuffer(NetworkBuffer *NetBuf,ConnBuf *conn,
       +                       gchar *addpt,guint addlen);
        
        gboolean OpenHttpConnection(HttpConnection **conn,gchar *HostName,
                                    unsigned Port,gchar *Proxy,unsigned ProxyPort,
       t@@ -196,8 +202,6 @@ gboolean IsHttpError(HttpConnection *conn);
        
        int CreateTCPSocket(LastError **error);
        gboolean BindTCPSocket(int sock,unsigned port,LastError **error);
       -gboolean StartConnect(int *fd,gchar *RemoteHost,unsigned RemotePort,
       -                      gboolean NonBlocking,LastError **error);
        void StartNetworking(void);
        void StopNetworking(void);
        
 (DIR) diff --git a/src/serverside.c b/src/serverside.c
       t@@ -138,6 +138,7 @@ static void MetaSocketStatus(NetworkBuffer *NetBuf,
                                     gboolean Read,gboolean Write);
        #endif
        
       +#ifdef NETWORKING
        static gboolean MetaConnectError(HttpConnection *conn) {
           GString *errstr;
           if (!IsHttpError(conn)) return FALSE;
       t@@ -148,6 +149,7 @@ static gboolean MetaConnectError(HttpConnection *conn) {
           g_string_free(errstr,TRUE);
           return TRUE;
        }
       +#endif
        
        void RegisterWithMetaServer(gboolean Up,gboolean SendData,
                                    gboolean RespectTimeout) {
       t@@ -808,7 +810,6 @@ Player *HandleNewConnection(void) {
               &cadsize))==-1) {
              perror("accept socket"); bgetch(); exit(1);
           }
       -   SetBlocking(ClientSock,FALSE);
           dopelog(2,_("got connection from %s"),inet_ntoa(ClientAddr.sin_addr));
           tmp=g_new(Player,1);
           FirstServer=AddPlayer(ClientSock,tmp,FirstServer);