initial commit, lordsawar source, slightly modified
[lordsawar] / src / network-connection.cpp
1 // Copyright (C) 2008 Ole Laursen
2 // Copyright (C) 2008 Ben Asselstine
3 //
4 //  This program is free software; you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation; either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU Library General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program; if not, write to the Free Software
16 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
17 //  02110-1301, USA.
18
19 #include "network-connection.h"
20 #include <iostream>
21 #include <cstring>
22 #include <cstdlib>
23 #include <gnet.h>
24 #include <stdio.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27
28 #include "network-common.h"
29
30 static void
31 event_helper(GConn* conn, GConnEvent* event, gpointer user_data) 
32 {
33   static_cast<NetworkConnection *>(user_data)->gotConnectionEvent(conn, event);
34 }
35
36
37 NetworkConnection::NetworkConnection(_GConn *conn)
38 {
39   this->conn = conn;
40   if (conn) {
41     gnet_conn_set_callback(conn, &event_helper, this);
42     receiving_message = false;
43     gnet_conn_readn(conn, MESSAGE_SIZE_BYTES);
44   }
45 }
46
47 NetworkConnection::~NetworkConnection()
48 {
49   if (conn)
50     {
51       gnet_conn_delete(conn);
52       conn = 0;
53     }
54 }
55
56 void NetworkConnection::connectToHost(std::string host, int port)
57 {
58   conn = gnet_conn_new(host.c_str(), port, &event_helper, this);
59   if (!conn)
60     exit(1);
61     ; // FIXME: report error
62
63   gnet_conn_connect(conn);
64   gnet_conn_set_watch_error(conn, true);
65   gnet_conn_timeout(conn, 30 * 1000);
66 }
67
68 void NetworkConnection::sendFile(MessageType type, const std::string filename)
69 {
70   if (gnet_conn_is_connected(conn) == false)
71     return;
72   FILE *fileptr = fopen (filename.c_str(), "r");
73   if (fileptr == NULL)
74     return;
75
76   struct stat statbuf;
77   stat (filename.c_str(), &statbuf);
78   // write the preamble
79   gchar buf[MESSAGE_SIZE_BYTES + MESSAGE_PREAMBLE_EXTRA_BYTES];
80   guint32 l = g_htonl(MESSAGE_PREAMBLE_EXTRA_BYTES + statbuf.st_size);
81   memcpy(buf, &l, MESSAGE_SIZE_BYTES);
82   buf[MESSAGE_SIZE_BYTES] = MESSAGE_PROTOCOL_VERSION;
83   buf[MESSAGE_SIZE_BYTES + 1] = type;
84   
85   gnet_conn_write(conn, buf, MESSAGE_SIZE_BYTES + MESSAGE_PREAMBLE_EXTRA_BYTES);
86
87   std::cerr << "sending file " << type <<" of length " << MESSAGE_PREAMBLE_EXTRA_BYTES + statbuf.st_size << " to " << gnet_inetaddr_get_name(conn->inetaddr) << std::endl;
88   char *buffer = (char*) malloc (statbuf.st_size);
89   size_t bytesread = fread (buffer, 1, statbuf.st_size, fileptr);
90   fclose (fileptr);
91   gnet_conn_write(conn, const_cast<gchar *>(buffer), bytesread);
92   free (buffer);
93 }
94
95 void NetworkConnection::send(MessageType type, const std::string &payload)
96 {
97   if (gnet_conn_is_connected(conn) == false)
98     return;
99   // write the preamble
100   gchar buf[MESSAGE_SIZE_BYTES + MESSAGE_PREAMBLE_EXTRA_BYTES];
101   guint32 l = g_htonl(MESSAGE_PREAMBLE_EXTRA_BYTES + payload.size());
102   memcpy(buf, &l, MESSAGE_SIZE_BYTES);
103   buf[MESSAGE_SIZE_BYTES] = MESSAGE_PROTOCOL_VERSION;
104   buf[MESSAGE_SIZE_BYTES + 1] = type;
105   
106   gnet_conn_write(conn, buf, MESSAGE_SIZE_BYTES + MESSAGE_PREAMBLE_EXTRA_BYTES);
107
108   std::cerr << "sending message " << type <<" of length " << MESSAGE_PREAMBLE_EXTRA_BYTES + payload.size() << " to " << gnet_inetaddr_get_name(conn->inetaddr) << std::endl;
109
110   // write the payload
111   gnet_conn_write(conn, const_cast<gchar *>(payload.data()), payload.size());
112 }
113
114 void NetworkConnection::gotConnectionEvent(GConn* conn, GConnEvent* event)
115 {
116   switch (event->type)
117   {
118   case GNET_CONN_CONNECT:
119     gnet_conn_timeout(conn, 0); // stop timeout
120     receiving_message = false;
121     gnet_conn_readn(conn, MESSAGE_SIZE_BYTES);
122     connected.emit();
123     break;
124     
125   case GNET_CONN_READ:
126     if (receiving_message)
127     {
128       if (event->length < 2)
129       {
130         // misbehaving server
131         gnet_conn_disconnect(conn);
132         break;
133       }
134
135       // protocol version is ignored for now
136       //int protocol_version = event->buffer[0];
137       MessageType type = MessageType(event->buffer[1]);
138       
139       got_message.emit(type, std::string(event->buffer + MESSAGE_PREAMBLE_EXTRA_BYTES,
140                                          event->length - MESSAGE_PREAMBLE_EXTRA_BYTES));
141       receiving_message = false;
142       gnet_conn_readn(conn, MESSAGE_SIZE_BYTES);
143     }
144     else
145     {
146       g_assert(event->length == MESSAGE_SIZE_BYTES);
147       guint32 val;
148       memcpy(&val, event->buffer, 4);
149       int message_size = g_ntohl(val);
150       
151       std::cerr << "going to read length " << message_size << std::endl;
152       receiving_message = true;
153       gnet_conn_readn(conn, message_size);
154       connection_received_data.emit();
155     }
156     break;
157
158   case GNET_CONN_WRITE:
159     break;
160
161   case GNET_CONN_CLOSE:
162   case GNET_CONN_TIMEOUT:
163   case GNET_CONN_ERROR:
164     connection_lost.emit();
165     gnet_conn_delete(conn);
166     break;
167
168   default:
169     g_assert_not_reached ();
170   }
171 }