aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Burwell <bburwell1@gmail.com>2013-04-29 16:13:34 -0400
committerBen Burwell <bburwell1@gmail.com>2013-04-29 16:13:34 -0400
commitca9e46c937e75df43d2f80f0d957fabb07892c29 (patch)
tree0d8ae2bc1d1e3e248c746586a5daf7cb58a79463
Init
-rw-r--r--chat_acceptor.h136
-rw-r--r--chat_request.h106
-rw-r--r--check_user_name.h27
-rw-r--r--clean_table.h47
-rw-r--r--loading.h24
-rw-r--r--localchat.c102
-rw-r--r--online.h35
-rw-r--r--own_ip.h64
-rw-r--r--parse.h71
-rw-r--r--peer.h13
-rw-r--r--receive.h47
-rw-r--r--status.h69
-rw-r--r--user_command.h62
13 files changed, 803 insertions, 0 deletions
diff --git a/chat_acceptor.h b/chat_acceptor.h
new file mode 100644
index 0000000..dd94ef5
--- /dev/null
+++ b/chat_acceptor.h
@@ -0,0 +1,136 @@
+/**
+ * Localchat
+ * Ben Burwell
+ *
+ * Chat request accepting functionality
+ */
+
+void *receive_chat_messages(void *arg) {
+
+ char in_buf[1024];
+ int length = 1024;
+ int retcode;
+
+ retcode = recv(client_s, in_buf, length, 0);
+ printf("%s \n", in_buf);
+
+ while (strcmp(in_buf, "SESQ")) {
+ retcode = recv(client_s, in_buf, length, 0);
+ if (retcode > 0) {
+ printf("%s \n", in_buf);
+ }
+ }
+
+}
+
+void *chat_acceptor(void *arg) {
+
+ int client_s;
+ char in_buf[4096];
+ char out_buf[4096];
+ int length = 4096;
+ char command[4096];
+ int retcode;
+ int i;
+ char * token;
+ struct sockaddr_in client_addr;
+
+ client_s = (int) arg;
+
+ retcode = recv(client_s, in_buf, length, 0);
+
+ if (strcmp(in_chat, "Y") == 0) {
+
+ // if already in a chat, reply no and close
+ strcpy(out_buf, "SESANS:N");
+ send(client_s, out_buf, strlen(out_buf)+1, 0);
+ close(client_s);
+
+ } else {
+
+ // not in a chat, ask the user
+
+ char ans[4];
+ printf("\n*** CHAT REQUEST *** \n");
+ printf("Accept [y/n]? ");
+ fflush(stdout);
+ fgets(ans, 2, stdin);
+
+ if (strcmp(ans, "y") == 0) {
+ strcpy(out_buf, "SESANS:Y");
+ send(client_s, out_buf, strlen(out_buf)+1, 0);
+
+ char inp[256];
+
+ pthread_t recv_thread;
+ pthread_create(&recv_thread, NULL, receive_chat_messages, NULL);
+
+ while (strcmp(inp, "/q\n") != 0) {
+ printf("chat> ");
+ fgets(inp, 256, stdin);
+
+ if (strcmp(inp, "/q\n") != 0) {
+ strcpy(out_buf, "SESMSG:");
+ strcat(out_buf, inp);
+ send(client_s, out_buf, strlen(out_buf)+1, 0);
+ } else {
+ strcpy(out_buf, "SESQ");
+ send(client_s, out_buf, strlen(out_buf)+1, 0);
+ }
+
+ }
+
+ } else {
+ strcpy(out_buf, "SESANS:N");
+ send(client_s, out_buf, strlen(out_buf)+1, 0);
+ }
+ }
+
+ close(client_s);
+
+ return;
+}
+
+void *server_loop(void *arg) {
+ int server_s;
+ struct sockaddr_in server_addr;
+ int client_s;
+ struct sockaddr_in client_addr;
+ struct in_addr client_ip_addr;
+ socklen_t addr_len;
+ pthread_t thread_id;
+ int retcode;
+
+ server_s = socket(AF_INET, SOCK_STREAM, 0);
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(CHAT_PORT);
+ server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ retcode = bind(server_s, (struct sockaddr *)&server_addr, sizeof(server_addr));
+ if (retcode < 0) {
+ printf("Network error, quitting... \n");
+ exit(-1);
+ }
+
+ listen(server_s, 100);
+
+ while (1) {
+ addr_len = sizeof(client_addr);
+
+ client_s = accept(server_s, (struct sockaddr *)&client_addr, &addr_len);
+
+ if (client_s == -1) {
+ printf("Network error, quitting... \n");
+ exit(-1);
+ }
+
+ pthread_create(&thread_id, NULL, chat_acceptor, (void *)client_s);
+ }
+
+ return;
+}
+
+void start_accepting_chat_requests() {
+ pthread_t thread;
+ pthread_create(&thread, NULL, server_loop, NULL);
+} \ No newline at end of file
diff --git a/chat_request.h b/chat_request.h
new file mode 100644
index 0000000..8b29886
--- /dev/null
+++ b/chat_request.h
@@ -0,0 +1,106 @@
+/**
+ * Localchat
+ * Ben Burwell
+ *
+ * Send chat request
+ */
+
+void send_chat_request(char * username) {
+
+ int i;
+ char ip[16];
+
+ pthread_mutex_lock(&peer_table_lock);
+
+ for (i = 0; i < num_peers_in_table; i++) {
+ if (strcmp(peers[i].username, username) == 0) {
+ strcpy(ip, peers[i].ip);
+ }
+ }
+
+ pthread_mutex_unlock(&peer_table_lock);
+
+ struct sockaddr_in server_addr;
+ char out_buf[4096];
+ char in_buf[4096];
+ int retcode;
+
+ // create the socket
+ client_s = socket(AF_INET, SOCK_STREAM, 0);
+
+ if (client_s < 0) {
+ printf("Network error, quitting... \n");
+ exit(-1);
+ }
+
+ // fill in server info
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(CHAT_PORT);
+ server_addr.sin_addr.s_addr = inet_addr(ip);
+
+ retcode = connect(client_s, (struct sockaddr *)&server_addr, sizeof(server_addr));
+
+ if (retcode < 0) {
+ printf("Network error, quitting... \n");
+ exit(-1);
+ }
+
+ // send the session request
+ strcpy(out_buf, "SESREQ");
+ send(client_s, out_buf, strlen(out_buf), 0);
+
+ retcode = recv(client_s, in_buf, sizeof(in_buf), 0);
+
+ if (strcmp(in_buf, "SESANS:Y") == 0) {
+ printf("Request accepted, type /q to leave. \n");
+
+ // set my in_chat flag
+ strcpy(in_chat, "Y");
+
+ // keep looping until no more input
+ int keep_going = 1;
+
+ while (keep_going) {
+
+ char inp[256];
+ printf("chat> ");
+ fgets(inp, 140, stdin);
+
+ if (strcmp(inp, "/q\n") == 0) {
+
+ // send quit message to peer
+ strcpy(out_buf, "SESQ");
+ send(client_s, out_buf, sizeof(out_buf), 0);
+ close(client_s);
+
+ // exit the loop
+ keep_going = 0;
+
+ // print message to user
+ printf("Session closed. \n");
+
+ // set my in_chat flag
+ strcpy(in_chat, "N");
+
+ } else {
+
+ // send the message
+ strcpy(out_buf, "SESMSG:");
+ strcat(out_buf, inp);
+ send(client_s, out_buf, sizeof(out_buf), 0);
+
+ // testing...
+ printf("%s \n", out_buf);
+
+ }
+
+ }
+
+ } else {
+ printf("Session declined. \n");
+ }
+
+ close(client_s);
+
+ return;
+} \ No newline at end of file
diff --git a/check_user_name.h b/check_user_name.h
new file mode 100644
index 0000000..77051ce
--- /dev/null
+++ b/check_user_name.h
@@ -0,0 +1,27 @@
+/**
+ * Localchat
+ * Ben Burwell
+ *
+ * Checks whether a username is in use
+ */
+
+int check_user_name(const char * username) {
+
+ int i;
+
+ pthread_mutex_lock(&peer_table_lock);
+
+ for (i = 0; i < num_peers_in_table; i++) {
+ if (strcmp(peers[i].username, username) == 0) {
+
+ pthread_mutex_unlock(&peer_table_lock);
+ return 1;
+
+ }
+ }
+
+ pthread_mutex_unlock(&peer_table_lock);
+
+ return 0;
+
+} \ No newline at end of file
diff --git a/clean_table.h b/clean_table.h
new file mode 100644
index 0000000..d5dc42e
--- /dev/null
+++ b/clean_table.h
@@ -0,0 +1,47 @@
+/**
+ * Localchat
+ * Ben Burwell
+ *
+ * Table cleaning functions
+ */
+
+void *clean_table(void *arg) {
+
+ int i;
+ int j;
+ time_t now;
+ double diff;
+
+ while (1) {
+
+ pthread_mutex_lock(&peer_table_lock);
+
+ time(&now);
+
+ for (i = 0; i < num_peers_in_table; i++) {
+ diff = difftime(now, peers[i].last_seen);
+ if (diff > 10) {
+ for (j = i; j < num_peers_in_table-1; j++) {
+ peers[j] = peers[j+1];
+ }
+ num_peers_in_table--;
+ }
+ }
+
+ pthread_mutex_unlock(&peer_table_lock);
+ sleep(1);
+ }
+
+ return NULL;
+}
+
+void start_clean_table_thread() {
+
+ pthread_t thread;
+ pthread_mutex_init(&peer_table_lock, NULL);
+
+ if (pthread_create(&thread, NULL, clean_table, NULL) > 0) {
+ printf("Error starting clean_table thread\n");
+ abort();
+ }
+} \ No newline at end of file
diff --git a/loading.h b/loading.h
new file mode 100644
index 0000000..6aa43de
--- /dev/null
+++ b/loading.h
@@ -0,0 +1,24 @@
+/**
+ * Localchat
+ * Ben Burwell
+ *
+ * Loading display
+ */
+
+void loading() {
+
+ printf(" Loading... 3");
+ fflush(stdout);
+ sleep(1);
+
+ printf("\r Loading... 2");
+ fflush(stdout);
+ sleep(1);
+
+ printf("\r Loading... 1");
+ fflush(stdout);
+ sleep(1);
+
+ printf("\r Loading... Done.\n\n");
+
+} \ No newline at end of file
diff --git a/localchat.c b/localchat.c
new file mode 100644
index 0000000..60c41d2
--- /dev/null
+++ b/localchat.c
@@ -0,0 +1,102 @@
+/**
+ * Localchat
+ * Ben Burwell
+ *
+ * Main Program
+ */
+
+// include necessary libraries
+#include <stdio.h>
+#include <time.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netdb.h>
+
+// define constants
+#define DEBUG 0
+#define MAX_NUM_PEERS 100
+#define CMD_PORT 6060
+#define CHAT_PORT 6061
+#define BROADCAST_IP "192.168.130.255"
+
+// structs
+#include "peer.h"
+
+// global variables
+struct peer peers[MAX_NUM_PEERS];
+int num_peers_in_table = 0;
+pthread_mutex_t peer_table_lock;
+
+char my_ip[64];
+char in_chat[4] = "N";
+const char * username;
+
+char command[256];
+int client_s;
+
+// include functions
+#include "clean_table.h"
+#include "online.h"
+#include "own_ip.h"
+#include "status.h"
+#include "parse.h"
+#include "receive.h"
+#include "check_user_name.h"
+#include "loading.h"
+#include "chat_request.h"
+#include "chat_acceptor.h"
+#include "user_command.h"
+
+int main(int argc, char const *argv[]) {
+
+ if (argc != 2) {
+ printf("Usage: %s [username]\n", argv[0]);
+ return -1;
+ }
+
+ // figure out IP address
+ get_own_ip();
+
+ // start some threads we need
+ start_receive_thread();
+ start_clean_table_thread();
+
+ // send an ONLINE message
+ send_online_broadcast();
+
+ // wait for responses
+ loading();
+
+ // check that the username they have requested
+ // is not already in use
+ if (check_user_name(argv[1])) {
+ printf("Sorry, the username you selected is already in use. \n");
+ printf("Please choose another. \n\n");
+ exit(1);
+ }
+
+ // now we can set the username field
+ username = argv[1];
+
+ // start broadcasting statuses
+ start_status_thread();
+
+ // we can accept chats
+ start_accepting_chat_requests();
+
+ // enter the user input loop
+ while (1) {
+ printf("> ");
+ fgets(command, 256, stdin);
+ process_user_command();
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/online.h b/online.h
new file mode 100644
index 0000000..d0cbd19
--- /dev/null
+++ b/online.h
@@ -0,0 +1,35 @@
+/**
+ * Localchat
+ * Ben Burwell
+ *
+ * Send online broadcast
+ */
+
+void send_online_broadcast() {
+ int client_sock; // Client socket descriptor
+ struct sockaddr_in server_addr; // Server Internet address
+ struct in_addr server_ip_addr; // Server IP Address
+ int addr_leng; // Internet address length
+ int retrncode; // Return code
+ int sOptVal; // Socket option value
+ int sOptLen; // Socket option length
+ char out_temp[10];
+
+ client_sock = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (client_sock < 0) {
+ exit(-1);
+ }
+
+ server_addr.sin_family = AF_INET; // Address family to use
+ server_addr.sin_port = htons(CMD_PORT); // Port num to use
+ server_addr.sin_addr.s_addr = inet_addr(BROADCAST_IP); // IP for Broadcast
+ sOptVal = 1;
+ sOptLen = sizeof(int);
+ setsockopt(client_sock, SOL_SOCKET, SO_BROADCAST, (void*)&sOptVal, sOptLen);
+
+ strcpy(out_temp, "ONLINE");
+ retrncode = sendto(client_sock, out_temp, (strlen(out_temp) + 1), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
+
+ return;
+} \ No newline at end of file
diff --git a/own_ip.h b/own_ip.h
new file mode 100644
index 0000000..7021c11
--- /dev/null
+++ b/own_ip.h
@@ -0,0 +1,64 @@
+/**
+ * Localchat
+ * Ben Burwell
+ *
+ * Gets host IP
+ */
+
+void get_own_ip() {
+
+ char name_buf[256];
+ unsigned int buf_len = 256;
+ struct hostent *host;
+ int ret_code;
+
+ struct addrinfo hints;
+ struct addrinfo *result;
+ struct addrinfo *rptr; // Pointer to linked list.
+ struct sockaddr_in *ipv4;
+ void * addr;
+ char ipstr[64];
+
+ // Get the name of the host.
+ ret_code = gethostname(name_buf, buf_len);
+ if (ret_code < 0) {
+ exit(1);
+ }
+
+ if (strcmp(name_buf, "mathcs") == 0) {
+
+ strcpy(my_ip, "192.168.130.10");
+
+ } else {
+
+ // Get the IP addresses of this host.
+ memset((void *) &hints, 0, (size_t) sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_protocol = 0;
+ hints.ai_addr = NULL;
+ hints.ai_next = NULL;
+
+ ret_code = getaddrinfo(name_buf, NULL, &hints, &result);
+ if (ret_code != 0) {
+ perror("Error from getaddrinfo\n");
+ exit(1);
+ }
+
+ rptr = result;
+ while (rptr != NULL) {
+ ipv4 = (struct sockaddr_in *) rptr->ai_addr;
+ addr = &(ipv4->sin_addr);
+ inet_ntop(rptr->ai_family, addr, ipstr, sizeof(ipstr));
+
+ rptr = rptr->ai_next;
+ }
+
+ strcpy(my_ip, ipstr);
+
+ freeaddrinfo(result); // free the linked list
+ }
+
+ return;
+} \ No newline at end of file
diff --git a/parse.h b/parse.h
new file mode 100644
index 0000000..f2d97e8
--- /dev/null
+++ b/parse.h
@@ -0,0 +1,71 @@
+/**
+ * Localchat
+ * Ben Burwell
+ *
+ * Parse received command packets
+ */
+
+void parse_command(char packet[256]) {
+
+ char separators[4] = ":";
+ char *type;
+ char *peer_username;
+ char *peer_ip;
+ char *peer_in_chat;
+ int updated_peer;
+ int i;
+
+ type = strtok(packet, separators);
+
+ if (strcmp(type, "ONLINE") == 0) {
+
+ // send a status broadcast
+ status_broadcast_once();
+
+ } else if (strcmp(type, "STATUS") == 0) {
+
+ // lock the peers
+ pthread_mutex_lock(&peer_table_lock);
+
+ // get username from packet
+ peer_username = strtok(NULL, separators);
+ peer_ip = strtok(NULL, separators);
+ peer_in_chat = strtok(NULL, separators);
+
+ // so we know whether to add a peer later
+ updated_peer = 0;
+
+ for (i = 0; i < num_peers_in_table; i++) {
+ if (strcmp(peer_username, peers[i].username) == 0) {
+
+ // found a matching username, update the peer
+ strcpy(peers[i].in_chat, peer_in_chat);
+ time( &(peers[i].last_seen) );
+
+ // we updated the peers, so we won't add it later
+ updated_peer = 1;
+
+ }
+ }
+
+ if (!updated_peer) {
+ // we didn't update a peer, so we need to add it to the table
+
+ strcpy(peers[num_peers_in_table].username, peer_username);
+ strcpy(peers[num_peers_in_table].ip, peer_ip);
+ strcpy(peers[num_peers_in_table].in_chat, peer_in_chat);
+ time( &(peers[num_peers_in_table].last_seen) );
+
+ num_peers_in_table++;
+
+ //printf("*** %s has come online. \n", peer_username);
+
+ }
+
+ pthread_mutex_unlock(&peer_table_lock);
+
+ } else {
+ if (DEBUG) printf("Unknown message type: %s \n", type);
+ }
+
+} \ No newline at end of file
diff --git a/peer.h b/peer.h
new file mode 100644
index 0000000..045578a
--- /dev/null
+++ b/peer.h
@@ -0,0 +1,13 @@
+/**
+ * Localchat
+ * Ben Burwell
+ *
+ * Peer structure
+ */
+
+struct peer {
+ char username[32];
+ char ip[16];
+ char in_chat[2];
+ time_t last_seen;
+}; \ No newline at end of file
diff --git a/receive.h b/receive.h
new file mode 100644
index 0000000..f89922f
--- /dev/null
+++ b/receive.h
@@ -0,0 +1,47 @@
+/**
+ * Localchat
+ * Ben Burwell
+ *
+ * Thread management functions
+ */
+
+void *receive_function(void *arg) {
+
+ char in_buf[256];
+ struct sockaddr_in server_addr;
+ int addr_len;
+ int client_sock = socket(AF_INET, SOCK_DGRAM, 0);
+ int ret_code;
+
+ if (client_sock < 0) {
+ exit(-1);
+ }
+
+ server_addr.sin_family = AF_INET; // Address family to use
+ server_addr.sin_port = htons(CMD_PORT); // Port num to use
+ server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // Listen on any IP address
+ ret_code = bind(client_sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
+
+ while (1) {
+
+ addr_len = sizeof(server_addr);
+ ret_code = recvfrom(client_sock, in_buf, sizeof(in_buf), 0, (struct sockaddr *)&server_addr, &addr_len);
+
+ if (DEBUG) printf("Received from server: %s (%d) \n", in_buf, ret_code);
+ parse_command(in_buf);
+ }
+
+ return;
+}
+
+void start_receive_thread() {
+
+ pthread_t thread;
+
+ if (pthread_create(&thread, NULL, receive_function, NULL) > 0) {
+ printf("Error starting receive thread \n");
+ exit(-1);
+ }
+
+ return;
+} \ No newline at end of file
diff --git a/status.h b/status.h
new file mode 100644
index 0000000..8c3df4b
--- /dev/null
+++ b/status.h
@@ -0,0 +1,69 @@
+/**
+ * Localchat
+ * Ben Burwell
+ *
+ * Status broadcast thread functionality
+ */
+
+void status_broadcast_once() {
+
+ if (username == NULL) {
+ return;
+ }
+
+ int client_sock; // Client socket descriptor
+ struct sockaddr_in server_addr; // Server Internet address
+ struct in_addr server_ip_addr; // Server IP Address
+ int addr_leng; // Internet address length
+ int retrncode; // Return code
+ int sOptVal; // Socket option value
+ int sOptLen; // Socket option length
+ char out_buf[4096]; // Output buffer for data
+ char in_buf[4096]; // Input buffer for data
+ char out_temp[4096];
+
+ client_sock = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (client_sock < 0) {
+ printf("Making socket failure \n");
+ exit(-1);
+ }
+
+ server_addr.sin_family = AF_INET; // Address family to use
+ server_addr.sin_port = htons(CMD_PORT); // Port num to use
+ server_addr.sin_addr.s_addr = inet_addr(BROADCAST_IP); // IP for Broadcast
+ sOptVal = 1;
+ sOptLen = sizeof(int);
+ setsockopt(client_sock, SOL_SOCKET, SO_BROADCAST, (void*)&sOptVal, sOptLen);
+
+ strcpy(out_temp, "STATUS:");
+ strcat(out_temp, username);
+ strcat(out_temp, ":");
+ strcat(out_temp, my_ip);
+ strcat(out_temp, ":");
+ strcat(out_temp, in_chat);
+
+ retrncode = sendto(client_sock, out_temp, (strlen(out_temp) + 1), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
+
+ return;
+}
+
+void *status_broadcast(void *arg) {
+
+ while (1) {
+ status_broadcast_once();
+ sleep(5);
+ }
+}
+
+void start_status_thread() {
+
+ pthread_t thread;
+
+ if (pthread_create(&thread, NULL, status_broadcast, NULL) > 0) {
+ printf("Error starting receive thread \n");
+ exit(-1);
+ }
+
+ return;
+} \ No newline at end of file
diff --git a/user_command.h b/user_command.h
new file mode 100644
index 0000000..1c554e6
--- /dev/null
+++ b/user_command.h
@@ -0,0 +1,62 @@
+/**
+ * Localchat
+ * Ben Burwell
+ *
+ * Process user input
+ */
+
+void process_user_command() {
+
+ int i;
+
+ if (strcmp(command, "quit\n") == 0) {
+ exit(0);
+ } else if (strcmp(command, "who\n") == 0) {
+
+ // print a list of online peers
+ pthread_mutex_lock(&peer_table_lock);
+
+ printf(" +---------+-----------------+----------------------------------+ \n");
+ printf(" | In Chat | IP | Username | \n");
+ printf(" +---------+-----------------+----------------------------------+ \n");
+
+ for (i = 0; i < num_peers_in_table; i++) {
+ printf(" | %s | %-15s | %-32s | \n", peers[i].in_chat, peers[i].ip, peers[i].username);
+ }
+
+ printf(" +---------+-----------------+----------------------------------+ \n");
+
+ pthread_mutex_unlock(&peer_table_lock);
+
+ } else if (strcmp(command, "chat\n") == 0) {
+
+ char user[32];
+ int exists;
+
+ printf("user: ");
+ fgets(user, 32, stdin);
+
+ user[strlen(user) - 1] = 0;
+
+ exists = check_user_name(user);
+
+ if (exists) {
+ printf("Sending chat request to %s... \n", user);
+ send_chat_request(user);
+ } else {
+ printf("%s does not exist. \n", user);
+ }
+
+
+ } else if (strcmp(command, "help\n") == 0 || strcmp(command, "?\n") == 0) {
+
+ printf("Command Summary: \n");
+ printf(" who: display a list of online peers \n");
+ printf(" chat: initiate a chat session \n");
+
+ } else if (strcmp(command, "\n") == 0) {
+ // no command, do nothing.
+ } else {
+ printf("Unrecognized command. Type `help` or `?` for help. \n");
+ }
+} \ No newline at end of file