diff -uNr ircservices-5.0.22/Changes ircservices-5.0.23/Changes --- ircservices-5.0.22/Changes 2003-09-16 04:13:50 +0900 +++ ircservices-5.0.23/Changes 2003-11-03 18:06:18 +0900 @@ -1,5 +1,25 @@ Version 5.0 ----------- +2003/11/01 .23 Fixed bug causing database contents to get lost for small + databases on full filesystems. Reported by + +2003/10/29 Fixed minor bugs in convert-db, and added checks to the + data before it is output. (As a result, convert-db + will take somewhat longer to process the same data.) +2003/10/27 Added support to convert-db for converting Cygnus databases. + Suggested by Jamie Penman-Smithson +2003/10/27 Modified default channel level definitions to use ACCLEV_* + constants from chanserv.h. If you change the constants + (not recommended for compatibility reasons), the + default channel levels will now change with them. +2003/10/24 Added note to ListenTo directive documentation concerning + the usage of low port numbers. +2003/10/18 Added FAQ F.10 regarding the ImmediatelySendAutokill option + to help people who have trouble reading documentation. +2003/10/17 Fixed a cosmetic bug when giving forbidden nicknames to the + AUTH-related commands. Reported by +2003/10/14 Fixed a typo in the ChanServ SET help text. Reported by + playa 2003/09/09 .22 The SHOWALLOCS compilation option can now be set from the configure script instead of editing memory.c directly. 2003/09/09 Fixed bug causing blank lines in help messages to not be diff -uNr ircservices-5.0.22/TODO ircservices-5.0.23/TODO --- ircservices-5.0.22/TODO 2003-09-01 00:13:25 +0900 +++ ircservices-5.0.23/TODO 2003-10-27 12:58:42 +0900 @@ -1,5 +1,7 @@ Things to probably do: +CS Add a modules.conf option to set the default channel mlock + [] NS Flags for LIST[EMAIL] to match against only nick or only usermask/email CS When moving to a new ircd, graceful handling for modelocked modes not available in the new ircd @@ -47,6 +49,10 @@ Things to think about: +NS Require E-mail address regged with nick for SENDPASS + [playa ] +NS Replace "may not be registered or used" with "is forbidden" in + messages to servopers/admins for forbidden nicks? OS Notify autokilled users of autokill expiration upon kill OS Make news memo-like: separate command to read, and notice of new news at logon diff -uNr ircservices-5.0.22/configure ircservices-5.0.23/configure --- ircservices-5.0.22/configure 2003-09-09 23:30:19 +0900 +++ ircservices-5.0.23/configure 2003-10-20 15:53:10 +0900 @@ -1908,6 +1908,13 @@ echo2 "strtok " fi + # stricmp() (STRing case-Insensitive CoMPare) is another name, used + # by at least the Amiga DICE C compiler, for what POSIX calls + # strcasecmp(). I prefer the former because the latter (1) is + # unnecessarily long and (2) implies a case-sensitive compare when + # it's really a case-INsensitive compare. If this system doesn't + # have stricmp() but does have strcasecmp(), we use a #define in + # defs.h to rename the latter to the former. MODE="check_stricmp " TEST='extern int strnicmp(const char *, const char *, int); return stricmp("ABC","abc")==0 && strnicmp("ABC","abd",2)==0 ? 0 : 1;' if test_function int stricmp "(const char *, const char *)" ; then diff -uNr ircservices-5.0.22/data/example-modules.conf ircservices-5.0.23/data/example-modules.conf --- ircservices-5.0.22/data/example-modules.conf 2003-09-16 04:17:47 +0900 +++ ircservices-5.0.23/data/example-modules.conf 2003-11-03 18:10:35 +0900 @@ -1120,6 +1120,10 @@ # server on the local network--and is susceptible to network or # DNS server outages, so IP addresses or "*" should be used # whenever possible. + # + # Note that many systems restrict low port numbers to the system + # administrator; in particular, Unix-like systems allow only the + # root user (UID 0) to use ports less than 1024. #ListenTo 127.0.0.1:12701 #ListenTo services.example.net:8080 diff -uNr ircservices-5.0.22/docs/5.html ircservices-5.0.23/docs/5.html --- ircservices-5.0.22/docs/5.html 2003-04-30 11:46:49 +0900 +++ ircservices-5.0.23/docs/5.html 2003-10-27 12:34:21 +0900 @@ -179,6 +179,10 @@ Bolivia IRC Services [ircservices.bolivia-internet.com] 1.2.0 +cygnus + Cygnus + [www.habber.net] + 0.2.0 daylight Daylight 12 @@ -286,6 +290,57 @@
  • E-mailing of log files is not supported. +

    Cygnus +

      +
    • Services does not store the UIN, real name, age, sex, or location for + nicknames; this information will be lost when the databases are + converted. +
    • Services does not send replies using PRIVMSG; the + NOTICE and PRIVMSG nickname settings will be + ignored. +
    • The following options/features are not supported: +
        +
      • NickServ NEVEROP, NOOP, and NOSUCCESSOR +
      • ChanServ LIMITED +
      • MemoServ RECEIPTS +
      • RootServ MARK +
      +
    • For nicknames awaiting authentication, the authentication code ("key" + in Cygnus terms) may be changed to conform to Services' + specifications. Note that Services uses the + AUTH command, not the + REGISTER command, to authenticate nicknames. +
    • Nicknames designated as CSOPs will be converted to Services operators. + Note that the privileges granted by IRC Services to Services + operators are different than those granted by Cygnus to CSOPs. +
    • Nicknames will retain their memo limit settings in the converted data; + however, they will not be affected by changes to the + MSMaxMemos + setting. +
    • If you have modified the timezone list used by Cygnus + (cygnus.zone), nickname time zones will not be transferred + correctly. +
    • Authentication for channels is not supported; authentication codes and + "verify" information will be discarded. +
    • Channel access list entries in IRC Services must always be registered + nicknames; user@host entries, and any + entries not matching a registered nickname, will be deleted. +
    • While the channel topic-lock setting will be retained, the lock level + (founder/SOP/AOP/HOP/VOP) will be reset to the IRC Services default + (AOP). If you use the LEVELS command (in the + access-levels ChanServ submodule), you can change the + level of the TOPIC command to set which users are + allowed to change the topic. +
    • The ChanServ VOPALL setting is not preserved; however, the + LEVELS command can be used to set the AUTOVOICE level to + 0, causing all users to be voiced on entering the channel. +
    • Channel memos are stored with the channel, and are not sent to + individual users on the channel. Anyone can send a memo to a + channel; only users with SOP (by default) access can read or delete + the memos. +
    +

    Daylight

    • Services does not support the XMANAGEMENT channel setting. diff -uNr ircservices-5.0.22/docs/6.html ircservices-5.0.23/docs/6.html --- ircservices-5.0.22/docs/6.html 2003-08-30 03:17:00 +0900 +++ ircservices-5.0.23/docs/6.html 2003-10-20 16:20:42 +0900 @@ -1666,7 +1666,7 @@
      i = j * 2;
      user->channelcount++; -
      result += (i+59)*60 + j/3600; +
      result += i*60 + (j+59)/60;

    • Always use NULL, not 0, in pointer comparisons. For character comparisons, 0 is preferred to @@ -1693,13 +1693,14 @@ if (flag)
          function();
      -

    • Spaces are placed after the comma of each parameter to a - function; however, they may be omitted in function calls which are - themselves parameters to a function, or when including them would - make the line exceed 79 columns in length. Spaces are also placed - after the semicolons in a for statement. Spaces are not - placed after the opening parenthesis or before the closing - parenthesis of a function call or control statement. Examples: +

    • Spaces are placed after the comma of each parameter to a function or + macro; however, they may be omitted in function calls which are + themselves parameters to a function or macro, or when including + them would make the line exceed 79 columns in length. Spaces are + also placed after (but not before) the semicolons in a for + statement. Spaces are not placed after the opening parenthesis or + before the closing parenthesis of a function/macro call or control + statement. Examples:
      function(param1, param2);
      function(param1, strchr(string,'/'), param3); @@ -1747,6 +1748,8 @@
       || !init_third()
       || !init_fourth()
      ) { +
          /* This example outdents the || by three spaces +
           * to make the terms line up. */
          . . .
      }

      @@ -1774,7 +1777,8 @@
          . . .
      } else {
          if (user->ni != NULL) { -
              /* "if" condition is different from "else" condition, thus separate */ +
              /* "if" condition is different from "else" +
               * condition, thus separate */
              
      . . .
          }
      }
      @@ -1795,31 +1799,57 @@
      while (!done) {
          /* Braces required because of the nested "if" */
          if (do_stuff()) -
              quit = 1; +
              done = 1;
      }


      if (state == 0) {
          a = b;
      } else if (state == 1) { -
          /* Every if/else body gets braces - because this body has two statements */ +
          /* Every if/else body gets braces because this body +
           * has two statements */
          b += a;
          a = 0;
      } else {
          state = 0;
      }
      +

    • When using multiply-nested control statements, or a single control + statement which has a long block, the closing brace of the block + should be followed by a comment indicating what control statement + the block belongs to. This should usually be the content of the + control statement, but may be abbreviated as long as its meaning is + clear. If a long if block is followed by an else + clause, the else should likewise be documented. Examples: +
      + while (i < count) { +
          
      . . . +
      }  /* while (i < count) */
      +
      +
      if (flag) { +
          
      . . . +
      } else {  /* !flag */ +
          
      . . . +
      }  /* if (flag) */
      +
      +
      for (node = first; node != NULL; node = node->next) { +
          
      . . . +
      }  /* for all nodes */
      +

    • Case labels for a switch should be indented half of a normal indentation unit (two columns) from the line containing the switch with which they are associated; statements associated with a case should be indented a full unit from the line containing the switch (half a unit from the case). - Example: + If a case requires its own block, such as when it declares its own + local variables, the opening brace is placed after the colon on the + case line. Example:
      switch (variable) { -
        case 123: +
        case 123: { +
          int foo;
          
      . . .
          break; +
        }  /* case 123 */
        default:
          
      . . .
          return -1; diff -uNr ircservices-5.0.22/docs/a.html ircservices-5.0.23/docs/a.html --- ircservices-5.0.22/docs/a.html 2003-08-30 05:27:20 +0900 +++ ircservices-5.0.23/docs/a.html 2003-10-24 12:42:17 +0900 @@ -1959,6 +1959,10 @@ DNS server outages, so IP addresses or "*" should be used whenever possible. +

      Note that many systems restrict low port numbers to the system +administrator; in particular, Unix-like systems allow only the +root user (UID 0) to use ports less than 1024. +

      Example: ListenTo 127.0.0.1:12701
      Example: ListenTo services.example.net:8080
      Example: ListenTo *:80 diff -uNr ircservices-5.0.22/docs/faq.html ircservices-5.0.23/docs/faq.html --- ircservices-5.0.22/docs/faq.html 2003-08-31 20:29:04 +0900 +++ ircservices-5.0.23/docs/faq.html 2003-10-18 02:22:46 +0900 @@ -138,6 +138,8 @@ command, Services cancels the G:line.
      F.9. Services doesn't add AKILLs/G:lines for users matching autokill masks. +
      F.10. Services doesn't kill users matching a newly-added + autokill mask even if ImmediatelySendAutokill is set.

      Z. Bug reporting and feature requests

      Z.1. Services doesn't speak my language! @@ -897,6 +899,21 @@ 3-4-3 for details. + +

      F.10. Services doesn't kill users matching a newly-added + autokill mask even if ImmediatelySendAutokill is set. +

      + Services never kills users when a new autokill is added; the + ImmediatelySendAutokill + configuration directive only causes Services to send the autokill + itself (that is, the user/host mask to prohibit new connections + from) to the IRC servers on your network. This is a safety feature + intended to limit the damage caused by a mistyped autokill. +

      + Note that some IRC servers will themselves kill users matching a + newly-added autokill; this is unrelated to Services. +

      +


      diff -uNr ircservices-5.0.22/lang/de.l ircservices-5.0.23/lang/de.l --- ircservices-5.0.22/lang/de.l 2003-09-10 23:04:12 +0900 +++ ircservices-5.0.23/lang/de.l 2003-10-14 11:50:28 +0900 @@ -3326,7 +3326,7 @@ benutzt werden ENFORCE Zwingt die Autoop und Autovoice status - /msg %S HELP Option fr weitere Informationen zu + /msg %S HELP SET Option fr weitere Informationen zu einer Option. CHAN_HELP_SET_FOUNDER diff -uNr ircservices-5.0.22/lang/en_us.l ircservices-5.0.23/lang/en_us.l --- ircservices-5.0.22/lang/en_us.l 2003-09-09 22:56:04 +0900 +++ ircservices-5.0.23/lang/en_us.l 2003-10-14 11:54:24 +0900 @@ -3428,8 +3428,8 @@ OPNOTICE Send a notice when OP/VOICE commands are used ENFORCE Enforce auto-op, auto-voice status - Type /msg %S HELP option for more information on a - particular option. + Type /msg %S HELP SET option for more information + on a particular option. CHAN_HELP_SET_FOUNDER Syntax: SET channel FOUNDER nickname @@ -5042,4 +5042,4 @@ # version of the master (English) language file was used to create a # translated file. -# CVS: $Revision: 2.213 $ +# CVS: $Revision: 2.214 $ diff -uNr ircservices-5.0.22/lang/es.l ircservices-5.0.23/lang/es.l --- ircservices-5.0.22/lang/es.l 2003-09-16 04:12:51 +0900 +++ ircservices-5.0.23/lang/es.l 2003-10-14 11:51:06 +0900 @@ -3444,8 +3444,8 @@ OPNOTICE Notifca cuando los comandos OP/VOICE son usados ENFORCE Impone estatus auto-op y auto-voice de immediato - Escriba /msg %S HELP Opcin para ms informacin sobre una - opcin en particular. + Escriba /msg %S HELP SET Opcin para ms informacin + sobre una opcin en particular. CHAN_HELP_SET_FOUNDER Sintaxis: SET Canal FOUNDER Apodo diff -uNr ircservices-5.0.22/lang/fr.l ircservices-5.0.23/lang/fr.l --- ircservices-5.0.22/lang/fr.l 2003-09-10 23:04:01 +0900 +++ ircservices-5.0.23/lang/fr.l 2003-10-14 11:51:24 +0900 @@ -3478,8 +3478,8 @@ commandes OP/VOICE sont utilises ENFORCE Rgulation des status auto-op et auto-voice - Tapez /msg %S HELP option pour plus d'information sur une - option particulire. + Tapez /msg %S HELP SET option pour plus d'information + sur une option particulire. CHAN_HELP_SET_FOUNDER Syntaxe: SET canal FOUNDER surnom diff -uNr ircservices-5.0.22/lang/hu.l ircservices-5.0.23/lang/hu.l --- ircservices-5.0.22/lang/hu.l 2003-09-10 23:02:21 +0900 +++ ircservices-5.0.23/lang/hu.l 2003-10-14 11:51:28 +0900 @@ -3392,7 +3392,7 @@ ENFORCE Megvdi az auto-op, auto-voice sttuszt (nincs deop,devoice) Tovbbi informcirt egy adott opcirl rd be: - /msg %S HELP opci. + /msg %S HELP SET opci. CHAN_HELP_SET_FOUNDER Syntax: SET csatorna FOUNDER nick diff -uNr ircservices-5.0.22/lang/it.l ircservices-5.0.23/lang/it.l --- ircservices-5.0.22/lang/it.l 2003-08-31 23:31:36 +0900 +++ ircservices-5.0.23/lang/it.l 2003-10-14 11:51:36 +0900 @@ -1819,7 +1819,7 @@ # LEAVEOPS Non deoppare mai gli user # OPNOTICE Manda un notice agli OP # -# Scrivi /msg %S HELP opzione per maggiori informazioni. +# Scrivi /msg %S HELP SET opzione per maggiori informazioni. CHAN_HELP_SET_FOUNDER Sintassi: SET canale FOUNDER nick diff -uNr ircservices-5.0.22/lang/ja_euc.l ircservices-5.0.23/lang/ja_euc.l --- ircservices-5.0.22/lang/ja_euc.l 2003-09-09 22:55:40 +0900 +++ ircservices-5.0.23/lang/ja_euc.l 2003-10-14 11:52:53 +0900 @@ -3257,6 +3257,9 @@ OPNOTICE OP/VOICEΥޥɻѤͥ˼Τ ENFORCE ưڥ졼ưȯѹ + ƥץξܺ٤ˤĤƤϡ/msg %S HELP SET + ޥ̾פǥإ׾ɽǤޤ + ΥޥɤλѤϥǥեȤǥͥ߼Ԥ˸¤ ꡢѤ%SIDENTIFYޥɤǥͥǧڤ ԤʤФʤޤ diff -uNr ircservices-5.0.22/lang/ja_sjis.l ircservices-5.0.23/lang/ja_sjis.l --- ircservices-5.0.22/lang/ja_sjis.l 2003-09-09 23:06:06 +0900 +++ ircservices-5.0.23/lang/ja_sjis.l 2003-10-17 16:05:44 +0900 @@ -3257,6 +3257,9 @@ OPNOTICE OP/VOICẼR}hgp`lɎm ENFORCE Iy[^EǗݒύX + eIvV̏ڍׂɂ‚ẮAu/msg %S HELP SET + R}hvŃwv\ł܂B + ̃R}h̎gp̓ftHgŃ`l̑nݎ҂Ɍ Agp̑O%SIDENTIFYR}hŃ`lF؂ sȂ΂Ȃ܂B diff -uNr ircservices-5.0.22/lang/nl.l ircservices-5.0.23/lang/nl.l --- ircservices-5.0.22/lang/nl.l 2003-09-10 07:47:52 +0900 +++ ircservices-5.0.23/lang/nl.l 2003-10-14 11:53:34 +0900 @@ -3505,8 +3505,8 @@ commando's gebruikt worden ENFORCE Behoudt auto-op en auto-voice in de kamer. - Typ /msg %S HELP voor meer informatie over een - bepaald commando. + Typ /msg %S HELP SET optie voor meer informatie over + een bepaald commando. CHAN_HELP_SET_FOUNDER Syntax: SET kamer FOUNDER bijnaam diff -uNr ircservices-5.0.22/lang/tr.l ircservices-5.0.23/lang/tr.l --- ircservices-5.0.22/lang/tr.l 2003-09-12 11:21:37 +0900 +++ ircservices-5.0.23/lang/tr.l 2003-10-14 11:53:44 +0900 @@ -3306,8 +3306,8 @@ OPNOTICE OP/VOICE kullanildiginda bir notice gnderir. ENFORCE Autoop/Autovoice seviyelerini koruma (enforce) - /msg %S HELP secenek yazarak belirli bir secenek hakkinda - daha ayrintili bilgi alabilirsiniz. + /msg %S HELP SET secenek yazarak belirli bir secenek + hakkinda daha ayrintili bilgi alabilirsiniz. CHAN_HELP_SET_FOUNDER Kullanimi: SET kanal FOUNDER nick diff -uNr ircservices-5.0.22/modules/chanserv/access.c ircservices-5.0.23/modules/chanserv/access.c --- ircservices-5.0.22/modules/chanserv/access.c 2003-09-16 04:17:48 +0900 +++ ircservices-5.0.23/modules/chanserv/access.c 2003-11-03 18:10:36 +0900 @@ -29,51 +29,51 @@ /* Dummy entry for channel owner; flags filled in by init_access */ { CA_AUTOOWNER, ACCLEV_FOUNDER, "", -1, CL_SET_MODE, { cumode: {"", 1} } }, /* FIXME: `field:' gcc-ism */ - { CA_AUTOPROTECT, 100, "AUTOPROTECT", CHAN_LEVEL_AUTOPROTECT, + { CA_AUTOPROTECT, ACCLEV_SOP, "AUTOPROTECT", CHAN_LEVEL_AUTOPROTECT, CL_SET_MODE, { cumode: {"a",0} } }, - { CA_AUTOOP, 50, "AUTOOP", CHAN_LEVEL_AUTOOP, + { CA_AUTOOP, ACCLEV_AOP, "AUTOOP", CHAN_LEVEL_AUTOOP, CL_SET_MODE, { cumode: {"o",1} } }, - { CA_AUTOHALFOP, 40, "AUTOHALFOP", CHAN_LEVEL_AUTOHALFOP, + { CA_AUTOHALFOP, ACCLEV_HOP, "AUTOHALFOP", CHAN_LEVEL_AUTOHALFOP, CL_SET_MODE, { cumode: {"h",1} } }, - { CA_AUTOVOICE, 30, "AUTOVOICE", CHAN_LEVEL_AUTOVOICE, + { CA_AUTOVOICE, ACCLEV_VOP, "AUTOVOICE", CHAN_LEVEL_AUTOVOICE, CL_SET_MODE, { cumode: {"v",0} } }, /* Internal use; not settable by users. These two levels change based * on the SECUREOPS and RESTRICTED settings. */ - { CA_AUTODEOP, -1, "", -1, + { CA_AUTODEOP, -1, "", -1, CL_CLEAR_MODE|CL_LESSEQUAL, { cumode: {"oh",0} } }, - { CA_NOJOIN, -100, "", -1, + { CA_NOJOIN, -100, "", -1, CL_OTHER|CL_LESSEQUAL }, - { CA_INVITE, 50, "INVITE", CHAN_LEVEL_INVITE, + { CA_INVITE, ACCLEV_AOP, "INVITE", CHAN_LEVEL_INVITE, CL_ALLOW_CMD, { {"INVITE"} } }, - { CA_AKICK, 100, "AKICK", CHAN_LEVEL_AKICK, + { CA_AKICK, ACCLEV_SOP, "AKICK", CHAN_LEVEL_AKICK, CL_ALLOW_CMD, { {"AKICK"} } }, - { CA_SET, ACCLEV_FOUNDER, "SET", CHAN_LEVEL_SET, + { CA_SET, ACCLEV_FOUNDER, "SET", CHAN_LEVEL_SET, CL_ALLOW_CMD, { {"SET"} } }, - { CA_CLEAR, 100, "CLEAR", CHAN_LEVEL_CLEAR, + { CA_CLEAR, ACCLEV_SOP, "CLEAR", CHAN_LEVEL_CLEAR, CL_ALLOW_CMD, { {"CLEAR"} } }, - { CA_UNBAN, 50, "UNBAN", CHAN_LEVEL_UNBAN, + { CA_UNBAN, ACCLEV_AOP, "UNBAN", CHAN_LEVEL_UNBAN, CL_ALLOW_CMD, { {"UNBAN"} } }, - { CA_ACCESS_LIST, 0, "ACC-LIST", CHAN_LEVEL_ACCESS_LIST, + { CA_ACCESS_LIST, 0, "ACC-LIST", CHAN_LEVEL_ACCESS_LIST, CL_ALLOW_CMD, { {"ACCESS","LIST"} } }, - { CA_ACCESS_CHANGE, 40, "ACC-CHANGE", CHAN_LEVEL_ACCESS_CHANGE, + { CA_ACCESS_CHANGE, ACCLEV_HOP, "ACC-CHANGE", CHAN_LEVEL_ACCESS_CHANGE, CL_ALLOW_CMD, { {"ACCESS"} } }, - { CA_MEMO, 100, "MEMO", CHAN_LEVEL_MEMO, + { CA_MEMO, ACCLEV_SOP, "MEMO", CHAN_LEVEL_MEMO, CL_OTHER }, - { CA_OPDEOP, 50, "OP-DEOP", CHAN_LEVEL_OPDEOP, + { CA_OPDEOP, ACCLEV_AOP, "OP-DEOP", CHAN_LEVEL_OPDEOP, CL_ALLOW_CMD, { {"OP"} } }, /* also includes "DEOP" */ - { CA_VOICE, 30, "VOICE", CHAN_LEVEL_VOICE, + { CA_VOICE, ACCLEV_VOP, "VOICE", CHAN_LEVEL_VOICE, CL_ALLOW_CMD, { {"VOICE"} } }, - { CA_HALFOP, 40, "HALFOP", CHAN_LEVEL_HALFOP, + { CA_HALFOP, ACCLEV_HOP, "HALFOP", CHAN_LEVEL_HALFOP, CL_ALLOW_CMD, { {"HALFOP"} } }, - { CA_PROTECT, 100, "PROTECT", CHAN_LEVEL_PROTECT, + { CA_PROTECT, ACCLEV_SOP, "PROTECT", CHAN_LEVEL_PROTECT, CL_ALLOW_CMD, { {"PROTECT"} } }, - { CA_KICK, 50, "KICK", CHAN_LEVEL_KICK, + { CA_KICK, ACCLEV_AOP, "KICK", CHAN_LEVEL_KICK, CL_ALLOW_CMD, { {"KICK"} } }, - { CA_TOPIC, 50, "TOPIC", CHAN_LEVEL_TOPIC, + { CA_TOPIC, ACCLEV_AOP, "TOPIC", CHAN_LEVEL_TOPIC, CL_ALLOW_CMD, { {"TOPIC"} } }, - { CA_STATUS, 100, "STATUS", CHAN_LEVEL_STATUS, + { CA_STATUS, ACCLEV_SOP, "STATUS", CHAN_LEVEL_STATUS, CL_ALLOW_CMD, { {"STATUS"} } }, { -1 } diff -uNr ircservices-5.0.22/modules/chanserv/chanserv.h ircservices-5.0.23/modules/chanserv/chanserv.h --- ircservices-5.0.22/modules/chanserv/chanserv.h 2003-09-16 04:17:49 +0900 +++ ircservices-5.0.23/modules/chanserv/chanserv.h 2003-11-03 18:10:36 +0900 @@ -34,8 +34,8 @@ #define ACCLEV_FOUNDER 1000 /* Numeric level indicating founder access */ #define ACCLEV_INVALID -1000 /* Used in levels[] for disabled settings */ -/* Access levels used to represent AOP's, SOP's and VOP's in channel access - * lists. */ +/* Access levels used to represent SOPs, AOPs, HOPs and VOPs in channel + * access lists. */ #define ACCLEV_SOP 100 #define ACCLEV_AOP 50 diff -uNr ircservices-5.0.22/modules/database/fileutil.c ircservices-5.0.23/modules/database/fileutil.c --- ircservices-5.0.22/modules/database/fileutil.c 2003-09-16 04:17:49 +0900 +++ ircservices-5.0.23/modules/database/fileutil.c 2003-11-03 18:10:36 +0900 @@ -185,7 +185,8 @@ void restore_db(dbFILE *f) { int errno_orig = errno; - fclose(f->fp); + if (f->fp) + fclose(f->fp); if (f->mode == 'w' && *f->tempname) remove(f->tempname); free(f); @@ -201,7 +202,15 @@ int close_db(dbFILE *f) { - fclose(f->fp); + int res; + if (!f->fp) { + errno = EINVAL; + return -1; + } + res = fclose(f->fp); + f->fp = NULL; + if (res != 0) + return -1; if (f->mode=='w' && *f->tempname && strcmp(f->tempname,f->filename)!=0) { if (rename(f->tempname, f->filename) < 0) { #ifndef CONVERT_DB diff -uNr ircservices-5.0.22/modules/database/version4.c ircservices-5.0.23/modules/database/version4.c --- ircservices-5.0.22/modules/database/version4.c 2003-09-16 04:17:49 +0900 +++ ircservices-5.0.23/modules/database/version4.c 2003-11-03 18:10:36 +0900 @@ -726,7 +726,7 @@ } SAFE(write_int8(0, f)); - close_db(f); + SAFE(close_db(f)); sync_operserv_db(NULL); /* Write out new admin/oper lists */ return 0; @@ -1324,7 +1324,7 @@ } SAFE(write_int8(0, f)); - close_db(f); + SAFE(close_db(f)); return 0; fail: @@ -1503,7 +1503,7 @@ if (!no_supass) SAFE(write_buffer(supass, f)); - close_db(f); + SAFE(close_db(f)); return 0; fail: @@ -1676,7 +1676,7 @@ SAFE(write_int32(newslist[i].time, f)); } - close_db(f); + SAFE(close_db(f)); return 0; fail: @@ -1955,6 +1955,7 @@ int sync_akill_db(const char *dbname) { dbFILE *f; + static time_t lastwarn = 0; if (!(f = open_db(dbname, "w", 11))) return 0; @@ -1963,12 +1964,16 @@ SAFE(write_int8(1, f)); if (!write_maskdata(MD_EXCLUSION, dbname, f)) return 0; - close_db(f); + SAFE(close_db(f)); return 0; fail: restore_db(f); - module_log("Read error on %s", f->filename); + module_log_perror("Write error on %s", dbname); + if (time(NULL) - lastwarn > WarningTimeout) { + wallops(NULL, "Write error on %s: %s", dbname, strerror(errno)); + lastwarn = time(NULL); + } return 0; } @@ -2083,12 +2088,22 @@ int sync_exception_db(const char *dbname) { dbFILE *f; + static time_t lastwarn = 0; if (!(f = open_db(dbname, "w", 11))) return 0; if (!write_maskdata(MD_EXCEPTION, dbname, f)) return 0; - close_db(f); + SAFE(close_db(f)); + return 0; + + fail: + restore_db(f); + module_log_perror("Write error on %s", dbname); + if (time(NULL) - lastwarn > WarningTimeout) { + wallops(NULL, "Write error on %s: %s", dbname, strerror(errno)); + lastwarn = time(NULL); + } return 0; } @@ -2127,6 +2142,7 @@ int sync_sline_db(const char *dbname) { dbFILE *f; + static time_t lastwarn = 0; if (!(f = open_db(dbname, "w", 11))) return 0; @@ -2136,7 +2152,16 @@ return 0; if (!write_maskdata(MD_SZLINE, dbname, f)) return 0; - close_db(f); + SAFE(close_db(f)); + return 0; + + fail: + restore_db(f); + module_log_perror("Write error on %s", dbname); + if (time(NULL) - lastwarn > WarningTimeout) { + wallops(NULL, "Write error on %s: %s", dbname, strerror(errno)); + lastwarn = time(NULL); + } return 0; } @@ -2341,7 +2366,7 @@ SAFE(write_time(ss->t_join, f)); } - close_db(f); + SAFE(close_db(f)); return 0; fail: diff -uNr ircservices-5.0.22/modules/httpd/auth-password.c ircservices-5.0.23/modules/httpd/auth-password.c --- ircservices-5.0.22/modules/httpd/auth-password.c 2003-09-16 04:17:49 +0900 +++ ircservices-5.0.23/modules/httpd/auth-password.c 2003-11-03 18:10:36 +0900 @@ -54,7 +54,7 @@ /* Check and make sure they're actually there. */ if (s) { /* Skip past any extra whitespace... */ - while (s && (*s == ' ' || *s == '\t')) + while (*s == ' ' || *s == '\t') s++; /* ... then compare against the configured username/password. */ if (strcmp(s, protected[i].userpass) == 0) { diff -uNr ircservices-5.0.22/modules/nickserv/mail-auth.c ircservices-5.0.23/modules/nickserv/mail-auth.c --- ircservices-5.0.22/modules/nickserv/mail-auth.c 2003-09-16 04:17:49 +0900 +++ ircservices-5.0.23/modules/nickserv/mail-auth.c 2003-11-03 18:10:36 +0900 @@ -162,6 +162,8 @@ notice_lang(s_NickServ, u, NICK_AUTH_DISABLED); } else if (!(ni = u->ni)) { notice_lang(s_NickServ, u, NICK_NOT_REGISTERED); + } else if (ni->status & NS_VERBOTEN) { + notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, u->nick); } else if (!(ngi = u->ngi) || ngi == NICKGROUPINFO_INVALID) { notice_lang(s_NickServ, u, INTERNAL_ERROR); } else if (!ngi->authcode) { @@ -234,6 +236,8 @@ syntax_error(s_NickServ, u, "SENDAUTH", NICK_SENDAUTH_SYNTAX); } else if (!(ni = u->ni)) { notice_lang(s_NickServ, u, NICK_NOT_REGISTERED); + } else if (ni->status & NS_VERBOTEN) { + notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, u->nick); } else if (!(ngi = u->ngi) || ngi == NICKGROUPINFO_INVALID) { notice_lang(s_NickServ, u, INTERNAL_ERROR); } else if (!ngi->authcode) { @@ -275,6 +279,8 @@ syntax_error(s_NickServ, u, "SETAUTH", NICK_SETAUTH_SYNTAX); } else if (!(ni = get_nickinfo(nick))) { notice_lang(s_NickServ, u, NICK_X_NOT_REGISTERED, nick); + } else if (ni->status & NS_VERBOTEN) { + notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, nick); } else if (!(ngi = get_ngi(ni))) { notice_lang(s_NickServ, u, INTERNAL_ERROR); } else if (ngi->authcode) { @@ -329,6 +335,8 @@ syntax_error(s_NickServ, u, "GETAUTH", NICK_GETAUTH_SYNTAX); } else if (!(ni = get_nickinfo(nick))) { notice_lang(s_NickServ, u, NICK_X_NOT_REGISTERED, nick); + } else if (ni->status & NS_VERBOTEN) { + notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, nick); } else if (!(ngi = get_ngi(ni))) { notice_lang(s_NickServ, u, INTERNAL_ERROR); } else if (!ngi->authcode) { @@ -353,6 +361,8 @@ syntax_error(s_NickServ, u, "CLEARAUTH", NICK_CLEARAUTH_SYNTAX); } else if (!(ni = get_nickinfo(nick))) { notice_lang(s_NickServ, u, NICK_X_NOT_REGISTERED, nick); + } else if (ni->status & NS_VERBOTEN) { + notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, nick); } else if (!(ngi = get_ngi(ni))) { notice_lang(s_NickServ, u, INTERNAL_ERROR); } else if (!ngi->authcode) { diff -uNr ircservices-5.0.22/modules/nickserv/util.c ircservices-5.0.23/modules/nickserv/util.c --- ircservices-5.0.22/modules/nickserv/util.c 2003-09-16 04:17:49 +0900 +++ ircservices-5.0.23/modules/nickserv/util.c 2003-11-03 18:10:36 +0900 @@ -482,6 +482,9 @@ * are set appropriately, and a pointer to the NickGroupInfo is returned * in that variable. Note that makenick() may return NULL if a nick group * is requested and new_nickgroupinfo() returns NULL. + * + * If `nick' is longer than NICKMAX-1 characters, it is silently truncated + * in the new NickInfo and NickGroupInfo. */ NickInfo *makenick(const char *nick, NickGroupInfo **nickgroup_ret) diff -uNr ircservices-5.0.22/tools/Makefile ircservices-5.0.23/tools/Makefile --- ircservices-5.0.22/tools/Makefile 2003-09-16 04:17:48 +0900 +++ ircservices-5.0.23/tools/Makefile 2003-11-03 18:10:35 +0900 @@ -19,6 +19,7 @@ # These aren't "modules" in the real sense; this is just a convenient way # to list the object files that handle each database format. CONVERT_DB_MODULES = \ + convert-cygnus.o \ convert-epona.o \ convert-magick.o \ convert-ptlink.o \ @@ -64,6 +65,7 @@ convert-db.o: convert-db.c $(TOPDIR)/language.h $(TOPDIR)/modules/misc/xml.h +convert-cygnus.o: convert-cygnus.c convert-epona.o: convert-epona.c convert-magick.o: convert-magick.c convert-ptlink.o: convert-ptlink.c $(TOPDIR)/language.h diff -uNr ircservices-5.0.22/tools/convert-cygnus.c ircservices-5.0.23/tools/convert-cygnus.c --- ircservices-5.0.22/tools/convert-cygnus.c 1970-01-01 09:00:00 +0900 +++ ircservices-5.0.23/tools/convert-cygnus.c 2003-11-03 18:10:36 +0900 @@ -0,0 +1,1324 @@ +/* Conversion routines for Cygnus 0.2.0. + * + * IRC Services is copyright (c) 1996-2003 Andrew Church. + * E-mail: + * Parts written by Andrew Kempe and others. + * This program is free but copyrighted software; see the file COPYING for + * details. + */ + +#include "convert-db.h" + +#define WHAT(a,b) ((a)<<8 | (b)) + +/* Analog of strtok_remaining() from misc.c that strips newlines. */ +static inline char *cyg_strtok_remaining() { + char *s = strtok(NULL, "\r\n"); + while (s && *s == ' ') + s++; + return s; +} + +/* Short macro to convert a string `str' to a time_t value with error + * checking and store the result in the variable `var'; if the value is not + * a valid integer, a message is printed to stderr and the current time is + * used instead. `errfmt' must be a string literal. */ +#define CVT_TIME(var,str,errfmt,...) do { \ + unsigned long time_l = strtoul(str, (char **)&str, 10); \ + if (str && *str) { \ + fprintf(stderr, "%s:%d: " errfmt " is not a valid number;" \ + " setting to current time\n", fname, line , ##__VA_ARGS__);\ + var = time(NULL); \ + } else { \ + var = time_l; \ + } \ +} while (0) + +/* The same thing, but defaulting to zero instead of the current time + * (good for expirations). */ +#define CVT_TIME_0(var,str,errfmt,...) do { \ + unsigned long time_l = strtoul(str, (char **)&str, 10); \ + if (str && *str) { \ + fprintf(stderr, "%s:%d: " errfmt " is not a valid number;" \ + " setting to zero\n", fname, line , ##__VA_ARGS__); \ + var = time(NULL); \ + } else { \ + var = time_l; \ + } \ +} while (0) + +/*************************************************************************/ + +/* Mapping from timezone IDs to offsets in minutes, based on cygnus.zone + * distributed with Cygnus 0.2.0 */ +static const int default_timezones[] = { + -720, + -660, + -600, + -600, + -600, + -540, + -540, + -540, + -480, + -480, + -480, + -420, + -420, + -360, + -360, + -300, + -300, + -240, + -240, + -240, + -210, + -210, + -180, + -180, + -180, + -150, + -120, + -60, + 0, + 0, + 0, + 0, + 0, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 180, + 180, + 180, + 180, + 210, + 240, + 300, + 330, + 360, + 390, + 420, + 480, + 480, + 480, + 480, + 540, + 540, + 570, + 600, + 600, + 630, + 660, + 720, + 720, + 720, + 780 +}; +static int *timezones, ntimezones; + +static void cyg_load_nick(const char *sourcedir) +{ + FILE *f; + char fname[PATH_MAX+1]; + char buf[4096]; /* Cygnus uses 2048; let's be safe */ + char *s; + NickInfo *ni = NULL; + NickGroupInfo *ngi = NULL; + int ncount = 0, lcount = 0, mcount = 0; /* to check against DE line */ + int line; + + snprintf(fname, sizeof(fname), "%s/nickserv.db", sourcedir); + f = fopen(fname, "r"); + if (!f) { + fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno)); + exit(1); + } + fgets(buf, sizeof(buf), f); + if (!(s = strtok(buf, " \r\n")) + || strcmp(s,"NV") != 0 + || !(s = strtok(NULL, " \r\n")) + ) { + fprintf(stderr, "%s: Version number missing", fname); + exit(1); + } + if (atoi(s) != 3) { + fprintf(stderr, "%s: Bad version number (expected 3, got %d)\n", + fname, atoi(s)); + exit(1); + } + + line = 1; + while (fgets(buf, sizeof(buf), f)) { + line++; + if (!(s = strtok(buf, " \r\n"))) + continue; + switch (WHAT(s[0],s[1])) { + case WHAT('S','S'): /* Previous servicestamp value; ignore */ + break; + + case WHAT('N','I'): { /* Basic nickname info */ + char *pass; /* may be truncated */ + const char *reg, *seen, *flags, *memolimit, *zone, *key, + *usermask, *realname; + long myflags, tmp; + int truncated = 0; + + ncount++; + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "%s:%d: Corrupt NI line, ignoring nickname\n", + fname, line); + ni = NULL; + ngi = NULL; + break; + } + if (strlen(s) > NICKMAX-1) { + fprintf(stderr, "%s:%d: Truncating nickname `%s' (exceeds" + " NICKMAX-1 characters)\n", fname, line, s); + s[NICKMAX-1] = 0; + truncated = 1; + } + if (get_nickinfo(s)) { + fprintf(stderr, "%s:%d: Nickname `%s' already exists%s," + " skipping\n", fname, line, s, + truncated ? " (due to truncation?)" : ""); + ni = NULL; + ngi = NULL; + break; + } + pass = strtok(NULL, " \r\n"); + reg = strtok(NULL, " \r\n"); + seen = strtok(NULL, " \r\n"); + (void) strtok(NULL, " \r\n"); /* last ircd timestamp, not used */ + (void) strtok(NULL, " \r\n"); /* last servicestamp, not used */ + flags = strtok(NULL, " \r\n"); + memolimit = strtok(NULL, " \r\n"); + zone = strtok(NULL, " \r\n"); + key = strtok(NULL, " \r\n"); + usermask = strtok(NULL, " \r\n"); + realname = strtok(NULL, " \r\n"); + if (!pass || !reg || !seen || !flags || !memolimit || !zone + || !key || !usermask || !realname + ) { + fprintf(stderr, "%s:%d: Corrupt NI line, ignoring nickname\n", + fname, line); + ni = NULL; + ngi = NULL; + break; + } + myflags = strtol(flags, (char **)&flags, 10); + if (flags && *flags) { + fprintf(stderr, "%s:%d: Flags value for `%s' is not a" + " valid number; ignoring nickname\n", + fname, line, ni->nick); + ni = NULL; + ngi = NULL; + break; + } + ni = makenick(s, &ngi); + if (!ni) { + fprintf(stderr, "%s:%d: Unable to add nickname `%s' to" + " database, skipping\n", fname, line, s); + ni = NULL; + ngi = NULL; + break; + } + if (strlen(pass) > sizeof(ngi->pass)-1) { + fprintf(stderr, "%s:%d: Password for `%s' truncated to %d" + " characters\n", fname, line, ni->nick, + sizeof(ngi->pass)-1); + pass[sizeof(ngi->pass)-1] = 0; + } + strscpy(ngi->pass, pass, sizeof(ngi->pass)); + CVT_TIME(ni->time_registered, reg, + "Registration time for `%s'", ni->nick); + CVT_TIME(ni->last_seen, seen, "Last-seen time for `%s'", ni->nick); + tmp = strtol(memolimit, (char **)&memolimit, 10); + if (memolimit && *memolimit) { + fprintf(stderr, "%s:%d: Memo limit for `%s' is not a valid" + " number; setting to default\n", + fname, line, ni->nick); + ngi->memos.memomax = MEMOMAX_DEFAULT; + } else if (tmp < 0 || tmp > 32767) { + fprintf(stderr, "%s:%d: Memo limit for `%s' is out of range;" + " setting to default\n", fname, line, ni->nick); + ngi->memos.memomax = MEMOMAX_DEFAULT; + } else { + ngi->memos.memomax = tmp; + } + tmp = strtol(zone, (char **)&zone, 10); + if (zone && *zone) { + fprintf(stderr, "%s:%d: Timezone index for `%s' is not a" + " valid number; setting to default\n", + fname, line, ni->nick); + ngi->timezone = TIMEZONE_DEFAULT; + } else if (tmp == 0) { + ngi->timezone = TIMEZONE_DEFAULT; + } else if (tmp < 1 || tmp > ntimezones) { + fprintf(stderr, "%s:%d: Timezone index for `%s' is out of" + " range; setting to default\n", fname, line, ni->nick); + ngi->timezone = TIMEZONE_DEFAULT; + } else { + ngi->timezone = timezones[tmp-1]; + } + ngi->authcode = strtol(key, (char **)&key, 10); + if (key && *key) { + fprintf(stderr, "%s:%d: Authentication code for `%s' is not a" + " valid number; clearing\n", fname, line, ni->nick); + ngi->authcode = 0; + } else if (ngi->authcode != 0) { + ngi->authset = time(NULL); + /* Assume it's a code for registration; if it's one for an + * E-mail change, we'll find that out later */ + ngi->authcode &= ~(3<<8); /*FIXME <<8 based on mail-auth code*/ + } + ni->last_usermask = sstrdup(usermask); + ni->last_realname = sstrdup(realname); + /* Now set flags */ + ngi->flags = 0; + if (myflags & 0x0001) + ngi->flags |= NF_KILLPROTECT; + if (myflags & 0x0002) + ngi->flags |= NF_SECURE; + if (myflags & 0x0004) + ngi->flags |= NF_HIDE_EMAIL; + if (myflags & 0x0080) + ngi->flags |= NF_PRIVATE; + if (myflags & 0x0400) /* NOMEMO */ + ngi->memos.memomax = 0; + if (myflags & 0x1000) /* MEMOMAIL */ + ngi->flags |= NF_MEMO_FWDCOPY; /* Cygnus uses forward+copy */ + if (myflags & 0x2000) { /* FROZEN */ + ngi->suspendinfo = smalloc(sizeof(*ngi->suspendinfo)); + strscpy(ngi->suspendinfo->who, "", + sizeof(ngi->suspendinfo->who)); + ngi->suspendinfo->reason = + sstrdup("Unknown (imported from Cygnus)"); + ngi->suspendinfo->suspended = time(NULL); + ngi->suspendinfo->expires = 0; + } + if (myflags & 0x4000) /* HELD, i.e. not expiring */ + ni->status |= NS_NOEXPIRE; + if (myflags & 0x20000000) + ngi->os_priv = NP_SERVOPER; + if (myflags & 0x80000000) { + if (!ngi->authcode) { + fprintf(stderr, "%s:%d: WAITAUTH set for `%s' but no" + " authentication code! Corrupt database?", + fname, line, ni->nick); + } + } else { + if (ngi->authcode) { + fprintf(stderr, "%s:%d: `%s' has authentication code, but" + " WAITAUTH not set (corrupt database?); clearing" + " authcode", fname, line, ni->nick); + ngi->authcode = 0; + ngi->authset = 0; + } + } + break; + } /* case WHAT('N','I') */ + + case WHAT('A','C'): /* Access list entry */ + if (!ngi) + break; + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "%s:%d: Corrupt AC line, ignoring\n", + fname, line); + break; + } + ARRAY_EXTEND(ngi->access); + ngi->access[ngi->access_count-1] = sstrdup(s); + break; + + case WHAT('L','N'): /* Subnick (link) list */ + while ((s = strtok(NULL, " \r\n")) != NULL) { + lcount++; + if (ngi) { + int truncated = 0; + NickInfo *ni2; + if (strlen(s) > NICKMAX-1) { + fprintf(stderr, "%s:%d: Truncating nickname `%s'" + " (exceeds NICKMAX-1 characters)\n", + fname, line, s); + s[NICKMAX-1] = 0; + truncated = 1; + } + if (get_nickinfo(s)) { + fprintf(stderr, "%s:%d: Nickname `%s' (link to `%s')" + " already exists%s, skipping\n", + fname, line, s, ni->nick, + truncated ? " (due to truncation?)" : ""); + } else if (!(ni2 = makenick(s, NULL))) { + fprintf(stderr, "%s:%d: Unable to add nickname `%s'" + " (link to `%s') to database, skipping\n", + fname, line, s, ni->nick); + } else { + ni2->nickgroup = ni->nickgroup; + ni2->time_registered = ni->time_registered; + ni2->last_seen = ni->last_seen; + ARRAY_EXTEND(ngi->nicks); + strscpy(ngi->nicks[ngi->nicks_count-1], s, + sizeof(*ngi->nicks)); + } + } else { + fprintf(stderr, "%s:%d: Ignoring link `%s' to ignored" + " nickname\n", fname, line, s); + } + } + break; + + case WHAT('M','O'): { /* Memo */ + const char *num_s, *flags_s, *time_s, *text; + char *sender; /* we modify it */ + int i; + unsigned long num; + long flags, rtime; + + mcount++; + if (!ngi) + break; + num_s = strtok(NULL, " \r\n"); + flags_s = strtok(NULL, " \r\n"); + time_s = strtok(NULL, " \r\n"); + sender = strtok(NULL, " \r\n"); + text = cyg_strtok_remaining(); + if (!num_s || !flags_s || !time_s || !sender || !text) { + fprintf(stderr, "%s:%d: Corrupt MO line, ignoring\n", + fname, line); + break; + } + num = strtoul(num_s, (char **)&num_s, 10); + if (num_s && *num_s) { + fprintf(stderr, "%s:%d: Memo number is not a valid number;" + " ignoring memo\n", fname, line); + break; + } + flags = strtol(flags_s, (char **)&flags_s, 10); + if (flags_s && *flags_s) { + fprintf(stderr, "%s:%d: Flag value is not a valid number;" + " ignoring memo\n", fname, line); + break; + } + CVT_TIME(rtime, time_s, "Memo timestamp"); + if (strlen(sender) > NICKMAX-1) { + fprintf(stderr, "%s:%d: Truncating sender nickname `%s'" + " (exceeds NICKMAX-1 characters)\n", fname, line, + sender); + sender[NICKMAX-1] = 0; + } + ARRAY_EXTEND(ngi->memos.memos); + i = ngi->memos.memos_count - 1; + ngi->memos.memos[i].number = num; + ngi->memos.memos[i].flags = 0; + if (flags & 1) + ngi->memos.memos[i].flags |= MF_UNREAD; + ngi->memos.memos[i].time = rtime; + strscpy(ngi->memos.memos[i].sender, sender, + sizeof(ngi->memos.memos[i].sender)); + ngi->memos.memos[i].text = sstrdup(text); + break; + } /* case WHAT('M','O') */ + + case WHAT('F','W'): /* Nick to forward memos to; ignore */ + break; + + case WHAT('F','R'): /* Freeze (suspend) reason */ + if (!ngi) + break; + s = cyg_strtok_remaining(); + if (!s) { + fprintf(stderr, "%s:%d: Corrupt FR line, ignoring\n", + fname, line); + break; + } + if (!ngi->suspendinfo) { + fprintf(stderr, "%s:%d: FR line for `%s', but FREEZE flag" + " not set (corrupt database?), ignoring\n", + fname, line, ni->nick); + break; + } + free(ngi->suspendinfo->reason); + ngi->suspendinfo->reason = sstrdup(s); + break; + + case WHAT('E','M'): /* E-mail address */ + if (!ngi) + break; + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "%s:%d: Corrupt RM line, ignoring\n", + fname, line); + break; + } + if (ngi->email && ((ngi->authcode>>8) & 3) == 1) { + /* New address already waiting for AUTH; ignore this one */ + } else { + free(ngi->email); + ngi->email = sstrdup(s); + } + break; + + case WHAT('T','E'): /* Temporary E-mail address */ + /* This is set when a SET EMAIL command has been given and the + * change has not yet been authenticated. We discard the old + * address and store this one instead, then modify the auth + * code (or set one if needed) appropriately. */ + if (!ngi) + break; + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "%s:%d: Corrupt TE line, ignoring\n", + fname, line); + break; + } + free(ngi->email); + ngi->email = sstrdup(s); + if (ngi->authcode) { + ngi->authcode |= 1<<8; + } else { + fprintf(stderr, "%s:%d: TE line for nickname `%s' without" + " authentication code! Assigning new code.\n", + fname, line, ni->nick); + ngi->authcode = rand()%(900000000-(6<<8)) + (100000000+(3<<8)); + ngi->authcode &= ~(3<<8); + ngi->authcode |= 1<<8; + } + break; + + case WHAT('U','R'): /* URL */ + if (!ngi) + break; + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "%s:%d: Corrupt UR line, ignoring\n", + fname, line); + break; + } + ngi->url = sstrdup(s); + break; + + case WHAT('U','N'): /* UIN; ignore */ + break; + + case WHAT('N','A'): /* "Real name" (not ni->last_realname); ignore */ + break; + + case WHAT('A','G'): /* Age; ignore */ + break; + + case WHAT('S','X'): /* Sex; ignore (TODO: get some) */ + break; + + case WHAT('L','O'): /* Location; ignore */ + break; + + case WHAT('D','E'): /* Database end; check counts */ + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "Warning: check line corrupt; database may" + " be invalid"); + break; + } + if (atoi(s) != ncount) { + fprintf(stderr, "Warning: bad nickname count (%d expected," + " %d found)--database may be corrupt\n", + atoi(s), ncount); + } + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "Warning: check line corrupt; database may" + " be invalid"); + break; + } + if (atoi(s) != lcount) { + fprintf(stderr, "Warning: bad link count (%d expected, %d" + " found)--database may be corrupt\n", atoi(s), lcount); + } + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "Warning: check line corrupt; database may" + " be invalid"); + break; + } + if (atoi(s) != mcount) { + fprintf(stderr, "Warning: bad memo count (%d expected, %d" + " found)--database may be corrupt\n", atoi(s), mcount); + } + break; + } /* switch (WHAT(s)) */ + } /* while (fgets()) */ + + fclose(f); +} + +/*************************************************************************/ + +static struct { + int32 flag; + char mode; +} cmodes[] = { + { 0x00000001, 't' }, + { 0x00000002, 'n' }, + { 0x00000004, 's' }, + { 0x00000008, 'm' }, + { 0x00000010, 'l' }, + { 0x00000020, 'i' }, + { 0x00000040, 'p' }, + { 0x00000080, 'k' }, + { 0x00000100, 0 }, /* cumode +o */ + { 0x00000200, 0 }, /* cumode +v */ + { 0x00000400, 'R' }, + { 0x00000800, 0 }, /* 'r', never set in mlock */ + { 0x00001000, 'c' }, + { 0x00002000, 'O' }, + { 0x00004000, 'Q' }, + { 0x00008000, 'S' }, + { 0x00010000, 'K' }, + { 0x00020000, 'V' }, + { 0x00040000, 'f' }, + { 0x00080000, 'H' }, + { 0x00100000, 'G' }, + { 0x00200000, 'C' }, + { 0x00400000, 'u' }, + { 0x00800000, 'z' }, + { 0x01000000, 'N' }, + { 0x02000000, 'L' }, + { 0x04000000, 'A' }, + { 0x08000000, 0 }, /* cumode +h */ + { 0x10000000, 0 }, /* cumode +a */ + { 0x20000000, 0 }, /* cumode +q */ + { 0, 0 } +}; + +static void cyg_load_chan(const char *sourcedir) +{ + FILE *f; + char fname[PATH_MAX+1]; + char buf[4096]; /* Cygnus uses 2048; let's be safe */ + char *s; + ChannelInfo *ci = NULL; + int ccount = 0; /* to check against DE line */ + int line, i; + + snprintf(fname, sizeof(fname), "%s/chanserv.db", sourcedir); + f = fopen(fname, "r"); + if (!f) { + fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno)); + exit(1); + } + fgets(buf, sizeof(buf), f); + if (!(s = strtok(buf, " \r\n")) + || strcmp(s,"CV") != 0 + || !(s = strtok(NULL, " \r\n")) + ) { + fprintf(stderr, "%s: Version number missing", fname); + exit(1); + } + if (atoi(s) != 3) { + fprintf(stderr, "%s: Bad version number (expected 3, got %d)\n", + fname, atoi(s)); + exit(1); + } + + line = 1; + while (fgets(buf, sizeof(buf), f)) { + line++; + if (!(s = strtok(buf, " \r\n"))) + continue; + switch (WHAT(s[0],s[1])) { + case WHAT('C','I'): { /* Basic channel info */ + char *pass; /* may be truncated */ + const char *founder, *reg, *used, *flags, *lockon_s, *lockoff_s, + *topiclock; + char *on, *off; /* for mode lock */ + long myflags, lockon, lockoff, tmp; + int truncated = 0; + NickInfo *ni; + + ccount++; + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "%s:%d: Corrupt CI line, ignoring channel\n", + fname, line); + ci = NULL; + break; + } + if (strlen(s) > CHANMAX-1) { + fprintf(stderr, "%s:%d: Truncating channel name `%s' (exceeds" + " CHANMAX-1 characters)\n", fname, line, s); + s[CHANMAX-1] = 0; + truncated = 1; + } + if (get_channelinfo(s)) { + fprintf(stderr, "%s:%d: Channel `%s' already exists%s," + " skipping\n", fname, line, s, + truncated ? " (due to truncation?)" : ""); + ci = NULL; + break; + } + founder = strtok(NULL, " \r\n"); + pass = strtok(NULL, " \r\n"); + reg = strtok(NULL, " \r\n"); + used = strtok(NULL, " \r\n"); + flags = strtok(NULL, " \r\n"); + lockon_s = strtok(NULL, " \r\n"); + lockoff_s = strtok(NULL, " \r\n"); + topiclock = strtok(NULL, " \r\n"); + /* memolevel and key ignored */ + if (!founder || !pass || !reg || !used || !flags || !lockon_s + || !lockoff_s || !topiclock + ) { + fprintf(stderr, "%s:%d: Corrupt CI line, ignoring channel\n", + fname, line); + ci = NULL; + break; + } + ni = get_nickinfo(founder); + if (!ni) { + fprintf(stderr, "%s:%d: %s founder `%s' is not a registered" + " nickname, ignoring channel\n", + fname, line, s, founder); + ci = NULL; + break; + } else if (ni->status & NS_VERBOTEN) { + fprintf(stderr, "%s:%d: %s founder `%s' is a forbidden" + " nickname, ignoring channel\n", + fname, line, s, founder); + ci = NULL; + break; + } else if (!ni->nickgroup) { + fprintf(stderr, "%s:%d: %s fuonder `%s' has an invalid" + " nickname record (BUG?), ignoring channel\n", + fname, line, s, founder); + ci = NULL; + break; + } + myflags = strtol(flags, (char **)&flags, 10); + if (flags && *flags) { + fprintf(stderr, "%s:%d: Flags value for `%s' is not a" + " valid number; ignoring channel\n", fname, line, s); + ci = NULL; + break; + } + ci = scalloc(sizeof(*ci), 1); + if (!ci) { + fprintf(stderr, "%s:%d: Unable to add channel `%s' to" + " database, skipping\n", fname, line, s); + ci = NULL; + break; + } + strscpy(ci->name, s, sizeof(ci->name)); + add_channelinfo(ci); + ci->founder = ni->nickgroup; + if (strlen(pass) > sizeof(ci->founderpass)-1) { + fprintf(stderr, "%s:%d: Password for `%s' truncated to %d" + " characters\n", fname, line, ci->name, + sizeof(ci->founderpass)-1); + pass[sizeof(ci->founderpass)-1] = 0; + } + strscpy(ci->founderpass, pass, sizeof(ci->founderpass)); + CVT_TIME(ci->time_registered, reg, + "Registration time for `%s'", ci->name); + CVT_TIME(ci->last_used, used, "Last-used time for `%s'", ci->name); + lockon = strtol(lockon_s, (char **)&lockon_s, 10); + if (lockon_s && *lockon_s) { + fprintf(stderr, "%s:%d: Locked-on mode value for `%s' is not" + " a valid number; clearing\n", fname, line, ci->name); + lockon = 0; + } + lockoff = strtol(lockoff_s, (char **)&lockoff_s, 10); + if (lockoff_s && *lockoff_s) { + fprintf(stderr, "%s:%d: Locked-off mode value for `%s' is not" + " a valid number; clearing\n", fname, line, ci->name); + lockoff = 0; + } + ci->mlock_on = on = scalloc(64, 1); + ci->mlock_off = off = scalloc(64, 1); + for (i = 0; cmodes[i].flag != 0; i++) { + if (lockon & cmodes[i].flag) + *on++ = cmodes[i].mode; + if (lockoff & cmodes[i].flag) + *off++ = cmodes[i].mode; + } + *on = 0; + *off = 0; + tmp = strtol(topiclock, (char **)&topiclock, 10); + if (topiclock && *topiclock) { + fprintf(stderr, "%s:%d: Topic lock value for `%s' is not a" + " valid number; clearing\n", fname, line, ci->name); + tmp = 0; + } else if (tmp < 0 || tmp > 5) { + fprintf(stderr, "%s:%d: Topic lock value for `%s' is out of" + " range; clearing\n", fname, line, ci->name); + tmp = 0; + } + /* Now set flags */ + ci->flags = 0; + if (tmp) + ci->flags |= CI_TOPICLOCK; + if (myflags & 0x0001) + ci->flags |= CI_OPNOTICE; + if (myflags & 0x0004) + ci->flags |= CI_SECURE; + if (myflags & 0x0008) + ci->flags |= CI_RESTRICTED; + if (myflags & 0x0010) { /* FROZEN */ + ci->suspendinfo = smalloc(sizeof(*ci->suspendinfo)); + strscpy(ci->suspendinfo->who, "", + sizeof(ci->suspendinfo->who)); + ci->suspendinfo->reason = + sstrdup("Unknown (imported from Cygnus)"); + ci->suspendinfo->suspended = time(NULL); + ci->suspendinfo->expires = 0; + } + if (myflags & 0x0020) /* HELD, i.e. not expiring */ + ci->flags |= CI_NOEXPIRE; + break; + } /* case WHAT('C','I') */ + + case WHAT('C','A'): { /* Access list entry */ + const char *nick = strtok(NULL, " \r\n"); + const char *level_s = strtok(NULL, " \r\n"); + const NickInfo *ni; + long level; + int i; + if (!ci) + break; + if (!nick || !level_s) { + fprintf(stderr, "%s:%d: Corrupt CA line, ignoring\n", + fname, line); + break; + } + ni = get_nickinfo(nick); + if (!ni) { + fprintf(stderr, "%s:%d: %s access entry `%s' is not a" + " registered nickname, ignoring entry\n", + fname, line, ci->name, nick); + break; + } else if (ni->status & NS_VERBOTEN) { + fprintf(stderr, "%s:%d: %s access entry `%s' is a" + " forbidden nickname, ignoring entry\n", + fname, line, ci->name, nick); + break; + } else if (!ni->nickgroup) { + fprintf(stderr, "%s:%d: %s access entry `%s' has an" + " invalid nickname record (BUG?), ignoring entry\n", + fname, line, ci->name, nick); + break; + } else if (ni->nickgroup == ci->founder) { + fprintf(stderr, "%s:%d: %s access entry `%s' is the channel" + " founder (or is in the same nickname group)," + " ignoring entry\n", fname, line, ci->name, nick); + break; + } + for (i = 0; i < ci->access_count; i++) { + if (ni->nickgroup == ci->access[i].nickgroup) { + fprintf(stderr, "%s:%d: %s access entry `%s': nickname" + " group is already on the access list, ignoring" + " entry\n", fname, line, ci->name, nick); + break; + } + } + if (i < ci->access_count) + break; + level = strtol(level_s, (char **)&level_s, 10); + if (level_s && *level_s) { + fprintf(stderr, "%s:%d: %s access entry `%s': Invalid" + " access level, ignoring entry\n", + fname, line, ci->name, nick); + break; + } else if (level < 0 || level > 5) { + fprintf(stderr, "%s:%d: %s access entry `%s': Access level" + " out of range, ignoring entry\n", + fname, line, ci->name, nick); + } + switch (level) { + case 5: + case 4: level = ACCLEV_SOP; break; + case 3: level = ACCLEV_AOP; break; + case 2: level = ACCLEV_HOP; break; + case 1: level = ACCLEV_VOP; break; + default: level = 0; break; + } + ARRAY_EXTEND(ci->access); + ci->access[ci->access_count-1].nickgroup = ni->nickgroup; + ci->access[ci->access_count-1].level = level; + break; + } /* case WHAT('C','A') */ + + case WHAT('A','K'): { /* Autokick entry */ + const char *mask = strtok(NULL, " \r\n"); + const char *setter = strtok(NULL, " \r\n"); + const char *time_s = strtok(NULL, " \r\n"); + const char *reason = cyg_strtok_remaining(); + time_t stime; + if (!ci) + break; + if (!mask || !setter || !time_s) { + fprintf(stderr, "%s:%d: Corrupt AK line, ignoring\n", + fname, line); + break; + } + if (reason && !*reason) + reason = NULL; + CVT_TIME(stime, time_s, + "%s autokick `%s': Set time", ci->name, mask); + ARRAY_EXTEND(ci->akick); + ci->akick[ci->akick_count-1].mask = sstrdup(mask); + ci->akick[ci->akick_count-1].reason = reason ? sstrdup(reason) + : NULL; + strscpy(ci->akick[ci->akick_count-1].who, setter, + sizeof(ci->akick[ci->akick_count-1].who)); + ci->akick[ci->akick_count-1].set = stime; + ci->akick[ci->akick_count-1].lastused = 0; + break; + } /* case WHAT('A','K') */ + + case WHAT('S','U'): { /* URL */ + NickInfo *ni; + if (!ci) + break; + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "%s:%d: Corrupt UR line, ignoring\n", + fname, line); + break; + } + ni = get_nickinfo(s); + if (!ni) { + fprintf(stderr, "%s:%d: %s successor `%s' is not a" + " registered nickname, ignoring\n", + fname, line, ci->name, s); + break; + } else if (ni->status & NS_VERBOTEN) { + fprintf(stderr, "%s:%d: %s successor `%s' is a forbidden" + " nickname, ignoring\n", fname, line, ci->name, s); + break; + } else if (!ni->nickgroup) { + fprintf(stderr, "%s:%d: %s successor `%s' has an invalid" + " nickname record (BUG?), ignoring\n", + fname, line, ci->name, s); + break; + } else if (ni->nickgroup == ci->founder) { + fprintf(stderr, "%s:%d: %s successor `%s' is the channel" + " founder (or is in the same nickname group)," + " ignoring\n", fname, line, ci->name, s); + break; + } + ci->successor = ni->nickgroup; + break; + } /* case WHAT('S','U') */ + + case WHAT('G','R'): /* URL */ + if (!ci) + break; + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "%s:%d: Corrupt GR line, ignoring\n", + fname, line); + break; + } + ci->entry_message = sstrdup(s); + break; + + case WHAT('U','R'): /* URL */ + if (!ci) + break; + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "%s:%d: Corrupt UR line, ignoring\n", + fname, line); + break; + } + ci->url = sstrdup(s); + break; + + case WHAT('C','T'): { /* Channel topic */ + const char *nick = strtok(NULL, " \r\n"); + const char *time_s = strtok(NULL, " \r\n"); + const char *topic = cyg_strtok_remaining(); + if (!nick || !time_s || !topic) { + fprintf(stderr, "%s:%d: Corrupt CA line, ignoring\n", + fname, line); + break; + } + CVT_TIME(ci->last_topic_time, time_s, + "%s topic: Set time", ci->name); + strscpy(ci->last_topic_setter, nick, + sizeof(ci->last_topic_setter)); + ci->last_topic = sstrdup(topic); + break; + } /* case WHAT('C','T') */ + + case WHAT('K','Y'): /* Locked key */ + if (!ci) + break; + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "%s:%d: Corrupt KY line, ignoring\n", + fname, line); + break; + } + if (!strchr(ci->mlock_on, 'k')) { + fprintf(stderr, "%s:%d: Locked key given for channel %s" + " without MLOCK +k, ignoring\n", + fname, line, ci->name); + break; + } + ci->mlock_key = sstrdup(s); + break; + + case WHAT('L','M'): { /* Locked limit */ + long limit; + if (!ci) + break; + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "%s:%d: Corrupt LM line, ignoring\n", + fname, line); + break; + } + if (!strchr(ci->mlock_on, 'l')) { + fprintf(stderr, "%s:%d: Locked limit given for channel %s" + " without MLOCK +l, ignoring\n", + fname, line, ci->name); + break; + } + limit = strtol(s, (char **)&s, 10); + if (s && *s) { + fprintf(stderr, "%s:%d: Locked limit for channel %s is" + " invalid, ignoring\n", fname, line, ci->name); + s = strchr(ci->mlock_on, 'l'); + if (s) /* always true, from check above */ + memmove(s, s+1, strlen(s+1)+1); + break; + } + ci->mlock_limit = limit; + break; + } /* case WHAT('L','M') */ + + case WHAT('F','L'): /* Locked flood setting */ + if (!ci) + break; + s = cyg_strtok_remaining(); + if (!s) { + fprintf(stderr, "%s:%d: Corrupt FL line, ignoring\n", + fname, line); + break; + } + if (!strchr(ci->mlock_on, 'f')) { + fprintf(stderr, "%s:%d: Locked flood setting given for" + " channel %s without MLOCK +f, ignoring\n", + fname, line, ci->name); + break; + } + ci->mlock_flood = sstrdup(s); + break; + + case WHAT('L','K'): /* Locked link */ + if (!ci) + break; + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "%s:%d: Corrupt LK line, ignoring\n", + fname, line); + break; + } + if (!strchr(ci->mlock_on, 'L')) { + fprintf(stderr, "%s:%d: Locked link given for channel %s" + " without MLOCK +L, ignoring\n", + fname, line, ci->name); + break; + } + ci->mlock_link = sstrdup(s); + break; + + case WHAT('V','F'): /* Channel verify; ignore */ + break; + + case WHAT('D','E'): /* Database end; check counts */ + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "Warning: check line corrupt; database may" + " be invalid"); + break; + } + if (atoi(s) != ccount) { + fprintf(stderr, "Warning: bad channel count (%d expected," + " %d found)--database may be corrupt\n", + atoi(s), ccount); + } + break; + } /* switch (WHAT(s)) */ + } /* while (fgets()) */ + + fclose(f); + + /* Sanity check for locked modes vs. their parameters */ + for (ci = first_channelinfo(); ci; ci = next_channelinfo()) { + s = strchr(ci->mlock_on, 'k'); + if (s && !ci->mlock_key) { + fprintf(stderr, "%s: Channel %s has MLOCK +k but no locked key," + " removing MLOCK +k", fname, ci->name); + memmove(s, s+1, strlen(s+1)+1); + } + s = strchr(ci->mlock_on, 'l'); + if (s && !ci->mlock_limit) { + fprintf(stderr, "%s: Channel %s has MLOCK +l but no locked limit," + " removing MLOCK +l", fname, ci->name); + memmove(s, s+1, strlen(s+1)+1); + } + s = strchr(ci->mlock_on, 'f'); + if (s && !ci->mlock_flood) { + fprintf(stderr, "%s: Channel %s has MLOCK +k but no locked flood" + " setting, removing MLOCK +f", fname, ci->name); + memmove(s, s+1, strlen(s+1)+1); + } + s = strchr(ci->mlock_on, 'L'); + if (s && !ci->mlock_link) { + fprintf(stderr, "%s: Channel %s has MLOCK +L but no locked link," + " removing MLOCK +L", fname, ci->name); + memmove(s, s+1, strlen(s+1)+1); + } + } +} + +/*************************************************************************/ + +static void cyg_load_root(const char *sourcedir) +{ + FILE *f; + char fname[PATH_MAX+1]; + char buf[4096]; /* Cygnus uses 2048; let's be safe */ + char *s; + MaskData *md; + int acount = 0, tcount = 0, ecount = 0; /* to check against DE line */ + int line, i; + + snprintf(fname, sizeof(fname), "%s/rootserv.db", sourcedir); + f = fopen(fname, "r"); + if (!f) { + fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno)); + exit(1); + } + fgets(buf, sizeof(buf), f); + if (!(s = strtok(buf, " \r\n")) + || strcmp(s,"RV") != 0 + || !(s = strtok(NULL, " \r\n")) + ) { + fprintf(stderr, "%s: Version number missing", fname); + exit(1); + } + if (atoi(s) != 3) { + fprintf(stderr, "%s: Bad version number (expected 3, got %d)\n", + fname, atoi(s)); + exit(1); + } + + line = 1; + while (fgets(buf, sizeof(buf), f)) { + line++; + if (!(s = strtok(buf, " \r\n"))) + continue; + switch (WHAT(s[0],s[1])) { + case WHAT('A','K'): { /* Autokills / SGlines */ + const char *mask = strtok(NULL, " \r\n"); + const char *setter = strtok(NULL, " \r\n"); + const char *realname = strtok(NULL, " \r\n"); + const char *set = strtok(NULL, " \r\n"); + const char *expires = strtok(NULL, " \r\n"); + const char *reason = cyg_strtok_remaining(); + acount++; + if (!mask || !setter || !realname || !set || !expires || !reason) { + fprintf(stderr, "%s:%d: Corrupt AK line, ignoring\n", + fname, line); + break; + } + i = strtol(realname, (char **)&realname, 10); + if (realname && *realname) { + fprintf(stderr, "%s:%d: Autokill `%s' realname value is" + " invalid, ignoring autokill", fname, line, mask); + break; + } + md = scalloc(sizeof(*md), 1); + md->mask = sstrdup(mask); + md->reason = sstrdup(reason); + strscpy(md->who, setter, sizeof(md->who)); + CVT_TIME(md->time, set, "Autokill `%s' set-time value", mask); + CVT_TIME_0(md->expires, expires, + "Autokill `%s' expires value", mask); + if (md->expires) + md->expires += md->time; + md->lastused = 0; + add_maskdata(i ? MD_SGLINE : MD_AKILL, md); + break; + } /* case WHAT('A','K') */ + + case WHAT('T','R'): { /* Triggers (limited exceptions) */ + const char *mask = strtok(NULL, " \r\n"); + const char *limit_s = strtok(NULL, " \r\n"); + int limit; + tcount++; + if (!mask || !limit_s) { + fprintf(stderr, "%s:%d: Corrupt TR line, ignoring\n", + fname, line); + break; + } + limit = strtol(limit_s, (char **)&limit_s, 10); + if (limit_s && *limit_s) { + fprintf(stderr, "%s:%d: Trigger `%s' limit value is invalid," + " ignoring trigger", fname, line, mask); + break; + } else if (limit < 0) { + fprintf(stderr, "%s:%d: Trigger `%s' limit value is negative," + " ignoring trigger", fname, line, mask); + break; + } else if (limit == 0) { + fprintf(stderr, "%s:%d: Trigger `%s' limit value is zero," + " converting to autokill", fname, line, mask); + } else if (limit > 32767) { /* FIXME: this should be a constant */ + limit = 32767; + fprintf(stderr, "%s:%d: Trigger `%s' limit value is too large," + " setting to %d", fname, line, mask, limit); + } + md = scalloc(sizeof(*md), 1); + md->mask = sstrdup(mask); + md->limit = limit; + md->reason = sstrdup("Unknown (imported from Cygnus)"); + strscpy(md->who, "", sizeof(md->who)); + md->time = time(NULL); + md->expires = 0; + md->lastused = 0; + add_maskdata(limit ? MD_EXCEPTION : MD_AKILL, md); + break; + } /* case WHAT('T','R') */ + + case WHAT('E','X'): /* Exceptions (unlimited exceptions) */ + ecount++; + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "%s:%d: Corrupt EX line, ignoring\n", + fname, line); + break; + } + md = scalloc(sizeof(*md), 1); + md->mask = sstrdup(s); + md->limit = 0; + md->reason = sstrdup("Unknown (imported from Cygnus)"); + strscpy(md->who, "", sizeof(md->who)); + md->time = time(NULL); + md->expires = 0; + md->lastused = 0; + add_maskdata(MD_EXCEPTION, md); + break; + + case WHAT('N','W'): /* Last news update; ignore */ + break; + + case WHAT('R','S'): /* Maximum uptime; ignore */ + break; + + case WHAT('D','E'): /* Database end; check counts */ + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "Warning: check line corrupt; database may" + " be invalid"); + break; + } + if (atoi(s) != acount) { + fprintf(stderr, "Warning: bad autokill count (%d expected, %d" + " found)--database may be corrupt\n", atoi(s), acount); + } + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "Warning: check line corrupt; database may" + " be invalid"); + break; + } + if (atoi(s) != tcount) { + fprintf(stderr, "Warning: bad trigger count (%d expected, %d" + " found)--database may be corrupt\n", atoi(s), tcount); + } + s = strtok(NULL, " \r\n"); + if (!s) { + fprintf(stderr, "Warning: check line corrupt; database may" + " be invalid"); + break; + } + if (atoi(s) != ecount) { + fprintf(stderr, "Warning: bad exception count (%d expected, %d" + " found)--database may be corrupt\n", atoi(s), ecount); + } + break; + } /* switch (WHAT(s)) */ + } /* while (fgets()) */ + + fclose(f); +} + +/*************************************************************************/ +/*************************************************************************/ + +static const char *check_cygnus(const char *sourcedir) +{ + char buf[PATH_MAX+1]; + FILE *f; + + snprintf(buf, sizeof(buf), "%s/rootserv.db", sourcedir); + f = fopen(buf, "r"); + if (f) { + char *s; + fgets(buf, sizeof(buf), f); + fclose(f); + s = strtok(buf, " \r\n"); + if (strcmp(s,"RV") == 0) { + s = strtok(NULL, " \r\n"); + if (atoi(s) == 3) + return "Cygnus 0.2.0"; + } + } + return NULL; +} + +static void load_cygnus(const char *sourcedir, int verbose) +{ + timezones = (int *)default_timezones; + ntimezones = lenof(default_timezones); + if (verbose) + fprintf(stderr, "Loading nickserv.db...\n"); + cyg_load_nick(sourcedir); + if (verbose) + fprintf(stderr, "Loading chanserv.db...\n"); + cyg_load_chan(sourcedir); + if (verbose) + fprintf(stderr, "Loading rootserv.db...\n"); + cyg_load_root(sourcedir); + if (verbose) + fprintf(stderr, "Data files successfully loaded.\n"); +} + +/*************************************************************************/ +/*************************************************************************/ + +DBTypeInfo dbtype_cygnus = { + "cygnus", + check_cygnus, + load_cygnus +}; + +/*************************************************************************/ diff -uNr ircservices-5.0.22/tools/convert-db.c ircservices-5.0.23/tools/convert-db.c --- ircservices-5.0.22/tools/convert-db.c 2003-09-16 04:17:48 +0900 +++ ircservices-5.0.23/tools/convert-db.c 2003-11-03 18:10:36 +0900 @@ -34,6 +34,7 @@ static DBTypeInfo *dbtypes[] = { &dbtype_auspice, &dbtype_bolivia, + &dbtype_cygnus, &dbtype_daylight, &dbtype_epona, &dbtype_ircs_1_2, @@ -88,7 +89,7 @@ /*************************************************************************/ -/* Safe string copying (from misc.c). */ +/* Safe string copying/duplication (from misc.c/memory.c). */ char *strscpy(char *d, const char *s, size_t len) { @@ -97,6 +98,49 @@ return d; } +char *sstrdup(const char *s) +{ + int len = strlen(s)+1; + char *d = smalloc(len); + memcpy(d, s, len); + return d; +} + +/*************************************************************************/ + +/* NickInfo/NickGroupInfo allocation (from modules/nickserv/util.c). + * Always succeeds (if the memory allocations fail, aborts the program). */ + +NickInfo *makenick(const char *nick, NickGroupInfo **nickgroup_ret) +{ + NickInfo *ni; + NickGroupInfo *ngi; + +#ifdef CLEAN_COMPILE + ngi = NULL; +#endif + if (nickgroup_ret) { + ngi = scalloc(sizeof(*ngi), 1); + do { + /* We assume that eventually we'll find one that's not in use */ + ngi->id = rand() + rand(); + if (ngi->id == 0) + ngi->id = 1; + } while (get_nickgroupinfo(ngi->id)); + } + ni = scalloc(sizeof(*ni), 1); + strscpy(ni->nick, nick, sizeof(ni->nick)); + if (nickgroup_ret) { + ni->nickgroup = ngi->id; + ARRAY_EXTEND(ngi->nicks); + strscpy(ngi->nicks[0], nick, sizeof(ngi->nicks[0])); + add_nickgroupinfo(ngi); + *nickgroup_ret = ngi; + } + add_nickinfo(ni); + return ni; +} + /*************************************************************************/ /* Open a (Services pre-5.0 style) data file and check the version number. @@ -360,6 +404,105 @@ } /*************************************************************************/ + +/* Perform sanity checks on the data after it's been read in. */ + +static void sanity_checks(void) +{ + NickInfo *ni; + NickGroupInfo *ngi; + ChannelInfo *ci; + int i; + + /* Check for non-forbidden nicknames with no nickgroup */ + for (ni = first_nickinfo(); ni; ni = next_nickinfo()) { + if (!(ni->status & NS_VERBOTEN) && !ni->nickgroup) { + fprintf(stderr, "BUG: Nickname %s has no nickgroup! Deleting.\n", + ni->nick); + del_nickinfo(ni); + } + } + + /* Make sure every non-forbidden nick belongs to the right nickgroup */ + for (ni = first_nickinfo(); ni; ni = next_nickinfo()) { + if (ni->status & NS_VERBOTEN) + continue; + ngi = get_nickgroupinfo(ni->nickgroup); + if (!ngi) { + fprintf(stderr, "BUG: Nickname %s points to nonexistent nickgroup" + " %u! Deleting.\n", ni->nick, ni->nickgroup); + del_nickinfo(ni); + } else { + /* Nicknames in the nicks[] array should match those in the + * NickInfo structure, so use strcmp() (this keeps us from + * having to worry about irc_stricmp() idiosyncrasies). */ + ARRAY_SEARCH_PLAIN(ngi->nicks, ni->nick, strcmp, i); + if (i >= ngi->nicks_count) { + fprintf(stderr, "BUG: Nickname %s points to nickgroup %u," + " but the nickgroup doesn't contain the nickname!" + " Deleting.\n", ni->nick, ni->nickgroup); + del_nickinfo(ni); + } + } + } + + /* Make sure nickgroups don't contain extra nicks. */ + for (ngi = first_nickgroupinfo(); ngi; ngi = next_nickgroupinfo()) { + ARRAY_FOREACH(i, ngi->nicks) { + ni = get_nickinfo(ngi->nicks[i]); + if (!ni) { + fprintf(stderr, "BUG: Nickgroup %u contains nonexistent" + " nickname %s! Removing nickname from group.\n", + ngi->id, ngi->nicks[i]); + ARRAY_REMOVE(ngi->nicks, i); + i--; /* to make sure we don't skip any */ + if (ngi->nicks_count <= 0) + del_nickgroupinfo(ngi); + } + } + } + + /* Check for non-forbidden channels with no founder */ + for (ci = first_channelinfo(); ci; ci = next_channelinfo()) { + if (!(ci->flags & CI_VERBOTEN) && !ci->founder) { + fprintf(stderr, "BUG: Channel %s has no founder! Deleting.\n", + ci->name); + del_channelinfo(ci); + } + } + + /* Check that channel successors and access list entries have valid + * nickgroup IDs, and that access levels are in range */ + for (ci = first_channelinfo(); ci; ci = next_channelinfo()) { + if (ci->flags & CI_VERBOTEN) + continue; + if (ci->successor && !get_nickgroupinfo(ci->successor)) { + fprintf(stderr, "BUG: Channel %s successor is an invalid" + " nickgroup! Clearing.\n", ci->name); + ci->successor = 0; + } + ARRAY_FOREACH (i, ci->access) { + if (ci->access[i].nickgroup) { + if (!get_nickgroupinfo(ci->access[i].nickgroup)) { + fprintf(stderr, "BUG: Channel %s access entry %d has" + " an invalid nickgroup! Clearing.\n", + ci->name, i); + ci->access[i].nickgroup = 0; + ci->access[i].level = 0; + } else if (ci->access[i].level <= ACCLEV_INVALID + || ci->access[i].level >= ACCLEV_FOUNDER) { + fprintf(stderr, "BUG: Channel %s access entry %d has" + " an out-of-range level (%d)! Clearing.\n", + ci->name, i, ci->access[i].level); + ci->access[i].nickgroup = 0; + ci->access[i].level = 0; + } + } + } + } +} + +/*************************************************************************/ /****************************** Main program *****************************/ /*************************************************************************/ @@ -460,6 +603,9 @@ * program for us */ load(sourcedir, verbose); + /* Do sanity checks. */ + sanity_checks(); + /* Write the database to standard output in XML format */ xml_export((xml_writefunc_t)fprintf, stdout); diff -uNr ircservices-5.0.22/tools/convert-db.h ircservices-5.0.23/tools/convert-db.h --- ircservices-5.0.22/tools/convert-db.h 2003-09-16 04:17:48 +0900 +++ ircservices-5.0.23/tools/convert-db.h 2003-11-03 18:10:36 +0900 @@ -15,6 +15,7 @@ /* Common includes. */ #include "services.h" +#include "encrypt.h" #include "modules/database/fileutil.h" #include "modules/nickserv/nickserv.h" #include "modules/chanserv/chanserv.h" @@ -45,6 +46,7 @@ extern DBTypeInfo dbtype_auspice, dbtype_bolivia, + dbtype_cygnus, dbtype_daylight, dbtype_epona, dbtype_ircs_1_2, @@ -78,6 +80,11 @@ /* Safe memory reallocation. If the buffer is grown, any extra memory is * _not_ cleared. */ extern void *srealloc(void *ptr, long size); +/* Safe string duplication. */ +extern char *sstrdup(const char *s); + +/* Allocate a new, cleared NickInfo (and possibly NickGroupInfo) structure. */ +extern NickInfo *makenick(const char *nick, NickGroupInfo **nickgroup_ret); /* Open a (Services pre-5.0 style) data file and check the version number. * Prints an error message and exits with 1 if either the file cannot be diff -uNr ircservices-5.0.22/tools/convert-epona.c ircservices-5.0.23/tools/convert-epona.c --- ircservices-5.0.22/tools/convert-epona.c 2003-09-16 04:17:48 +0900 +++ ircservices-5.0.23/tools/convert-epona.c 2003-11-03 18:10:36 +0900 @@ -36,14 +36,8 @@ for (i = 0; i < 1024; i++) { while ((c = getc_db(f)) == 1) { char nickbuf[32]; - ni = scalloc(sizeof(*ni), 1); - ngi = new_nickgroupinfo(); - ni->nickgroup = ngi->id; SAFE(read_string(&s, f)); - strscpy(ni->nick, s, NICKMAX); - ngi->nicks = smalloc(sizeof(*ngi->nicks)); - ngi->nicks_count = 1; - strscpy(ngi->nicks[0], ni->nick, NICKMAX); + ni = makenick(s, &ngi); SAFE(read_string(&s, f)); /* For some reason nick cores can get null passwords (???) */ if (!s) { @@ -112,8 +106,6 @@ } SAFE(read_int16(&tmp16, f)); /* channelcount */ SAFE(read_int16(&tmp16, f)); /* channelmax */ - add_nickinfo(ni); - add_nickgroupinfo(ngi); } /* while (getc_db(f) == 1) */ if (c != 0) { fprintf(stderr, "%s is corrupt, aborting.\n", f->filename); @@ -129,8 +121,7 @@ ni = get_nickinfo(s); if (!ni) { islink = 1; - ni = scalloc(sizeof(*ni), 1); - strscpy(ni->nick, s, NICKMAX); + ni = makenick(s, NULL); } SAFE(read_string(&ni->last_usermask, f)); if (!ni->last_usermask) @@ -160,7 +151,6 @@ continue; } ni->nickgroup = root->nickgroup; - add_nickinfo(ni); ngi = get_nickgroupinfo(ni->nickgroup); if (ngi) { ARRAY_EXTEND(ngi->nicks); @@ -425,7 +415,8 @@ } } - add_channelinfo(ci); + if ((ci->flags & CI_VERBOTEN) || ci->founder) + add_channelinfo(ci); } /* while (getc_db(f) == 1) */ if (c != 0) { @@ -434,12 +425,6 @@ } } /* for (i) */ close_db(f); - - /* Check for non-forbidden channels with no founder */ - for (ci = first_channelinfo(); ci; ci = next_channelinfo()) { - if (!(ci->flags & CI_VERBOTEN) && !ci->founder) - del_channelinfo(ci); - } } /*************************************************************************/ diff -uNr ircservices-5.0.22/tools/convert-magick.c ircservices-5.0.23/tools/convert-magick.c --- ircservices-5.0.22/tools/convert-magick.c 2003-09-16 04:17:48 +0900 +++ ircservices-5.0.23/tools/convert-magick.c 2003-11-03 18:10:36 +0900 @@ -60,13 +60,7 @@ SAFE(read_string(&oldni.url, f)); SAFE(read_string(&oldni.usermask, f)); SAFE(read_string(&oldni.realname, f)); - ni = scalloc(sizeof(*ni), 1); - ngi = new_nickgroupinfo(); - ni->nickgroup = ngi->id; - strscpy(ni->nick, oldni.nick, NICKMAX); - ngi->nicks = smalloc(sizeof(*ngi->nicks)); - ngi->nicks_count = 1; - strscpy(ngi->nicks[0], ni->nick, NICKMAX); + ni = makenick(oldni.nick, &ngi); ni->last_usermask = oldni.usermask; ni->last_realname = oldni.realname; ni->last_quit = NULL; @@ -270,9 +264,13 @@ fprintf(stderr, "Warning: Founder %s for channel %s not found\n", oldci.founder, oldci.name); - } else if (!ni->nickgroup) { + } else if (ni->status & NS_VERBOTEN) { fprintf(stderr, "Warning: Founder %s for channel %s is a" " forbidden nick\n", oldci.founder, oldci.name); + } else if (!ni->nickgroup) { + fprintf(stderr, "Warning: Founder %s for channel %s has" + " an invalid nickname record (bug?)\n", + oldci.founder, oldci.name); } else { ci->founder = ni->nickgroup; } diff -uNr ircservices-5.0.22/tools/convert-ptlink.c ircservices-5.0.23/tools/convert-ptlink.c --- ircservices-5.0.22/tools/convert-ptlink.c 2003-09-16 04:17:48 +0900 +++ ircservices-5.0.23/tools/convert-ptlink.c 2003-11-03 18:10:36 +0900 @@ -53,16 +53,10 @@ for (i = 0; i < 256; i++) { while ((c = getc_db(f)) == 1) { char nickbuf[32], passbuf[32]; - ni = scalloc(sizeof(*ni), 1); - ngi = new_nickgroupinfo(); - ni->nickgroup = ngi->id; SAFE(read_buffer(nickbuf, f)); + ni = makenick(nickbuf, &ngi); SAFE(read_buffer(passbuf, f)); - strscpy(ni->nick, nickbuf, NICKMAX); memcpy(ngi->pass, passbuf, sizeof(passbuf)); - ngi->nicks = smalloc(sizeof(*ngi->nicks)); - ngi->nicks_count = 1; - strscpy(ngi->nicks[0], ni->nick, NICKMAX); SAFE(read_string(&ngi->url, f)); SAFE(read_string(&ngi->email, f)); SAFE(read_string(&s, f)); /* icq_number */ @@ -83,6 +77,7 @@ SAFE(read_int16(&tmp16, f)); /* status */ if (tmp16 & 0x0002) { ni->status |= NS_VERBOTEN; + del_nickgroupinfo(ngi); ni->nickgroup = 0; } if (tmp16 & 0x0004) @@ -191,9 +186,6 @@ case 4: ngi->language = LANG_IT; break; } } - add_nickinfo(ni); - if (ni->nickgroup) - add_nickgroupinfo(ngi); count++; } /* while (getc_db(f) == 1) */ if (c != 0) { @@ -470,7 +462,8 @@ SAFE(read_string(&ci->entry_message, f)); - add_channelinfo(ci); + if ((ci->flags & CI_VERBOTEN) || ci->founder) + add_channelinfo(ci); } /* while (getc_db(f) == 1) */ if (c != 0) { @@ -479,12 +472,6 @@ } } /* for (i) */ close_db(f); - - /* Check for non-forbidden channels with no founder */ - for (ci = first_channelinfo(); ci; ci = next_channelinfo()) { - if (!(ci->flags & CI_VERBOTEN) && !ci->founder) - del_channelinfo(ci); - } } /*************************************************************************/ diff -uNr ircservices-5.0.22/tools/convert-sirv.c ircservices-5.0.23/tools/convert-sirv.c --- ircservices-5.0.22/tools/convert-sirv.c 2003-09-16 04:17:48 +0900 +++ ircservices-5.0.23/tools/convert-sirv.c 2003-11-03 18:10:36 +0900 @@ -134,13 +134,7 @@ SAFE(read_string(&oldni.usermask, f)); if (type != TYPE_AUSPICE || oldni.realname) SAFE(read_string(&oldni.realname, f)); - ni = scalloc(sizeof(*ni), 1); - ngi = new_nickgroupinfo(); - ni->nickgroup = ngi->id; - strscpy(ni->nick, oldni.nick, NICKMAX); - ngi->nicks = smalloc(sizeof(*ngi->nicks)); - ngi->nicks_count = 1; - strscpy(ngi->nicks[0], ni->nick, NICKMAX); + ni = makenick(oldni.nick, &ngi); ni->last_usermask = oldni.usermask; ni->last_realname = oldni.realname; if (type == TYPE_AUSPICE) @@ -159,6 +153,7 @@ ngi->flags |= NF_SECURE; if (oldni.flags & 0x00000004) { ni->status |= NS_VERBOTEN; + del_nickgroupinfo(ngi); ni->nickgroup = 0; /* Just in case--to differentiate from linked nicks */ ni->last_usermask = NULL; @@ -227,9 +222,6 @@ } } } - add_nickinfo(ni); - if (ni->nickgroup) - add_nickgroupinfo(ngi); } /* while ((c = getc_db(f)) == 1) */ if (c != 0) { fprintf(stderr, "%s is corrupt, aborting.\n", f->filename); diff -uNr ircservices-5.0.22/tools/convert-trircd.c ircservices-5.0.23/tools/convert-trircd.c --- ircservices-5.0.22/tools/convert-trircd.c 2003-09-16 04:17:48 +0900 +++ ircservices-5.0.23/tools/convert-trircd.c 2003-11-03 18:10:36 +0900 @@ -32,14 +32,8 @@ for (i = 0; i < 256; i++) { while ((c = getc_db(f)) == 1) { char nickbuf[32], passbuf[32]; - ni = scalloc(sizeof(*ni), 1); - ngi = new_nickgroupinfo(); - ni->nickgroup = ngi->id; SAFE(read_buffer(nickbuf, f)); - strscpy(ni->nick, nickbuf, NICKMAX); - ngi->nicks = smalloc(sizeof(*ngi->nicks)); - ngi->nicks_count = 1; - strscpy(ngi->nicks[0], ni->nick, NICKMAX); + ni = makenick(nickbuf, &ngi); SAFE(read_buffer(passbuf, f)); memcpy(ngi->pass, passbuf, sizeof(passbuf)); SAFE(read_string(&ngi->url, f)); @@ -69,6 +63,7 @@ } if (tmp16 & 0x0002) { ni->status |= NS_VERBOTEN; + del_nickgroupinfo(ngi); ni->nickgroup = 0; } if (tmp16 & 0x0004) @@ -145,9 +140,6 @@ SAFE(read_int16(&tmp16, f)); /* channelmax */ SAFE(read_int16(&ngi->language, f)); } - add_nickinfo(ni); - if (ni->nickgroup) - add_nickgroupinfo(ngi); } /* while (getc_db(f) == 1) */ if (c != 0) { fprintf(stderr, "%s is corrupt, aborting.\n", f->filename); @@ -496,7 +488,8 @@ SAFE(read_string(&ci->entry_message, f)); - add_channelinfo(ci); + if ((ci->flags & CI_VERBOTEN) || ci->founder) + add_channelinfo(ci); } /* while (getc_db(f) == 1) */ if (c != 0) { @@ -505,13 +498,6 @@ } } /* for (i) */ close_db(f); - - /* Check for non-forbidden channels with no founder */ - for (ci = first_channelinfo(); ci; ci = next_channelinfo()) { - if (!(ci->flags & CI_VERBOTEN) && !ci->founder) - del_channelinfo(ci); - } - } /*************************************************************************/ diff -uNr ircservices-5.0.22/tools/convert-ver8.c ircservices-5.0.23/tools/convert-ver8.c --- ircservices-5.0.22/tools/convert-ver8.c 2003-09-16 04:17:48 +0900 +++ ircservices-5.0.23/tools/convert-ver8.c 2003-11-03 18:10:36 +0900 @@ -36,14 +36,8 @@ for (i = 0; i < 256; i++) { while ((c = getc_db(f)) == 1) { char nickbuf[32]; - ni = scalloc(sizeof(*ni), 1); - ngi = new_nickgroupinfo(); - ni->nickgroup = ngi->id; SAFE(read_buffer(nickbuf, f)); - strscpy(ni->nick, nickbuf, NICKMAX); - ngi->nicks = smalloc(sizeof(*ngi->nicks)); - ngi->nicks_count = 1; - strscpy(ngi->nicks[0], ni->nick, NICKMAX); + ni = makenick(nickbuf, &ngi); if (type == TYPE_IRCS) { char passbuf[16]; SAFE(read_buffer(passbuf, f)); @@ -80,6 +74,7 @@ } if (tmp16 & 0x0002) { ni->status |= NS_VERBOTEN; + del_nickgroupinfo(ngi); ni->nickgroup = 0; } if (tmp16 & 0x0004) @@ -143,9 +138,6 @@ SAFE(read_int16(&tmp16, f)); /* channelmax */ SAFE(read_int16(&ngi->language, f)); } - add_nickinfo(ni); - if (ni->nickgroup) - add_nickgroupinfo(ngi); } /* while (getc_db(f) == 1) */ if (c != 0) { fprintf(stderr, "%s is corrupt, aborting.\n", f->filename); @@ -337,6 +329,7 @@ if (tmp32 & 0x00000400) ci->flags |= CI_MEMO_HARDMAX; + /* Levels data is ignored */ SAFE(read_int16(&tmp16, f)); for (j = tmp16; j > 0; j--) SAFE(read_int16(&tmp16, f)); @@ -433,7 +426,8 @@ SAFE(read_string(&ci->entry_message, f)); - add_channelinfo(ci); + if ((ci->flags & CI_VERBOTEN) || ci->founder) + add_channelinfo(ci); } /* while (getc_db(f) == 1) */ if (c != 0) { @@ -442,13 +436,6 @@ } } /* for (i) */ close_db(f); - - /* Check for non-forbidden channels with no founder */ - for (ci = first_channelinfo(); ci; ci = next_channelinfo()) { - if (!(ci->flags & CI_VERBOTEN) && !ci->founder) - del_channelinfo(ci); - } - } /*************************************************************************/ diff -uNr ircservices-5.0.22/version.sh ircservices-5.0.23/version.sh --- ircservices-5.0.22/version.sh 2003-09-16 04:14:15 +0900 +++ ircservices-5.0.23/version.sh 2003-11-03 18:06:24 +0900 @@ -6,7 +6,7 @@ # $PROGRAM is the string returned as the first part of a /VERSION reply, # and must not contain spaces. It is not used anywhere else. PROGRAM=ircservices -VERSION=5.0.22 +VERSION=5.0.23 # Increment Services build number if [ -f version.c ] ; then