diff --git a/IRC-MSG.COB b/IRC-MSG.COB new file mode 100644 index 0000000..bc7134d --- /dev/null +++ b/IRC-MSG.COB @@ -0,0 +1,50 @@ + IDENTIFICATION DIVISION. + PROGRAM-ID. "IRC-MSG". + + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 PREFIX-WORK PIC X(96). + 01 MSG-POINTER PIC 9(3). + + LINKAGE SECTION. + 01 BUFFER. + 03 MSG-LENGTH PIC 9(3). + 03 MSG-BODY PIC X(512). + 03 MSG-BODY-1 REDEFINES MSG-BODY PIC X. + 88 HAS-PREFIX VALUE ":". + 01 IRC-MESSAGE. + 03 PREFIX. + 05 NICK PIC X(16). + 05 IDENT PIC X(16). + 05 HOST PIC X(64). + 03 COMMAND PIC X(16). + 03 PARAMETERS. + 05 TARGET PIC X(50). + 05 REST PIC X(480). + + PROCEDURE DIVISION USING BUFFER, IRC-MESSAGE. + INITIALIZE IRC-MESSAGE, PREFIX-WORK, MSG-POINTER. + IF HAS-PREFIX THEN + PERFORM WITH-PREFIX + ELSE + MOVE 1 TO MSG-POINTER. + UNSTRING MSG-BODY, + DELIMITED BY SPACES, + INTO COMMAND, TARGET + WITH POINTER MSG-POINTER. + UNSTRING MSG-BODY, + DELIMITED BY " ", + INTO REST, + WITH POINTER MSG-POINTER. + + WITH-PREFIX. + MOVE 2 TO MSG-POINTER. + UNSTRING MSG-BODY, + DELIMITED BY SPACES, + INTO PREFIX-WORK, + WITH POINTER MSG-POINTER. + UNSTRING PREFIX-WORK, + DELIMITED BY "!" OR "@", + INTO NICK, + IDENT, + HOST. diff --git a/PRINTCNF.COB b/PRINTCNF.COB new file mode 100644 index 0000000..feeac65 --- /dev/null +++ b/PRINTCNF.COB @@ -0,0 +1,70 @@ + IDENTIFICATION DIVISION. + PROGRAM-ID. "PRINT-CONFIG". + + ENVIRONMENT DIVISION. + INPUT-OUTPUT SECTION. + FILE-CONTROL. + SELECT CONFIG + ORGANIZATION IS INDEXED + ACCESS MODE IS SEQUENTIAL + RECORD KEY IS CONFIG-KEY. + SELECT USERS + ORGANIZATION IS INDEXED + ACCESS MODE IS SEQUENTIAL + RECORD KEY IS USER-NAME. + SELECT CHANNELS + ORGANIZATION IS SEQUENTIAL. + + DATA DIVISION. + FILE SECTION. + FD CONFIG. + 01 CONFIG-RECORD. + 03 CONFIG-KEY PIC X(16). + 03 CONFIG-VALUE PIC X(64). + FD USERS. + 01 USER-RECORD. + 03 USER-NAME PIC X(16). + 03 USER-LEVEL PIC 9(2). + FD CHANNELS. + 01 CHANNEL-RECORD. + 03 CHANNEL-NAME PIC X(50). + + WORKING-STORAGE SECTION. + 01 STATE PIC 9(2) VALUE 0. + 88 DONE VALUE 10. + + PROCEDURE DIVISION. + DISPLAY "CONFIGURATION ENTRIES:" + OPEN INPUT CONFIG. + PERFORM PRINT-CONFIG-ENTRY UNTIL DONE. + INITIALIZE STATE. + CLOSE CONFIG. + DISPLAY "USER ENTRIES:" + OPEN INPUT USERS. + PERFORM PRINT-USER-ENTRY UNTIL DONE. + INITIALIZE STATE. + CLOSE USERS. + DISPLAY "CHANNEL ENTRIES:" + OPEN INPUT CHANNELS. + PERFORM PRINT-CHANNEL-ENTRY UNTIL DONE. + INITIALIZE STATE. + CLOSE CHANNELS. + EXIT PROGRAM. + + PRINT-CONFIG-ENTRY. + READ CONFIG NEXT RECORD + AT END MOVE 10 TO STATE. + IF NOT DONE THEN + DISPLAY CONFIG-RECORD. + + PRINT-USER-ENTRY. + READ USERS NEXT RECORD + AT END MOVE 10 TO STATE. + IF NOT DONE THEN + DISPLAY USER-RECORD. + + PRINT-CHANNEL-ENTRY. + READ CHANNELS NEXT RECORD + AT END MOVE 10 TO STATE. + IF NOT DONE THEN + DISPLAY CHANNEL-RECORD. diff --git a/WOPO-CNF.COB b/WOPO-CNF.COB new file mode 100644 index 0000000..1f25261 --- /dev/null +++ b/WOPO-CNF.COB @@ -0,0 +1,81 @@ + IDENTIFICATION DIVISION. + PROGRAM-ID. "WOPO-CNF". + + ENVIRONMENT DIVISION. + INPUT-OUTPUT SECTION. + FILE-CONTROL. + SELECT CONFIG + ORGANIZATION IS INDEXED + ACCESS MODE IS RANDOM + RECORD KEY IS CONFIG-KEY. + SELECT USERS + ORGANIZATION IS INDEXED + ACCESS MODE IS RANDOM + RECORD KEY IS USER-NAME. + SELECT CHANNELS + ORGANIZATION IS SEQUENTIAL. + + DATA DIVISION. + FILE SECTION. + FD CONFIG. + 01 CONFIG-RECORD. + 03 CONFIG-KEY PIC X(16). + 03 CONFIG-VALUE PIC X(64). + FD USERS. + 01 USER-RECORD. + 03 USER-NAME PIC X(16). + 03 USER-LEVEL PIC 9(2). + FD CHANNELS. + 01 CHANNEL-RECORD. + 03 CHANNEL-NAME PIC X(50). + + WORKING-STORAGE SECTION. + 01 STATE PIC 9(2) VALUE 0. + 88 DONE VALUE 10. + + PROCEDURE DIVISION. + DISPLAY "WOPO CONFIGURATION PROGRAM". + DISPLAY "BLANK ENTRY TO EXIT SECTION". + DISPLAY "WRITING CONFIGURATION ENTRIES:". + OPEN OUTPUT CONFIG. + PERFORM WRITE-CONFIG-ENTRY UNTIL DONE. + INITIALIZE STATE. + CLOSE CONFIG. + DISPLAY "WRITING USER ENTRIES:" + OPEN OUTPUT USERS. + PERFORM WRITE-USER-ENTRY UNTIL DONE. + INITIALIZE STATE. + CLOSE USERS. + OPEN OUTPUT CHANNELS. + DISPLAY "WRITING CHANNEL AUTOJOINS:" + PERFORM WRITE-CHANNEL-ENTRY UNTIL DONE. + INITIALIZE STATE. + CLOSE CHANNELS. + CALL "PRINT-CONFIG". + STOP RUN. + + WRITE-CONFIG-ENTRY. + DISPLAY "KEY?" + ACCEPT CONFIG-KEY. + DISPLAY "VALUE?" + ACCEPT CONFIG-VALUE. + IF CONFIG-KEY EQUALS SPACES OR CONFIG-VALUE EQUALS SPACES + THEN MOVE 10 TO STATE + ELSE WRITE CONFIG-RECORD. + + WRITE-USER-ENTRY. + DISPLAY "USER?" + ACCEPT USER-NAME. + DISPLAY "LEVEL?" + ACCEPT USER-LEVEL. + IF USER-NAME EQUALS SPACES OR USER-LEVEL EQUALS 0 + THEN MOVE 10 TO STATE + ELSE WRITE USER-RECORD. + + WRITE-CHANNEL-ENTRY. + DISPLAY "CHANNEL?" + ACCEPT CHANNEL-NAME. + IF CHANNEL-NAME EQUALS SPACES + THEN MOVE 10 TO STATE + ELSE WRITE CHANNEL-RECORD. + diff --git a/WOPO.COB b/WOPO.COB new file mode 100644 index 0000000..603fdf3 --- /dev/null +++ b/WOPO.COB @@ -0,0 +1,355 @@ + IDENTIFICATION DIVISION. + PROGRAM-ID. "WOPO". + + ENVIRONMENT DIVISION. + INPUT-OUTPUT SECTION. + FILE-CONTROL. + SELECT CONFIG + ORGANIZATION IS INDEXED + ACCESS MODE IS RANDOM + RECORD KEY IS CONFIG-KEY. + SELECT USERS + ORGANIZATION IS INDEXED + ACCESS MODE IS RANDOM + RECORD KEY IS USER-NAME. + SELECT CHANNELS + ORGANIZATION IS SEQUENTIAL. + + DATA DIVISION. + FILE SECTION. + FD CONFIG. + 01 CONFIG-RECORD. + 03 CONFIG-KEY PIC X(16). + 03 CONFIG-VALUE PIC X(64). + FD USERS. + 01 USER-RECORD. + 03 USER-NAME PIC X(16). + 03 USER-LEVEL PIC 9(2). + FD CHANNELS. + 01 CHANNEL-RECORD. + 03 CHANNEL-NAME PIC X(50). + + WORKING-STORAGE SECTION. + 01 STATE PIC 9(2). + 88 SUCCESS VALUE 0. + 88 DONE VALUE 99. + 01 BUFFER. + 03 MSG-LENGTH PIC 9(3). + 03 MSG-BODY PIC X(512). + 01 IRC-MESSAGE. + 03 PREFIX. + 05 NICK PIC X(16). + 05 IDENT PIC X(16). + 05 HOST PIC X(64). + 03 COMMAND PIC X(16). + 88 PING VALUE "PING". + 88 PRIVMSG VALUE "PRIVMSG". + 03 PARAMETERS. + 05 TARGET PIC X(50). + 05 REST PIC X(480). + 01 WAITING-COMMAND PIC X(16). + 01 PARAMS. + 03 WORK PIC X(50). + 03 WORK-PREFIX REDEFINES WORK PIC X. + 88 IS-COMMAND VALUE "$". + 88 REST-PARAM VALUE ":". + 03 PARAM PIC X(50) OCCURS 5 TIMES. + 03 REG PIC X(50) OCCURS 5 TIMES. + + PROCEDURE DIVISION. + DISPLAY "CONFIGURATION FOLLOWS:". + CALL "PRINT-CONFIG". + MOVE LENGTH OF MSG-BODY TO MSG-LENGTH. + CALL "CHANNEL-INIT" + USING BUFFER. + OPEN INPUT CONFIG. + MOVE "SERVER" TO CONFIG-KEY. + PERFORM READ-CONFIG-ENTRY. + MOVE 1 TO MSG-LENGTH. + STRING + CONFIG-VALUE, DELIMITED BY SPACE, + INTO MSG-BODY, + WITH POINTER MSG-LENGTH. + CALL "CHANNEL-OPEN", GIVING STATE. + IF NOT SUCCESS THEN DISPLAY MSG-BODY + GO TO DIE. + MOVE "NICK" TO CONFIG-KEY. + PERFORM READ-CONFIG-ENTRY. + MOVE 1 TO MSG-LENGTH. + INITIALIZE MSG-BODY. + STRING "NICK" + INTO MSG-BODY + WITH POINTER MSG-LENGTH. + ADD 1 TO MSG-LENGTH. + STRING CONFIG-VALUE DELIMITED BY SPACE, + INTO MSG-BODY + WITH POINTER MSG-LENGTH. + PERFORM SEND-LINE. + MOVE 1 TO MSG-LENGTH. + INITIALIZE MSG-BODY. + STRING "USER" + INTO MSG-BODY + WITH POINTER MSG-LENGTH. + ADD 1 TO MSG-LENGTH. + MOVE "IDENT" TO CONFIG-KEY. + PERFORM READ-CONFIG-ENTRY. + STRING CONFIG-VALUE DELIMITED BY SPACE, + INTO MSG-BODY + WITH POINTER MSG-LENGTH. + ADD 1 TO MSG-LENGTH. + MOVE "REAL-NAME" TO CONFIG-KEY. + PERFORM READ-CONFIG-ENTRY. + STRING "BOGUS HOST :" DELIMITED BY SIZE, + CONFIG-VALUE DELIMITED BY " ", + INTO MSG-BODY + WITH POINTER MSG-LENGTH. + PERFORM SEND-LINE. + MOVE "NICKSERV-PASSWORD" TO CONFIG-KEY. + READ CONFIG RECORD + INVALID KEY MOVE SPACES TO CONFIG-KEY. + IF CONFIG-KEY IS NOT EQUAL TO SPACES THEN + INITIALIZE MSG-BODY + MOVE 1 TO MSG-LENGTH + STRING "PRIVMSG NICKSERV :IDENTIFY " DELIMITED BY SIZE, + CONFIG-VALUE DELIMITED BY SPACE, + INTO MSG-BODY + WITH POINTER MSG-LENGTH + PERFORM SEND-LINE. + OPEN INPUT CHANNELS. + PERFORM AUTOJOIN-CHANNELS UNTIL DONE. + CLOSE CHANNELS. + OPEN I-O USERS. + PERFORM MAIN FOREVER. + + DIE. + DISPLAY STATE. + STOP RUN. + + AUTOJOIN-CHANNELS. + READ CHANNELS RECORD + AT END MOVE 99 TO STATE. + IF NOT DONE THEN + MOVE 1 TO MSG-LENGTH + STRING "JOIN " DELIMITED BY SIZE, + CHANNEL-NAME DELIMITED BY SPACES, + INTO MSG-BODY + WITH POINTER MSG-LENGTH + PERFORM SEND-LINE. + + READ-CONFIG-ENTRY. + READ CONFIG RECORD + INVALID KEY DISPLAY "REQUIRED KEY UNSPECIFIED:" + DISPLAY CONFIG-KEY + GO TO DIE. + + SEND-LINE. + CALL "CHANNEL-SEND" GIVING STATE. + IF NOT SUCCESS THEN DISPLAY MSG-BODY + GO TO DIE. + + RECEIVE-LINE. + INITIALIZE MSG-BODY. + CALL "CHANNEL-RECV" GIVING STATE. + IF NOT SUCCESS THEN GO TO DIE. + CALL "IRC-MSG" USING BUFFER, IRC-MESSAGE. + + WAIT-FOR-COMMAND. + PERFORM RECEIVE-LINE UNTIL COMMAND EQUALS WAITING-COMMAND. + + GET-PARAMS. + UNSTRING REST DELIMITED BY SPACE INTO + PARAM(1) + PARAM(2) + PARAM(3) + PARAM(4) + PARAM(5). + MOVE PARAM(1) TO WORK. + IF REST-PARAM THEN + UNSTRING WORK DELIMITED BY ":" INTO PARAM(1), PARAM(1). + + VALIDATE-USER. + MOVE NICK TO USER-NAME. + READ USERS RECORD + INVALID KEY MOVE 0 TO USER-LEVEL. + DISPLAY USER-RECORD. + IF USER-LEVEL IS GREATER THAN 0 THEN + INITIALIZE MSG-BODY + MOVE 1 TO MSG-LENGTH + STRING "PRIVMSG NICKSERV :ACC" + INTO MSG-BODY + WITH POINTER MSG-LENGTH + ADD 1 TO MSG-LENGTH + STRING NICK + INTO MSG-BODY + WITH POINTER MSG-LENGTH + PERFORM SEND-LINE + MOVE "NOTICE" TO WAITING-COMMAND + MOVE 0 TO STATE + PERFORM WAIT-FOR-ACC UNTIL DONE. + + WAIT-FOR-ACC. + PERFORM WAIT-FOR-COMMAND. + PERFORM GET-PARAMS. + IF PARAM(1) EQUALS USER-NAME AND PARAM(2) EQUALS "ACC" THEN + MOVE 99 TO STATE + IF PARAM(3) IS NOT EQUAL TO "3" THEN + MOVE 0 TO USER-LEVEL + ELSE NEXT SENTENCE + ELSE INITIALIZE COMMAND. + + MAIN. + PERFORM RECEIVE-LINE. + MOVE 1 TO MSG-LENGTH. + IF PING THEN + PERFORM PONG + ELSE IF PRIVMSG THEN + PERFORM HANDLE-PRIVMSG. + + PONG. + DISPLAY "PONG". + STRING "PONG" + INTO MSG-BODY + WITH POINTER MSG-LENGTH. + PERFORM SEND-LINE. + + HANDLE-PRIVMSG. + PERFORM GET-PARAMS. + MOVE PARAM(1) TO WORK. + IF IS-COMMAND THEN + UNSTRING WORK DELIMITED BY "$" INTO PARAM(1), PARAM(1) + IF PARAM(1) IS EQUAL TO "HELP" THEN + PERFORM HANDLE-HELP + ELSE IF PARAM(1) IS EQUAL TO "SOURCE" THEN + PERFORM HANDLE-SOURCE + ELSE IF PARAM(1) IS EQUAL TO "LEVEL" THEN + PERFORM HANDLE-LEVEL + ELSE IF PARAM(1) IS EQUAL TO "OP" THEN + PERFORM HANDLE-OP + ELSE IF PARAM(1) IS EQUAL TO "JOIN" THEN + PERFORM HANDLE-JOIN + ELSE IF PARAM(1) IS EQUAL TO "PART" THEN + PERFORM HANDLE-PART + ELSE IF PARAM(1) IS EQUAL TO "QUIT" THEN + PERFORM HANDLE-QUIT + ELSE IF PARAM(1) IS EQUAL TO "RELEVEL" THEN + PERFORM HANDLE-RELEVEL. + + HANDLE-HELP. + INITIALIZE MSG-BODY. + MOVE 1 TO MSG-LENGTH. + STRING "PRIVMSG " + TARGET + INTO MSG-BODY + WITH POINTER MSG-LENGTH. + ADD 1 TO MSG-LENGTH. + STRING ":$HELP $LEVEL $OP $JOIN $PART $QUIT $RELEVEL" + INTO MSG-BODY + WITH POINTER MSG-LENGTH. + PERFORM SEND-LINE. + + HANDLE-SOURCE. + INITIALIZE MSG-BODY. + MOVE 1 TO MSG-LENGTH. + STRING "PRIVMSG " + TARGET + INTO MSG-BODY + WITH POINTER MSG-LENGTH. + ADD 1 TO MSG-LENGTH. + STRING ":HTTPS://GITHUB.COM/HEDDWCH/WOPO" + INTO MSG-BODY + WITH POINTER MSG-LENGTH. + PERFORM SEND-LINE. + + HANDLE-LEVEL. + IF PARAM(2) IS NOT EQUAL TO SPACES THEN + MOVE PARAM(2) TO USER-NAME + ELSE + MOVE NICK TO USER-NAME. + READ USERS RECORD + INVALID KEY MOVE 0 TO USER-LEVEL. + INITIALIZE MSG-BODY. + MOVE 1 TO MSG-LENGTH. + STRING USER-RECORD + INTO MSG-BODY + WITH POINTER MSG-LENGTH. + PERFORM SEND-LINE. + + HANDLE-OP. + MOVE TARGET TO REG(1). + MOVE PARAM(2) TO REG(2). + IF REG(2) IS EQUAL TO SPACES THEN + MOVE NICK TO REG(2). + PERFORM VALIDATE-USER. + IF USER-LEVEL IS GREATER THAN 50 THEN + INITIALIZE MSG-BODY + MOVE 1 TO MSG-LENGTH + STRING "MODE " DELIMITED BY SIZE, + REG(1) DELIMITED BY SPACES + INTO MSG-BODY + WITH POINTER MSG-LENGTH + ADD 1 TO MSG-LENGTH + STRING "+O " DELIMITED BY SIZE, + REG(2) DELIMITED BY SPACES + INTO MSG-BODY + WITH POINTER MSG-LENGTH + PERFORM SEND-LINE. + + HANDLE-JOIN. + MOVE PARAM(2) TO REG(1). + PERFORM VALIDATE-USER. + IF USER-LEVEL IS GREATER THAN 80 THEN + INITIALIZE MSG-BODY + MOVE 1 TO MSG-LENGTH + STRING "JOIN ", REG(1) + INTO MSG-BODY + WITH POINTER MSG-LENGTH + PERFORM SEND-LINE. + + HANDLE-PART. + MOVE PARAM(2) TO REG(1). + IF REG(1) EQUALS SPACES THEN + MOVE TARGET TO REG(1). + PERFORM VALIDATE-USER. + IF USER-LEVEL IS GREATER THAN 80 THEN + INITIALIZE MSG-BODY + MOVE 1 TO MSG-LENGTH + STRING "PART ", REG(1) + INTO MSG-BODY + WITH POINTER MSG-LENGTH + PERFORM SEND-LINE. + + HANDLE-QUIT. + MOVE "QUIT-MESSAGE" TO CONFIG-KEY. + READ CONFIG RECORD + INVALID KEY MOVE SPACES TO CONFIG-VALUE. + PERFORM VALIDATE-USER. + IF USER-LEVEL IS GREATER THAN 90 THEN + INITIALIZE MSG-BODY + MOVE 1 TO MSG-LENGTH + STRING "QUIT :", CONFIG-VALUE + INTO MSG-BODY + WITH POINTER MSG-LENGTH + PERFORM SEND-LINE + GO TO QUIT. + + HANDLE-RELEVEL. + MOVE PARAM(2) TO USER-NAME. + MOVE PARAM(3) TO USER-LEVEL. + PERFORM VALIDATE-USER. + IF USER-LEVEL IS EQUAL TO 99 THEN + WRITE USER-RECORD. + READ USERS RECORD + INVALID KEY MOVE 0 TO USER-LEVEL. + INITIALIZE MSG-BODY. + MOVE 1 TO MSG-LENGTH. + STRING USER-RECORD + INTO MSG-BODY + WITH POINTER MSG-LENGTH. + PERFORM SEND-LINE. + + QUIT. + CALL "CHANNEL-CLOSE". + CLOSE CONFIG. + CLOSE USERS. + STOP RUN. + diff --git a/channel.c b/channel.c new file mode 100644 index 0000000..e66363f --- /dev/null +++ b/channel.c @@ -0,0 +1,211 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LENGTH_DIGITS 3 +#define DEFAULT_PORT "6667" +#define RECV_BUF_SIZE 1024 +/* + * 01 BUFFER + * 03 MSG-LENGTH PIC 9(LENGTH_DIGITS) + * 03 MSG-BODY PIC X(x) + */ +#define BODY(buf) (char *)(buf + LENGTH_DIGITS) + +// 01 CHANNEL-STATUS PIC 9(2) +#define EBADDEST 10 +#define EOPENFAIL 20 +#define EHUP 30 +#define ESERV 40 + +char *msg_length, *msg_body; +int msg_body_len; +int sockfd; +char recv_buf[RECV_BUF_SIZE]; +size_t recv_buf_pos = 0; + +int channel_message_length(void) +{ + char c_length[LENGTH_DIGITS + 1]; + memcpy(c_length, msg_length, LENGTH_DIGITS); + c_length[LENGTH_DIGITS] = '\0'; + return (int)strtol(c_length, NULL, 10) - 1; +} + +void channel_from_cobol(void) +{ + int message_length = channel_message_length(); + memset(msg_body + message_length, 0, msg_body_len - message_length); + for(char *c = msg_body; *c; c++) { + *c = tolower(*c); + } + return; +} + +void channel_to_cobol(void) +{ + char *c; + for(c = msg_body; *c; c++) { + *c = toupper(*c); + } + memset(c, ' ', msg_body_len - strlen(msg_body)); + + return; +} + +void channel_string_to_cobol(const char *s) +{ + strncpy(msg_body, s, msg_body_len - 2); + msg_body[msg_body_len - 1] = '\0'; + channel_to_cobol(); + return; +} + +/* + * MOVE LENGTH OF MSG-BODY OF BUFFER TO MSG-LENGTH OF BUFFER. + * CALL "CHANNEL-INIT" USING BUFFER. + */ +void CHANNEL__INIT(char *buffer) +{ + msg_length = buffer; + msg_body = BODY(buffer); + msg_body_len = channel_message_length(); + + return; +} + +/* + * STRING "HOST[:PORT]" + * INTO MSG-BODY + * WITH POINTER MSG-LENGTH. + * CALL "CHANNEL-OPEN" GIVING CHANNEL-STATUS. + */ +int CHANNEL__OPEN(void) +{ + channel_from_cobol(); + printf("Connecting to: %s\n", msg_body); + if(!strlen(msg_body)) { + channel_string_to_cobol("No host specified"); + return EBADDEST; + } + char *port = strchr(msg_body, ':'); + if(port) { + *port = '\0'; + port++; + if(!strlen(port)) { + channel_string_to_cobol("Port separator specified, but not port"); + return EBADDEST; + } + } else { + port = DEFAULT_PORT; + } + + struct addrinfo hints, *res; + int status; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; + if((status = getaddrinfo(msg_body, port, &hints, &res))) { + channel_string_to_cobol(gai_strerror(status)); + return EBADDEST; + } + + struct addrinfo *curr_addr; + for(curr_addr = res; curr_addr; curr_addr = curr_addr->ai_next) { + sockfd = socket(curr_addr->ai_family, curr_addr->ai_socktype, curr_addr->ai_protocol); + if(sockfd == -1) { + perror("socket"); + continue; + } + if(connect(sockfd, curr_addr->ai_addr, curr_addr->ai_addrlen) == -1) { + perror("connect"); + close(sockfd); + continue; + } + + break; + } + + if(!curr_addr) { + channel_string_to_cobol("Unable to connect to host"); + return EOPENFAIL; + } + + return 0; +} + +int CHANNEL__SEND(void) +{ + char *msg; + int sent, total; + channel_from_cobol(); + printf("Sending: %s\n", msg_body); + sent = 0; + total = strlen(msg_body); + msg_body[total++] = '\n'; + while(sent < total) { + int status = send(sockfd, msg_body + sent, total - sent, 0); + if(status == -1) { + perror("send"); + close(sockfd); + channel_string_to_cobol("Hung up"); + return EHUP; + } + sent += status; + } + + return 0; +} + +int CHANNEL__RECV(void) +{ + char *message_end; + get_buffered: + message_end = memchr(recv_buf, '\n', recv_buf_pos); + if(message_end) { + size_t message_size = message_end - recv_buf; + if (message_size > msg_body_len) { + channel_string_to_cobol("Server sent too-long message"); + return ESERV; + } + memcpy(msg_body, recv_buf, message_size); + msg_body[message_size - 1] = '\0'; + recv_buf_pos -= message_size + 1; + message_end++; + for(size_t i = 0; i < recv_buf_pos; i++) { + recv_buf[i] = message_end[i]; + } + printf("Received: %s\n", msg_body); + channel_to_cobol(); + return 0; + } + if(recv_buf_pos < RECV_BUF_SIZE - 1) { + ssize_t received = recv(sockfd, recv_buf + recv_buf_pos, RECV_BUF_SIZE - recv_buf_pos, 0); + if(received != -1) { + recv_buf_pos += received; + goto get_buffered; + } + perror("recv"); + channel_string_to_cobol("Hung up"); + return EHUP; + } + + channel_string_to_cobol("Server failed to send newline"); + return ESERV; +} + +void CHANNEL__CLOSE(void) +{ + close(sockfd); + + return; +} + diff --git a/gnu-cobol.sh b/gnu-cobol.sh new file mode 100644 index 0000000..79b3dd5 --- /dev/null +++ b/gnu-cobol.sh @@ -0,0 +1,5 @@ +#!/bin/sh +${CC:-cc} -o channel.o -c channel.c +${COBC:-cobc} -x WOPO-CNF.COB PRINTCNF.COB +${COBC:-cobc} -x WOPO.COB IRC-MSG.COB PRINTCNF.COB channel.o +