1 //
2 // aegis - project change supervisor
3 // Copyright (C) 2004-2006, 2008, 2011, 2012 Peter Miller
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or (at
8 // your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 //
18 
19 #include <common/ac/assert.h>
20 #include <common/ac/stdlib.h>
21 
22 #include <common/symtab.h>
23 #include <libaegis/input/crop.h>
24 #include <libaegis/input/stdin.h>
25 #include <libaegis/os.h>
26 #include <libaegis/output/file.h>
27 #include <libaegis/output/filter/prefix.h>
28 #include <libaegis/output/stdout.h>
29 #include <libaegis/output/tee.h>
30 
31 #include <aecvsserver/file_info.h>
32 #include <aecvsserver/net.h>
33 #include <aecvsserver/response.h>
34 
35 
net_ty()36 net_ty::net_ty() :
37     in(new input_stdin()),
38     out(output_stdout::create()),
39     rooted(0),
40     response_queue_length(0),
41     response_queue_max(0),
42     response_queue_item(0),
43     dir_info_cs(0),
44     dir_info_ss(0),
45     curdir(0),
46     file_info(0)
47 {
48     //
49     // Initialize which responses the client is capable of receiving.
50     //
51     for (int j = 0; j < response_code_MAX; ++j)
52         response_valid[j] = 0;
53     response_valid[response_code_E] = 1;
54     response_valid[response_code_error] = 1;
55     response_valid[response_code_hate] = 1;
56     response_valid[response_code_love] = 1;
57     response_valid[response_code_ok] = 1;
58     response_valid[response_code_Valid_requests] = 1;
59 
60     //
61     // Set SO_KEEPALIVE on the socket, so that we don't hang forever
62     // if the client dies while we are waiting for input.
63     //
64     in->keepalive();
65 }
66 
67 
~net_ty()68 net_ty::~net_ty()
69 {
70     out.reset();
71 
72     if (updating_verbose)
73     {
74         str_free(updating_verbose);
75         updating_verbose = 0;
76     }
77 
78     log_client.reset();
79     if (dir_info_cs)
80     {
81         delete dir_info_cs;
82         dir_info_cs = 0;
83     }
84     if (dir_info_ss)
85     {
86         delete dir_info_ss;
87         dir_info_ss = 0;
88     }
89     curdir = 0;
90     if (file_info)
91     {
92         delete file_info;
93         file_info = 0;
94     }
95 }
96 
97 
98 bool
getline(nstring & s)99 net_ty::getline(nstring &s)
100 {
101     bool result = in->one_line(s);
102     if (result && log_client)
103     {
104         log_client->fprintf("%s\n", s.c_str());
105         log_client->flush();
106     }
107     return result;
108 }
109 
110 
111 void
printf(const char * fmt,...)112 net_ty::printf(const char *fmt, ...)
113 {
114     va_list ap;
115     assert(fmt);
116     va_start(ap, fmt);
117     out->vfprintf(fmt, ap);
118     va_end(ap);
119 }
120 
121 
122 void
response_queue(response * rp)123 net_ty::response_queue(response *rp)
124 {
125     //
126     // Don't bother queueing responses the client has asked us not to send.
127     //
128     response_code_ty code = rp->code_get();
129     if (!response_valid[code])
130     {
131         delete rp;
132         return;
133     }
134 
135     //
136     // Make sure there is enough room in the queue.
137     //
138     if (response_queue_length >= response_queue_max)
139     {
140         response_queue_max = response_queue_max * 2 + 4;
141         response **new_queue = new response * [response_queue_max];
142         for (size_t k = 0; k < response_queue_length; ++k)
143             new_queue[k] = response_queue_item[k];
144         delete [] response_queue_item;
145         response_queue_item = new_queue;
146     }
147 
148     //
149     // Put the response on the end of the queue.
150     //
151     response_queue_item[response_queue_length++] = rp;
152 
153     //
154     // Some codes cause an immediate flush.
155     //
156     if (rp->flushable())
157         response_flush();
158 }
159 
160 
161 void
response_flush()162 net_ty::response_flush()
163 {
164     //
165     // Write any pending responses to the client.
166     //
167     for (size_t j = 0; j < response_queue_length; ++j)
168     {
169         response     *rp;
170 
171         rp = response_queue_item[j];
172         rp->write(out);
173         delete rp;
174         if (log_client)
175             out->flush();
176     }
177     response_queue_length = 0;
178 
179     //
180     // Make sure the output is written to the client.
181     //
182     out->flush();
183 }
184 
185 
186 void
log_to_file(string_ty * filename)187 net_ty::log_to_file(string_ty *filename)
188 {
189     // This only works once
190     if (log_client)
191         return;
192 
193     os_become_orig();
194     output::pointer op = output_file::text_open(filename);
195     os_become_undo();
196     log_client = output_filter_prefix::create(op, "C: ");
197     output::pointer log_server = output_filter_prefix::create(op, "S: ");
198     out = output_tee::create(out, log_server);
199 }
200 
201 
202 void
log_by_env(const char * envar)203 net_ty::log_by_env(const char *envar)
204 {
205     const char *cp = getenv(envar);
206     if (!cp || !*cp)
207         return;
208     string_ty *s = str_from_c(cp);
209     log_to_file(s);
210     str_free(s);
211 }
212 
213 
214 void
argument(string_ty * s)215 net_ty::argument(string_ty *s)
216 {
217     assert(s);
218     argument_list.push_back(s);
219 }
220 
221 
222 void
argumentx(string_ty * s)223 net_ty::argumentx(string_ty *s)
224 {
225     assert(s);
226     if (argument_list.nstrings)
227     {
228         static string_ty *newline;
229         string_ty       **spp;
230         string_ty       *s2;
231 
232         if (!newline)
233             newline = str_from_c("\n");
234         spp = argument_list.string + argument_list.nstrings - 1;
235         s2 = str_cat_three(*spp, newline, s);
236         str_free(*spp);
237         *spp = s2;
238     }
239     else
240         argument_list.push_back(s);
241 }
242 
243 
244 void
accumulator_reset()245 net_ty::accumulator_reset()
246 {
247     argument_list.clear();
248     if (file_info)
249         file_info->clear();
250     if (dir_info_cs)
251         dir_info_cs->clear();
252     if (dir_info_ss)
253         dir_info_ss->clear();
254     curdir = 0;
255     if (updating_verbose)
256     {
257         str_free(updating_verbose);
258         updating_verbose = 0;
259     }
260 }
261 
262 
263 static void
file_info_reaper(void * p)264 file_info_reaper(void *p)
265 {
266     file_info_delete((file_info_ty *)p);
267 }
268 
269 
270 file_info_ty *
file_info_find(string_ty * server_side,int auto_alloc)271 net_ty::file_info_find(string_ty *server_side, int auto_alloc)
272 {
273     file_info_ty    *fip;
274 
275     if (!file_info)
276     {
277         file_info = new symtab_ty(5);
278         file_info->set_reap(file_info_reaper);
279     }
280     fip = (file_info_ty *)file_info->query(server_side);
281     if (!fip && auto_alloc)
282     {
283         fip = file_info_new();
284         file_info->assign(server_side, fip);
285     }
286     return fip;
287 }
288 
289 
290 static void
dir_reaper(void * p)291 dir_reaper(void *p)
292 {
293     directory_ty    *dp;
294 
295     dp = (directory_ty *)p;
296     directory_delete(dp);
297 }
298 
299 
300 void
directory_set(string_ty * client_side,string_ty * server_side)301 net_ty::directory_set(string_ty *client_side, string_ty *server_side)
302 {
303     directory_ty    *dp;
304 
305     dp = directory_new(client_side, server_side);
306     curdir = dp;
307     if (!dir_info_cs)
308     {
309         dir_info_cs = new symtab_ty(5);
310         dir_info_cs->set_reap(dir_reaper);
311     }
312     dir_info_cs->assign(client_side, dp);
313     if (!dir_info_ss)
314     {
315         dir_info_ss = new symtab_ty(5);
316         // NO reaper for this one.
317     }
318     dir_info_ss->assign(server_side, dp);
319 }
320 
321 
322 directory_ty *
directory_find_client_side(string_ty * client_side)323 net_ty::directory_find_client_side(string_ty *client_side)
324 {
325     if (!dir_info_cs)
326         return 0;
327     return (directory_ty *)dir_info_cs->query(client_side);
328 }
329 
330 
331 directory_ty *
directory_find_server_side(string_ty * server_side)332 net_ty::directory_find_server_side(string_ty *server_side)
333 {
334     if (!dir_info_ss)
335         return 0;
336     return (directory_ty *)dir_info_ss->query(server_side);
337 }
338 
339 
340 void
set_updating_verbose(string_ty * s)341 net_ty::set_updating_verbose(string_ty *s)
342 {
343     if (updating_verbose)
344     {
345         str_free(updating_verbose);
346         updating_verbose = 0;
347     }
348     if (s)
349         updating_verbose = str_copy(s);
350 }
351 
352 
353 input
in_crop(long length)354 net_ty::in_crop(long length)
355 {
356     return new input_crop(in, length);
357 }
358 
359 
360 // vim: set ts=8 sw=4 et :
361