prot-a-parse.c 28.5 KB
Newer Older
Linus Tolke Y's avatar
Linus Tolke Y committed
1
/*
Per Cederqvist's avatar
Per Cederqvist committed
2
 * $Id: prot-a-parse.c,v 0.41 1999/06/03 22:11:09 ceder Exp $
3
 * Copyright (C) 1991-1999  Lysator Academic Computer Association.
Linus Tolke Y's avatar
Linus Tolke Y committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 *
 * This file is part of the LysKOM server.
 * 
 * LysKOM is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 1, or (at your option) 
 * any later version.
 * 
 * LysKOM is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with LysKOM; see the file COPYING.  If not, write to
 * Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
 * or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
 * MA 02139, USA.
 *
 * Please mail bug reports to bug-lyskom@lysator.liu.se. 
 */
Per Cederqvist's avatar
Per Cederqvist committed
25
26
27
28
29
30
/*
 * prot-a-parse.c - parse protocol-A messages.
 *
 * BUG: Not all functions are used, I think. /ceder
 */

David Byers's avatar
David Byers committed
31
32
33
34
35

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

36
static const char *
Per Cederqvist's avatar
Per Cederqvist committed
37
rcsid = "$Id: prot-a-parse.c,v 0.41 1999/06/03 22:11:09 ceder Exp $";
38
39
#include "rcs.h"
USE(rcsid);
Per Cederqvist's avatar
Per Cederqvist committed
40

41
#include <assert.h>
Per Cederqvist's avatar
Per Cederqvist committed
42
#include <stdio.h>
Per Cederqvist's avatar
Per Cederqvist committed
43
#include <setjmp.h>
Per Cederqvist's avatar
Per Cederqvist committed
44
#include <time.h>
Per Cederqvist's avatar
Per Cederqvist committed
45
#include <sys/types.h>
46
#include <sys/socket.h>
47
48
49
#ifdef HAVE_STDLIB_H
#  include <stdlib.h>
#endif
Per Cederqvist's avatar
Per Cederqvist committed
50

51
#include "debug.h"
Per Cederqvist's avatar
Per Cederqvist committed
52
#include "s-string.h"
Per Cederqvist's avatar
Per Cederqvist committed
53
#include "kom-types.h"
54
#include "server/smalloc.h"
Per Cederqvist's avatar
Per Cederqvist committed
55
#include "com.h"
56
#include "async.h"
Per Cederqvist's avatar
Per Cederqvist committed
57
58
59
#include "connections.h"
#include "prot-a-parse.h"
#include "isc-parse.h"
David Byers's avatar
David Byers committed
60
#include "kom-config.h"
61
#include "isc-interface.h"
Per Cederqvist's avatar
Per Cederqvist committed
62
#include "log.h"
Per Cederqvist's avatar
Per Cederqvist committed
63
#include "minmax.h"
64
#include "param.h"
David Byers's avatar
David Byers committed
65
#include "kom-memory.h"
66

67
68
BUGDECL;

David Byers's avatar
David Byers committed
69
70
71
72
/* Forward declarations */

static void prot_a_hunt_array_end(Connection *client);

73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/*
 * Return next token from the input stream. Note that the String returned
 * by this call points into data that might be freed by the next call to
 * get_token or any function which reads from the stream.
 */

static String
prot_a_get_token(Connection *client)
{
    String result;
    String_size old_first;

    old_first = client->first_to_parse;
    
    result = s_strtok(client->unparsed, &client->first_to_parse,
		      s_fcrea_str(WHITESPACE));

    /* Check that there was at least one trailing blank. */
    
    if ( client->first_to_parse >= s_strlen(client->unparsed) )
    {
94
95
        if (client->first_to_parse - old_first > 1000)
	{
96
97
	    isc_puts("%%Insane token length.\n", client->isc_session);
	    isc_flush(client->isc_session);
98
99
	    longjmp(parse_env, ISC_LOGOUT);
	}
100
101
102
103
104
105
106
107
	client->first_to_parse = old_first;
	longjmp(parse_env, ISC_MSG_INCOMPLETE);
    }

    return result;
}


Per Cederqvist's avatar
Per Cederqvist committed
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
long
prot_a_parse_long(Connection *client)
{
    String	token;
    String_size end;
    long	res;

    token = prot_a_get_token(client);
    res = s_strtol(token, &end, PROTOCOL_NUMBER_BASE);
    if (end != s_strlen(token))
	longjmp(parse_env, ISC_PROTOCOL_ERR);
    
    return res;
}

123
124
125

void
prot_a_parse_num_list(Connection *client,
126
127
                      Number_list *res,
		      int maxlen)
128
{
129
    static unsigned long err_cnt = 0;
130

131
    switch (client->array_parse_pos)
132
    {
133
134
135
136
137
    case 0:			/* The array size. */

	/* This could just as well have been an assertion. */
	if ((res->length != 0 || res->data != NULL) && err_cnt++ < 20)
	{
David Byers's avatar
David Byers committed
138
	    kom_log("WNG: prot_a_parse_num_list(): len = %lu data = %lu\n",
139
140
141
142
		(unsigned long)res->length, (unsigned long)res->data);
	    res->length = 0;
	    res->data = NULL;
	    if (err_cnt == 20)
David Byers's avatar
David Byers committed
143
		kom_log("The above warning is now turned off.");
144
145
	}

David Byers's avatar
David Byers committed
146
147
        client->array_parse_parsed_length = prot_a_parse_long(client);
	if (client->array_parse_parsed_length < 0)
148
	{
149
150
	    isc_puts("%%Insane array size.\n", client->isc_session);
	    isc_flush(client->isc_session);
151
152
153
	    longjmp(parse_env, ISC_LOGOUT);
	}

David Byers's avatar
David Byers committed
154
	res->length = min(maxlen+1, client->array_parse_parsed_length);
155
156
157
158
159
160
	client->array_parse_pos = 1;
	/* Fall through */
    case 1:			/* The opening curly brace. */
	if ( parse_nonwhite_char(client) != '{' )
	    longjmp(parse_env, ISC_PROTOCOL_ERR);

David Byers's avatar
David Byers committed
161
        res->data   = smalloc(sizeof(*res->data) * res->length);
162
163
164
165
166

	client->array_parse_index = 0;
        client->array_parse_pos = 2;
	/* Fall through */
    case 2:			/* The elements in the array. */
David Byers's avatar
David Byers committed
167
	while (client->array_parse_index < client->array_parse_parsed_length)
168
169
	{
	    long tmp = prot_a_parse_long(client);
David Byers's avatar
David Byers committed
170
171
172
173

            /* Enter the value into the array only if the
               parse index is inside the allocated array */

David Byers's avatar
David Byers committed
174
            if (client->array_parse_index < res->length)
David Byers's avatar
David Byers committed
175
176
177
178
            {
                res->data[client->array_parse_index] = tmp;
            }
            client->array_parse_index += 1;
179
180
181
182
	}
	client->array_parse_pos = 3;
	/* Fall through */
    case 3:			/* Closing brace. */
David Byers's avatar
David Byers committed
183
184
185
        /* Adjust the result length if we were parsing a long array */

        /* Read the closing brace */
186
187
188
189
	if ( parse_nonwhite_char(client) != '}' )
	    longjmp(parse_env, ISC_PROTOCOL_ERR);
    default:
	client->array_parse_pos = 0;
190
191
192
    }
}

Per Cederqvist's avatar
Per Cederqvist committed
193
194
195
196
197
void
prot_a_parse_priv_bits(Connection *client,
		       Priv_bits      *res)
{
    String token;
David Byers's avatar
David Byers committed
198
    String_size len;
Per Cederqvist's avatar
Per Cederqvist committed
199
200

    token = prot_a_get_token(client);
David Byers's avatar
David Byers committed
201
202
    len = s_strlen(token);
    if (len <= 0 )
Per Cederqvist's avatar
Per Cederqvist committed
203
204
	longjmp(parse_env, ISC_PROTOCOL_ERR);
    
David Byers's avatar
David Byers committed
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
    init_priv_bits(res);
    switch (len)
    {
    default:
    case 16: res->flg16 =       token.string[ 15 ] != '0';
    case 15: res->flg15 =       token.string[ 14 ] != '0';
    case 14: res->flg14 =       token.string[ 13 ] != '0';
    case 13: res->flg13 =       token.string[ 12 ] != '0';
    case 12: res->flg12 =       token.string[ 11 ] != '0';
    case 11: res->flg11 =       token.string[ 10 ] != '0';
    case 10: res->flg10 =       token.string[ 9 ]  != '0';
    case 9:  res->flg9 =        token.string[ 8 ]  != '0';
    case 8:  res->flg8 =        token.string[ 7 ]  != '0';
    case 7:  res->extern_gw =   token.string[ 6 ]  != '0';
    case 6:  res->change_name = token.string[ 5 ]  != '0';
    case 5:  res->create_conf = token.string[ 4 ]  != '0';
    case 4:  res->create_pers = token.string[ 3 ]  != '0';
    case 3:  res->statistic =   token.string[ 2 ]  != '0';
    case 2:  res->admin =       token.string[ 1 ]  != '0';
    case 1:  res->wheel =       token.string[ 0 ]  != '0';
    }
Per Cederqvist's avatar
Per Cederqvist committed
226
227
}

David Byers's avatar
David Byers committed
228
229
230
231
232
233
234
235
236
237
238
239
240
void
prot_a_parse_pers_flags(Connection *client,
                        Personal_flags *res)
{
    String          token;
    String_size     len;

    token = prot_a_get_token(client);
    len = s_strlen(token);
    if (len <= 0)
        longjmp(parse_env, ISC_PROTOCOL_ERR);

    init_personal_flags(res);
David Byers's avatar
David Byers committed
241
    switch (len)
David Byers's avatar
David Byers committed
242
243
244
245
246
247
248
249
250
251
252
253
254
    {
    default:
    case 8: res->flg8               = token.string[ 7 ] != '0';
    case 7: res->flg7               = token.string[ 6 ] != '0';
    case 6: res->flg6               = token.string[ 5 ] != '0';
    case 5: res->flg5               = token.string[ 4 ] != '0';
    case 4: res->flg4               = token.string[ 3 ] != '0';
    case 3: res->flg3               = token.string[ 2 ] != '0';
    case 2: res->flg2               = token.string[ 1 ] != '0';
    case 1: res->unread_is_secret   = token.string[ 0 ] != '0';
    }
}

255
256
257
258
259
void
prot_a_parse_membership_type(Connection *client,
                             Membership_type *res)
{
    String token;
David Byers's avatar
David Byers committed
260
    String_size len;
261
262

    token = prot_a_get_token(client);
David Byers's avatar
David Byers committed
263
264
    len = s_strlen(token);
    if (len <= 0)
265
266
	longjmp(parse_env, ISC_PROTOCOL_ERR);
  
David Byers's avatar
David Byers committed
267
268
269
270
271
272
273
274
275
276
277
278
279
    init_membership_type(res);
    switch (len)
    {
    default:
    case 8: res->reserved5  = token.string[ 7 ] != '0';
    case 7: res->reserved4  = token.string[ 6 ] != '0';
    case 6: res->reserved3  = token.string[ 5 ] != '0';
    case 5: res->reserved2  = token.string[ 4 ] != '0';
    case 4: res->reserved1  = token.string[ 3 ] != '0';
    case 3: res->secret     = token.string[ 2 ] != '0';
    case 2: res->passive    = token.string[ 1 ] != '0';
    case 1: res->invitation = token.string[ 0 ] != '0';
    }
280
281
}

Per Cederqvist's avatar
Per Cederqvist committed
282
283
284
285
286
void
prot_a_parse_conf_type(Connection *client,
		Conf_type      *res)
{
    String token;
David Byers's avatar
David Byers committed
287
    String_size len;
Per Cederqvist's avatar
Per Cederqvist committed
288
289

    token = prot_a_get_token(client);
David Byers's avatar
David Byers committed
290
291
    len = s_strlen(token);
    if (len <= 0)
Per Cederqvist's avatar
Per Cederqvist committed
292
293
	longjmp(parse_env, ISC_PROTOCOL_ERR);
  
David Byers's avatar
David Byers committed
294
295
    init_conf_type(res);
    switch (len)
296
    {
David Byers's avatar
David Byers committed
297
298
299
300
301
302
303
304
305
    default:
    case 8: res->reserved3      = token.string[ 7 ] != '0';
    case 7: res->reserved2      = token.string[ 6 ] != '0';
    case 6: res->forbid_secret  = token.string[ 5 ] != '0';
    case 5: res->allow_anon     = token.string[ 4 ] != '0';
    case 4: res->letter_box     = token.string[ 3 ] != '0';
    case 3: res->secret         = token.string[ 2 ] != '0';
    case 2: res->original       = token.string[ 1 ] != '0';
    case 1: res->rd_prot        = token.string[ 0 ] != '0';
306
    }
Per Cederqvist's avatar
Per Cederqvist committed
307
308
309
310
311
}

/*
 * Parse a string. At most 'maxlen' characters are allowed. If the
 * client sends a longer string only the first 'maxlen+1' characters
Per Cederqvist's avatar
Per Cederqvist committed
312
313
 * are read. Any remaining characters are discarded.
 */
Per Cederqvist's avatar
Per Cederqvist committed
314
315
void
prot_a_parse_string(Connection  *client,
Per Cederqvist's avatar
Per Cederqvist committed
316
317
		    String	*result,
		    int		 maxlen)
Per Cederqvist's avatar
Per Cederqvist committed
318
319
320
321
322
{
    String_size hptr;		/* Pointer to 'H' */
    String_size client_len;	/* The len the client is sending. */
    String_size truncated_len;	/* How much the server will receive. */
    String_size to_skip;
Per Cederqvist's avatar
Per Cederqvist committed
323
    static unsigned long err_cnt = 0;
Per Cederqvist's avatar
Per Cederqvist committed
324
325
326
327

    switch ( client->string_parse_pos )
    {
    case 0:
328
329
	if ( (result->len != 0 || result->string != NULL) && err_cnt++ < 20 )
	{
David Byers's avatar
David Byers committed
330
	    kom_log ("%s == %lu, result->string == %lu. %s\n",
Per Cederqvist's avatar
Per Cederqvist committed
331
332
333
		 "prot_a_parse_string(): result->len",
		 (unsigned long)result->len, (unsigned long)result->string,
		 "This memory will not be free()'d.");
334
335
	    *result = EMPTY_STRING;
	    if ( err_cnt == 20 )
David Byers's avatar
David Byers committed
336
		kom_log("Won't log the above warning no more.");
337
338
	}

Per Cederqvist's avatar
Per Cederqvist committed
339
	/* Get number and discard trailing 'H' */
Per Cederqvist's avatar
Per Cederqvist committed
340
341
342
343
	client_len = s_strtol(s_fsubstr(client->unparsed,
					client->first_to_parse,
					END_OF_STRING),
			      &hptr, PROTOCOL_NUMBER_BASE);
Per Cederqvist's avatar
Per Cederqvist committed
344
345
346
347
348
349
350
351

	if ( hptr == -1
	    || client->first_to_parse + hptr
	    >= s_strlen(client->unparsed) )
	{
	    longjmp(parse_env, ISC_MSG_INCOMPLETE);
	}
	
352
353
	if (client_len < 0)
	{
354
355
	    isc_puts("%%Insane string length.\n", client->isc_session);
	    isc_flush(client->isc_session);
356
357
358
359
	    BUG(("%%%%Insane string length.\n"));
	    longjmp(parse_env, ISC_LOGOUT);
	}
	
Per Cederqvist's avatar
Per Cederqvist committed
360
361
362
363
364
365
366
367
368
369
370
371
372
	/* Check that
	      a) there is a trailing H
	      b) there was at least one digit before the H */

	if ( client->unparsed.string[ client->first_to_parse
					  + hptr ] != 'H'
	    || hptr <= 0 )
	{
	    longjmp(parse_env, ISC_PROTOCOL_ERR);
	}

	client->first_to_parse += 1 + hptr;
	client->string_parse_pos = 1;
373
	result->len = client_len; /* +++ Transfer */
Per Cederqvist's avatar
Per Cederqvist committed
374
375
	/* Fall through */
    case 1:
376
	client_len = result->len;
Per Cederqvist's avatar
Per Cederqvist committed
377
378
379
	/* Check that the entire string is transmitted. */
	/* (Don't care about the trailing part that will be skipped if the
	 *  string is longer than maxlen) */
Per Cederqvist's avatar
Per Cederqvist committed
380
	truncated_len = min(maxlen + 1, client_len);
Per Cederqvist's avatar
Per Cederqvist committed
381
382
383
384
385
386
387
	
	if ( client->first_to_parse + truncated_len
	    > s_strlen(client->unparsed) )
	{
	    longjmp(parse_env, ISC_MSG_INCOMPLETE);
	}

388
	*result = EMPTY_STRING;
Per Cederqvist's avatar
Per Cederqvist committed
389
390
391
392
393

	s_mem_crea_str(result,
		       client->unparsed.string + client->first_to_parse,
		       truncated_len);
	result->len = client_len; /* Ugly! +++ */
Per Cederqvist's avatar
Per Cederqvist committed
394
395
396
397
398
399

	client->first_to_parse += truncated_len;
	client->string_parse_pos = 2;
	/* Fall through */
    case 2:
	/* Was the string too long? If so, skip the truncated data. */
David Byers's avatar
David Byers committed
400
401
402
        /* It looks like we're leaving one extra character in the
           string here, which is necessary if we want clients of
           this function to be able to detect long strings. */
Per Cederqvist's avatar
Per Cederqvist committed
403
404

	client_len = result->len;
405
	truncated_len = min(maxlen+1, client_len);
Per Cederqvist's avatar
Per Cederqvist committed
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
	
	if ( client_len > truncated_len )
	{
	    to_skip = min(client_len - truncated_len,
			   client->unparsed.len - client->first_to_parse);
	    client_len -= to_skip;
	    client->first_to_parse += to_skip;
	}

	result->len = client_len;
	
	if ( client_len > truncated_len )
	    longjmp(parse_env, ISC_MSG_INCOMPLETE);
	/* Fall through */
    default:
	client->string_parse_pos = 0;
    }
}

425
426
427
428
429
extern void
prot_a_parse_aux_item_flags(Connection *client,
                            Aux_item_flags *res)
{
    String token;
David Byers's avatar
David Byers committed
430
    String_size len;
431
432

    token = prot_a_get_token(client);
David Byers's avatar
David Byers committed
433
434
    len = s_strlen(token);
    if (len <= 0)
435
	longjmp(parse_env, ISC_PROTOCOL_ERR);
David Byers's avatar
David Byers committed
436
437
438
439
440
441
442
443
444
445
446
447
448
449
    
    init_aux_item_flags(res);
    switch (len)
    {
    default:
    case 8: res->reserved5      = token.string[ 7 ] != '0';
    case 7: res->reserved4      = token.string[ 6 ] != '0';
    case 6: res->reserved3      = token.string[ 5 ] != '0';
    case 5: res->dont_garb      = token.string[ 4 ] != '0';
    case 4: res->hide_creator   = token.string[ 3 ] != '0';
    case 3: res->secret         = token.string[ 2 ] != '0';
    case 2: res->inherit        = token.string[ 1 ] != '0';
    case 1: res->deleted        = token.string[ 0 ] != '0';
    }
450
451
452
453
454
455
456
457
458
459
}


extern void
prot_a_parse_aux_item(Connection *client,
                      Aux_item *result)
{
    switch ( client->struct_parse_pos )
    {
    case 0:
David Byers's avatar
David Byers committed
460
        init_aux_item(result);
461
        result->tag = prot_a_parse_long(client);
462
463
        client->struct_parse_pos = 1;
    case 1:
464
        prot_a_parse_aux_item_flags(client, &result->flags);
465
466
        client->struct_parse_pos = 2;
    case 2:
467
        result->inherit_limit = prot_a_parse_long(client);
468
469
470
471
472
473
474
475
476
477
478
479
480
        client->struct_parse_pos = 3;
    case 3:
        prot_a_parse_string(client,
                            &(result->data),
                            param.text_len);
    default:
        client->struct_parse_pos = 0;
    }
}

extern void
prot_a_parse_aux_item_list(Connection *client,
                           Aux_item_list *result,
David Byers's avatar
David Byers committed
481
                           int maxlen)
482
{
David Byers's avatar
David Byers committed
483
    static unsigned long err_cnt = 0;
484
    int i;
David Byers's avatar
David Byers committed
485
    Aux_item dummy_aux_item;
486
487
488
489

    switch (client->array_parse_pos)
    {
    case 0:
David Byers's avatar
David Byers committed
490
491
492
493
494
495
496
497
498
499
	if ((result->length != 0 || result->items != NULL) && err_cnt++ < 20)
	{
	    kom_log("WNG: prot_a_parse_aux_item_list(): len = %lu data = %lu\n",
		(unsigned long)result->length, (unsigned long)result->items);
	    result->length = 0;
	    result->items = NULL;
	    if (err_cnt == 20)
		kom_log("The above warning is now turned off.");
	}

David Byers's avatar
David Byers committed
500
501
	client->array_parse_parsed_length = prot_a_parse_long(client);
        if (client->array_parse_parsed_length < 0)
David Byers's avatar
David Byers committed
502
503
504
505
506
        {
	    isc_puts("%%Insane array size.\n", client->isc_session);
	    isc_flush(client->isc_session);
	    longjmp(parse_env, ISC_LOGOUT);
        }
David Byers's avatar
David Byers committed
507
508

        result->length = min(maxlen+1, client->array_parse_parsed_length);
509
	client->array_parse_pos = 1;
David Byers's avatar
David Byers committed
510
511
        /* Fall through */

512
513
514
515
    case 1:
	if ( parse_nonwhite_char(client) != '{' )
	    longjmp(parse_env, ISC_PROTOCOL_ERR);

David Byers's avatar
David Byers committed
516
        result->items = smalloc(result->length * sizeof(Aux_item));
David Byers's avatar
David Byers committed
517
        
David Byers's avatar
David Byers committed
518
        for (i = 0; i < result->length; i++)
519
520
521
522
523
524
        {
            result->items[i].data = EMPTY_STRING;
        }
	client->array_parse_index = 0;
	client->array_parse_pos = 2;
    case 2:
David Byers's avatar
David Byers committed
525
	while( client->array_parse_index < client->array_parse_parsed_length)
526
	{
David Byers's avatar
David Byers committed
527
528
            /* Enter a parsed aux item into the list only if we
               have parsed less than the length of the list */
David Byers's avatar
David Byers committed
529
            if (client->array_parse_index < result->length)
David Byers's avatar
David Byers committed
530
531
532
533
534
535
536
537
            {
                prot_a_parse_aux_item(client,
                                      &result->items[
                                          client->array_parse_index]);
            }
            else
            {
                prot_a_parse_aux_item(client, &dummy_aux_item);
David Byers's avatar
David Byers committed
538
                clear_aux_item(&dummy_aux_item);
David Byers's avatar
David Byers committed
539
            }
540
541
542
543
	    client->array_parse_index += 1;
	}
	client->array_parse_pos = 3;
    case 3:
David Byers's avatar
David Byers committed
544
545
546
        /* Adjust the length of the result to the length allocated */

        /* Read the closing brace */
547
548
549
550
551
552
553
	if ( parse_nonwhite_char(client) != '}' )
	    longjmp(parse_env, ISC_PROTOCOL_ERR);
    default:
	client->array_parse_pos = 0;
    }
}
                               
David Byers's avatar
David Byers committed
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
extern void
prot_a_parse_misc_info_list(Connection *client,
                            Misc_info_list  *result,
                            int maxlen)
{
    static unsigned long err_cnt = 0;
    int i;
    Misc_info dummy_misc_info;

    switch (client->array_parse_pos)
    {
    case 0:
	if ((result->no_of_misc != 0 || result->misc != NULL) && err_cnt++ < 20)
	{
	    kom_log("WNG: prot_a_parse_aux_item_list(): len = %lu data = %lu\n",
		(unsigned long)result->no_of_misc, (unsigned long)result->misc);
	    result->no_of_misc = 0;
	    result->misc = NULL;
	    if (err_cnt == 20)
		kom_log("The above warning is now turned off.");
	}

David Byers's avatar
David Byers committed
576
577
	client->array_parse_parsed_length = prot_a_parse_long(client);
        if (client->array_parse_parsed_length < 0)
David Byers's avatar
David Byers committed
578
579
580
581
582
        {
	    isc_puts("%%Insane array size.\n", client->isc_session);
	    isc_flush(client->isc_session);
	    longjmp(parse_env, ISC_LOGOUT);
        }
David Byers's avatar
David Byers committed
583
584

        result->no_of_misc = min(maxlen+1, client->array_parse_parsed_length);
David Byers's avatar
David Byers committed
585
586
587
588
589
590
591
	client->array_parse_pos = 1;
        /* Fall through */

    case 1:
	if ( parse_nonwhite_char(client) != '{' )
	    longjmp(parse_env, ISC_PROTOCOL_ERR);

David Byers's avatar
David Byers committed
592
        result->misc = smalloc(result->no_of_misc * sizeof(Misc_info));
David Byers's avatar
David Byers committed
593
        
David Byers's avatar
David Byers committed
594
        for (i = 0; i < result->no_of_misc; i++)
David Byers's avatar
David Byers committed
595
596
597
598
599
600
        {
            result->misc[i].type = unknown_info;
        }
	client->array_parse_index = 0;
	client->array_parse_pos = 2;
    case 2:
David Byers's avatar
David Byers committed
601
	while(client->array_parse_index < client->array_parse_parsed_length)
David Byers's avatar
David Byers committed
602
603
604
	{
            /* Enter a parsed aux item into the list only if we
               have parsed less than the no_of_misc of the list */
David Byers's avatar
David Byers committed
605
            if (client->array_parse_index < result->no_of_misc)
David Byers's avatar
David Byers committed
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
            {
                prot_a_parse_misc_info(client,
                                      &result->misc[
                                          client->array_parse_index]);
                
                /* If the most recently parsed item is an unknown, the
                   misc-info parser will have skipped to the end of the
                   array. Make allowances for this. */
                if (result->misc[client->array_parse_index].type == unknown_info)
                {
                    result->no_of_misc = client->array_parse_index + 1;
                }
            }
            else
            {
                prot_a_parse_misc_info(client, &dummy_misc_info);
            }
	    client->array_parse_index += 1;
	}
	client->array_parse_pos = 3;
    case 3:
        /* Adjust the no_of_misc of the result to the length allocated */

        /* Read the closing brace */
	if ( parse_nonwhite_char(client) != '}' )
	    longjmp(parse_env, ISC_PROTOCOL_ERR);
    default:
	client->array_parse_pos = 0;
    }
}

Per Cederqvist's avatar
Per Cederqvist committed
637
638
639
640
641

extern void
prot_a_parse_misc_info(Connection *client,
		       Misc_info      *result)
{
David Byers's avatar
David Byers committed
642
643
    struct tm tmp_time;

Per Cederqvist's avatar
Per Cederqvist committed
644
645
646
    switch ( client->struct_parse_pos )
    {
    case 0:
David Byers's avatar
David Byers committed
647
648
649
650
651
652
653
654
        /*
          FIXME: Omigawd this is ugly. Stupid compiler complains
          FIXME: about casting the result of a prot_a_parse_long
          FIXME: to an Info_type, but it won't complain if I cast
          FIXME: it to an int first.
        */
        
	result->type = (Info_type)(int)prot_a_parse_long(client);
Per Cederqvist's avatar
Per Cederqvist committed
655
656
657
658
659
660
661
662
663
664
665
666
667
	client->struct_parse_pos = 1;
	/* Fall through */
    case 1:
	switch( result->type )
	{
	case recpt:
	    result->datum.recipient = prot_a_parse_long(client);
	    break;

	case cc_recpt:
	    result->datum.cc_recipient = prot_a_parse_long(client);
	    break;
	    
668
669
670
671
	case bcc_recpt:
	    result->datum.bcc_recipient = prot_a_parse_long(client);
	    break;
	    
Per Cederqvist's avatar
Per Cederqvist committed
672
673
674
675
676
677
678
679
680
681
682
683
684
	case loc_no:
	    result->datum.local_no = prot_a_parse_long(client);
	    break;
	    
	case comm_to:
	    result->datum.comment_to = prot_a_parse_long(client);
	    break;
	    
	case footn_to:
	    result->datum.footnote_to = prot_a_parse_long(client);
	    break;

	case comm_in:
David Byers's avatar
David Byers committed
685
686
687
            result->datum.commented_in = prot_a_parse_long(client);
            break;
            
Per Cederqvist's avatar
Per Cederqvist committed
688
	case footn_in:
David Byers's avatar
David Byers committed
689
690
691
            result->datum.footnoted_in = prot_a_parse_long(client);
            break;
            
Per Cederqvist's avatar
Per Cederqvist committed
692
	case rec_time:
David Byers's avatar
David Byers committed
693
694
695
696
            prot_a_parse_time_date(client, &tmp_time);
            result->datum.received_at = mktime(&tmp_time);
            break;
            
Per Cederqvist's avatar
Per Cederqvist committed
697
	case sent_by:
David Byers's avatar
David Byers committed
698
699
700
            result->datum.sender = prot_a_parse_long(client);
            break;
            
Per Cederqvist's avatar
Per Cederqvist committed
701
	case sent_at:
David Byers's avatar
David Byers committed
702
703
704
705
            prot_a_parse_time_date(client, &tmp_time);
            result->datum.sent_at = mktime(&tmp_time);
            break;
            
Per Cederqvist's avatar
Per Cederqvist committed
706
	default:
David Byers's avatar
David Byers committed
707
708
709
710
            result->datum.unknown_type = result->type;
            result->type = unknown_info;
            client->array_hunt_depth = 1;
            prot_a_hunt_array_end(client);
Per Cederqvist's avatar
Per Cederqvist committed
711
712
713
714
715
716
717
	}
    default:
	client->struct_parse_pos = 0;
    }
}


Per Cederqvist's avatar
Per Cederqvist committed
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
void
prot_a_parse_time_date(Connection *client,
		       struct tm  *result)
{
    /* A time never occur inside a string, and a string never occur
       inside a time. Thus I use string_parse_pos here instead of
       inventing a new variable. Lacgen will allocate variables as
       needed... */

    switch( client->string_parse_pos )
    {
    case 0:
	result->tm_sec = prot_a_parse_long(client);
	client->string_parse_pos = 1;
	/* Fall through */
    case 1:
	result->tm_min = prot_a_parse_long(client);
	client->string_parse_pos = 2;
	/* Fall through */
    case 2:
	result->tm_hour = prot_a_parse_long(client);
	client->string_parse_pos = 3;
	/* Fall through */
    case 3:
	result->tm_mday = prot_a_parse_long(client);
	client->string_parse_pos = 4;
	/* Fall through */
    case 4:
	result->tm_mon = prot_a_parse_long(client);
	client->string_parse_pos = 5;
	/* Fall through */
    case 5:
	result->tm_year = prot_a_parse_long(client);
	client->string_parse_pos = 6;
	/* Fall through */
    case 6:
	result->tm_wday = prot_a_parse_long(client);
	client->string_parse_pos = 7;
	/* Fall through */
    case 7:
	result->tm_yday = prot_a_parse_long(client);
	client->string_parse_pos = 8;
	/* Fall through */
    case 8:
	result->tm_isdst = prot_a_parse_long(client);
	/* Fall through */
    default:
	client->string_parse_pos = 0;
    }
}

769
770
771
772
773


void prot_a_parse_info(Connection *client,
                       Info *result)
{
774
    switch( client->struct_parse_pos )
775
776
777
    {
    case 0:
        result->version = prot_a_parse_long(client);
778
        client->struct_parse_pos = 1;
779
780
781
        /* Fall through */
    case 1:
        result->conf_pres_conf = prot_a_parse_long(client);
782
        client->struct_parse_pos = 2;
783
784
785
        /* Fall through */
    case 2:
        result->pers_pres_conf = prot_a_parse_long(client);
786
        client->struct_parse_pos = 3;
787
788
789
        /* Fall through */
    case 3:
        result->motd_conf = prot_a_parse_long(client);
790
        client->struct_parse_pos = 4;
791
792
793
        /* Fall through */
    case 4:
        result->kom_news_conf = prot_a_parse_long(client);
794
        client->struct_parse_pos = 5;
795
796
797
798
799
        /* Fall through */
    case 5:
        result->motd_of_lyskom = prot_a_parse_long(client);
        /* Fall through */
    default:
800
        client->struct_parse_pos = 0;
801
802
    }
}
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908

void
prot_a_hunt_nl(Connection *client)
{
    String_size number_end;
    String_size len;

    while (1) 
    {
	switch (client->fnc_parse_pos)
	{
	case 0:			/* whitspace/simple tokens */
	    if ( client->first_to_parse >= s_strlen(client->unparsed) )
		longjmp(parse_env, ISC_MSG_INCOMPLETE);

	    switch(client->unparsed.string[client->first_to_parse])
	    {
	    case ' ':
	    case '\r':
	    case '\t':
	    case '{':
	    case '}':
	    case '*':
		client->first_to_parse++;
		break;
	    case '\n':
		/* We found a newline -- end of message.  Return now. */
		client->first_to_parse++;
		return;
	    case '0': case '1': case '2': case '3': case '4':
	    case '5': case '6': case '7': case '8': case '9':
		/* Entering a number -- possibly a string. */
		/* Don't increase first_to_parse. */
		client->fnc_parse_pos = 1;
		break;
	    default:
		longjmp(parse_env, ISC_PROTOCOL_ERR);
	    }
	    break;
	case 1:			/* number/string */
	    /* Entering a number.  The first char is known to be a digit.
	       Parse the entire number at once. */
	    len = client->unparsed.string[client->first_to_parse] - '0';
	    for (number_end = client->first_to_parse + 1;
		 number_end < s_strlen(client->unparsed);
		 number_end++)
	    {
		if (client->unparsed.string[number_end] >= '0'
		    && client->unparsed.string[number_end] <= '9')
		{
		    len = 10 * len + client->unparsed.string[number_end] - '0';
		}
		else
		{
		    /* The end of the number was reached. */
		    break;
		}
	    }

	    if (number_end == s_strlen(client->unparsed))
		longjmp(parse_env, ISC_MSG_INCOMPLETE);

	    if (client->unparsed.string[number_end] == 'H')
	    {
		/* We are entering a string.   Use num0 to store the
		   lenght of the string to skip. */
		client->num0 = len;
		client->first_to_parse = number_end + 1;
		client->fnc_parse_pos = 2;
	    }
	    else
	    {
		/* We have just skipped past a number that was not the
		   start of a string. */
		client->first_to_parse = number_end;
		client->fnc_parse_pos = 0;
	    }
	    break;
	case 2:			/* Skipping a string. */
	    if (client->num0 == 0)
	    {
		/* The entire string has been skipped. */
		client->fnc_parse_pos = 0;
		break;
	    }
	    len = s_strlen(client->unparsed) - client->first_to_parse;
	    if (client->num0 <= len)
	    {
		/* The entire string is present.  Skip it. */
		client->first_to_parse += client->num0;
		client->fnc_parse_pos = 0;
		break;
	    }
	    else
	    {
		/* Skip as much of the string as possible. */
		client->num0 -= len;
		client->first_to_parse = s_strlen(client->unparsed);
		longjmp(parse_env, ISC_MSG_INCOMPLETE);
	    }
	    abort();
	default:
	    abort();
	}
    }
}
David Byers's avatar
David Byers committed
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034

/*
 * Hunt for the end of an array. For this to work you need to set
 * array_hunt_depth to the number of nested arrays you want to
 * break out of (normally 1.) After this call the next token on
 * the input stream from the client will be the closing brace of
 * the array.
 */

static void
prot_a_hunt_array_end(Connection *client)
{
    String_size number_end;
    String_size len;

    while (1) 
    {
	switch (client->hunt_parse_pos)
	{
	case 0:			/* whitspace/simple tokens */
	    if ( client->first_to_parse >= s_strlen(client->unparsed) )
		longjmp(parse_env, ISC_MSG_INCOMPLETE);

	    switch(client->unparsed.string[client->first_to_parse])
	    {
	    case ' ':
	    case '\r':
	    case '\t':
	    case '*':
	    case '\n':
		client->first_to_parse++;
		break;
	    case '{':
                client->array_hunt_depth += 1;
                client->first_to_parse++;
                break;
	    case '}':
                client->array_hunt_depth -= 1;
                if (client->array_hunt_depth <= 0)
                {
                    client->hunt_parse_pos = 0;
                    return;
                }
                else
                {
                    client->first_to_parse++;
                }
                break;

	    case '0': case '1': case '2': case '3': case '4':
	    case '5': case '6': case '7': case '8': case '9':
		/* Entering a number -- possibly a string. */
		/* Don't increase first_to_parse. */
		client->hunt_parse_pos = 1;
		break;
	    default:
		longjmp(parse_env, ISC_PROTOCOL_ERR);
	    }
	    break;
	case 1:			/* number/string */
	    /* Entering a number.  The first char is known to be a digit.
	       Parse the entire number at once. */
	    len = client->unparsed.string[client->first_to_parse] - '0';
	    for (number_end = client->first_to_parse + 1;
		 number_end < s_strlen(client->unparsed);
		 number_end++)
	    {
		if (client->unparsed.string[number_end] >= '0'
		    && client->unparsed.string[number_end] <= '9')
		{
		    len = 10 * len + client->unparsed.string[number_end] - '0';
		}
		else
		{
		    /* The end of the number was reached. */
		    break;
		}
	    }

	    if (number_end == s_strlen(client->unparsed))
		longjmp(parse_env, ISC_MSG_INCOMPLETE);

	    if (client->unparsed.string[number_end] == 'H')
	    {
		/* We are entering a string.   Use num0 to store the
		   lenght of the string to skip. */
		client->array_hunt_num = len;
		client->first_to_parse = number_end + 1;
		client->hunt_parse_pos = 2;
	    }
	    else
	    {
		/* We have just skipped past a number that was not the
		   start of a string. */
		client->first_to_parse = number_end;
		client->hunt_parse_pos = 0;
	    }
	    break;
	case 2:			/* Skipping a string. */
	    if (client->array_hunt_num == 0)
	    {
		/* The entire string has been skipped. */
		client->hunt_parse_pos = 0;
		break;
	    }
	    len = s_strlen(client->unparsed) - client->first_to_parse;
	    if (client->array_hunt_num <= len)
	    {
		/* The entire string is present.  Skip it. */
		client->first_to_parse += client->array_hunt_num;
		client->hunt_parse_pos = 0;
		break;
	    }
	    else
	    {
		/* Skip as much of the string as possible. */
		client->array_hunt_num -= len;
		client->first_to_parse = s_strlen(client->unparsed);
		longjmp(parse_env, ISC_MSG_INCOMPLETE);
	    }
	    abort();            /* NOTREACHED */
	default:
            abort();
	}
    }
}