]> andersk Git - moira.git/blobdiff - clients/moira/pobox.c
Dribbles of missing EXCHANGE support. This should probably look up the
[moira.git] / clients / moira / pobox.c
index 5f03bec175807e50fbca3d6e53a98c41f044abde..ea55de52e6b807ce5ff0580ddec1e332a07ac08c 100644 (file)
@@ -1,38 +1,30 @@
-#if (!defined(lint) && !defined(SABER))
-  static char rcsid_module_c[] = "$Header$";
-#endif lint
-
-/*     This is the file pobox.c for the MOIRA Client, which allows a nieve
- *      user to quickly and easily maintain most parts of the MOIRA database.
+/* $Id$
+ *
+ *     This is the file pobox.c for the Moira Client, which allows users
+ *      to quickly and easily maintain most parts of the Moira database.
  *     It Contains: Functions for handling the poboxes.
- *     
+ *
  *     Created:        7/10/88
  *     By:             Chris D. Peterson
  *
- *      $Source$
- *      $Author$
- *      $Header$
- *     
- *     Copyright 1988 by the Massachusetts Institute of Technology.
- *
- *     For further information on copyright and distribution 
- *     see the file mit-copyright.h
+ * Copyright (C) 1988-1998 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
  */
 
-#include <stdio.h>
-#include <strings.h>
-#include <ctype.h>
+#include <mit-copyright.h>
 #include <moira.h>
 #include <moira_site.h>
-#include <menu.h>
-
-#include "mit-copyright.h"
+#include <mrclient.h>
 #include "defs.h"
 #include "f_defs.h"
 #include "globals.h"
 
-#define FOREIGN_BOX ("SMTP")
-#define LOCAL_BOX ("POP")
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+RCSID("$Header$");
 
 /*     Function Name: PrintPOBox
  *     Description: Yet another specialized print function.
  *     Returns: MR_CONT
  */
 
-static void
-PrintPOBox(info)
-char ** info;
+static void PrintPOBox(char **info)
 {
-    char buf[BUFSIZ];
-    
-    sprintf(buf, "Address: %-10s Box: %-35s Type: %s", info[PO_NAME],
-           info[PO_BOX], info[PO_TYPE]);
-    Put_message(buf);
-    sprintf(buf, MOD_FORMAT, info[4], info[3], info[5]);
-    Put_message(buf);
+  char buf[BUFSIZ];
+
+  sprintf(buf, "User: %-10s Box: %-35s Type: %s", info[PO_NAME],
+         info[PO_BOX], info[PO_TYPE]);
+  Put_message(buf);
+  if (info[6])
+    {
+      sprintf(buf, "Address: %s", info[PO_ADDR]);
+      Put_message(buf);
+      sprintf(buf, MOD_FORMAT, info[5], info[4], info[6]);
+      Put_message(buf);
+    }
+  else
+    {
+      sprintf(buf, MOD_FORMAT, info[4], info[3], info[5]);
+      Put_message(buf);
+    }
 }
 
 /*     Function Name: RealPrintPOMachines
@@ -59,11 +59,9 @@ char ** info;
  *     Returns: none.
  */
 
-static void
-RealPrintPOMachines(info)
-char ** info;
+static void RealPrintPOMachines(char **info)
 {
-    Print(1, info + 1, (char *) NULL);
+  Print(1, info + 1, NULL);
 }
 
 /*     Function Name: PrintPOMachines
@@ -72,23 +70,23 @@ char ** info;
  *     Returns: SUB_ERROR if the machines could not be printed.
  */
 
-static int
-PrintPOMachines()
+static int PrintPOMachines(void)
 {
-    register int status;
-    static char * args[] = {"pop", NULL};
-    struct qelem * top = NULL;
-    
-    if ( (status = do_mr_query("get_server_locations", CountArgs(args), args,
-                               StoreInfo, (char *)&top)) != MR_SUCCESS) {
-       com_err(program_name, status, " in get_server_locations.");
-       return(SUB_ERROR);
+  int status;
+  static char *args[] = {"pop", NULL};
+  struct mqelem *top = NULL;
+
+  if ((status = do_mr_query("get_server_locations", CountArgs(args), args,
+                           StoreInfo, &top)))
+    {
+      com_err(program_name, status, " in get_server_locations.");
+      return SUB_ERROR;
     }
-    
-    top = QueueTop(top);
-    Loop(top, RealPrintPOMachines);
-    FreeQueue(top);
-    return(SUB_NORMAL);
+
+  top = QueueTop(top);
+  Loop(top, RealPrintPOMachines);
+  FreeQueue(top);
+  return SUB_NORMAL;
 }
 
 /*     Function Name: GetUserPOBox
@@ -97,35 +95,31 @@ PrintPOMachines()
  *     Returns: DM_NORMAL.
  */
 
-/* ARGSUSED */
-int
-GetUserPOBox(argc, argv)
-int argc;
-char ** argv;
+int GetUserPOBox(int argc, char **argv)
 {
-    register int status;
-    struct qelem * top = NULL;
-    char buf[BUFSIZ];
-
-    if (!ValidName(argv[1]))
-       return(DM_NORMAL);
-    
-    switch (status = do_mr_query("get_pobox", 1, argv + 1, StoreInfo, 
-                                 (char *)&top)) {
+  int status;
+  struct mqelem *top = NULL;
+  char buf[BUFSIZ];
+
+  if (!ValidName(argv[1]))
+    return DM_NORMAL;
+
+  switch ((status = do_mr_query("get_pobox", 1, argv + 1, StoreInfo, &top)))
+    {
     case MR_NO_MATCH:
-       Put_message("This user has no P.O. Box.");
-       break;
+      Put_message("This user has no P.O. Box.");
+      break;
     case MR_SUCCESS:
-       sprintf(buf,"Current pobox for user %s: \n", argv[1]);
-       Put_message("");
-       top = QueueTop(top);
-       Loop(top, PrintPOBox);  /* should only return 1 box. */
-       FreeQueue(top);
-       break;
+      sprintf(buf, "Current pobox for user %s: \n", argv[1]);
+      Put_message("");
+      top = QueueTop(top);
+      Loop(top, PrintPOBox);   /* should only return 1 box. */
+      FreeQueue(top);
+      break;
     default:
-       com_err(program_name, status, " in get_pobox.");
+      com_err(program_name, status, " in get_pobox.");
     }
-    return(DM_NORMAL);
+  return DM_NORMAL;
 }
 
 /*     Function Name: GetNewLocalPOBox
@@ -134,24 +128,263 @@ char ** argv;
  *     Returns: machine - name of the machine for then new pop box, or NULL.
  */
 
-static char *
-GetNewLocalPOBox(local_user)
-char * local_user;
+static char *GetNewLocalPOBox(char *local_user)
+{
+  char temp_buf[BUFSIZ];
+
+  sprintf(temp_buf, "%s %s", "Pick one of the following",
+         "machines for this user's Post Office.");
+  Put_message(temp_buf);
+  Put_message("");
+  if (PrintPOMachines() == SUB_NORMAL)
+    {
+      Put_message("");
+      if (!Prompt_input("Which Machine? ", temp_buf, BUFSIZ))
+       return (char *) SUB_ERROR;
+      return canonicalize_hostname(strdup(temp_buf));
+    }
+  Put_message("Could not get machines to choose from, quitting.");
+  return (char *) SUB_ERROR;
+}
+
+static int InsertSortedImapPart(int argc, char **argv, void *arg)
 {
-    char temp_buf[BUFSIZ];
-
-    sprintf(temp_buf,"%s %s", "Pick one of the following",
-           "machines for this user's Post Office.");
-    Put_message(temp_buf);
-    Put_message("");
-    if (PrintPOMachines() == SUB_NORMAL) {
-       Put_message("");
-       if (!Prompt_input("Which Machine? ", temp_buf, BUFSIZ))
-         return((char *) SUB_ERROR);
-       return(canonicalize_hostname(strsave(temp_buf)));
+  struct mqelem *new = NULL;
+  struct mqelem *elem, *prev, **parts = arg;
+  int avail = atoi(argv[NFS_SIZE]) - atoi(argv[NFS_ALLOC]);
+
+  /* Dup the argv into a new mqelem. */
+  StoreInfo(argc, argv, &new);
+
+  if (!*parts)
+    {
+      *parts = new;
+      return MR_CONT;
+    }
+
+  /* Find the right place in parts for it. */
+  elem = *parts;
+  prev = NULL;
+  while (elem)
+    {
+      char **xargv = elem->q_data;
+      if (atoi(xargv[NFS_SIZE]) - atoi(xargv[NFS_ALLOC]) < avail)
+       break;
+      prev = elem;
+      elem = elem->q_forw;
+    }
+
+  if (prev)
+    AddQueue(new, prev);
+  else
+    {
+      new->q_forw = *parts;
+      (*parts)->q_back = new;
+      *parts = new;
+    }
+
+  return MR_CONT;
+}
+
+static int AddImapPartitions(char *server, struct mqelem **parts)
+{
+  char *argv[2];
+  int status;
+
+  argv[0] = server;
+  argv[1] = "*";
+
+  status = do_mr_query("get_nfsphys", 2, argv, InsertSortedImapPart, parts);
+  if (status)
+    {
+      com_err(program_name, status, " in AddImapPartitions");
+      return SUB_ERROR;
+    }
+  return SUB_NORMAL;
+}
+
+char *CreateImapBox(char *user)
+{
+  int status;
+  struct mqelem *elem = NULL, *servers = NULL, *partitions = NULL;
+  char *server = NULL, *partition = NULL;
+  char *argv[11], *fsname, temp_buf[BUFSIZ];
+  static char *default_imap_quota = NULL;
+
+  if (!default_imap_quota)
+    {
+      char **vargv;
+      argv[0] = "def_imap_quota";
+      status = do_mr_query("get_value", 1, argv, StoreInfo, &elem);
+      if (status)
+       {
+         com_err(program_name, status, " getting default IMAP quota");
+         return (char *)SUB_ERROR;
+       }
+      vargv = elem->q_data;
+      default_imap_quota = strdup(vargv[0]);
+      FreeQueue(elem);
+    }
+
+  argv[0] = "POSTOFFICE";
+  status = do_mr_query("get_server_locations", 1, argv, StoreInfo, &servers);
+  if (status)
+    {
+      com_err(program_name, status, " in GetImapBox.");
+      return (char *)SUB_ERROR;
+    }
+  servers = QueueTop(servers);
+
+  /* Get an IMAP server. */
+  while (!server)
+    {
+      server = strdup("[ANY]");
+      if (GetValueFromUser("IMAP Server? ['?' for a list]", &server) !=
+         SUB_NORMAL)
+       {
+         free(server);
+         FreeQueue(servers);
+         return (char *)SUB_ERROR;
+       }
+
+      if (!strcmp(server, "?"))
+       {
+         elem = servers;
+         while (elem)
+           {
+             char **sargv = elem->q_data;
+             sprintf(temp_buf, "  %s\n", sargv[1]);
+             Put_message(temp_buf);
+             elem = elem->q_forw;
+           }
+         free(server);
+         server = NULL;
+       }
+    }
+  server = canonicalize_hostname(server);
+
+  /* Get the partitions on that server. */
+  if (!strcasecmp(server, "[ANY]"))
+    {
+      char **sargv;
+
+      elem = servers;
+      while (elem && !status)
+       {
+         sargv = elem->q_data;
+         status = AddImapPartitions(sargv[1], &partitions);
+         elem = elem->q_forw;
+       }
+
+      if (partitions)
+       {
+         sargv = partitions->q_data;
+         server = strdup(sargv[NFS_NAME]);
+         partition = strdup(sargv[NFS_DIR]);
+       }
+    }
+  else
+    status = AddImapPartitions(server, &partitions);
+  partitions = QueueTop(partitions);
+
+  FreeQueue(servers);
+  if (status || !partitions)
+    {
+      if (!partitions)
+       com_err(program_name, 0, "No registered nfsphys on %s.", server);
+      else
+       FreeQueue(partitions);
+      free(server);
+      return (char *)SUB_ERROR;
+    }
+
+  /* Pick a partition */
+  while (!partition)
+    {
+      char **pargv = partitions->q_data;
+      partition = strdup(pargv[NFS_DIR]);
+      if (GetValueFromUser("Partition? ['?' for a list]", &partition) !=
+         SUB_NORMAL)
+       {
+         free(server);
+         free(partition);
+         FreeQueue(partitions);
+         return (char *)SUB_ERROR;
+       }
+
+      elem = partitions;
+      if (!strcmp(partition, "?"))
+       {
+         while (elem)
+           {
+             char **pargv = elem->q_data;
+             sprintf(temp_buf, "  %s (%s available, %d free)",
+                     pargv[NFS_DIR], pargv[NFS_SIZE],
+                     atoi(pargv[NFS_SIZE]) - atoi(pargv[NFS_ALLOC]));
+             Put_message(temp_buf);
+             elem = elem->q_forw;
+           }
+         free(partition);
+         partition = NULL;
+       }
+      else
+       {
+         while (elem)
+           {
+             char **pargv = elem->q_data;
+             if (!strcmp(partition, pargv[NFS_DIR]))
+               break;
+             elem = elem->q_forw;
+           }
+         if (!elem)
+           {
+             com_err(program_name, 0, "No such partition %s", partition);
+             free(partition);
+             partition = NULL;
+           }
+       }
+    }
+  FreeQueue(partitions);
+
+  fsname = malloc(strlen(user) + 4);
+  sprintf(fsname, "%s.po", user);
+  argv[FS_NAME] = fsname;
+  argv[FS_TYPE] = "IMAP";
+  argv[FS_MACHINE] = server;
+  argv[FS_PACK] = partition;
+  argv[FS_M_POINT] = "";
+  argv[FS_ACCESS] = "w";
+  argv[FS_COMMENTS] = "IMAP box";
+  argv[FS_OWNER] = user;
+  argv[FS_OWNERS] = "wheel";
+  argv[FS_CREATE] = "1";
+  argv[FS_L_TYPE] = "USER";
+
+  status = do_mr_query("add_filesys", 11, argv, NULL, NULL);
+  free(server);
+  free(partition);
+
+  if (status)
+    {
+      com_err(program_name, status, " creating IMAP filesys in CreateImapBox");
+      free(fsname);
+      return (char *)SUB_ERROR;
     }
-    Put_message("Could not get machines to choose from, quitting.");
-    return((char *) SUB_ERROR);
+
+  argv[Q_FILESYS] = fsname;
+  argv[Q_TYPE] = "USER";
+  argv[Q_NAME] = user;
+  argv[Q_QUOTA] = default_imap_quota;
+
+  status = do_mr_query("add_quota", 4, argv, NULL, NULL);
+  if (status)
+    {
+      com_err(program_name, status, " setting quota in CreateImapBox");
+      free(fsname);
+      return (char *)SUB_ERROR;
+    }
+  else
+    return fsname;
 }
 
 /*     Function Name: SetUserPOBox
@@ -160,90 +393,185 @@ char * local_user;
  *     Returns: DM_NORMAL.
  */
 
-int
-SetUserPOBox(argc, argv)
-int argc;
-char **argv;
+int SetUserPOBox(int argc, char **argv)
 {
-    register int status;
-    char *type, temp_buf[BUFSIZ], *local_user, *args[10], box[BUFSIZ];
-    char *temp_box;
-    local_user = argv[1];
-
-    if (!ValidName(local_user))
-       return(DM_NORMAL);
-    
-    (void) GetUserPOBox(argc, argv); /* print current info. */
-    
-    sprintf(temp_buf, "Assign %s a local PO Box (y/n)", local_user);
-    switch (YesNoQuestion(temp_buf, TRUE)) {
+  int status;
+  char *type, temp_buf[BUFSIZ], *local_user, *args[10], *box;
+  struct mqelem *top = NULL;
+
+  local_user = argv[1];
+  if (!ValidName(local_user))
+    return DM_NORMAL;
+
+  /* Print the current PO Box info */
+  switch ((status = do_mr_query("get_pobox", 1, argv + 1, StoreInfo, &top)))
+    {
+    case MR_SUCCESS:
+      sprintf(temp_buf, "Current pobox for user %s: \n", local_user);
+      Put_message("");
+      top = QueueTop(top);
+      Loop(top, PrintPOBox);   /* should only return 1 box. */
+      FreeQueue(top);
+      break;
+    case MR_NO_MATCH:
+      Put_message("This user has no P.O. Box.");
+      break;
+    default:
+      com_err(program_name, status, " in get_pobox.");
+      return DM_NORMAL;
+    }
+
+  Put_message("");
+
+  sprintf(temp_buf, "Assign %s a local PO Box (y/n)", local_user);
+  switch (YesNoQuestion(temp_buf, TRUE))
+    {
     case TRUE:
-       type = LOCAL_BOX;
-       switch (YesNoQuestion("Use Previous Local Box (y/n)", TRUE)) {
+      switch (YesNoQuestion("Use Previous Local Box (y/n)", TRUE))
+       {
        case TRUE:
-           switch (status = do_mr_query("set_pobox_pop", 1, 
-                                         &local_user, Scream, NULL)) {
+         switch ((status = do_mr_query("set_pobox_pop", 1,
+                                       &local_user, NULL, NULL)))
+           {
            case MR_SUCCESS:
-               return(DM_NORMAL);
+             return DM_NORMAL;
            case MR_MACHINE:
-               sprintf(temp_buf, "%s did not have a previous local PO Box.",
-                       local_user);
-               Put_message(temp_buf);
-               if ( (temp_box = GetNewLocalPOBox(local_user)) !=
-                       (char *) SUB_ERROR) {
-                   strcpy(box, temp_box);
-                   free(temp_box);
-               }
-               else
-                   return(DM_NORMAL);
-               break;
+             sprintf(temp_buf, "%s did not have a previous local PO Box.",
+                     local_user);
+             Put_message(temp_buf);
            default:
-               com_err(program_name, status, "in set_pobox_pop.");
-               return(DM_NORMAL);
+             com_err(program_name, status, " in set_pobox_pop.");
+             return DM_NORMAL;
            }
-           break;
+         /* Fall through from MR_MACHINE case. */
        case FALSE:
-           if ( (temp_box = GetNewLocalPOBox(local_user)) !=
-                       (char *) SUB_ERROR) {
-               strcpy(box, temp_box);
-               free(temp_box);
+         type = strdup("IMAP");
+         if (GetValueFromUser("Kind of Local PO Box?", &type) == SUB_ERROR)
+           {
+             free(type);
+             return DM_NORMAL;
+           }
+         if (!strcasecmp(type, "POP"))
+           {
+             free(type);
+             type = "POP";
+             if ((box = GetNewLocalPOBox(local_user)) == (char *) SUB_ERROR)
+               return DM_NORMAL;
+           }
+         else if (!strcasecmp(type, "IMAP"))
+           {
+             free(type);
+             type = "IMAP";
+             switch (YesNoQuestion("Create IMAP filesystem (y/n)", TRUE))
+               {
+               case TRUE:
+                 if ((box = CreateImapBox(local_user)) == (char *) SUB_ERROR)
+                   return DM_NORMAL;
+                 break;
+               case FALSE:
+                 box = malloc(strlen(local_user) + 4);
+                 sprintf(box, "%s.po", local_user);
+                 break;
+               default:
+                 return DM_NORMAL;
+               }
+           }
+         else if (!strcasecmp(type, "EXCHANGE"))
+           {
+             free(type);
+             type = "EXCHANGE";
+             box = "EXCHANGE.MIT.EDU";
+           }
+         else
+           {
+             sprintf(temp_buf, "Unknown local PO Box type %s", type);
+             Put_message(temp_buf);
+             free(type);
+             return DM_NORMAL;
            }
-           else
-               return(DM_NORMAL);
-           break;
+         break;
        default:
-           return(DM_NORMAL);
+         return DM_NORMAL;
        }
-       break;
+      break;
     case FALSE:
-       type = FOREIGN_BOX;
-       sprintf(temp_buf, "Set up a foreign PO Box for %s (y/n)", local_user);
-       switch( YesNoQuestion(temp_buf, TRUE)) {
+      type = "SMTP";
+      sprintf(temp_buf, "Set up a foreign PO Box for %s (y/n)", local_user);
+      switch (YesNoQuestion(temp_buf, TRUE))
+       {
        case TRUE:
-           if (!Prompt_input("Foreign PO Box for this user? ", box, BUFSIZ))
-               return(DM_NORMAL);
-           break;
+         if (!Prompt_input("Foreign PO Box for this user? ", temp_buf, BUFSIZ))
+           return DM_NORMAL;
+         if (mrcl_validate_pobox_smtp(local_user, temp_buf, &box) !=
+             MRCL_SUCCESS)
+           {
+             if (mrcl_get_message())
+               Put_message(mrcl_get_message());
+             return DM_NORMAL;
+           }
+         break;
        case FALSE:
        default:
-           return(DM_NORMAL);  /* ^C hit. */
+         return DM_NORMAL;     /* ^C hit. */
        }
-       break;
+      break;
     default:                   /* ^C hit. */
-       Put_message("Aborted.");
-       return(DM_NORMAL);
+      Put_message("Aborted.");
+      return DM_NORMAL;
     }
 
-    args[PO_NAME] = local_user;
-    args[PO_TYPE] = type;
-    args[PO_BOX] = box;
-    args[PO_END] = NULL;
-    if ( (status = do_mr_query("set_pobox", CountArgs(args), args, 
-                               Scream, NULL)) != MR_SUCCESS )
-       com_err(program_name, status, " in ChangeUserPOBox");
-    else
-       Put_message("PO Box assigned.");
-
-    return (DM_NORMAL);
+  args[PO_NAME] = local_user;
+  args[PO_TYPE] = type;
+  args[PO_BOX] = box;
+  args[3] = NULL;
+  if ((status = do_mr_query("set_pobox", CountArgs(args), args,
+                           NULL, NULL)))
+    com_err(program_name, status, " in ChangeUserPOBox");
+  else
+    Put_message("PO Box assigned.");
+  free(box);
+
+  return DM_NORMAL;
+}
+
+/*     Function Name: SplitUserPOBox
+ *     Description: Splits the user's PO Box between local and SMTP
+ *     Arguments: argc, argv - name of user in argv[1].
+ *     Returns: DM_NORMAL.
+ */
+
+int SplitUserPOBox(int argc, char **argv)
+{
+  char temp_buf[BUFSIZ], *args[3], *box;
+  int status;
+
+  if (!ValidName(argv[1]))
+    return DM_NORMAL;
+
+  if (!Prompt_input("Foreign PO Box for this user? ", temp_buf, BUFSIZ))
+    return DM_NORMAL;
+  if (mrcl_validate_pobox_smtp(argv[1], temp_buf, &box) !=
+      MRCL_SUCCESS)
+    {
+      if (mrcl_get_message())
+       Put_message(mrcl_get_message());
+      return DM_NORMAL;
+    }
+
+  args[0] = argv[1];
+  args[1] = "SPLIT";
+  args[2] = box;
+
+  status = do_mr_query("set_pobox", 3, args, NULL, NULL);
+  if (status == MR_MACHINE)
+    Put_message("User has no local PO Box--PO Box unchanged.");
+  else if (status)
+    com_err(program_name, status, " in SplitUserPOBox");
+  else
+    Put_message("PO Box split.");
+  free(box);
+
+  return DM_NORMAL;
 }
 
 /*     Function Name: RemoveUserPOBox
@@ -252,27 +580,24 @@ char **argv;
  *     Returns: DM_NORMAL.
  */
 
-/*ARGSUSED */
-int
-RemoveUserPOBox(argc, argv)
-int argc;
-char ** argv;
+int RemoveUserPOBox(int argc, char **argv)
 {
-    register int status;
-    char temp_buf[BUFSIZ];
-   
-    if (!ValidName(argv[1]))
-       return(DM_NORMAL);
-
-    sprintf(temp_buf,
-           "Are you sure that you want to remove %s's PO Box (y/n)", argv[1]);
-    
-    if (Confirm(temp_buf)) {
-       if ( (status = do_mr_query("delete_pobox", 1, argv + 1,
-                                   Scream, NULL)) != MR_SUCCESS)
-           com_err(program_name, status, "in delete_pobox.");
-       else
-           Put_message("PO Box removed.");
+  int status;
+  char temp_buf[BUFSIZ];
+
+  if (!ValidName(argv[1]))
+    return DM_NORMAL;
+
+  sprintf(temp_buf, "Are you sure that you want to remove %s's PO Box (y/n)",
+         argv[1]);
+
+  if (Confirm(temp_buf))
+    {
+      if ((status = do_mr_query("delete_pobox", 1, argv + 1,
+                               NULL, NULL)))
+       com_err(program_name, status, " in delete_pobox.");
+      else
+       Put_message("PO Box removed.");
     }
-    return(DM_NORMAL);
+  return DM_NORMAL;
 }
This page took 0.117929 seconds and 4 git commands to generate.