Nebula/src/network.c
Mid Favila 0e9ed3e250 Start work on eliminating redundant variable usage. Prevent
an I-bar from showing up on the output frame. Move the copyright
notice out to a header file.
2022-08-21 21:35:28 -04:00

431 lines
11 KiB
C

/* --------------------------------------------------------------------- *
* network.c *
* --------------------------------------------------------------------- *
* This file is part of the nebula irc client *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> /* for inet_ntoa and similar */
#include <netdb.h>
#include <strings.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <X11/Intrinsic.h>
#include "main.h"
#include "network.h"
#include "signals.h"
#include "interpreter.h"
extern systemStatusRecord sysState;
extern XtAppContext nebulaIrcAppContext;
extern XtInputId nebulaNetworkEventId;
/* this function opens the connection to the given server
* it also initializes the system information structure sysState
* and it logins the user into the server
*/
int connectToServer(char *servName, int port)
{
int sock;
struct hostent *hostentPtr;
struct sockaddr_in sockAddr;
char *tempStr;
appendText("Resolving ");
appendText(servName);
appendText("... ");
if((hostentPtr = gethostbyname(servName)) == NULL)
{
appendText("Could not resolve ");
appendText(servName);
appendText("\n");
return -1;
}
bcopy((char *)hostentPtr->h_addr, (char *)&sockAddr.sin_addr, hostentPtr->h_length);
sockAddr.sin_family = PF_INET;
sockAddr.sin_port = htons(port);
tempStr = inet_ntoa(sockAddr.sin_addr);
appendText(tempStr);
appendText("\n");
sock = socket(PF_INET, SOCK_STREAM, 0);
if (connect(sock, (struct sockaddr*) &sockAddr, sizeof(sockAddr)) < 0)
{
perror("connect failed: ");
return -1;
}
appendText("We are connected.\n");
/* initialize the system structures */
sysState.isConnected = YES;
sysState.isAuthenticated = NO;
sysState.socket = sock;
sysState.isInChannel = NO;
sysState.isInQuery = NO;
strcpy(sysState.serverName, servName);
sysState.port = port;
/* we add our socket to the select file descriptors as an Event source for X */
nebulaNetworkEventId = XtAppAddInput(nebulaIrcAppContext, sysState.socket, (XtPointer)(XtInputReadMask), readLine, NULL);
doNick(sysState.myNick);
doUser(sysState.myUnixname, sysState.myHostName, sysState.serverName, sysState.myName);
return 0;
}
void resetStatusToDisconnected()
{
/* we remove the network from the X event handler again */
XtRemoveInput(nebulaNetworkEventId);
sysState.isConnected = NO;
sysState.isAuthenticated = NO;
sysState.isInChannel = NO;
sysState.isInQuery = NO;
sysState.serverName[0]='\0';
sysState.currentChannel[0]='\0';
sysState.currentQuery[0]='\0';
}
/* reads one or more lines from the network
* possibly splits them up and invokes the interpreter
*/
void readLine()
{
char inBuff[MAX_COMMAND_LEN+1];
char inLine[MAX_COMMAND_LEN+1];
char rest[MAX_COMMAND_LEN+1];
int readBytes;
int buffLen;
char *firstNLPos;
readBytes = recv(sysState.socket, inBuff, MAX_COMMAND_LEN, 0);
if (readBytes < 0)
{
if (errno == ECONNRESET) /* connection reset by peer */
{
resetStatusToDisconnected();
appendText("Connectioni reset by peer.\n");
return;
} else
{
perror("read < 0 bytes from socket");
#ifdef DEBUG
printf("errno is: %d\n", errno);
#endif
}
}
if (readBytes == 0)
{
perror("Read 0 bytes from socket. Assuming connection closed.");
resetStatusToDisconnected();
appendText("Lost connection.\n");
return;
}
inBuff[readBytes] = '\0';
buffLen = strlen(inBuff);
firstNLPos = strstr(inBuff, "\r\n");
if (firstNLPos)
{
if (firstNLPos - inBuff == buffLen - 2)
{
#ifdef DEBUG
printf("The NL is only at the end\n");
#endif
inBuff[readBytes - 2] = '\0';
#ifdef DEBUG
printf("|%s|\n", inBuff);
assert(strstr(inBuff, "\r\n") == NULL);
assert(strlen(inBuff) < MAX_COMMAND_LEN);
#endif
interpretMessage(inBuff);
return;
} else
{
while ((firstNLPos - inBuff != buffLen - 2) && (firstNLPos != 0))
{
int lineLen;
int restLen;
lineLen = firstNLPos - inBuff;
strncpy(inLine, inBuff, lineLen);
inLine[lineLen] = '\0';
#ifdef DEBUG
printf("line = |%s|\n", inLine);
assert(strstr(inLine, "\r\n") == NULL);
assert(strlen(inLine) < MAX_COMMAND_LEN);
#endif
interpretMessage(inLine);
restLen = buffLen - 2 - lineLen;
strncpy(rest, inBuff+lineLen+2, restLen);
rest[restLen] = '\0';
strcpy(inBuff, rest);
buffLen = strlen(inBuff);
firstNLPos = strstr(inBuff, "\r\n");
}
if (firstNLPos - inBuff == buffLen - 2)
{
inBuff[strlen(inBuff) - 2] = '\0';
#ifdef DEBUG
printf("|%s|, %d\n", inBuff, strlen(inBuff));
assert(strstr(inBuff, "\r\n") == NULL);
assert(strlen(inBuff) < MAX_COMMAND_LEN);
#endif
interpretMessage(inBuff);
return;
}
}
}
/* ok what we read was not a full line */
/* the buffer was probably full (but it may not be anymore oafter the removal */
/* of the complete lines */
if (firstNLPos == NULL)
{
char inBuff2[MAX_COMMAND_LEN+1];
char ch[1];
int buff2Curs;
int gotNewLine;
int gotCR;
buff2Curs = 0;
gotNewLine = NO;
gotCR = NO;
printf("We have a not CR_LF terminated input buffer. Reading more.\n");
while (buff2Curs < MAX_COMMAND_LEN && !gotNewLine)
{
if (read(sysState.socket, ch, 1))
{
inBuff2[buff2Curs++] = *ch;
if (*ch == '\r')
gotCR = YES;
else if (gotCR)
{
if (*ch == '\n')
{
#ifdef DEBUG
printf("I got a CR-LF\n");
#endif
gotNewLine = YES;
inBuff2[--buff2Curs] = '\0';
inBuff2[--buff2Curs] = '\0';
} else
{
gotCR = NO;
}
}
}
}
if (gotNewLine)
{
if (strlen(inBuff) + strlen(inBuff2) < MAX_COMMAND_LEN)
{
strcat(inBuff, inBuff2);
printf("Complete command is: ||%s||\n", inBuff);
#ifdef DEBUG
assert(strlen(inBuff) < MAX_COMMAND_LEN);
#endif
interpretMessage(inBuff);
} else
{
printf("Error: our buffer is to small to fit all the size to the CR-LF\n");
}
} else
printf("We have a not CR_LF terminated input buffer, even on the second attempt.\n");
}
}
void sendLine(char *msg)
{
int len;
int sentBytes;
if (sysState.isConnected == NO)
return;
len = strlen(msg);
if (len <= 0)
return;
if (len >= MAX_CHUNK_LEN - 2)
{
msg[MAX_CHUNK_LEN] = '\0';
msg[MAX_CHUNK_LEN-1] = '\n';
msg[MAX_CHUNK_LEN-2] = '\r';
} else
{
msg[len++] = '\r';
msg[len] = '\n';
}
sentBytes = write(sysState.socket, msg, len);
if (sentBytes < len)
printf("Couldn't sent whole message.\n");
}
void disconnectFromServer()
{
/* close the socket if we are connected*/
if (sysState.isConnected)
close(sysState.socket);
}
/* nick is copied into the system status structure
* a check if the argument poitner is the same address as the sysState struct is done
* but beware of overlapping strings
*/
void doNick(char *nick)
{
char outBuff[MAX_COMMAND_LEN+1];
if (!sysState.isAuthenticated)
{
strcpy(outBuff, "NICK ");
strcat(outBuff, nick);
} else
{
sprintf(outBuff, ":%s NICK %s", sysState.myNick, nick);
}
#ifdef DEBUG
assert(strlen(nick) < MAX_NICK_LEN);
#endif
sendLine(outBuff);
/* check if strings are the same */
if (nick != sysState.myNick)
strcpy(sysState.myNick, nick);
}
void doUser(char *username, char *hostname, char *servername, char *realname)
/* should check for total length */
{
char outBuff[MAX_COMMAND_LEN+1];
if (strlen(username) + strlen(hostname) + strlen(servername) + strlen(realname) > MAX_COMMAND_LEN-9)
{
printf("command generated in doUser() is too long\n");
return;
}
strcpy(outBuff, "USER ");
strcat(outBuff, username);
strcat(outBuff, " ");
strcat(outBuff, hostname);
strcat(outBuff, " ");
strcat(outBuff, servername);
strcat(outBuff, " :");
strcat(outBuff, realname);
printf("sending: %s\n", outBuff);
sendLine(outBuff);
}
void doPrivateMsg(char *recipient, char *message)
/* should check for total length */
{
char outBuff[MAX_COMMAND_LEN+1];
char localEcho[MAX_COMMAND_LEN+1];
sprintf(outBuff, ":%s PRIVMSG %s :%s", sysState.myNick, recipient, message);
sendLine(outBuff);
if (!strcmp(recipient, sysState.currentChannel))
sprintf(localEcho, "%s : %s\n", sysState.myNick, message);
else
sprintf(localEcho, "[ %s ] : %s\n", sysState.myNick, message);
appendText(localEcho);
}
void doPong(char *dest)
{
char outBuff[MAX_COMMAND_LEN];
strcpy(outBuff, "PONG ");
strcat(outBuff, dest);
sendLine(outBuff);
#ifdef DEBUG
printf("Sent Pong to: %s\n", dest);
#endif
}
void doJoin(char *chan)
{
char outBuff[MAX_COMMAND_LEN];
strcpy(outBuff, "JOIN ");
strcat(outBuff, chan);
sendLine(outBuff);
sysState.isInChannel = YES;
strcpy(sysState.currentChannel, chan);
}
void doPart(char *chan)
{
char outBuff[MAX_COMMAND_LEN];
strcpy(outBuff, "PART ");
if (strcmp(chan, "*"))
{
strcat(outBuff, chan);
if (strcmp(chan, sysState.currentChannel))
sysState.isInChannel = NO;
} else
{
sysState.isInChannel = NO;
strcat(outBuff, sysState.currentChannel);
}
sendLine(outBuff);
}
void doMode (char *recipient, char *option, char *limit, char *user, char *banmask)
{
char outBuff[MAX_COMMAND_LEN];
sprintf(outBuff, "MODE %s %s %s %s %s", recipient, option, limit, user, banmask);
printf ("%s\n", outBuff);
sendLine(outBuff);
}
void doWhois(char *nick)
{
char outBuff[MAX_COMMAND_LEN];
strcpy(outBuff, "WHOIS ");
strcat(outBuff, nick);
sendLine(outBuff);
}
void doCTCPVersion (char *recipient)
{
char tempStr[2*MAX_COMMAND_LEN+2];
char localEcho[MAX_COMMAND_LEN+1];
sprintf(tempStr, ":%s PRIVMSG %s :\001VERSION\001\n", sysState.myNick, recipient);
sendLine(tempStr);
sprintf(localEcho, "[ requested Version from %s ]\n", recipient);
appendText(localEcho);
}
void doCTCPAction(char *recipient, char *message)
/* should check for total length */
{
char outBuff[MAX_COMMAND_LEN+1];
char localEcho[MAX_COMMAND_LEN+1];
sprintf(outBuff, ":%s PRIVMSG %s :\001ACTION %s\001", sysState.myNick, recipient, message);
sendLine(outBuff);
if (!strcmp(recipient, sysState.currentChannel))
sprintf(localEcho, "-- %s %s\n", sysState.myNick, message);
else
sprintf(localEcho, "[%s] %s\n", sysState.myNick, message);
appendText(localEcho);
}
#include "copyright.h"