Subversion Repositories general

Rev

Rev 1209 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1082 dev 1
#!/usr/bin/perl -w
2
 
3
#
4
# Sample backend for HostAdmiral
5
# (copyleft) Anatoli Klassen
6
#
7
 
8
# FIXME use transactions
9
 
10
use strict;
11
use vars;
12
use subs;
13
use Socket;
14
use DBI;
15
use Time::HiRes qw( gettimeofday tv_interval );
16
 
17
# == configuration =============================
18
 
19
my $host        = '127.0.0.1';
20
my $port        = 9097;
21
my $password    = '0123456789ABCDEF';
22
my $db_url      = 'DBI:mysql:database=mail;host=localhost;port=3306';
23
my $db_user     = 'root';
24
my $db_password = '';
25
my $log_level   = 9;                              # 0 - none, 9 - all
26
 
27
# == constants =================================
28
 
29
my $protocol_ver_maj   = "1";
30
my $protocol_ver_min   = "0";
31
my $protocol_header    = "HostAdmiral_TcpListener";
32
my $password_header    = "password=";
33
my $domain_header      = "inetDomain";
34
my $user_header        = "user";
35
my $system_user_header = "systemUser";
36
my $mailbox_header     = "mailbox";
37
my $mail_alias_header  = "mailAlias";
38
my $create_action      = "create";
39
my $modify_action      = "modify";
40
my $delete_action      = "delete";
41
 
42
# response codes
43
my $code_ok               = 200;
44
my $code_ok_but           = 201;
45
my $code_ignored          = 202;
46
my $code_no_body          = 400;
47
my $code_protocol_header  = 401;
48
my $code_no_end_lines     = 402;
49
my $code_no_password      = 403;
50
my $code_wrong_password   = 404;
51
my $code_no_command       = 405;
52
my $code_wrong_command    = 406;
53
my $code_unknown_command  = 407;
54
my $code_wrong_params     = 408;
55
my $code_db_connect_error = 501;
56
my $code_db_error         = 502;
57
my $code_db_close_error   = 503;
58
 
59
# == internal global variables =================
60
 
61
my %handlers;
62
 
63
sub connection_loop
64
{
65
	# listen for connections
66
	socket(SERVER, PF_INET, SOCK_STREAM, getprotobyname('tcp')) or die "$!\n";
67
	setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, 1)             or die "$!\n";
68
	bind(SERVER, sockaddr_in($port, inet_aton($host)))          or die "$!\n";
69
	listen(SERVER, 1);
70
 
71
	while(1) {
72
		# get connection
73
		my $rem_addr = accept(CLIENT, SERVER);
74
		my $buf;
75
		my $body = "";
76
		my %request = ();
77
		#log_debug("Remote: $rem_addr");
78
		$request{'start_timestamp'} = [gettimeofday];
79
 
80
		# receive request body
81
		while((my $size = sysread CLIENT, $buf, 65536) > 0) {
82
			$body .= $buf;
83
		}
84
		$request{'body'} = $body;
85
		$request{'body_timestamp'} = [gettimeofday];
86
 
87
		# call handler
88
		handle_request(\%request);
89
		$request{'done_timestamp'} = [gettimeofday];
90
 
91
		# print out response
92
		print CLIENT "$protocol_header $protocol_ver_maj.$protocol_ver_min"
93
			. "\n$request{'code'} $request{'response'}\n\n";
94
		close CLIENT;
95
		$request{'stop_timestamp'} = [gettimeofday];
1209 dev 96
		log_debug("Duration: " . tv_interval($request{'start_timestamp'},
1082 dev 97
			$request{'stop_timestamp'}) . " sec");
98
	}
99
 
100
	# close the port
101
	close SERVER;
102
}
103
 
104
sub handle_request
105
{
106
	my $request = shift @_;
107
 
108
	log_debug("Handle request [\n$request->{'body'}]");
109
 
110
	my @lines = split /\n/, $request->{'body'}, -1;
111
	my $cur   = 0;
112
 
113
	# check header
114
	if($#lines < $cur) {
115
		set_request_code($request, $code_no_body, "Request without body");
116
		return;
117
	}
118
	unless($lines[$cur] =~ /^$protocol_header $protocol_ver_maj\.\d+$/) {
119
		set_request_code($request, $code_protocol_header, "Request must start"
120
			. "with [$protocol_header $protocol_ver_maj.minor],"
121
			. " but [$lines[$cur]] found");
122
		return;
123
	}
124
	$cur++;
125
 
126
	# check end lines
127
	if($#lines < $cur+1 || $lines[$#lines-1] ne "" || $lines[$#lines] ne "") {
1209 dev 128
		set_request_code($request, $code_no_end_lines,
1082 dev 129
			"Request doesn't end with \\n\\n");
130
		return;
131
	}
132
 
133
	# check password
134
	if($password) {
135
		if($#lines < $cur || !($lines[$cur] =~ /^$password_header/)) {
1209 dev 136
			set_request_code($request, $code_no_password,
1082 dev 137
				"Password not specified");
138
			return;
139
		}
140
 
141
		unless($lines[$cur] =~ /^$password_header$password$/) {
142
			set_request_code($request, $code_wrong_password,
143
				"Wrong password");
144
			return;
145
		}
146
 
147
		$cur++;
148
	}
149
 
150
	# get command handler
151
	if($#lines < $cur) {
152
		set_request_code($request, $code_no_command, "Empty command");
153
		return;
154
	}
155
	unless($lines[$cur] =~ /^(\S+)\t(\S+)/) {
156
		set_request_code($request, $code_wrong_command, "Can not get command");
157
		return;
158
	}
159
 
160
	$request->{'command'}    = $1;
161
	$request->{'subcommand'} = $2;
162
	$request->{'handler'}    = $handlers{"$1_$2"};
163
 
164
	unless($request->{'handler'}) {
165
		set_request_code($request, $code_unknown_command,
166
			"Unknown command [$request->{'command'} $request->{'subcommand'}]");
167
		return;
168
	}
169
 
170
	# call
171
	log_debug("call $request->{'command'}_$request->{'subcommand'}");
172
	my @params = @lines[$cur..$#lines-2];
173
	&{$request->{'handler'}}($request, @params);
174
}
175
 
176
sub handle_user_create
177
{
178
	my $request = shift @_;
1209 dev 179
 
1082 dev 180
	set_request_code($request, $code_ignored, "Not interesting in users");
181
}
182
 
183
sub handle_user_modify
184
{
185
	my $request = shift @_;
1209 dev 186
 
1082 dev 187
	set_request_code($request, $code_ignored, "Not interesting in users");
188
}
189
 
190
sub handle_user_delete
191
{
192
	my $request = shift @_;
1209 dev 193
 
1082 dev 194
	set_request_code($request, $code_ignored, "Not interesting in users");
195
}
196
 
197
sub handle_domain_create
198
{
199
	my $request = shift @_;
200
	my %params  = parse_command_params($request, shift @_, ("name"));
201
	return unless(%params);
202
 
1209 dev 203
	my $res_action = save_to_db($request, "transport",
1082 dev 204
		{ domain => $params{"name"} },
205
		{ domain => $params{"name"}, comment => $params{"comment"},
206
		  transport => 'virtual:' } );
207
 
208
	if($res_action eq 'update') {
209
		return unless(restart_mail_system());
210
		set_request_code($request, $code_ok_but, "Domain exists, modified");
211
	}
212
	elsif($res_action eq 'insert') {
213
		return unless(restart_mail_system());
214
		set_request_code($request, $code_ok, "Domain created");
215
	}
216
}
217
 
218
sub handle_domain_modify
219
{
220
	my $request = shift @_;
221
	my %params  = parse_command_params($request, shift @_, ("oldName", "name"));
222
	return unless(%params);
223
 
1209 dev 224
	my $res_action = save_to_db($request, "transport",
1082 dev 225
		{ domain => $params{"oldName"} },
226
		{ domain => $params{"name"}, comment => $params{"comment"},
227
		  transport => 'virtual:' } );
228
 
229
	if($res_action eq 'update') {
230
		return unless(restart_mail_system());
231
		set_request_code($request, $code_ok, "Domain modified");
232
	}
233
	elsif($res_action eq 'insert') {
234
		return unless(restart_mail_system());
235
		set_request_code($request, $code_ok_but, "Domain not found, created");
236
	}
237
}
238
 
239
sub handle_domain_delete
240
{
241
	my $request = shift @_;
242
	my %params  = parse_command_params($request, shift @_, ("name"));
243
	return unless(%params);
244
 
1209 dev 245
	my $res_action = delete_from_db($request, "transport",
1082 dev 246
		{ domain => $params{"name"} } );
247
 
248
	if($res_action eq 'delete') {
249
		return unless(restart_mail_system());
250
		set_request_code($request, $code_ok, "Domain deleted");
251
	}
252
	elsif($res_action eq 'not found') {
253
		set_request_code($request, $code_ok_but, "Domain not found");
254
	}
255
}
256
 
257
sub handle_system_user_create
258
{
259
}
260
 
261
sub handle_system_user_modify
262
{
263
}
264
 
265
sub handle_system_user_delete
266
{
267
}
268
 
269
sub handle_mailbox_create
270
{
271
	my $request = shift @_;
272
	my %params  = parse_command_params($request, shift @_,
273
		("login", "password", "domain"));
274
	return unless(%params);
275
 
1209 dev 276
	my $res_action = save_to_db($request, "users",
1082 dev 277
		{ login => "$params{'login'}\@$params{'domain'}" },
278
		{ login => "$params{'login'}\@$params{'domain'}",
279
		  password => $params{"password"},
280
		  maildir => "$params{'domain'}/$params{'login'}/",
281
		  expired => ($params{"enabled"} eq "true" ? 0 : 1),
282
		  comment => $params{"comment"},
283
		  uid => ($params{"systemUser"} ? $params{"systemUser"} : undef) } );
284
 
285
	if($res_action eq 'update') {
286
		set_request_code($request, $code_ok_but, "Mailbox exists, modified");
287
	}
288
	elsif($res_action eq 'insert') {
289
		set_request_code($request, $code_ok, "Mailbox created");
290
	}
291
}
292
 
293
sub handle_mailbox_modify
294
{
295
	my $request = shift @_;
296
	my %params  = parse_command_params($request, shift @_,
297
		("oldLogin", "oldDomain", "login", "domain"));
298
	return unless(%params);
299
 
300
	# FIXME move the old maildir
301
 
1209 dev 302
	my $res_action = save_to_db($request, "users",
1082 dev 303
		{ login => "$params{'oldLogin'}\@$params{'oldDomain'}" },
304
		{ login => "$params{'login'}\@$params{'domain'}",
305
		  password => $params{"password"},
306
		  maildir => "$params{'domain'}/$params{'login'}/",
307
		  expired => ($params{"enabled"} eq "true" ? "0" : "1"),
308
		  comment => $params{"comment"},
309
		  uid => ($params{"systemUser"} ? $params{"systemUser"} : undef) } );
310
 
311
	if($res_action eq 'update') {
312
		set_request_code($request, $code_ok, "Mailbox modified");
313
	}
314
	elsif($res_action eq 'insert') {
315
		set_request_code($request, $code_ok_but, "Mailbox not found, created");
316
	}
317
}
318
 
319
sub handle_mailbox_delete
320
{
321
	my $request = shift @_;
322
	my %params  = parse_command_params($request, shift @_, ("login", "domain"));
323
	return unless(%params);
324
 
325
	# FIXME remove the maildir
326
 
1209 dev 327
	my $res_action = delete_from_db($request, "users",
1082 dev 328
		{ login => "$params{'login'}\@$params{'domain'}" } );
329
 
330
	if($res_action eq 'delete') {
331
		set_request_code($request, $code_ok, "Mailbox deleted");
332
	}
333
	elsif($res_action eq 'not found') {
334
		set_request_code($request, $code_ok_but, "Mailbox not found");
335
	}
336
}
337
 
338
sub handle_mail_alias_create
339
{
340
	my $request = shift @_;
341
	my %params  = parse_command_params($request, shift @_, ("address", "domain"));
342
	return unless(%params);
343
	my @rcpts = parse_command_array($request, @_);
344
 
1209 dev 345
	my $del_action = delete_from_db($request, "aliases",
1082 dev 346
		{ alias => "$params{'address'}\@$params{'domain'}" } );
347
	return if($del_action eq "error");
1209 dev 348
 
1082 dev 349
	foreach my $rcpt (@rcpts) {
350
		log_debug("save $rcpt");
1209 dev 351
		my $res_action = save_to_db($request, "aliases",
1082 dev 352
			undef,
353
			{ alias => "$params{'address'}\@$params{'domain'}",
354
			  rcpt => $rcpt, comment => $params{"comment"} } );
355
		return if($res_action eq "error");
356
	}
357
 
358
	if($del_action eq 'delete') {
359
		set_request_code($request, $code_ok_but, "Mail alias exists, modified");
360
	}
361
	elsif($del_action eq 'not found') {
362
		set_request_code($request, $code_ok, "Mail alias created");
363
	}
364
}
365
 
366
sub handle_mail_alias_modify
367
{
368
	my $request = shift @_;
369
	my %params  = parse_command_params($request, shift @_, ("address", "domain"));
370
	return unless(%params);
371
	my @rcpts = parse_command_array($request, @_);
372
 
1209 dev 373
	my $del_action = delete_from_db($request, "aliases",
1082 dev 374
		{ alias => "$params{'address'}\@$params{'domain'}" } );
375
	return if($del_action eq "error");
1209 dev 376
 
1082 dev 377
	foreach my $rcpt (@rcpts) {
378
		log_debug("save $rcpt");
1209 dev 379
		my $res_action = save_to_db($request, "aliases",
1082 dev 380
			undef,
381
			{ alias => "$params{'address'}\@$params{'domain'}",
382
			  rcpt => $rcpt, comment => $params{"comment"} } );
383
		return if($res_action eq "error");
384
	}
1209 dev 385
	# FIXME add the loopback destination? handle "a@domain.com => a@domain.com"
386
	# as "a@domain.com => _something_@domain.com + _something_@domain.com => _something_@domain.com" ?
1082 dev 387
 
388
	if($del_action eq 'delete') {
389
		set_request_code($request, $code_ok, "Mail alias modified");
390
	}
391
	elsif($del_action eq 'not found') {
392
		set_request_code($request, $code_ok_but, "Mail alias not found, created");
393
	}
394
}
395
 
396
sub handle_mail_alias_delete
397
{
398
	my $request = shift @_;
399
	my %params  = parse_command_params($request, shift @_, ("address", "domain"));
400
	return unless(%params);
401
 
1209 dev 402
	my $res_action = delete_from_db($request, "aliases",
1082 dev 403
		{ alias => "$params{'address'}\@$params{'domain'}" } );
404
 
405
	if($res_action eq 'delete') {
406
		set_request_code($request, $code_ok, "Mail alias deleted");
407
	}
408
	elsif($res_action eq 'not found') {
409
		set_request_code($request, $code_ok_but, "Mail alias not found");
410
	}
411
}
412
 
413
sub decode_param
414
{
415
	my $value = shift @_;
416
 
417
	$value =~ s/\\r/\r/g;
418
	$value =~ s/\\n/\n/g;
419
	$value =~ s/\\t/\t/g;
420
	$value =~ s/\\0/\000/g;
421
	$value =~ s/\\\\/\\/g;
1209 dev 422
 
1082 dev 423
	return $value;
424
}
425
 
426
sub parse_command_array
427
{
428
	my $request = shift @_;
429
	my @params  = ();
430
 
431
	map {
432
		if(/^\t(.*)$/) {
433
			push @params, decode_param($1);
434
		}
435
	} @_;
436
 
437
	return @params;
438
}
439
 
440
sub parse_command_params($$@)
441
{
442
	my $request  = shift @_;
443
	my @params   = split /\t/, shift @_, -1;
444
	my %required = map { $_ => 1 } @_; # convert array to hash
445
	my %values   = ();
1209 dev 446
 
1082 dev 447
	@params = @params[2..$#params]; # remove handler and action
448
	map {
449
		my ($key, $value) = split /=/, $_;
450
		$values{$key} = decode_param($value);
451
		delete($required{$key});
452
	} @params;
453
 
454
	if(%required) {
455
		set_request_code($request, $code_wrong_params,
456
			"Params " . join(', ', keys %required) . " expected but not found");
457
		return ();
458
	}
459
 
460
	return %values;
461
}
462
 
463
sub restart_mail_system
464
{
465
	my $request = shift @_;
466
 
467
	log_debug("Mail system restarted");
468
 
469
	return 1;
470
}
471
 
472
sub db_connect
473
{
474
	my $request = shift @_;
475
	my $dbh     = undef;
1209 dev 476
 
1082 dev 477
	eval { $dbh = DBI->connect($db_url, $db_user, $db_password); };
478
	if($@) {
479
		set_request_code($request, $code_db_connect_error, $@);
480
		$dbh = undef;
481
	}
482
 
483
	return $dbh;
484
}
485
 
486
sub db_close
487
{
488
	my $request  = shift @_;
489
	my $dbh      = shift @_;
490
	my $error    = shift @_;
491
	my $no_error = 1;
492
 
493
	if($error) {
494
		set_request_code($request, $code_db_error, $error);
495
		$no_error = 0;
496
	}
497
 
498
	eval {
499
		$dbh->disconnect() if($dbh);
500
	};
501
 
502
	if($@ && $no_error) {
503
		set_request_code($request, $code_db_close_error, $@);
504
		$no_error = 0;
505
	}
506
 
507
	return $no_error;
508
}
509
 
510
sub delete_from_db
511
{
512
	my $request     = shift @_;
513
	my $table       = shift @_;
514
	my $key_columns = shift @_;
515
 
516
	my $res_action  = 'none';
517
	my $dbh         = db_connect($request);
518
 
519
	return 'error' unless($dbh);
1209 dev 520
 
1082 dev 521
	eval {
522
		my $sql = "";
523
		while(my ($key, $value) = each(%$key_columns)) {
524
			next unless(defined $value);
525
			$sql .= " and " if($sql);
526
			$sql .= "$key = ?";
527
		}
528
		$sql = "delete from $table where $sql";
529
 
530
		my $sth   = $dbh->prepare($sql);
531
		my $count = 0;
532
		while(my ($key, $value) = each(%$key_columns)) {
533
			next unless(defined $value);
534
			$sth->bind_param(++$count, $value);
535
		}
1209 dev 536
 
1082 dev 537
		my $res = $sth->execute;
538
 
539
		if($res < 1) {
540
			$res_action = 'not found';
541
		}
542
		else {
543
			$res_action = 'delete';
544
		}
545
	};
546
 
547
	if(db_close($request, $dbh, $@)) {
548
		return $res_action;
549
	}
550
	else {
551
		return 'error';
552
	}
553
}
554
 
555
sub save_to_db
556
{
557
	my $request       = shift @_;
558
	my $table         = shift @_;
559
	my $key_columns   = shift @_;
560
	my $value_columns = shift @_;
561
 
562
	my $error_set  = 0;
563
	my $res_action = 'none';
564
	my $dbh        = db_connect($request);
565
 
566
	return 'error' unless($dbh);
1209 dev 567
 
1082 dev 568
	eval {
569
		my $res = 0;
570
 
571
		if($key_columns) {
572
			my $update_sql = "";
573
			my $where      = "";
574
			while(my ($key, $value) = each(%$value_columns)) {
575
				next unless(defined $value);
576
				$update_sql .= ", " if($update_sql);
577
				$update_sql .= "$key = ?";
578
			}
579
			while(my ($key, $value) = each(%$key_columns)) {
580
				next unless(defined $value);
581
				$where .= " and " if($where);
582
				$where .= "$key = ?";
583
			}
584
			$update_sql = "update $table set $update_sql where $where";
585
 
586
			my $update_sth = $dbh->prepare($update_sql);
587
			my $count      = 0;
588
			while(my ($key, $value) = each(%$value_columns)) {
589
				next unless(defined $value);
590
				$update_sth->bind_param(++$count, $value);
591
			}
592
			while(my ($key, $value) = each(%$key_columns)) {
593
				next unless(defined $value);
594
				$update_sth->bind_param(++$count, $value);
595
			}
1209 dev 596
 
1082 dev 597
			$res        = $update_sth->execute;
598
			$res_action = 'update';
599
		}
600
 
601
		if($res < 1) {
602
			my $insert_sql = "";
603
			my $sql_params = "";
604
			while(my ($key, $value) = each(%$key_columns)) {
605
				next unless(defined $value);
606
				next if($value_columns->{$key});
607
				if($insert_sql) {
608
					$insert_sql .= ", ";
609
					$sql_params .= ", ";
610
				}
611
				$insert_sql .= "$key";
612
				$sql_params .= "?";
613
			}
614
			while(my ($key, $value) = each(%$value_columns)) {
615
				next unless(defined $value);
616
				if($insert_sql) {
617
					$insert_sql .= ", ";
618
					$sql_params .= ", ";
619
				}
620
				$insert_sql .= "$key";
621
				$sql_params .= "?";
622
			}
623
			$insert_sql = "insert into $table ($insert_sql)"
624
				. " values ($sql_params)";
625
 
626
			my $insert_sth = $dbh->prepare($insert_sql);
627
			my $count      = 0;
628
			while(my ($key, $value) = each(%$key_columns)) {
629
				next unless(defined $value);
630
				next if($value_columns->{$key});
631
				$insert_sth->bind_param(++$count, $value);
632
			}
633
			while(my ($key, $value) = each(%$value_columns)) {
634
				next unless(defined $value);
635
				$insert_sth->bind_param(++$count, $value);
636
			}
1209 dev 637
 
1082 dev 638
			$res        = $insert_sth->execute;
639
			$res_action = 'insert';
640
		}
641
	};
1209 dev 642
    # FIXME handle exceptions?
1082 dev 643
 
644
	if(db_close($request, $dbh, $@)) {
645
		return $res_action;
646
	}
647
	else {
648
		return 'error';
649
	}
650
}
651
 
652
sub set_request_code
653
{
654
	my $request = shift @_;
655
	my $code    = shift @_;
656
	my $message = shift @_;
657
 
658
	$request->{'code'}     = $code;
659
	$request->{'response'} = $message;
660
 
661
	my $error = "Error $code '$message' in request [\n$request->{'body'}]";
662
	if($code >= 500 && $code < 600) {
663
		log_error($error);
664
	}
665
	elsif($code >= 400 && $code < 500) {
666
		log_warning($error);
667
	}
668
	elsif($code >= 200 && $code < 300) {
669
		log_info("$code $message");
670
	}
671
	else {
672
		log_error("unknown code $code");
673
	}
674
}
675
 
676
sub log_debug
677
{
678
	log_message("DEBUG", shift @_) if ($log_level >= 9);
679
}
680
 
681
sub log_info
682
{
683
	log_message("INFO", shift @_) if ($log_level >= 5);
684
}
685
 
686
sub log_warning
687
{
688
	log_message("WARN", shift @_) if ($log_level >= 3);
689
}
690
 
691
sub log_error
692
{
693
	log_message("ERROR", shift @_) if ($log_level >= 1);
694
}
695
 
696
sub log_message
697
{
698
	print shift @_, ":\t", shift @_, "\n";
699
}
700
 
701
sub init
702
{
703
	$handlers{"${user_header}_${create_action}"}        = \&handle_user_create;
704
	$handlers{"${user_header}_${modify_action}"}        = \&handle_user_modify;
705
	$handlers{"${user_header}_${delete_action}"}        = \&handle_user_delete;
706
	$handlers{"${domain_header}_${create_action}"}      = \&handle_domain_create;
707
	$handlers{"${domain_header}_${modify_action}"}      = \&handle_domain_modify;
708
	$handlers{"${domain_header}_${delete_action}"}      = \&handle_domain_delete;
709
	$handlers{"${system_user_header}_${create_action}"} = \&handle_system_user_create;
710
	$handlers{"${system_user_header}_${modify_action}"} = \&handle_system_user_modify;
711
	$handlers{"${system_user_header}_${delete_action}"} = \&handle_system_user_delete;
712
	$handlers{"${mailbox_header}_${create_action}"}     = \&handle_mailbox_create;
713
	$handlers{"${mailbox_header}_${modify_action}"}     = \&handle_mailbox_modify;
714
	$handlers{"${mailbox_header}_${delete_action}"}     = \&handle_mailbox_delete;
715
	$handlers{"${mail_alias_header}_${create_action}"}  = \&handle_mail_alias_create;
716
	$handlers{"${mail_alias_header}_${modify_action}"}  = \&handle_mail_alias_modify;
717
	$handlers{"${mail_alias_header}_${delete_action}"}  = \&handle_mail_alias_delete;
718
}
719
 
720
sub main
721
{
722
	#my $sth = $dbh->prepare("SELECT * FROM transport");
723
	#$sth->execute();
724
	#while(my $ref = $sth->fetchrow_hashref()) {
725
	#	print "id = $ref->{'id'}\n";
726
	#}
727
	#$sth->finish();
728
 
729
	init();
730
	connection_loop();
731
}
732
 
733
main();
734