Apply 79_archiver_slash.patch
[mspang/vmailman.git] / contrib / majordomo2mailman.pl
1 #!/usr/bin/perl -w
2
3 # majordomo2mailman.pl - Migrate Majordomo mailing lists to Mailman 2.0
4 #          Copyright (C) 2002 Heiko Rommel (rommel@suse.de)
5
6 # BAW: Note this probably needs to be upgraded to work with MM2.1
7
8 #
9 # License:
10 #
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 1, or (at your option)
14 # any later version.
15
16 #
17 # Warranty:
18 #
19 # There's absolutely no warranty.
20 #
21
22 # comments on possible debug messages during the conversion:
23 #
24 # "not an valid email address" : those addresses are rejected, i.e. not imported into the Mailman list
25 # "not a numeric value" : such a value will be converted to 0 (z.B. maxlength)
26 # "already subscribed" : will only once be subscribed on the Mailman list
27 # "...umbrella..." or "...taboo..." -> Mailman-Admin-Guide
28
29 use strict;
30 use Getopt::Long;
31 use Fcntl;
32 use POSIX qw (tmpnam);
33
34 use vars qw (
35              $majordomo $mydomain $myurl
36              $aliasin $listdir
37              $aliasout $mailmanbin
38              $umbrella_member_suffix $private 
39              $newsserver $newsprefix
40              $susehack $susearchuser
41              $help $debug $update $all $usagemsg
42              *FH
43              %mlaliases %mlowners %mlapprovers
44              %defaultmlconf %mlconf
45              %defaultmmconf %mmconf
46 );
47
48 #
49 # adjust your site-specific settings here
50 #
51
52 $mydomain               = "my.domain";
53 $majordomo              = "majordomo"; # the master Majordomo address for your site
54 $aliasin                = "/var/lib/majordomo/aliases";
55 $listdir                = "/var/lib/majordomo/lists";
56 $aliasout               = "/tmp/aliases";
57 $myurl                  = "http://my.domain/mailman/";
58 $mailmanbin             = "/usr/lib/mailman/bin";
59 $umbrella_member_suffix = "-owner";
60 $private                = "yes"; # is this a private/Intranet site ?
61 $newsserver             = "news.my.domain";
62 $newsprefix             = "intern.";
63
64 $susehack = "no";
65 $susearchuser = "archdummy";
66
67 #
68 # 0)
69 # parse the command line arguments
70 #
71
72 $usagemsg = "usage: majordomo2mailman [-h|--help] [-d|--debug] [-u|--update] < (-a|--all) | list-of-mailinglists >";
73
74 GetOptions(
75            "h|help" => \$help,
76            "d|debug" => \$debug,
77            "a|all" => \$all,
78            "u|update" => \$update
79 ) or die "$usagemsg\n";
80
81 if (defined($help)) { die "$usagemsg\n"; }
82
83 if ((not defined($all)) and (@ARGV<1)) { die "$usagemsg\n"; }
84
85 if ($<) { die "this script must be run as root!\n"; }
86
87 #
88 # 1)
89 # build a list of all aliases and extract the name of mailing lists plus their owners
90 #
91
92 %mlaliases = %mlowners = %mlapprovers = ();
93
94 open (FH, "< $aliasin") or die "can't open $aliasin\n";
95
96 while (<FH>) {
97   # first, build a list of all active aliases and their resolution
98   if (/^([^\#:]+)\s*:\s*(.*)$/) {
99     $mlaliases{$1} = $2;
100   }
101 }
102
103 my $mlalias;
104 for $mlalias (keys %mlaliases) {
105   # if we encounter an alias with :include: as expansion
106   # it is save to assume that the alias has the form
107   # <mailinglist>-outgoing -
108   # that way we find the names of all active mailing lists
109   if ($mlaliases{$mlalias} =~ /\:include\:/) {
110     my $ml;
111     ($ml = $mlalias) =~ s/-outgoing//g;
112     $mlowners{$ml} = $mlaliases{"owner-$ml"};
113     $mlapprovers{$ml} = $mlaliases{"$ml-approval"};
114   }
115 }
116
117 close (FH);
118
119 #
120 # 2)
121 # for each list read the Majordomo configuration params
122 # and create a Mailman clone
123 #
124
125 my $ml;
126 for $ml ((defined ($all)) ? sort keys %mlowners : @ARGV) {
127
128   init_defaultmlconf($ml);
129   %mlconf = %defaultmlconf;
130
131   init_defaultmmconf($ml);
132   %mmconf = %defaultmmconf;
133
134   my @privileged; # addresses that are mentioned in restrict_post
135   my @members;
136   my ($primaryowner, @secondaryowner);
137   my ($primaryapprover, @secondaryapprover);
138
139   my ($skey, $terminator);
140   my $filename;
141   my @args;
142
143   #
144   # a)
145   # parse the configuration file
146   #
147
148   open (FH, "< $listdir/$ml.config") or die "can't open $listdir/$ml.config\n";
149
150   while (<FH>) {
151     # key = value ?
152     if (/^\s*([^=\#\s]+)\s*=\s*(.*)\s*$/) {
153       $mlconf{$1} = $2;
154     }
155     # key << EOF
156     # value
157     # EOF ?
158     elsif (/^\s*([^<\#\s]+)\s*<<\s*(.*)\s*$/) {
159       ($skey, $terminator) = ($1, $2);
160       while (<FH>) {
161         last if (/^$terminator\s*$/);
162         $mlconf{$skey} .= $_;
163       }
164       chomp $mlconf{$skey};
165     }
166   }
167
168   close (FH);
169
170   #
171   # b)
172   # test if there are so-called flag files (clue that this is an old-style Majordomo lists)
173   # and overwrite previously parsed values
174   # (stolen from majordomo::config_parse.pl: handle_flag_files())
175   #
176
177   if ( -e "$listdir/$ml.private") {
178     $mlconf{"get_access"} = "closed";
179     $mlconf{"index_access"} = "closed";
180     $mlconf{"who_access"} = "closed";
181     $mlconf{"which_access"} = "closed";
182   }
183
184   $mlconf{"subscribe_policy"} = "closed" if ( -e "$listdir/$ml.closed");
185   $mlconf{"unsubscribe_policy"} = "closed" if ( -e "$listdir/$ml.closed");
186
187   if ( -e "$listdir/$ml.auto" && -e "$listdir/$ml.closed") {
188     print STDERR "sowohl $ml.auto als auch $ml.closed existieren. W√§hle $ml.closed\n"; 
189   }
190   else {
191     $mlconf{"subscribe_policy"} = "auto" if ( -e"$listdir/$ml.auto"); 
192     $mlconf{"unsubscribe_policy"} = "auto" if ( -e"$listdir/$ml.auto"); 
193   }
194
195   $mlconf{"strip"} = 1 if ( -e "$listdir/$ml.strip");
196   $mlconf{"noadvertise"} = "/.*/" if ( -e "$listdir/$ml.hidden");
197
198   # admin_passwd:
199   $filename = "$listdir/" . $mlconf{"admin_passwd"};
200   if ( -e "$listdir/$ml.passwd" ) {
201     $mlconf{"admin_passwd"} = read_from_file("$listdir/$ml.passwd");
202   }
203   elsif ( -e "$filename" ) {
204     $mlconf{"admin_passwd"} = read_from_file("$filename");
205   }
206   # else take it verbatim
207
208   # approve_passwd:
209   $filename = "$listdir/" . $mlconf{"approve_passwd"};
210   if ( -e "$listdir/$ml.passwd" ) {
211     $mlconf{"approve_passwd"} = read_from_file("$listdir/$ml.passwd");
212   }
213   elsif ( -e "$filename" ) {
214     $mlconf{"approve_passwd"} = read_from_file("$filename");
215   }
216   # else take it verbatim
217
218   #
219   # c)
220   # add some information from additional configuration files
221   #
222
223   # restrict_post
224   if (defined ($mlconf{"restrict_post"})) {
225     @privileged = ();
226     for $filename (split /\s+/, $mlconf{"restrict_post"}) {
227       open (FH, "< $listdir/$filename") or die "can't open $listdir/$filename\n";
228       push (@privileged, <FH>);
229       chomp @privileged;
230       close (FH);
231     }
232   }
233
234   if ($susehack =~ m/yes/i) {
235     @privileged = grep(!/$susearchuser\@$mydomain/i, @privileged);
236   }
237
238   $mlconf{"privileged"} = \@privileged;
239
240   # members
241   @members = ();
242   open (FH, "< $listdir/$ml") or die "can't open $listdir/$ml\n";
243   push (@members, <FH>);
244   chomp @members;
245   close (FH);
246
247   $mlconf{"gated"} = "no";
248
249   if ($susehack =~ m/yes/i) {
250     if (grep(/$susearchuser\@$mydomain/i, @members)) {
251       $mlconf{"gated"} = "yes";
252     }
253     @members = grep(!/$susearchuser\@$mydomain/i, @members);
254   }
255
256   $mlconf{"members"} = \@members;
257
258   # intro message
259   if (open (FH, "< $listdir/$ml.intro")) {
260     { local $/; $mlconf{"intro"} = <FH>; }
261   }
262   else { $mlconf{"intro"} = ""; }
263
264   # info message
265   if (open (FH, "< $listdir/$ml.info")) {
266     { local $/; $mlconf{"info"} = <FH>; }
267   }
268   else { $mlconf{"info"} = ""; }
269   
270   #
271   # d)
272   # take over some other params into the configuration table
273   #
274
275   $mlconf{"name"} = "$ml";
276
277   ($primaryowner, @secondaryowner) = 
278     expand_alias (split (/\s*,\s*/, aliassub($mlowners{$ml})));
279
280   ($primaryapprover, @secondaryapprover) =
281     expand_alias (split (/\s*,\s*/, aliassub($mlapprovers{$ml})));
282
283   $mlconf{"primaryowner"} = $primaryowner;
284   $mlconf{"secondaryowner"} = \@secondaryowner;
285
286   $mlconf{"primaryapprover"} = $primaryapprover;
287   $mlconf{"secondaryapprover"} = \@secondaryapprover;
288
289   #
290   # debugging output
291   #
292
293   if (defined ($debug)) {
294     print "##################### $ml ####################\n";
295     for $skey (sort keys %mlconf) {
296       if (defined ($mlconf{$skey})) { print "$skey = $mlconf{$skey}\n"; }
297       else { print "$skey = (?)\n"; }
298     }
299     my $priv;
300     for $priv (@privileged) {
301       print "\t$ml: $priv\n";
302     }
303   }
304
305   #
306   # e)
307   # with the help of Mailman commands - create a new list and subscribe the old staff
308   #
309
310   if (defined($update)) {
311     print "updating configuration of \"$ml\"\n";
312   }
313   else {
314     # Mailman lists can initially be only created with one owner
315     @args = ("$mailmanbin/newlist", "-q", "-o", "$aliasout", "$ml", $mlconf{"primaryowner"}, $mlconf{"admin_passwd"});
316     system (@args) == 0 or die "system @args failed: $?";
317   }
318    
319   # Mailman accepts only subscriber lists > 0
320   if (@members > 0) {
321     $filename = tmpnam();
322     open (FH, "> $filename") or die "can't open $filename\n";
323     for $skey (@members) {
324       print FH "$skey" . "\n";
325     }
326     close (FH);
327     @args = ("$mailmanbin/add_members", "-n", "$filename", "--welcome-msg=n", "$ml");
328     system (@args) == 0 or die "system @args failed: $?";
329   }
330
331   #
332   # f)
333   # "translate" the Majordomo list configuration
334   #
335
336   m2m();
337
338   # write the Mailman config
339
340   $filename = tmpnam();
341
342   open (FH, "> $filename") or die "can't open $filename\n";
343   for $skey (sort keys %mmconf) {
344     print FH "$skey = " . $mmconf{$skey} . "\n";
345   }
346   close (FH);
347
348   @args = ("$mailmanbin/config_list", "-i", "$filename", "$ml");
349   system (@args) == 0 or die "system @args failed: $?";
350
351   unlink($filename) or print STDERR "unable to unlink \"$filename\"!\n";
352
353 }
354
355 exit 0;
356
357 #############
358 # subs
359 #############
360
361 #
362 # I don't know how to write Perl code
363 # therefor I need this stupid procedure to cleanly read a value from file
364 #
365
366 sub read_from_file {
367   my $value;
368   local *FH;
369
370   open (FH, "< $_[0]") or die "can't open $_[0]\n";
371   $value = <FH>;
372   chomp $value;
373   close (FH);
374
375   return $value;
376 }
377
378
379 #
380 # add "@$mydomain" to each element that does not contain a "@"
381 #
382
383 sub expand_alias {
384   return map  { (not $_ =~ /@/) ? $_ .= "\@$mydomain" : $_ } @_;
385 }
386
387 #
388 # replace the typical owner-majordomo aliases
389 #
390
391 sub aliassub {
392   my $string = $_[0];
393
394   $string =~ s/(owner-$majordomo|$majordomo-owner)/mailman-owner/gi;
395
396   return $string;
397 }
398
399 #
400 # default values of Majordomo mailing lists
401 # (stolen from majordomo::config_parse.pl: %known_keys)
402 #
403
404 sub init_defaultmlconf {
405   my $ml = $_[0];
406
407   %defaultmlconf=(  
408                      'welcome', "yes",
409                      'announcements',   "yes",
410                      'get_access',      "open",
411                      'index_access',    "open",
412                      'who_access',      "open",
413                      'which_access',    "open",
414                      'info_access',     "open",
415                      'intro_access',    "open",
416                      'advertise',       "",
417                      'noadvertise',     "",
418                      'description',     "",
419                      'subscribe_policy',        "open",
420                      'unsubscribe_policy',      "open",
421                      'mungedomain',     "no",
422                      'admin_passwd',    "$ml.admin",
423                      'strip',           "yes",
424                      'date_info',       "yes",
425                      'date_intro',      "yes",
426                      'archive_dir',     "",
427                      'moderate',        "no",
428                      'moderator',       "",
429                      'approve_passwd', "$ml.pass",
430                      'sender',  "owner-$ml",
431                      'maxlength',       "40000",
432                      'precedence',      "bulk",
433                      'reply_to',        "",
434                      'restrict_post',   "",
435                      'purge_received', "no",
436                      'administrivia',   "yes",
437                      'resend_host',     "",
438                      'debug',           "no",
439                      'message_fronter', "",
440                      'message_footer',  "",
441                      'message_headers', "",
442                      'subject_prefix',  "",
443                      'taboo_headers',   "",
444                      'taboo_body',      "",
445                      'digest_volume',   "1",
446                      'digest_issue',    "1",
447                      'digest_work_dir', "",
448                      'digest_name',     "$ml",
449                      'digest_archive',  "",
450                      'digest_rm_footer',    "",
451                      'digest_rm_fronter',   "",  
452                      'digest_maxlines', "",
453                      'digest_maxdays',  "",
454                      'comments',        ""
455                     );
456 }
457
458
459 #
460 # Mailman mailing list params that are not derived from Majordomo mailing lists params
461 # (e.g. bounce_matching_headers+forbbiden_posters vs. taboo_headers+taboo_body)
462 # If you need one of this params to be variable remove it here and add some code to the 
463 # main procedure; additionally, you should compare it with what you have in
464 # /usr/lib/mailman/Mailman/mm_cfg.py
465 #
466
467 sub init_defaultmmconf {
468
469   %defaultmmconf=(  
470                   'goodbye_msg', "\'\'",
471                   'umbrella_list', "0",
472                   'umbrella_member_suffix', "\'$umbrella_member_suffix\'",
473                   'send_reminders', "0",
474                   'admin_immed_notify', "1",
475                   'admin_notify_mchanges', "0",
476                   'dont_respond_to_post_requests', "0",
477                   'obscure_addresses', "1",
478                   'require_explicit_destination', "1",
479                   'acceptable_aliases', "\"\"\"\n\"\"\"\n",
480                   'max_num_recipients', "10",
481                   'forbidden_posters', "[]",
482                   'bounce_matching_headers',  "\"\"\"\n\"\"\"\n",
483                   'anonymous_list', "0",
484                   'nondigestable', "1",
485                   'digestable', "1",
486                   'digest_is_default', "0",
487                   'mime_is_default_digest', "0",
488                   'digest_size_threshhold', "40",
489                   'digest_send_periodic', "1",
490                   'digest_header', "\'\'",
491                   'bounce_processing', "1",
492                   'minimum_removal_date', "4",
493                   'minimum_post_count_before_bounce_action', "3",
494                   'max_posts_between_bounces', "5",
495                   'automatic_bounce_action', "3",
496                   'archive_private', "0",
497                   'clobber_date', "1",
498                   'archive_volume_frequency', "1",
499                   'autorespond_postings', "0",
500                   'autoresponse_postings_text', "\'\'",
501                   'autorespond_admin', "0",
502                   'autoresponse_admin_text', "\'\'",
503                   'autorespond_requests', "0",
504                   'autoresponse_request_text', "\'\'",
505                   'autoresponse_graceperiod', "90"
506                  );
507 }
508
509 #
510 # convert a Majordomo mailing list configuration (%mlconf) into a 
511 # Mailman mailing list configuration (%mmconf)
512 # only those params are affected which can be derived from Majordomo 
513 # mailing list configurations
514 #
515
516 sub m2m {
517
518   my $elem;
519   my $admin;
520
521   $mmconf{"real_name"} = "\'" . $mlconf{"name"} . "\'";
522
523   # Mailman does not know the difference between owner and approver
524   for $admin (($mlconf{"primaryowner"}, @{$mlconf{"secondaryowner"}},
525                $mlconf{"primaryapprover"}, @{$mlconf{"secondarapprover"}})) {
526     # merging owners and approvers may result in a loop:
527     if (lc($admin) ne lc("owner-" . $mlconf{"name"} . "\@" . $mydomain)) {
528       $mmconf{"owner"} .= ",\'" . "$admin" . "\'";
529     }
530   }
531   $mmconf{"owner"} =~ s/^,//g;
532   $mmconf{"owner"} = "\[" . $mmconf{"owner"} . "\]";
533
534   # remove characters that will break Python
535   ($mmconf{"description"} = $mlconf{"description"}) =~ s/\'/\\\'/g;
536   $mmconf{"description"} = "\'" . $mmconf{"description"} . "\'";
537
538   $mmconf{"info"} = "\"\"\"\n" . $mlconf{"info"} . "\"\"\"\n";
539
540   $mmconf{"subject_prefix"} = "\'" . $mlconf{"subject_prefix"} . "\'";
541
542   $mmconf{"welcome_msg"} = "\"\"\"\n" . $mlconf{"intro"} . "\"\"\"\n";
543
544   # I don't know how to handle this because the reply_to param in the lists
545   # I had were not configured consistently
546   if ($mlconf{"reply_to"} =~ /\S+/) {
547     if ($mlconf{"name"} . "\@" =~ m/$mlconf{"reply_to"}/i) {
548       $mmconf{"reply_goes_to_list"} = "1";
549       $mmconf{"reply_to_address"} = "\'\'";
550     }
551     else {
552       $mmconf{"reply_goes_to_list"} = "2";
553       $mmconf{"reply_to_address"} = "\'" . $mlconf{"reply_to"} . "\'";
554     }
555   }
556   else {
557     $mmconf{"reply_goes_to_list"} = "0";
558     $mmconf{"reply_to_address"} = "\'\'";
559   }
560
561   $mmconf{"administrivia"} = ($mlconf{"administrivia"} =~ m/yes/i) ? "1" : "0";
562   $mmconf{"send_welcome_msg"} = ($mlconf{"welcome"} =~ m/yes/i) ? "1" : "0";
563
564   $mmconf{"max_message_size"} = int ($mlconf{"maxlength"} / 1000);
565
566   $mmconf{"host_name"} = ($mlconf{"resend_host"} =~ /\S+/) ? 
567     $mlconf{"resend_host"} : "\'" . $mydomain . "\'";
568
569   $mmconf{"web_page_url"} = "\'" . $myurl . "\'";
570
571   # problematic since Mailman does not know access patterns
572   # I assume, that if there was given a noadvertise pattern, the
573   # list shouldn't be visible at all
574   $mmconf{"advertised"} = ($mlconf{"noadvertise"} =~ /\.\*/) ? "0" : "1";
575
576   # confirm+approval is much to long winded for private sites
577   $mmconf{"subscribe_policy"} = 
578     ($mlconf{"subscribe_policy"} =~ m/(open|auto)/i) ? "1" : 
579       ($private =~ m/yes/i) ? "2" : "3";
580
581   # in case this is a private site allow list visiblity at most
582   $mmconf{"private_roster"} =
583     ($mlconf{"who_access"} =~ m/open/i and not $private =~ m/yes/i) ? "0" :
584       ($mlconf{"who_access"} =~ m/open|list/i) ? "1" : "2";
585
586   $mmconf{"moderated"} = ($mlconf{"moderate"} =~ m/yes/i) ? "1" : "0";
587   # there is no way to a set a separate moderator in Mailman
588
589   # external, since lengthy
590   mm_posters();
591
592   if ($mlconf{"message_fronter"} =~ /\S+/) {
593     $mmconf{"msg_header"} = "\"\"\"\n" . $mlconf{"message_fronter"} . "\"\"\"\n";
594   }
595   else {
596     $mmconf{"msg_header"} = "\'\'";
597   }
598
599   if ($mlconf{"message_footer"} =~ /\S+/) {
600     $mmconf{"msg_footer"} = "\"\"\"\n" . $mlconf{"message_footer"} . "\"\"\"\n";
601   }
602   else {
603     $mmconf{"msg_footer"} = "\'\'";
604   }
605
606   # gateway to news
607   $mmconf{"nntp_host"} = "\'" . $newsserver . "\'";
608   $mmconf{"linked_newsgroup"} = "\'" . $newsprefix . $mlconf{"name"} . "\'";
609
610   if ($mlconf{"gated"} =~ m/yes/i) {
611     $mmconf{"gateway_to_news"} = "1";
612     $mmconf{"gateway_to_mail"} = "1";
613     $mmconf{"archive"} = "1";
614   }
615   else {
616     $mmconf{"gateway_to_news"} = "0";
617     $mmconf{"gateway_to_mail"} = "0";
618     $mmconf{"archive"} = "0";
619   }
620
621   # print warnings if this seems to be an umbrella list
622   for $elem (@{$mlconf{"privileged"}}, @{$mlconf{"members"}}) {
623     $elem =~ s/\@$mydomain//gi;
624     if (defined($mlaliases{$elem . $umbrella_member_suffix})) {
625       print STDERR "\"" . $mlconf{"name"} .
626          "\" possibly forms part off/is an umbrella list, since \"$elem\" is a local mailing list alias\n";   
627     }
628   }
629
630   # print warnings if we encountered a Taboo-Header or Taboo-Body
631   if ($mlconf{"taboo_headers"} =~ /\S+/ or $mlconf{"taboo_body"} =~ /\S+/) {
632     print STDERR "\"" . $mlconf{"name"} . "\" taboo_headers or taboo_body seem to be set - please check manually.\n";
633   }
634 }
635
636 #
637 # with some set theory on the member and priviliged list try to determine the params
638 # $mmconf{"member_posting_only"} and $mmconf{"posters"}
639 #
640
641 sub mm_posters {
642   if ($mlconf{"restrict_post"} =~ /\S+/) {
643     my %privileged = ();
644     my %members = ();
645     my $key;
646
647     foreach $key (@{$mlconf{"privileged"}}) { $privileged{$key} = "OK"; }
648     foreach $key (@{$mlconf{"members"}}) { $members{$key} = "OK"; }
649
650     # are all members privileged, too ?
651     my $included = 1;
652     foreach $key (keys %members) {
653       if (not exists $privileged{$key}) {
654         $included = 0;
655         last;
656       }
657     }
658     if ($included) {
659       $mmconf{"member_posting_only"} = "1";
660
661       # posters = privileged - members:
662       my %diff = %privileged;
663       foreach $key (keys %members) {
664         delete $diff{$key} if exists $members{$key};
665       }
666
667       $mmconf{"posters"} = "";
668       for $key (sort keys %diff) {
669         $mmconf{"posters"} .= ",\'" . $key . "\'";
670       }
671       $mmconf{"posters"} =~ s/^,//g;
672       $mmconf{"posters"} = "[" . $mmconf{"posters"} . "]";
673     }
674     else {
675       $mmconf{"member_posting_only"} = "0";
676
677       # posters = privileged:
678       $mmconf{"posters"} = "";
679       for $key (sort keys %privileged) {
680         $mmconf{"posters"} .= ",\'" . $key . "\'";
681       }
682       $mmconf{"posters"} =~ s/^,//g;
683       $mmconf{"posters"} = "[" . $mmconf{"posters"} . "]";
684     }
685   }
686   else {
687     $mmconf{"member_posting_only"} = "0";
688     $mmconf{"posters"} = "[]";
689   }
690 }
691