/* --------------------------------------------------------------------- * * network.c * * --------------------------------------------------------------------- * * This file is part of the nebula irc client * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include #include #include #include /* for inet_ntoa and similar */ #include #include #include #include #include #include #include #include #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); doUser(sysState.myUnixname, sysState.myHostName, sysState.serverName, sysState.myName); printf("nick: %s\n", sysState.myNick); doNick(sysState.myNick); 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, " :"); /* Right now, realname is never set. We need to fix that in main(). */ /* Anyway, using the user's username as a realname when realname isn't set should be fine. */ /* It's a hell of a lot better than fighting with cryptic error messages, that's for sure. */ if(strlen(realname)) { strcat(outBuff, realname); } else { strcat(outBuff, username); } 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"